diff --git a/DEPS b/DEPS
index 1045ea2e..6a6b797 100644
--- a/DEPS
+++ b/DEPS
@@ -244,7 +244,7 @@
   #
   # CQ_INCLUDE_TRYBOTS=luci.chrome.try:lacros-amd64-generic-chrome-skylab
   # CQ_INCLUDE_TRYBOTS=luci.chrome.try:lacros-arm-generic-chrome-skylab
-  'lacros_sdk_version': '15672.0.0',
+  'lacros_sdk_version': '15673.0.0',
 
   # Generate location tag metadata to include in tests result data uploaded
   # to ResultDB. This isn't needed on some configs and the tool that generates
@@ -278,7 +278,7 @@
   # reclient CIPD package
   'reclient_package': 'infra/rbe/client/',
   # reclient CIPD package version
-  'reclient_version': 're_client_version:0.118.1.ae3c3be-gomaip',
+  'reclient_version': 're_client_version:0.120.1.f75cfb7-gomaip',
 
   # The path of the sysroots.json file.
   # This is used by vendor builds like Electron.
@@ -305,19 +305,19 @@
   # 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': '1b190e361c56e70d2c0e57fc9b56dc7af3a75fea',
+  'src_internal_revision': '373659c559effc4f18b0e2a1a8c5ec10c86fa316',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'ae6df7264c6eedbe988e05a12455c619e1a0f242',
+  'skia_revision': 'e653081ca3138dda1a9abaa9e90cdbf5ae980523',
   # 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': 'eb5ccbd8cda19af0d91ad55fb750637a3ec0f0b8',
+  'v8_revision': '6fc23a31385211f1e580eb2646f398576c5d3a80',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'f773a79fef3814af079b9648e9c4395c5a5123b3',
+  'angle_revision': 'aec4ffac02c0a093c7ff03d2069c4660ce552599',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -400,7 +400,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': 'c3e6c9af4f1260fce7879783b5fb8bbcf0b273bd',
+  'devtools_frontend_revision': '97d7a065030a8930d942dc0b3ee66e79e89c6c78',
   # 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.
@@ -424,7 +424,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'e30eacb0b6e8725e79c283908b7cb0c7f219ac44',
+  'dawn_revision': '18ac67fc72dfff47efec97c2a8bcf654a13a9e37',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -456,7 +456,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': '629e7ba7c964a62c97f6a882bffaec3c8490bb1e',
+  'nearby_revision': 'a83fbe3ebb47980e204c39cf23f20813e2048a2f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -824,12 +824,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'b0a7c7b4fab38799037256bba133600deda76ca8',
+    'a9deb4b2dbe57163358275cf16643421f9c56e8e',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + '83e3cf98ab4c33fb95bdc40dc55912f48d9e0178',
+    'url': Var('chromium_git') + '/website.git' + '@' + 'ca6f472417d974ec082d6f70523b4d3fc24b0c1b',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1183,7 +1183,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '65c1393ab1f7ae98bb8a60ca0cfb1ad5e2d57e9f',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '095fc4f448750165f1e6546410069fcdaceaf617',
       'condition': 'checkout_chromeos',
   },
 
@@ -1224,7 +1224,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'b32c6af7c0da8b57e81dd7217f1a519e3b8805e0',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'a3d37368696ffdbea916cb1114c64f06755a6f79',
     'condition': 'checkout_src_internal',
   },
 
@@ -1874,7 +1874,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '4e27db8161aec185923afa024f62870ef5a6442c',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '283a5fd7ec64d231b1369843ac24d18222ecfde7',
+    Var('webrtc_git') + '/src.git' + '@' + '2baa7ae9e03553da5da3b2c232431602a64fcd50',
 
   # 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.
@@ -2008,7 +2008,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'uvGAle7s0ijkqz00m7SbpvxiXf8bIyxajULN8gk8qEQC',
+        'version': 'KiR8FvdSkddQUE_SwQ0jwTbEMBVegZGkKTlmQuqPoUUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3977,7 +3977,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '2b12e66f237b926fad8f2eb19a5ef0a6d0154a9f',
+        'c1a3cc7cf94d42bccee1147e2299eaafec54bf12',
       'condition': 'checkout_src_internal',
   },
 
@@ -4031,7 +4031,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '6fedcb89fabbeccebd8c87753620098b0037fcfe',
+        '2f446279a877a5769e71bb6aa1433312173e9cc1',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 63b9785..02f8cf7 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -6,11 +6,9 @@
 
 import android.app.Application;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
-import android.content.res.AssetManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.SystemClock;
@@ -274,29 +272,7 @@
      * This must not be called until {@link #initialize(WebViewDelegate)} has set mWebViewDelegate.
      */
     public void addWebViewAssetPath(Context ctx) {
-        mWebViewDelegate.addWebViewAssetPath(new ContextWrapper(ctx) {
-            // In the Android framework (<= API level 23)
-            // ContextThemeWrapper provides an implementation of
-            // getResources() that may proxy to either the wrapped
-            // context or a newly constructed context, but it does not
-            // provide an implementation of getAssets() that overrides
-            // the ContextWrapper implementation that always proxies
-            // to the wrapped context. This means that getAssets() and
-            // getResources().getAssets() may potentially return
-            // different AssetManagers, confusing WebView.
-            //
-            // To work around this problem, we provide an additional
-            // wrapper here here to avoid calling the getAssets()
-            // proxy chain (which we cannot change because it is in
-            // WebView framework code).
-            //
-            // We should be able to remove this workaround once we
-            // drop support for API 23.
-            @Override
-            public AssetManager getAssets() {
-                return getResources().getAssets();
-            }
-        });
+        mWebViewDelegate.addWebViewAssetPath(ctx);
     }
 
     @SuppressWarnings("NoContextGetApplicationContext")
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 668f6e7..deaa2d0 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -595,6 +595,8 @@
     "events/keyboard_driven_event_rewriter.h",
     "events/peripheral_customization_event_rewriter.cc",
     "events/peripheral_customization_event_rewriter.h",
+    "events/prerewritten_event_forwarder.cc",
+    "events/prerewritten_event_forwarder.h",
     "events/select_to_speak_event_handler.cc",
     "events/select_to_speak_event_handler.h",
     "fast_ink/cursor/cursor_view.cc",
@@ -3248,6 +3250,7 @@
     "events/keyboard_capability_unittest.cc",
     "events/keyboard_driven_event_rewriter_unittest.cc",
     "events/peripheral_customization_event_rewriter_unittest.cc",
+    "events/prerewritten_event_forwarder_unittest.cc",
     "events/select_to_speak_event_handler_unittest.cc",
     "extended_desktop_unittest.cc",
     "fast_ink/fast_ink_host_frame_utils_unittest.cc",
@@ -3657,7 +3660,6 @@
     "system/unified/screen_capture_tray_item_view_unittest.cc",
     "system/unified/tasks_bubble_view_unittest.cc",
     "system/unified/tasks_combobox_model_unittest.cc",
-    "system/unified/unified_system_tray_controller_unittest.cc",
     "system/unified/unified_system_tray_unittest.cc",
     "system/unified/user_chooser_detailed_view_controller_unittest.cc",
     "system/update/update_notification_controller_unittest.cc",
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc
index e5dc0fe..9d14272 100644
--- a/ash/ash_prefs.cc
+++ b/ash/ash_prefs.cc
@@ -150,7 +150,6 @@
   SnoopingProtectionController::RegisterProfilePrefs(registry);
   TabletModeTuckEducation::RegisterProfilePrefs(registry);
   TouchDevicesController::RegisterProfilePrefs(registry, for_test);
-  UnifiedSystemTrayController::RegisterProfilePrefs(registry);
   UserEducationController::RegisterProfilePrefs(registry);
   MediaTray::RegisterProfilePrefs(registry);
   UsbPeripheralNotificationController::RegisterProfilePrefs(registry);
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 8582aa18..df193bd84 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -2489,12 +2489,6 @@
       <message name="IDS_ASH_DESKS_TEMPLATES_MANAGEMENT_STATUS_DESCRIPTION" desc="The text of the description under template item if the desks templates is shared by the administrator.">
         Shared by your administrator
       </message>
-      <message name="IDS_ASH_DESKS_TEMPLATES_LIBRARY_TEMPLATES_GRID_LABEL" desc="The label visible above template items in the saved desk library.">
-        Templates
-      </message>
-      <message name="IDS_ASH_DESKS_TEMPLATES_LIBRARY_SAVE_AND_RECALL_GRID_LABEL" desc="The label visible above the save and recall items in the saved desk library.">
-        Saved for later
-      </message>
       <message name="IDS_ASH_DESKS_TEMPLATES_LIBRARY_NO_TEMPLATES_OR_DESKS_LABEL" desc="The text that shows in the saved desk library when there are no saved desks or templates.">
         No saved desks or templates
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_LIBRARY_SAVE_AND_RECALL_GRID_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_LIBRARY_SAVE_AND_RECALL_GRID_LABEL.png.sha1
deleted file mode 100644
index 3cbe840..0000000
--- a/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_LIBRARY_SAVE_AND_RECALL_GRID_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-afbc646ed55870bbab30c89d480d38665fec7c66
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_LIBRARY_TEMPLATES_GRID_LABEL.png.sha1 b/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_LIBRARY_TEMPLATES_GRID_LABEL.png.sha1
deleted file mode 100644
index 71eee23..0000000
--- a/ash/ash_strings_grd/IDS_ASH_DESKS_TEMPLATES_LIBRARY_TEMPLATES_GRID_LABEL.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-eeaa7cf6df7ebf843f6e266e4be156193aaf6a2a
\ No newline at end of file
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 1f38829..205b11a 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -1140,9 +1140,6 @@
 inline constexpr char kSystemBluetoothAdapterEnabled[] =
     "ash.system.bluetooth.adapter_enabled";
 
-// Boolean pref to persist the expanded state of the system tray across reboots.
-inline constexpr char kSystemTrayExpanded[] = "ash.system_tray.expanded";
-
 // A boolean pref indicating whether the camera is allowed to be used.
 inline constexpr char kUserCameraAllowed[] = "ash.user.camera_allowed";
 
diff --git a/ash/controls/contextual_tooltip.cc b/ash/controls/contextual_tooltip.cc
index 0297585..8f68b56 100644
--- a/ash/controls/contextual_tooltip.cc
+++ b/ash/controls/contextual_tooltip.cc
@@ -62,6 +62,8 @@
       return "keyboard_backlight_wallpaper_color";
     case TooltipType::kTimeOfDayFeatureBanner:
       return "time_of_day_feature_banner";
+    case TooltipType::kTimeOfDayWallpaperDialog:
+      return "time_of_day_wallpaper_dialog";
   }
   return "invalid";
 }
@@ -133,7 +135,9 @@
       (type == TooltipType::kKeyboardBacklightColor &&
        success_count >= kSuccessLimitKeyboardBacklightColor) ||
       (type == TooltipType::kTimeOfDayFeatureBanner &&
-       success_count >= kSuccessLimitTimeOfDayFeatureBanner)) {
+       success_count >= kSuccessLimitTimeOfDayFeatureBanner) ||
+      (type == TooltipType::kTimeOfDayWallpaperDialog &&
+       success_count >= kSuccessLimitTimeOfDayWallpaperDialog)) {
     set_recheck_delay(base::TimeDelta());
     return false;
   }
diff --git a/ash/controls/contextual_tooltip.h b/ash/controls/contextual_tooltip.h
index 3861e7e..f05529ed 100644
--- a/ash/controls/contextual_tooltip.h
+++ b/ash/controls/contextual_tooltip.h
@@ -22,6 +22,7 @@
   kKeyboardBacklightColor,
   kKeyboardBacklightWallpaperColor,
   kTimeOfDayFeatureBanner,
+  kTimeOfDayWallpaperDialog,
 };
 
 // These values are persisted to logs. Entries should not be renumbered and
@@ -49,6 +50,7 @@
 constexpr int kSuccessLimitBackGesture = 1;
 constexpr int kSuccessLimitKeyboardBacklightColor = 1;
 constexpr int kSuccessLimitTimeOfDayFeatureBanner = 1;
+constexpr int kSuccessLimitTimeOfDayWallpaperDialog = 1;
 
 // Minimum time between showing contextual nudges to the user.
 constexpr base::TimeDelta kMinInterval = base::Days(1);
diff --git a/ash/events/OWNERS b/ash/events/OWNERS
index 35f1e123..7e0136f 100644
--- a/ash/events/OWNERS
+++ b/ash/events/OWNERS
@@ -2,3 +2,4 @@
 per-file select_to_speak_event_handler*=file://ash/accessibility/OWNERS
 per-file keyboard_capability*=zentaro@chromium.org,dpad@google.com
 per-file peripheral_customization*=zentaro@chromium.org,dpad@google.com
+per-file prerewritten_event_forwarder*=zentaro@chromium.org,dpad@google.com,jimmyxgong@chromium.org
diff --git a/ash/events/event_rewriter_controller_impl.cc b/ash/events/event_rewriter_controller_impl.cc
index 2103ed2..c5e6de7f 100644
--- a/ash/events/event_rewriter_controller_impl.cc
+++ b/ash/events/event_rewriter_controller_impl.cc
@@ -14,12 +14,14 @@
 #include "ash/events/accessibility_event_rewriter.h"
 #include "ash/events/keyboard_driven_event_rewriter.h"
 #include "ash/events/peripheral_customization_event_rewriter.h"
+#include "ash/events/prerewritten_event_forwarder.h"
 #include "ash/public/cpp/accessibility_event_rewriter_delegate.h"
 #include "ash/shell.h"
 #include "ash/system/input_device_settings/input_device_settings_controller_impl.h"
 #include "base/command_line.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/events/ash/keyboard_device_id_event_rewriter.h"
 #include "ui/events/event_sink.h"
 #include "ui/events/event_source.h"
@@ -74,7 +76,8 @@
 
   std::unique_ptr<PeripheralCustomizationEventRewriter>
       peripheral_customization_event_rewriter;
-  if (features::IsPeripheralCustomizationEnabled()) {
+  if (features::IsPeripheralCustomizationEnabled() ||
+      ::features::IsShortcutCustomizationEnabled()) {
     peripheral_customization_event_rewriter =
         std::make_unique<PeripheralCustomizationEventRewriter>(
             Shell::Get()->input_device_settings_controller());
@@ -82,6 +85,14 @@
         peripheral_customization_event_rewriter.get();
   }
 
+  std::unique_ptr<PrerewrittenEventForwarder> prerewritten_event_forwarder;
+  if (features::IsPeripheralCustomizationEnabled() ||
+      ::features::IsShortcutCustomizationEnabled()) {
+    prerewritten_event_forwarder =
+        std::make_unique<PrerewrittenEventForwarder>();
+    prerewritten_event_forwarder_ = prerewritten_event_forwarder.get();
+  }
+
   std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter =
       std::make_unique<AccessibilityEventRewriter>(
           event_rewriter_ash.get(), accessibility_event_rewriter_delegate);
@@ -89,7 +100,12 @@
 
   // EventRewriters are notified in the order they are added.
   AddEventRewriter(std::move(accessibility_event_rewriter));
-  if (features::IsPeripheralCustomizationEnabled()) {
+  if (features::IsPeripheralCustomizationEnabled() ||
+      ::features::IsShortcutCustomizationEnabled()) {
+    AddEventRewriter(std::move(prerewritten_event_forwarder));
+  }
+  if (features::IsPeripheralCustomizationEnabled() ||
+      ::features::IsShortcutCustomizationEnabled()) {
     AddEventRewriter(std::move(peripheral_customization_event_rewriter));
   }
   AddEventRewriter(std::move(keyboard_driven_event_rewriter));
diff --git a/ash/events/event_rewriter_controller_impl.h b/ash/events/event_rewriter_controller_impl.h
index d0fb0eb5..bd6679b8 100644
--- a/ash/events/event_rewriter_controller_impl.h
+++ b/ash/events/event_rewriter_controller_impl.h
@@ -23,6 +23,7 @@
 
 class AccessibilityEventRewriter;
 class KeyboardDrivenEventRewriter;
+class PrerewrittenEventForwarder;
 
 // Owns ui::EventRewriters and ensures that they are added to each root window
 // EventSource, current and future, in the order that they are added to this.
@@ -74,6 +75,8 @@
       accessibility_event_rewriter_ = nullptr;
   raw_ptr<PeripheralCustomizationEventRewriter, ExperimentalAsh>
       peripheral_customization_event_rewriter_ = nullptr;
+  raw_ptr<PrerewrittenEventForwarder, ExperimentalAsh>
+      prerewritten_event_forwarder_ = nullptr;
   raw_ptr<KeyboardDrivenEventRewriter, ExperimentalAsh>
       keyboard_driven_event_rewriter_ = nullptr;
   raw_ptr<ui::EventRewriterAsh, ExperimentalAsh> event_rewriter_ash_ = nullptr;
diff --git a/ash/events/peripheral_customization_event_rewriter.cc b/ash/events/peripheral_customization_event_rewriter.cc
index 2e33854..b2cb767 100644
--- a/ash/events/peripheral_customization_event_rewriter.cc
+++ b/ash/events/peripheral_customization_event_rewriter.cc
@@ -19,6 +19,7 @@
 #include "base/containers/fixed_flat_map.h"
 #include "base/notreached.h"
 #include "base/ranges/algorithm.h"
+#include "ui/base/ui_base_features.h"
 #include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
@@ -659,7 +660,8 @@
 ui::EventDispatchDetails PeripheralCustomizationEventRewriter::RewriteEvent(
     const ui::Event& event,
     const Continuation continuation) {
-  DCHECK(features::IsPeripheralCustomizationEnabled());
+  DCHECK(features::IsPeripheralCustomizationEnabled() ||
+         ::features::IsShortcutCustomizationEnabled());
 
   if (event.IsMouseEvent()) {
     return RewriteMouseEvent(*event.AsMouseEvent(), continuation);
diff --git a/ash/events/prerewritten_event_forwarder.cc b/ash/events/prerewritten_event_forwarder.cc
new file mode 100644
index 0000000..f247a0e
--- /dev/null
+++ b/ash/events/prerewritten_event_forwarder.cc
@@ -0,0 +1,24 @@
+// 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 "ash/events/prerewritten_event_forwarder.h"
+
+#include "ash/constants/ash_features.h"
+#include "base/check.h"
+#include "ui/base/ui_base_features.h"
+
+namespace ash {
+
+PrerewrittenEventForwarder::PrerewrittenEventForwarder() = default;
+PrerewrittenEventForwarder::~PrerewrittenEventForwarder() = default;
+
+ui::EventDispatchDetails PrerewrittenEventForwarder::RewriteEvent(
+    const ui::Event& event,
+    const Continuation continuation) {
+  DCHECK(::features::IsShortcutCustomizationEnabled() ||
+         features::IsPeripheralCustomizationEnabled());
+  return SendEvent(continuation, &event);
+}
+
+}  // namespace ash
diff --git a/ash/events/prerewritten_event_forwarder.h b/ash/events/prerewritten_event_forwarder.h
new file mode 100644
index 0000000..c814599
--- /dev/null
+++ b/ash/events/prerewritten_event_forwarder.h
@@ -0,0 +1,32 @@
+// 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 ASH_EVENTS_PREREWRITTEN_EVENT_FORWARDER_H_
+#define ASH_EVENTS_PREREWRITTEN_EVENT_FORWARDER_H_
+
+#include "ash/ash_export.h"
+#include "ui/events/event.h"
+#include "ui/events/event_rewriter.h"
+
+namespace ash {
+
+// PrerewrittenEventForwarder listens to input events and supplies the raw event
+// to clients that want events before any subsequent event rewrites.
+class ASH_EXPORT PrerewrittenEventForwarder : public ui::EventRewriter {
+ public:
+  PrerewrittenEventForwarder();
+  PrerewrittenEventForwarder(const PrerewrittenEventForwarder&) = delete;
+  PrerewrittenEventForwarder& operator=(const PrerewrittenEventForwarder&) =
+      delete;
+  ~PrerewrittenEventForwarder() override;
+
+  // ui::EventRewriter:
+  ui::EventDispatchDetails RewriteEvent(
+      const ui::Event& event,
+      const Continuation continuation) override;
+};
+
+}  // namespace ash
+
+#endif  // ASH_EVENTS_PREREWRITTEN_EVENT_FORWARDER_H_
diff --git a/ash/events/prerewritten_event_forwarder_unittest.cc b/ash/events/prerewritten_event_forwarder_unittest.cc
new file mode 100644
index 0000000..9ba87918
--- /dev/null
+++ b/ash/events/prerewritten_event_forwarder_unittest.cc
@@ -0,0 +1,39 @@
+// 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 "ash/events/prerewritten_event_forwarder.h"
+
+#include "ash/test/ash_test_base.h"
+
+namespace ash {
+
+class PrerewrittenEventForwarderTest : public AshTestBase {
+ public:
+  PrerewrittenEventForwarderTest() = default;
+  PrerewrittenEventForwarderTest(const PrerewrittenEventForwarderTest&) =
+      delete;
+  PrerewrittenEventForwarderTest& operator=(
+      const PrerewrittenEventForwarderTest&) = delete;
+  ~PrerewrittenEventForwarderTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    AshTestBase::SetUp();
+    rewriter_ = std::make_unique<PrerewrittenEventForwarder>();
+  }
+
+  void TearDown() override {
+    rewriter_.reset();
+    AshTestBase::TearDown();
+  }
+
+ protected:
+  std::unique_ptr<PrerewrittenEventForwarder> rewriter_;
+};
+
+TEST_F(PrerewrittenEventForwarderTest, InitializationTest) {
+  EXPECT_NE(rewriter_.get(), nullptr);
+}
+
+}  // namespace ash
diff --git a/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.cc b/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.cc
index a836416..93cb1b96 100644
--- a/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.cc
+++ b/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.cc
@@ -40,6 +40,19 @@
   is_connected_ = is_connected;
 }
 
+void FakeFastPairGattServiceClient::ReadModelIdAsync(
+    base::OnceCallback<void(
+        absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+        const std::vector<uint8_t>& value)> callback) {
+  read_model_id_callback_ = std::move(callback);
+}
+
+void FakeFastPairGattServiceClient::RunReadModelIdCallback(
+    absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+    const std::vector<uint8_t>& value) {
+  std::move(read_model_id_callback_).Run(error_code, value);
+}
+
 void FakeFastPairGattServiceClient::WriteRequestAsync(
     uint8_t message_type,
     uint8_t flags,
diff --git a/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.h b/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.h
index 52f2e19..cd5bc8b8 100644
--- a/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.h
+++ b/ash/quick_pair/fast_pair_handshake/fake_fast_pair_gatt_service_client.h
@@ -36,6 +36,12 @@
   bool IsConnected() override;
   void SetConnected(bool is_connected);
 
+  void ReadModelIdAsync(
+      base::OnceCallback<
+          void(absl::optional<device::BluetoothGattService::GattErrorCode>
+                   error_code,
+               const std::vector<uint8_t>& value)> callback) override;
+
   void WriteRequestAsync(uint8_t message_type,
                          uint8_t flags,
                          const std::string& provider_address,
@@ -68,6 +74,10 @@
   void RunOnGattClientInitializedCallback(
       absl::optional<PairFailure> failure = absl::nullopt);
 
+  void RunReadModelIdCallback(
+      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value);
+
   void RunWriteResponseCallback(
       std::vector<uint8_t> data,
       absl::optional<PairFailure> failure = absl::nullopt);
@@ -86,6 +96,10 @@
   bool is_connected_ = false;
   base::OnceCallback<void(absl::optional<PairFailure>)>
       on_initialized_callback_;
+  base::OnceCallback<void(
+      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value)>
+      read_model_id_callback_;
   base::OnceCallback<void(std::vector<uint8_t>, absl::optional<PairFailure>)>
       key_based_write_response_callback_;
   base::OnceCallback<void(std::vector<uint8_t>, absl::optional<PairFailure>)>
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client.h b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client.h
index 3ebfcea..19e8668 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client.h
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client.h
@@ -23,6 +23,15 @@
   ~FastPairGattServiceClient() override = default;
   virtual device::BluetoothRemoteGattService* gatt_service() = 0;
 
+  // Gets ModelID from model ID characteristic. Upon successful completion
+  // |error_code| will not have a value and |value| may be used. When
+  // unsuccessful |error_code| will have a value and |value| must be ignored.
+  virtual void ReadModelIdAsync(
+      base::OnceCallback<
+          void(absl::optional<device::BluetoothGattService::GattErrorCode>
+                   error_code,
+               const std::vector<uint8_t>& value)> callback) = 0;
+
   // Constructs a data vector based on the message type, flags, provider
   // address, and seekers address. Starts a notify session for key based
   // Pairing. Once the notify session has been started, the message data will be
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc
index 5199785..4f9e16a 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.cc
@@ -26,6 +26,9 @@
 
 // We have two UUID possibilities for each characteristic because they changed
 // across different Fast Pair versions.
+const device::BluetoothUUID kModelIDCharacteristicUuidV1("1233");
+const device::BluetoothUUID kModelIDCharacteristicUuidV2(
+    "FE2C1233-8366-4814-8EB0-01DE32100BEA");
 const device::BluetoothUUID kKeyBasedCharacteristicUuidV1("1234");
 const device::BluetoothUUID kKeyBasedCharacteristicUuidV2(
     "FE2C1234-8366-4814-8EB0-01DE32100BEA");
@@ -580,6 +583,17 @@
   // session for it later.
   account_key_characteristic_ = account_key_characteristics[0];
 
+  // The model ID characteristic is required for retroactive pairing for BLE HID
+  // devices
+  auto model_id_characteristics = GetCharacteristicsByUUIDs(
+      kModelIDCharacteristicUuidV1, kModelIDCharacteristicUuidV2);
+  if (model_id_characteristics.empty()) {
+    CD_LOG(WARNING, Feature::FP)
+        << __func__ << ": Failed to discover Model ID characteristic.";
+  } else {
+    model_id_characteristic_ = model_id_characteristics[0];
+  }
+
   auto additional_data_characteristics = GetCharacteristicsByUUIDs(
       kAdditionalDataCharacteristicUuidV1, kAdditionalDataCharacteristicUuidV2);
 
@@ -723,6 +737,22 @@
   return gatt_connection_ && gatt_connection_->IsConnected();
 }
 
+void FastPairGattServiceClientImpl::ReadModelIdAsync(
+    base::OnceCallback<void(
+        absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+        const std::vector<uint8_t>& value)> callback) {
+  DCHECK(is_initialized_);
+
+  if (!model_id_characteristic_) {
+    std::move(callback).Run(
+        device::BluetoothGattService::GattErrorCode::kNotSupported,
+        std::vector<uint8_t>{});
+    return;
+  }
+
+  model_id_characteristic_->ReadRemoteCharacteristic(std::move(callback));
+}
+
 void FastPairGattServiceClientImpl::WriteRequestAsync(
     uint8_t message_type,
     uint8_t flags,
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h
index 4e63d10..3fcab13 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl.h
@@ -70,6 +70,12 @@
 
   bool IsConnected() override;
 
+  void ReadModelIdAsync(
+      base::OnceCallback<
+          void(absl::optional<device::BluetoothGattService::GattErrorCode>
+                   error_code,
+               const std::vector<uint8_t>& value)> callback) override;
+
   void WriteRequestAsync(uint8_t message_type,
                          uint8_t flags,
                          const std::string& provider_address,
@@ -259,6 +265,9 @@
 
   raw_ptr<device::BluetoothRemoteGattCharacteristic,
           DanglingUntriaged | ExperimentalAsh>
+      model_id_characteristic_ = nullptr;
+  raw_ptr<device::BluetoothRemoteGattCharacteristic,
+          DanglingUntriaged | ExperimentalAsh>
       key_based_characteristic_ = nullptr;
   raw_ptr<device::BluetoothRemoteGattCharacteristic,
           DanglingUntriaged | ExperimentalAsh>
diff --git a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl_unittest.cc b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl_unittest.cc
index 66750eb7..0329cc9 100644
--- a/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl_unittest.cc
+++ b/ash/quick_pair/fast_pair_handshake/fast_pair_gatt_service_client_impl_unittest.cc
@@ -94,8 +94,12 @@
 const std::string kUUIDString2 = "passkey";
 const std::string kUUIDString3 = "accountkey";
 const std::string kUUIDString4 = "additional data";
+const std::string kUUIDString5 = "model id";
 const device::BluetoothUUID kNonFastPairUuid("0xFE2B");
 
+const device::BluetoothUUID kModelIDCharacteristicUuid1("1233");
+const device::BluetoothUUID kModelIDCharacteristicUuid2(
+    "FE2C1233-8366-4814-8EB0-01DE32100BEA");
 const device::BluetoothUUID kKeyBasedCharacteristicUuid1("1234");
 const device::BluetoothUUID kKeyBasedCharacteristicUuid2(
     "FE2C1234-8366-4814-8EB0-01DE32100BEA");
@@ -491,6 +495,15 @@
     gatt_service_->AddMockCharacteristic(
         std::move(fake_account_key_characteristic));
 
+    if (!model_id_char_error_) {
+      auto fake_model_id_characteristic =
+          std::make_unique<FakeBluetoothGattCharacteristic>(
+              gatt_service_.get(), kUUIDString5, kModelIDCharacteristicUuid2,
+              kProperties, kPermissions);
+      gatt_service_->AddMockCharacteristic(
+          std::move(fake_model_id_characteristic));
+    }
+
     auto fake_additional_data_characteristic =
         std::make_unique<FakeBluetoothGattCharacteristic>(
             gatt_service_.get(), kUUIDString4,
@@ -540,6 +553,10 @@
     keybased_notify_session_error_ = keybased_notify_session_error;
   }
 
+  void SetModelIdCharacteristicError(bool model_id_char_error) {
+    model_id_char_error_ = model_id_char_error;
+  }
+
   void InitializedTestCallback(absl::optional<PairFailure> failure) {
     initalized_failure_ = failure;
   }
@@ -548,6 +565,12 @@
     return initalized_failure_;
   }
 
+  void ReadModelIdCallback(
+      absl::optional<device::BluetoothGattService::GattErrorCode> error_code,
+      const std::vector<uint8_t>& value) {
+    read_failure_ = error_code;
+  }
+
   void WriteTestCallback(std::vector<uint8_t> response,
                          absl::optional<PairFailure> failure) {
     write_failure_ = failure;
@@ -569,6 +592,11 @@
     return write_failure_;
   }
 
+  absl::optional<device::BluetoothGattService::GattErrorCode>
+  GetReadCallbackResult() {
+    return read_failure_;
+  }
+
   void SetPasskeyNotifySessionTimeout(bool timeout) {
     passkey_notify_session_timeout_ = timeout;
   }
@@ -598,6 +626,12 @@
     task_environment_.FastForwardBy(kAllGattRetriesPeriod);
   }
 
+  void ReadModelId() {
+    gatt_service_client_->ReadModelIdAsync(base::BindRepeating(
+        &::ash::quick_pair::FastPairGattServiceClientTest::ReadModelIdCallback,
+        weak_ptr_factory_.GetWeakPtr()));
+  }
+
   void WriteRequestToKeyBased() {
     gatt_service_client_->WriteRequestAsync(
         kMessageType, kFlags, kProviderAddress, kSeekersAddress,
@@ -695,11 +729,14 @@
   bool passkey_write_error_ = false;
   bool passkey_write_timeout_ = false;
   bool write_account_key_timeout_ = false;
+  bool model_id_char_error_ = false;
 
   absl::optional<PairFailure> initalized_failure_;
   absl::optional<PairFailure> write_failure_;
   absl::optional<PairFailure> additional_data_failure_;
 
+  absl::optional<device::BluetoothGattService::GattErrorCode> read_failure_;
+
   std::unique_ptr<FakeBluetoothDevice> unique_fake_bt_device_;
   std::unique_ptr<FakeBluetoothGattCharacteristic>
       fake_key_based_characteristic_;
@@ -1291,6 +1328,30 @@
   WritePersonalizedName(empty);
 }
 
+TEST_F(FastPairGattServiceClientTest, SuccessfulReadModelId) {
+  SuccessfulGattConnectionSetUp();
+  FastForwardTimeByGattDisconnectCoolOff();
+  NotifyGattDiscoveryCompleteForService(
+      ash::quick_pair::kFastPairBluetoothUuid);
+  EXPECT_EQ(GetInitializedCallbackResult(), absl::nullopt);
+  EXPECT_TRUE(ServiceIsSet());
+  ReadModelId();
+  EXPECT_EQ(GetReadCallbackResult(), absl::nullopt);
+}
+
+TEST_F(FastPairGattServiceClientTest, FailedReadModelId) {
+  SetModelIdCharacteristicError(true);
+  SuccessfulGattConnectionSetUp();
+  FastForwardTimeByGattDisconnectCoolOff();
+  NotifyGattDiscoveryCompleteForService(
+      ash::quick_pair::kFastPairBluetoothUuid);
+  EXPECT_EQ(GetInitializedCallbackResult(), absl::nullopt);
+  EXPECT_TRUE(ServiceIsSet());
+  ReadModelId();
+  EXPECT_EQ(GetReadCallbackResult(),
+            device::BluetoothGattService::GattErrorCode::kNotSupported);
+}
+
 // Regression test for b/300596153
 TEST_F(FastPairGattServiceClientTest,
        NoCrashWhenGattDiscoveryCompleteForServiceCalledTwice) {
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index b9b4cd2..e664e027 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -999,6 +999,12 @@
     absl::optional<OverviewStartAction> action,
     absl::optional<OverviewEnterExitType> type,
     WindowSnapActionSource snap_action_source) {
+  if (!OverviewController::Get()->CanEnterOverview()) {
+    // If we can't start overview, we shouldn't start split view overview
+    // either. This can happen when we restore snap state after exiting
+    // overview.
+    return;
+  }
   split_view_overview_session_ =
       std::make_unique<SplitViewOverviewSession>(window, snap_action_source);
   split_view_overview_session_->Init(action, type);
diff --git a/ash/system/phonehub/phone_hub_tray.cc b/ash/system/phonehub/phone_hub_tray.cc
index 2199e963..00e282d 100644
--- a/ash/system/phonehub/phone_hub_tray.cc
+++ b/ash/system/phonehub/phone_hub_tray.cc
@@ -54,6 +54,7 @@
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/border.h"
+#include "ui/views/controls/button/button_controller.h"
 #include "ui/views/controls/image_view.h"
 
 namespace ash {
@@ -97,6 +98,11 @@
   if (features::IsEcheSWAEnabled()) {
     auto eche_icon = std::make_unique<views::ImageButton>(base::BindRepeating(
         &PhoneHubTray::EcheIconActivated, weak_factory_.GetWeakPtr()));
+    eche_icon->SetButtonController(std::make_unique<views::ButtonController>(
+        /*views::Button*=*/eche_icon.get(),
+        std::make_unique<TrayBackgroundView::TrayButtonControllerDelegate>(
+            /*views::Button*=*/eche_icon.get(),
+            TrayBackgroundViewCatalogName::kPhoneHub)));
     eche_icon->SetImageVerticalAlignment(
         views::ImageButton::VerticalAlignment::ALIGN_MIDDLE);
     eche_icon->SetImageHorizontalAlignment(
@@ -112,6 +118,11 @@
   }
   auto icon = std::make_unique<views::ImageButton>(base::BindRepeating(
       &PhoneHubTray::PhoneHubIconActivated, weak_factory_.GetWeakPtr()));
+  icon->SetButtonController(std::make_unique<views::ButtonController>(
+      /*views::Button*=*/icon.get(),
+      std::make_unique<TrayBackgroundView::TrayButtonControllerDelegate>(
+          /*views::Button*=*/icon.get(),
+          TrayBackgroundViewCatalogName::kPhoneHub)));
   icon->SetFocusBehavior(FocusBehavior::NEVER);
   icon->SetTooltipText(
       l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TRAY_ACCESSIBLE_NAME));
diff --git a/ash/system/phonehub/phone_hub_tray.h b/ash/system/phonehub/phone_hub_tray.h
index c8a4b8b..04bb704 100644
--- a/ash/system/phonehub/phone_hub_tray.h
+++ b/ash/system/phonehub/phone_hub_tray.h
@@ -124,8 +124,9 @@
                                      bool aborted) override;
 
  private:
-  FRIEND_TEST_ALL_PREFIXES(PhoneHubTrayTest, SafeAccessToHeaderView);
   FRIEND_TEST_ALL_PREFIXES(PhoneHubTrayTest, EcheIconActivatesCallback);
+  FRIEND_TEST_ALL_PREFIXES(PhoneHubTrayTest, SafeAccessToHeaderView);
+  FRIEND_TEST_ALL_PREFIXES(PhoneHubTrayTest, TrayPressedMetrics);
 
   // TrayBubbleView::Delegate:
   std::u16string GetAccessibleNameForBubble() override;
diff --git a/ash/system/phonehub/phone_hub_tray_unittest.cc b/ash/system/phonehub/phone_hub_tray_unittest.cc
index b1ca72e..d4bd242b 100644
--- a/ash/system/phonehub/phone_hub_tray_unittest.cc
+++ b/ash/system/phonehub/phone_hub_tray_unittest.cc
@@ -19,6 +19,7 @@
 #include "ash/test/ash_test_base.h"
 #include "base/memory/raw_ptr.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
@@ -45,6 +46,8 @@
     phonehub::MultideviceFeatureAccessManager::AccessProhibitedReason;
 
 constexpr base::TimeDelta kConnectingViewGracePeriod = base::Seconds(40);
+constexpr char kTrayBackgroundViewHistogramName[] =
+    "Ash.StatusArea.TrayBackgroundView.Pressed";
 const std::string kPhoneHubNudgeId = "PhoneHubNudge";
 
 // A mock implementation of |NewWindowDelegate| for use in tests.
@@ -141,6 +144,8 @@
     return phone_hub_tray_->content_view_for_testing();
   }
 
+  PhoneHubTray* phone_hub_tray() { return phone_hub_tray_; }
+
   MultideviceFeatureOptInView* multidevice_feature_opt_in_view() {
     return static_cast<MultideviceFeatureOptInView*>(bubble_view()->GetViewByID(
         PhoneHubViewID::kMultideviceFeatureOptInView));
@@ -865,4 +870,19 @@
   EXPECT_TRUE(launched_app_window);
 }
 
+// Makes sure metrics are recorded for the phone hub tray or any nested button
+// being pressed.
+TEST_F(PhoneHubTrayTest, TrayPressedMetrics) {
+  base::HistogramTester histogram_tester;
+
+  LeftClickOn(phone_hub_tray());
+  histogram_tester.ExpectTotalCount(kTrayBackgroundViewHistogramName, 1);
+
+  LeftClickOn(phone_hub_tray()->icon_);
+  histogram_tester.ExpectTotalCount(kTrayBackgroundViewHistogramName, 2);
+
+  LeftClickOn(phone_hub_tray()->eche_icon_);
+  histogram_tester.ExpectTotalCount(kTrayBackgroundViewHistogramName, 3);
+}
+
 }  // namespace ash
diff --git a/ash/system/toast/system_toast_view.cc b/ash/system/toast/system_toast_view.cc
index 11b8df0..071e223 100644
--- a/ash/system/toast/system_toast_view.cc
+++ b/ash/system/toast/system_toast_view.cc
@@ -6,9 +6,12 @@
 
 #include <string>
 
+#include "ash/accessibility/accessibility_controller_impl.h"
+#include "ash/accessibility/scoped_a11y_override_window_setter.h"
 #include "ash/public/cpp/ash_view_ids.h"
 #include "ash/public/cpp/style/color_provider.h"
 #include "ash/public/cpp/system/toast_data.h"
+#include "ash/shell.h"
 #include "ash/style/ash_color_id.h"
 #include "ash/style/pill_button.h"
 #include "ash/style/system_shadow.h"
@@ -42,7 +45,9 @@
 
 }  // namespace
 
-SystemToastView::SystemToastView(const ToastData& toast_data) {
+SystemToastView::SystemToastView(const ToastData& toast_data)
+    : scoped_a11y_overrider_(
+          std::make_unique<ScopedA11yOverrideWindowSetter>()) {
   // Paint to layer so the background can be transparent.
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
@@ -84,7 +89,7 @@
 
   const bool has_button = !toast_data.dismiss_text.empty();
   if (has_button) {
-    AddChildView(
+    dismiss_button_ = AddChildView(
         views::Builder<PillButton>()
             .SetID(VIEW_ID_TOAST_BUTTON)
             .SetCallback(std::move(toast_data.dismiss_callback))
@@ -118,6 +123,36 @@
 
 SystemToastView::~SystemToastView() = default;
 
+bool SystemToastView::ToggleA11yFocus() {
+  if (!dismiss_button_ ||
+      !Shell::Get()->accessibility_controller()->spoken_feedback().enabled()) {
+    return false;
+  }
+
+  auto* focus_ring = views::FocusRing::Get(dismiss_button_);
+  focus_ring->SetOutsetFocusRingDisabled(true);
+  focus_ring->SetHasFocusPredicate(base::BindRepeating(
+      [](const SystemToastView* toast_view, const views::View* view) {
+        return toast_view->is_dismiss_button_highlighted_;
+      },
+      base::Unretained(this)));
+
+  is_dismiss_button_highlighted_ = !is_dismiss_button_highlighted_;
+  scoped_a11y_overrider_->MaybeUpdateA11yOverrideWindow(
+      is_dismiss_button_highlighted_
+          ? dismiss_button_->GetWidget()->GetNativeWindow()
+          : nullptr);
+
+  if (is_dismiss_button_highlighted_) {
+    dismiss_button_->NotifyAccessibilityEvent(ax::mojom::Event::kSelection,
+                                              true);
+  }
+
+  focus_ring->SetVisible(is_dismiss_button_highlighted_);
+  focus_ring->SchedulePaint();
+  return is_dismiss_button_highlighted_;
+}
+
 void SystemToastView::AddedToWidget() {
   shadow_->ObserveColorProviderSource(GetWidget());
 
diff --git a/ash/system/toast/system_toast_view.h b/ash/system/toast/system_toast_view.h
index 759b5f11..9da5a94 100644
--- a/ash/system/toast/system_toast_view.h
+++ b/ash/system/toast/system_toast_view.h
@@ -10,9 +10,14 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/layout/flex_layout_view.h"
 
+namespace views {
+class LabelButton;
+}  // namespace views
+
 namespace ash {
 
 struct ToastData;
+class ScopedA11yOverrideWindowSetter;
 class SystemShadow;
 
 // The System Toast view. (go/toast-style-spec)
@@ -28,12 +33,30 @@
   SystemToastView& operator=(const SystemToastView&) = delete;
   ~SystemToastView() override;
 
+  bool is_dismiss_button_highlighted() const {
+    return is_dismiss_button_highlighted_;
+  }
+
+  views::LabelButton* dismiss_button() const { return dismiss_button_; }
+
+  // Returns true if there's a button and it was highlighted for accessibility.
+  bool ToggleA11yFocus();
+
  private:
+  // Owned by the views hierarchy.
+  raw_ptr<views::LabelButton> dismiss_button_ = nullptr;
+
   std::unique_ptr<SystemShadow> shadow_;
 
   // views::View:
   void AddedToWidget() override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
+
+  bool is_dismiss_button_highlighted_ = false;
+
+  // Updates the current a11y override window when the dismiss button is being
+  // highlighted.
+  std::unique_ptr<ScopedA11yOverrideWindowSetter> scoped_a11y_overrider_;
 };
 
 }  // namespace ash
diff --git a/ash/system/toast/toast_manager_impl.cc b/ash/system/toast/toast_manager_impl.cc
index 4615ae7d..2a198c55 100644
--- a/ash/system/toast/toast_manager_impl.cc
+++ b/ash/system/toast/toast_manager_impl.cc
@@ -297,11 +297,8 @@
   auto& new_overlay = root_window_to_overlay_[root_window];
   DCHECK(!new_overlay);
   DCHECK(current_toast_data_);
-  new_overlay = std::make_unique<ToastOverlay>(
-      this, current_toast_data_->text, current_toast_data_->dismiss_text,
-      *current_toast_data_->leading_icon, current_toast_data_->duration,
-      current_toast_data_->persist_on_hover, root_window,
-      current_toast_data_->dismiss_callback);
+  new_overlay =
+      std::make_unique<ToastOverlay>(this, *current_toast_data_, root_window);
   new_overlay->Show(true);
 
   // We only want to record this value when the first instance of the toast is
diff --git a/ash/system/toast/toast_overlay.cc b/ash/system/toast/toast_overlay.cc
index 6fc94b2..16b1603 100644
--- a/ash/system/toast/toast_overlay.cc
+++ b/ash/system/toast/toast_overlay.cc
@@ -18,7 +18,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/pill_button.h"
-#include "ash/style/system_toast_style.h"
+#include "ash/system/toast/system_toast_view.h"
 #include "ash/wm/work_area_insets.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
@@ -146,26 +146,22 @@
 ///////////////////////////////////////////////////////////////////////////////
 //  ToastOverlay
 ToastOverlay::ToastOverlay(Delegate* delegate,
-                           const std::u16string& text,
-                           const std::u16string& dismiss_text,
-                           const gfx::VectorIcon& leading_icon,
-                           base::TimeDelta duration,
-                           bool persist_on_hover,
-                           aura::Window* root_window,
-                           base::RepeatingClosure dismiss_callback)
+                           ToastData& toast_data,
+                           aura::Window* root_window)
     : delegate_(delegate),
-      text_(text),
-      dismiss_text_(dismiss_text),
+      text_(toast_data.text),
+      dismiss_text_(toast_data.dismiss_text),
       overlay_widget_(new views::Widget),
-      overlay_view_(new SystemToastStyle(
-          base::BindRepeating(&ToastOverlay::OnButtonClicked,
-                              base::Unretained(this)),
-          text,
-          dismiss_text,
-          leading_icon)),
       display_observer_(std::make_unique<ToastDisplayObserver>(this)),
       root_window_(root_window),
-      dismiss_callback_(std::move(dismiss_callback)) {
+      dismiss_callback_(std::move(toast_data.dismiss_callback)) {
+  // TODO(b/303253656): Refactor toast dismiss callback so it does not have to
+  // bounce from the view back to ToastOverlay.
+  // The provided `toast_data`'s callback was stored in `dismiss_callback_`.
+  toast_data.dismiss_callback = base::BindRepeating(
+      &ToastOverlay::OnButtonClicked, base::Unretained(this));
+  overlay_view_ = std::make_unique<SystemToastView>(toast_data);
+
   views::Widget::InitParams params;
   params.type = views::Widget::InitParams::TYPE_POPUP;
   params.name = "ToastOverlay";
@@ -189,7 +185,8 @@
 
   // Only toasts that expire should be able to persist on hover (i.e. toasts
   // with infinite duration persist regardless of hover).
-  if (persist_on_hover && (duration != ToastData::kInfiniteDuration)) {
+  if (toast_data.persist_on_hover &&
+      (toast_data.duration != ToastData::kInfiniteDuration)) {
     hover_observer_ = std::make_unique<ToastHoverObserver>(
         overlay_window, base::BindRepeating(&ToastOverlay::OnHoverStateChanged,
                                             base::Unretained(this)));
diff --git a/ash/system/toast/toast_overlay.h b/ash/system/toast/toast_overlay.h
index 0de4948..a0c7ac9b 100644
--- a/ash/system/toast/toast_overlay.h
+++ b/ash/system/toast/toast_overlay.h
@@ -10,6 +10,7 @@
 
 #include "ash/ash_export.h"
 #include "ash/public/cpp/keyboard/keyboard_controller_observer.h"
+#include "ash/public/cpp/system/toast_data.h"
 #include "ash/shelf/shelf_observer.h"
 #include "ash/system/unified/unified_system_tray.h"
 #include "base/functional/callback.h"
@@ -37,7 +38,7 @@
 namespace ash {
 
 class ToastManagerImplTest;
-class SystemToastStyle;
+class SystemToastView;
 
 class ASH_EXPORT ToastOverlay : public ui::ImplicitAnimationObserver,
                                 public KeyboardControllerObserver,
@@ -65,13 +66,8 @@
   // To test different Toast UI variations, enable debug shortcuts by building
   // with flag `--ash-debug-shortcuts` and use command "Shift + Ctrl + Alt + O".
   ToastOverlay(Delegate* delegate,
-               const std::u16string& text,
-               const std::u16string& dismiss_text,
-               const gfx::VectorIcon& leading_icon,
-               base::TimeDelta duration,
-               bool persist_on_hover,
-               aura::Window* root_window,
-               base::RepeatingClosure dismiss_callback);
+               ToastData& toast_data,
+               aura::Window* root_window);
 
   ToastOverlay(const ToastOverlay&) = delete;
   ToastOverlay& operator=(const ToastOverlay&) = delete;
@@ -141,7 +137,7 @@
   const std::u16string text_;
   const std::u16string dismiss_text_;
   std::unique_ptr<views::Widget> overlay_widget_;
-  std::unique_ptr<SystemToastStyle> overlay_view_;
+  std::unique_ptr<SystemToastView> overlay_view_;
   std::unique_ptr<ToastDisplayObserver> display_observer_;
   raw_ptr<aura::Window, ExperimentalAsh> root_window_;
   base::RepeatingClosure dismiss_callback_;
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index eea8354..20f14fd 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -66,6 +66,7 @@
 #include "ui/views/animation/animation_builder.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/background.h"
+#include "ui/views/controls/button/button_controller.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/controls/menu/menu_runner.h"
@@ -183,6 +184,19 @@
 
 }  // namespace
 
+TrayBackgroundView::TrayButtonControllerDelegate::TrayButtonControllerDelegate(
+    views::Button* button,
+    TrayBackgroundViewCatalogName catalogue_name)
+    : views::Button::DefaultButtonControllerDelegate(button),
+      catalog_name_(catalogue_name) {}
+
+void TrayBackgroundView::TrayButtonControllerDelegate::NotifyClick(
+    const ui::Event& event) {
+  base::UmaHistogramEnumeration("Ash.StatusArea.TrayBackgroundView.Pressed",
+                                catalog_name_);
+  DefaultButtonControllerDelegate::NotifyClick(event);
+}
+
 // Used to track when the anchor widget changes position on screen so that the
 // bubble position can be updated.
 class TrayBackgroundView::TrayWidgetObserver : public views::WidgetObserver {
@@ -264,6 +278,9 @@
       handler_(new TrayBackgroundViewSessionChangeHandler(this)),
       should_close_bubble_on_lock_state_change_(true) {
   DCHECK(shelf_);
+  SetButtonController(std::make_unique<views::ButtonController>(
+      this,
+      std::make_unique<TrayButtonControllerDelegate>(this, catalog_name)));
   SetNotifyEnterExitOnChild(true);
 
   // Override the settings of inkdrop ripple only since others like Highlight
@@ -616,12 +633,6 @@
   return views::View::RecreateLayer();
 }
 
-void TrayBackgroundView::NotifyClick(const ui::Event& event) {
-  base::UmaHistogramEnumeration("Ash.StatusArea.TrayBackgroundView.Pressed",
-                                catalog_name_);
-  views::Button::NotifyClick(event);
-}
-
 void TrayBackgroundView::OnThemeChanged() {
   views::Button::OnThemeChanged();
   UpdateBackground();
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index b1945fc..eaae8d1 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -55,6 +55,21 @@
     virtual void OnVisiblePreferredChanged(bool visible_preferred) = 0;
   };
 
+  // Records TrayBackgroundView.Pressed metrics for all types of trays. All
+  // nested buttons inside `TrayBackgroundView` should use this delegate if they
+  // want to record the TrayBackgroundView.Pressed metric.
+  class TrayButtonControllerDelegate
+      : public Button::DefaultButtonControllerDelegate {
+   public:
+    TrayButtonControllerDelegate(views::Button* button,
+                                 TrayBackgroundViewCatalogName catalog_name);
+    void NotifyClick(const ui::Event& event) override;
+
+   private:
+    // The catalog name, used to record metrics on feature integrations.
+    TrayBackgroundViewCatalogName catalog_name_;
+  };
+
   enum RoundedCornerBehavior {
     kNotRounded,
     kStartRounded,
@@ -81,7 +96,6 @@
 
   // views::Button:
   void OnThemeChanged() override;
-  void NotifyClick(const ui::Event& event) override;
 
   // VirtualKeyboardModel::Observer:
   void OnVirtualKeyboardVisibilityChanged() override;
diff --git a/ash/system/unified/unified_system_tray.cc b/ash/system/unified/unified_system_tray.cc
index 3dabed7f..fbdd2b8 100644
--- a/ash/system/unified/unified_system_tray.cc
+++ b/ash/system/unified/unified_system_tray.cc
@@ -315,10 +315,6 @@
   }
 }
 
-bool UnifiedSystemTray::IsQuickSettingsExplicitlyExpanded() const {
-  return model_->IsExplicitlyExpanded();
-}
-
 gfx::Rect UnifiedSystemTray::GetBubbleBoundsInScreen() const {
   return bubble_ ? bubble_->GetBoundsInScreen() : gfx::Rect();
 }
diff --git a/ash/system/unified/unified_system_tray.h b/ash/system/unified/unified_system_tray.h
index 9780d15a..82af3fe 100644
--- a/ash/system/unified/unified_system_tray.h
+++ b/ash/system/unified/unified_system_tray.h
@@ -163,9 +163,6 @@
   // view in the foreground.
   void NotifyLeavingCalendarView();
 
-  // Returns true if the user manually expanded the quick settings.
-  bool IsQuickSettingsExplicitlyExpanded() const;
-
   // This enum is for the ChromeOS.SystemTray.FirstInteraction UMA histogram and
   // should be kept in sync.
   enum class FirstInteractionType {
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index c2996b4..7b59169 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -9,7 +9,6 @@
 
 #include "ash/capture_mode/capture_mode_feature_pod_controller.h"
 #include "ash/constants/ash_features.h"
-#include "ash/constants/ash_pref_names.h"
 #include "ash/constants/quick_settings_catalogs.h"
 #include "ash/public/cpp/metrics_util.h"
 #include "ash/public/cpp/pagination/pagination_controller.h"
@@ -73,7 +72,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
-#include "components/prefs/pref_service.h"
 #include "media/base/media_switches.h"
 #include "media/capture/video/chromeos/video_capture_features_chromeos.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -87,30 +85,11 @@
 // TODO(amehfooz): Add histograms for pagination metrics in system tray.
 void RecordPageSwitcherSourceByEventType(ui::EventType type) {}
 
-void ReportExpandAnimationSmoothness(int smoothness) {
-  UMA_HISTOGRAM_PERCENTAGE(
-      "ChromeOS.SystemTray.AnimationSmoothness."
-      "TransitionToExpanded",
-      smoothness);
-}
-
-void ReportCollapseAnimationSmoothness(int smoothness) {
-  UMA_HISTOGRAM_PERCENTAGE(
-      "ChromeOS.SystemTray.AnimationSmoothness."
-      "TransitionToCollapsed",
-      smoothness);
-}
-
 UnifiedSystemTrayController::UnifiedSystemTrayController(
     scoped_refptr<UnifiedSystemTrayModel> model,
     UnifiedSystemTrayBubble* bubble,
     views::View* owner_view)
-    : model_(model),
-      bubble_(bubble),
-      active_user_prefs_(
-          Shell::Get()->session_controller()->GetLastActiveUserPrefService()) {
-  LoadIsExpandedPref();
-
+    : model_(model), bubble_(bubble) {
   model_->pagination_model()->SetTransitionDurations(base::Milliseconds(250),
                                                      base::Milliseconds(50));
 
@@ -119,12 +98,7 @@
       base::BindRepeating(&RecordPageSwitcherSourceByEventType));
 }
 
-UnifiedSystemTrayController::~UnifiedSystemTrayController() {
-  if (active_user_prefs_) {
-    active_user_prefs_->SetBoolean(prefs::kSystemTrayExpanded,
-                                   model_->IsExpandedOnOpen());
-  }
-}
+UnifiedSystemTrayController::~UnifiedSystemTrayController() = default;
 
 void UnifiedSystemTrayController::AddObserver(Observer* observer) {
   if (observer) {
@@ -138,18 +112,6 @@
   }
 }
 
-// static
-void UnifiedSystemTrayController::RegisterProfilePrefs(
-    PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kSystemTrayExpanded,
-                                /*default_value=*/true);
-}
-
-void UnifiedSystemTrayController::OnActiveUserPrefServiceChanged(
-    PrefService* prefs) {
-  active_user_prefs_ = prefs;
-}
-
 std::unique_ptr<QuickSettingsView>
 UnifiedSystemTrayController::CreateQuickSettingsView(int max_height) {
   DCHECK(!quick_settings_view_);
@@ -409,16 +371,6 @@
   quick_settings_view_->SetShowMediaView(show_media_view);
 }
 
-void UnifiedSystemTrayController::LoadIsExpandedPref() {
-  if (active_user_prefs_ &&
-      active_user_prefs_->HasPrefPath(prefs::kSystemTrayExpanded)) {
-    model_->set_expanded_on_open(
-        active_user_prefs_->GetBoolean(prefs::kSystemTrayExpanded)
-            ? UnifiedSystemTrayModel::StateOnOpen::EXPANDED
-            : UnifiedSystemTrayModel::StateOnOpen::COLLAPSED);
-  }
-}
-
 void UnifiedSystemTrayController::InitFeatureTiles() {
   std::vector<std::unique_ptr<FeatureTile>> tiles;
 
diff --git a/ash/system/unified/unified_system_tray_controller.h b/ash/system/unified/unified_system_tray_controller.h
index 86d0307..fd471a6 100644
--- a/ash/system/unified/unified_system_tray_controller.h
+++ b/ash/system/unified/unified_system_tray_controller.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/session/session_observer.h"
 #include "ash/system/audio/unified_volume_slider_controller.h"
 #include "ash/system/media/unified_media_controls_controller.h"
 #include "ash/system/time/calendar_metrics.h"
@@ -18,9 +17,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 
-class PrefRegistrySimple;
-class PrefService;
-
 namespace views {
 class View;
 }  // namespace views
@@ -39,8 +35,7 @@
 
 // Controller class of `QuickSettingsView`. Handles events of the view.
 class ASH_EXPORT UnifiedSystemTrayController
-    : public SessionObserver,
-      public UnifiedVolumeSliderController::Delegate,
+    : public UnifiedVolumeSliderController::Delegate,
       public UnifiedMediaControlsController::Delegate {
  public:
   class Observer : public base::CheckedObserver {
@@ -66,9 +61,6 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  // Registers pref to preserve tray expanded state between reboots.
-  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
-
   // Create the view in the bubble.
   std::unique_ptr<QuickSettingsView> CreateQuickSettingsView(int max_height);
 
@@ -95,8 +87,7 @@
   void ShowNetworkDetailedView();
   // Show the detailed view of hotspot. Called from the view.
   void ShowHotspotDetailedView();
-  // Show the detailed view of bluetooth. If collapsed, it doesn't show the
-  // detailed view. Called from the view.
+  // Show the detailed view of bluetooth.
   void ShowBluetoothDetailedView();
   // Show the detailed view of cast. Called from the view.
   void ShowCastDetailedView();
@@ -140,9 +131,6 @@
   // Return whether a detailed view is currently being shown.
   bool IsDetailedViewShown() const;
 
-  // SessionObserver:
-  void OnActiveUserPrefServiceChanged(PrefService* pref_service) override;
-
   // UnifiedVolumeSliderController::Delegate:
   void OnAudioSettingsButtonClicked() override;
 
@@ -192,9 +180,6 @@
   // enum is used to back an UMA histogram and should be treated as append-only.
   enum ManagedType { MANAGED_TYPE_ENTERPRISE = 0, MANAGED_TYPE_COUNT };
 
-  // Loads the `kSystemTrayExpanded` pref to the model.
-  void LoadIsExpandedPref();
-
   // Initialize feature pod controllers and their feature tile views.
   void InitFeatureTiles();
 
@@ -213,9 +198,6 @@
   // Unowned.
   raw_ptr<UnifiedSystemTrayBubble, ExperimentalAsh> bubble_ = nullptr;
 
-  // The pref service of the currently active user. Can be null in tests.
-  raw_ptr<PrefService, ExperimentalAsh> active_user_prefs_ = nullptr;
-
   // The controller of the current detailed view. If the main view is shown,
   // it's null. Owned.
   std::unique_ptr<DetailedViewController> detailed_view_controller_;
diff --git a/ash/system/unified/unified_system_tray_controller_unittest.cc b/ash/system/unified/unified_system_tray_controller_unittest.cc
deleted file mode 100644
index 637895fb..0000000
--- a/ash/system/unified/unified_system_tray_controller_unittest.cc
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/system/unified/unified_system_tray_controller.h"
-#include <memory>
-
-#include "ash/system/brightness/unified_brightness_slider_controller.h"
-#include "ash/system/status_area_widget.h"
-#include "ash/system/unified/unified_system_tray_model.h"
-#include "ash/test/ash_test_base.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/scoped_refptr.h"
-#include "chromeos/ash/services/network_config/public/cpp/cros_network_config_test_helper.h"
-#include "ui/views/test/views_test_utils.h"
-#include "ui/views/view_observer.h"
-
-namespace ash {
-
-class UnifiedSystemTrayControllerTest : public AshTestBase {
- public:
-  UnifiedSystemTrayControllerTest() = default;
-  UnifiedSystemTrayControllerTest(const UnifiedSystemTrayControllerTest&) =
-      delete;
-  UnifiedSystemTrayControllerTest& operator=(
-      const UnifiedSystemTrayControllerTest&) = delete;
-  ~UnifiedSystemTrayControllerTest() override = default;
-
-  // AshTestBase:
-  void SetUp() override {
-    network_config_helper_ =
-        std::make_unique<network_config::CrosNetworkConfigTestHelper>();
-    AshTestBase::SetUp();
-    // Networking stubs may have asynchronous initialization.
-    base::RunLoop().RunUntilIdle();
-
-    model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
-    controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
-  }
-
-  void TearDown() override {
-    DCHECK(quick_settings_view_);
-    widget_.reset();
-    quick_settings_view_ = nullptr;
-    controller_.reset();
-    model_.reset();
-
-    AshTestBase::TearDown();
-  }
-
-  void InitializeQuickSettingsView() {
-    widget_ = CreateFramelessTestWidget();
-    widget_->SetFullscreen(true);
-    quick_settings_view_ =
-        widget_->SetContentsView(controller_->CreateQuickSettingsView(600));
-  }
-
-  std::unique_ptr<network_config::CrosNetworkConfigTestHelper>
-      network_config_helper_;
-  scoped_refptr<UnifiedSystemTrayModel> model_;
-  std::unique_ptr<UnifiedSystemTrayController> controller_;
-  std::unique_ptr<views::Widget> widget_;
-
-  // Owned by `widget_`.
-  raw_ptr<QuickSettingsView, DanglingUntriaged | ExperimentalAsh>
-      quick_settings_view_;
-};
-
-// Tests that setting the `UnifiedSystemTrayModel::StateOnOpen` pref to
-// collapsed is a no-op.
-TEST_F(UnifiedSystemTrayControllerTest, ExpandedPrefIsNoOp) {
-  // Set the pref to collapsed, there should be no effect.
-  model_->set_expanded_on_open(UnifiedSystemTrayModel::StateOnOpen::COLLAPSED);
-
-  InitializeQuickSettingsView();
-
-  EXPECT_TRUE(model_->IsExpandedOnOpen());
-}
-
-}  // namespace ash
diff --git a/ash/system/unified/unified_system_tray_model.cc b/ash/system/unified/unified_system_tray_model.cc
index a9d0e36e..66d92fd 100644
--- a/ash/system/unified/unified_system_tray_model.cc
+++ b/ash/system/unified/unified_system_tray_model.cc
@@ -5,7 +5,6 @@
 #include "ash/system/unified/unified_system_tray_model.h"
 
 #include "ash/accessibility/accessibility_controller_impl.h"
-#include "ash/constants/ash_features.h"
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/shell_observer.h"
@@ -165,20 +164,6 @@
   observers_.RemoveObserver(observer);
 }
 
-bool UnifiedSystemTrayModel::IsExpandedOnOpen() const {
-  // They System Tray is always expanded with QsRevamp enabled.
-  if (features::IsQsRevampEnabled()) {
-    return true;
-  }
-
-  return expanded_on_open_ != StateOnOpen::COLLAPSED ||
-         Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
-}
-
-bool UnifiedSystemTrayModel::IsExplicitlyExpanded() const {
-  return expanded_on_open_ == StateOnOpen::EXPANDED;
-}
-
 absl::optional<bool> UnifiedSystemTrayModel::GetNotificationExpanded(
     const std::string& notification_id) const {
   auto it = notification_changes_.find(notification_id);
diff --git a/ash/system/unified/unified_system_tray_model.h b/ash/system/unified/unified_system_tray_model.h
index 0f7a90e..0eded1c 100644
--- a/ash/system/unified/unified_system_tray_model.h
+++ b/ash/system/unified/unified_system_tray_model.h
@@ -28,15 +28,6 @@
 class ASH_EXPORT UnifiedSystemTrayModel
     : public base::RefCounted<UnifiedSystemTrayModel> {
  public:
-  enum class StateOnOpen {
-    // The user has not made any changes to the quick settings state.
-    UNSET,
-    // Quick settings has been explicitly set to collapsed by the user.
-    COLLAPSED,
-    // Quick settings has been explicitly set to expanded by the user.
-    EXPANDED
-  };
-
   enum class NotificationTargetMode {
     // Notification list scrolls to the last notification.
     LAST_NOTIFICATION,
@@ -80,13 +71,6 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
-  // Returns true if the tray should be expanded when initially opened.
-  bool IsExpandedOnOpen() const;
-
-  // Returns true if the user explicitly set the tray to its
-  // expanded state.
-  bool IsExplicitlyExpanded() const;
-
   // Returns empty if it's not manually expanded/collapsed. Otherwise, the value
   // is true if the notification is manually expanded, and false if it's
   // manually collapsed.
@@ -113,10 +97,6 @@
   float display_brightness() const { return display_brightness_; }
   float keyboard_brightness() const { return keyboard_brightness_; }
 
-  void set_expanded_on_open(StateOnOpen expanded_on_open) {
-    expanded_on_open_ = expanded_on_open;
-  }
-
   void set_notification_target_mode(NotificationTargetMode mode) {
     notification_target_mode_ = mode;
   }
@@ -164,10 +144,6 @@
   // is NOTIFICATION_ID.
   std::string notification_target_id_;
 
-  // If UnifiedSystemTray bubble is expanded on its open. It's expanded by
-  // default, and if a user collapses manually, it remembers previous state.
-  StateOnOpen expanded_on_open_ = StateOnOpen::UNSET;
-
   // The last value of the display brightness slider. Between 0.0 and 1.0.
   float display_brightness_ = 1.f;
 
diff --git a/ash/system/video_conference/video_conference_tray.cc b/ash/system/video_conference/video_conference_tray.cc
index e5edc15..f11e41e 100644
--- a/ash/system/video_conference/video_conference_tray.cc
+++ b/ash/system/video_conference/video_conference_tray.cc
@@ -43,6 +43,7 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/scoped_canvas.h"
+#include "ui/views/controls/button/button_controller.h"
 #include "ui/views/controls/highlight_path_generator.h"
 
 namespace ash {
@@ -89,6 +90,11 @@
                    /*is_togglable=*/true,
                    /*has_border=*/true),
         tray_(tray) {
+    SetButtonController(std::make_unique<views::ButtonController>(
+        /*views::Button*=*/this,
+        std::make_unique<TrayBackgroundView::TrayButtonControllerDelegate>(
+            /*views::Button*=*/this,
+            TrayBackgroundViewCatalogName::kVideoConferenceTray)));
     // Reduce the focus ring padding which is installed by default by
     // `IconButton`. The default padding results in the focus ring being painted
     // outside of the available bounds.
@@ -136,6 +142,12 @@
       accessible_name_id_(accessible_name_id),
       icon_(icon),
       capturing_icon_(capturing_icon) {
+  SetButtonController(std::make_unique<views::ButtonController>(
+      /*views::Button*=*/this,
+      std::make_unique<TrayBackgroundView::TrayButtonControllerDelegate>(
+          /*views::Button*=*/this,
+          TrayBackgroundViewCatalogName::kVideoConferenceTray)));
+
   SetBackgroundToggledColor(cros_tokens::kCrosSysSystemNegativeContainer);
   SetIconToggledColor(cros_tokens::kCrosSysSystemOnNegativeContainer);
 
diff --git a/ash/system/video_conference/video_conference_tray_unittest.cc b/ash/system/video_conference/video_conference_tray_unittest.cc
index 8d77777..39b6f35 100644
--- a/ash/system/video_conference/video_conference_tray_unittest.cc
+++ b/ash/system/video_conference/video_conference_tray_unittest.cc
@@ -49,6 +49,8 @@
     "Ash.VideoConferenceTray.MicrophoneMuteButton.Click";
 constexpr char kStopScreenShareHistogramName[] =
     "Ash.VideoConferenceTray.StopScreenShareButton.Click";
+constexpr char kTrayBackgroundViewHistogramName[] =
+    "Ash.StatusArea.TrayBackgroundView.Pressed";
 
 constexpr base::TimeDelta kGetMediaAppsDelayTime = base::Milliseconds(100);
 
@@ -264,6 +266,25 @@
   EXPECT_FALSE(toggle_bubble_button()->toggled());
 }
 
+// Makes sure metrics are recorded for the video conference tray or any nested
+// button being pressed.
+TEST_F(VideoConferenceTrayTest, TrayPressedMetrics) {
+  base::HistogramTester histogram_tester;
+  SetTrayAndButtonsVisible();
+
+  LeftClickOn(toggle_bubble_button());
+  histogram_tester.ExpectTotalCount(kTrayBackgroundViewHistogramName, 1);
+
+  LeftClickOn(camera_icon());
+  histogram_tester.ExpectTotalCount(kTrayBackgroundViewHistogramName, 2);
+
+  LeftClickOn(audio_icon());
+  histogram_tester.ExpectTotalCount(kTrayBackgroundViewHistogramName, 3);
+
+  LeftClickOn(screen_share_icon());
+  histogram_tester.ExpectTotalCount(kTrayBackgroundViewHistogramName, 4);
+}
+
 // Tests that tapping directly on the VideoConferenceTray (not the child toggle
 // buttons) toggles the bubble.
 TEST_F(VideoConferenceTrayTest, ClickTrayBackgroundViewTogglesBubble) {
diff --git a/ash/wallpaper/views/wallpaper_base_view.cc b/ash/wallpaper/views/wallpaper_base_view.cc
index 9c7e3ff3..bae52b5 100644
--- a/ash/wallpaper/views/wallpaper_base_view.cc
+++ b/ash/wallpaper/views/wallpaper_base_view.cc
@@ -11,7 +11,6 @@
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "base/numerics/safe_conversions.h"
 #include "cc/paint/paint_flags.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/canvas.h"
 
@@ -26,15 +25,11 @@
 
   auto* controller = Shell::Get()->wallpaper_controller();
   if (controller->IsOobeWallpaper()) {
-    color = chromeos::features::IsJellyrollEnabled()
-                ? cros_tokens::kCrosSysScrim2
-                : static_cast<ui::ColorId>(kColorAshShieldAndBase60);
+    color = cros_tokens::kCrosSysScrim2;
   } else if (Shell::Get()->session_controller()->IsUserSessionBlocked()) {
     color = kColorAshShieldAndBase80;
   } else {
-    color = chromeos::features::IsJellyrollEnabled()
-                ? cros_tokens::kCrosSysScrim2
-                : static_cast<ui::ColorId>(kColorAshShieldAndBase40);
+    color = cros_tokens::kCrosSysScrim2;
   }
 
   DCHECK(widget);
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 50a18944..9fa7a4ed3 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -387,9 +387,6 @@
 absl::optional<SkColor> WallpaperControllerImpl::GetCachedWallpaperColorForUser(
     const AccountId& account_id,
     bool should_use_k_means) const {
-  if (!chromeos::features::IsJellyEnabled()) {
-    return {};
-  }
   WallpaperInfo info;
   if (!pref_manager_->GetLocalWallpaperInfo(account_id, &info)) {
     return {};
@@ -540,7 +537,7 @@
 bool WallpaperControllerImpl::ShouldApplyShield() const {
   bool needs_shield = false;
   if (Shell::Get()->overview_controller()->InOverviewSession()) {
-    needs_shield = !chromeos::features::IsJellyrollEnabled();
+    needs_shield = false;
   } else if (Shell::Get()->session_controller()->IsUserSessionBlocked()) {
     needs_shield = true;
   } else if (Shell::Get()->tablet_mode_controller()->InTabletMode() &&
@@ -1511,10 +1508,8 @@
       info.location, wallpaper_calculated_colors.prominent_colors);
   pref_manager_->CacheKMeanColor(info.location,
                                  wallpaper_calculated_colors.k_mean_color);
-  if (chromeos::features::IsJellyEnabled()) {
-    pref_manager_->CacheCelebiColor(info.location,
-                                    wallpaper_calculated_colors.celebi_color);
-  }
+  pref_manager_->CacheCelebiColor(info.location,
+                                  wallpaper_calculated_colors.celebi_color);
   SetCalculatedColors(wallpaper_calculated_colors);
 
   // Release the color calculator after it has returned a result by calling this
@@ -1632,19 +1627,17 @@
 void WallpaperControllerImpl::OnOverviewModeStarting() {
   // Only in tablet mode, we need to call `RepaintWallpaper` to update the
   // wallpaper shield on overview mode changes, since in clamshell mode, we
-  // don't apply the wallpaper shield no matter it's in overview mode or not if
-  // the feature `kJellyroll` is enabled. However, in tablet mode, we need to
-  // apply the wallpaper shield when it's not in the overview mode.
-  if (chromeos::features::IsJellyrollEnabled() &&
-      Shell::Get()->tablet_mode_controller()->InTabletMode()) {
+  // don't apply the wallpaper shield no matter it's in overview mode or not.
+  // However, in tablet mode, we need to apply the wallpaper shield when it's
+  // not in the overview mode.
+  if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
     RepaintWallpaper();
   }
 }
 
 void WallpaperControllerImpl::OnOverviewModeEnded() {
   // Refer to the comment in `OnOverviewModeStarting`.
-  if (chromeos::features::IsJellyrollEnabled() &&
-      Shell::Get()->tablet_mode_controller()->InTabletMode()) {
+  if (Shell::Get()->tablet_mode_controller()->InTabletMode()) {
     RepaintWallpaper();
   }
 }
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index cd2c248..fc9e9e9 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -879,29 +879,20 @@
 };
 
 // All possible feature combinations that can occur in the real world.
-enum class JellyFeatureCombination {
-  kDisabled,
-  kEnabled,
-  kEnabledWithTimeOfDay
-};
+enum class TimeOfDayFeatureCombination { kDisabled, kTimeOfDay };
 
 class WallpaperControllerTest
     : public WallpaperControllerTestBase,
-      public testing::WithParamInterface<JellyFeatureCombination> {
+      public testing::WithParamInterface<TimeOfDayFeatureCombination> {
  public:
   WallpaperControllerTest() {
     std::vector<base::test::FeatureRef> enabled_features;
     std::vector<base::test::FeatureRef> disabled_features;
     switch (GetParam()) {
-      case JellyFeatureCombination::kDisabled:
+      case TimeOfDayFeatureCombination::kDisabled:
         disabled_features = personalization_app::GetTimeOfDayDisabledFeatures();
-        disabled_features.push_back(chromeos::features::kJelly);
         break;
-      case JellyFeatureCombination::kEnabled:
-        disabled_features = personalization_app::GetTimeOfDayDisabledFeatures();
-        enabled_features.push_back(chromeos::features::kJelly);
-        break;
-      case JellyFeatureCombination::kEnabledWithTimeOfDay:
+      case TimeOfDayFeatureCombination::kTimeOfDay:
         enabled_features = personalization_app::GetTimeOfDayEnabledFeatures();
         break;
     }
@@ -913,22 +904,11 @@
 
   ~WallpaperControllerTest() override = default;
 
-  bool IsJellyEnabled() const {
-    switch (GetParam()) {
-      case JellyFeatureCombination::kDisabled:
-        return false;
-      case JellyFeatureCombination::kEnabled:
-      case JellyFeatureCombination::kEnabledWithTimeOfDay:
-        return true;
-    }
-  }
-
   bool IsTimeOfDayEnabled() const {
     switch (GetParam()) {
-      case JellyFeatureCombination::kDisabled:
-      case JellyFeatureCombination::kEnabled:
+      case TimeOfDayFeatureCombination::kDisabled:
         return false;
-      case JellyFeatureCombination::kEnabledWithTimeOfDay:
+      case TimeOfDayFeatureCombination::kTimeOfDay:
         return true;
     }
   }
@@ -938,12 +918,10 @@
     std::string operator()(
         const testing::TestParamInfo<ParamType>& info) const {
       switch (info.param) {
-        case JellyFeatureCombination::kDisabled:
-          return "JellyOff";
-        case JellyFeatureCombination::kEnabled:
-          return "JellyOn";
-        case JellyFeatureCombination::kEnabledWithTimeOfDay:
-          return "JellyOnWithTimeOfDay";
+        case TimeOfDayFeatureCombination::kDisabled:
+          return "TimeOfDayOff";
+        case TimeOfDayFeatureCombination::kTimeOfDay:
+          return "TimeOfDayOn";
       }
     }
   };
@@ -1036,18 +1014,16 @@
     // Empty to simplify gtest output
     ,
     WallpaperControllerTest,
-    ::testing::Values(JellyFeatureCombination::kDisabled,
-                      JellyFeatureCombination::kEnabled,
-                      JellyFeatureCombination::kEnabledWithTimeOfDay),
+    ::testing::Values(TimeOfDayFeatureCombination::kDisabled,
+                      TimeOfDayFeatureCombination::kTimeOfDay),
     WallpaperControllerTest::PrintToStringParamName());
 
 INSTANTIATE_TEST_SUITE_P(
     // Empty to simplify gtest output
     ,
     WallpaperControllerAutoScheduleTest,
-    ::testing::Values(JellyFeatureCombination::kDisabled,
-                      JellyFeatureCombination::kEnabled,
-                      JellyFeatureCombination::kEnabledWithTimeOfDay),
+    ::testing::Values(TimeOfDayFeatureCombination::kDisabled,
+                      TimeOfDayFeatureCombination::kTimeOfDay),
     WallpaperControllerTest::PrintToStringParamName());
 
 TEST_P(WallpaperControllerTest, Client) {
@@ -1397,32 +1373,7 @@
   load_preview_image_loop.Run();
 }
 
-TEST_P(WallpaperControllerTest, CelebiNotSavedWhenJellyIsDisabled) {
-  base::test::ScopedFeatureList features;
-  features.InitAndDisableFeature(chromeos::features::kJelly);
-  TestWallpaperControllerObserver observer(controller_);
-
-  const char location[] = "test_wallpaper_here";
-
-  // Set the wallpaper with a valid location.
-  WallpaperInfo wallpaper_info = CreateWallpaperInfo(WALLPAPER_LAYOUT_STRETCH);
-  wallpaper_info.location = location;
-  const gfx::ImageSkia kImage = CreateImage(10, 10, kWallpaperColor);
-  controller_->ShowWallpaperImage(kImage, wallpaper_info,
-                                  /*preview_mode=*/false,
-                                  /*is_override=*/false);
-  SetSessionState(SessionState::ACTIVE);
-
-  // Wait for color computation to complete.
-  base::RunLoop colors_loop;
-  observer.SetOnColorsCalculatedCallback(colors_loop.QuitClosure());
-  colors_loop.Run();
-
-  EXPECT_FALSE(pref_manager_->GetCelebiColor(location));
-}
-
-TEST_P(WallpaperControllerTest, SaveCelebiColorWhenJellyActive) {
-  base::test::ScopedFeatureList features(chromeos::features::kJelly);
+TEST_P(WallpaperControllerTest, SaveCelebiColor) {
   TestWallpaperControllerObserver observer(controller_);
 
   const char location[] = "test_wallpaper_here";
@@ -1459,24 +1410,13 @@
   // Reset to login screen.
   GetSessionControllerClient()->RequestSignOut();
 
-  if (IsJellyEnabled()) {
-    // User's wallpaper colors are accessible from login screen.
-    EXPECT_EQ(kWallpaperColor, controller_->GetCachedWallpaperColorForUser(
-                                   kAccountId1, /* use_k_means= */ false));
-  } else {
-    // User's celebi color is only retrieved when Jelly is enabled.
-    EXPECT_FALSE(controller_
-                     ->GetCachedWallpaperColorForUser(kAccountId1,
-                                                      /* use_k_means= */ false)
-                     .has_value());
-  }
+  // User's wallpaper colors are accessible from login screen.
+  EXPECT_EQ(kWallpaperColor, controller_->GetCachedWallpaperColorForUser(
+                                 kAccountId1, /* use_k_means= */ false));
 }
 
 TEST_P(WallpaperControllerTest,
        GetCachedWallpaperColorForUser_WithKMeansColor) {
-  if (!IsJellyEnabled()) {
-    return;
-  }
   // Cache some wallpapers and store that in the local prefs. Otherwise, we
   // can't cache colors.
   base::FilePath relative_path = PrecacheWallpapers(kAccountId1);
@@ -1506,106 +1446,6 @@
   EXPECT_EQ(1, observer.colors_changed_count());
 }
 
-TEST_P(WallpaperControllerTest, ProminentColor_CachedColorsAvailableAtLogin) {
-  if (IsJellyEnabled()) {
-    // Prominent colors only apply when jelly is disabled.
-    return;
-  }
-  // Cache some wallpapers and store that in the local prefs. Otherwise, we
-  // can't cache colors.
-  base::FilePath relative_path = PrecacheWallpapers(kAccountId1);
-  WallpaperInfo info = InfoWithType(WallpaperType::kCustomized);
-  info.location = relative_path.value();
-  ASSERT_TRUE(pref_manager_->SetLocalWallpaperInfo(kAccountId1, info));
-
-  // Store colors in local prefs simulating cache behavior.
-  const std::vector<SkColor> prominent_colors = {SK_ColorGREEN, SK_ColorRED,
-                                                 SK_ColorBLUE,  SK_ColorWHITE,
-                                                 SK_ColorWHITE, SK_ColorWHITE};
-  pref_manager_->CacheProminentColors(relative_path.value(), prominent_colors);
-  const SkColor k_means_color = SK_ColorLTGRAY;
-  pref_manager_->CacheKMeanColor(relative_path.value(), k_means_color);
-
-  // Reset to login screen.
-  GetSessionControllerClient()->RequestSignOut();
-
-  TestWallpaperControllerObserver observer(controller_);
-  ASSERT_EQ(0, observer.colors_changed_count());
-
-  // Show user wallpaper in login screen. We are *not* logged in yet.
-  controller_->ShowUserWallpaper(kAccountId1,
-                                 user_manager::UserType::USER_TYPE_REGULAR);
-  task_environment()->RunUntilIdle();
-
-  // Showing a user wallpaper should cause the cached colors to be fetched and
-  // reported.
-  EXPECT_EQ(1, observer.colors_changed_count());
-
-  // DARK_VIBRANT happens to be prominent color 0.
-  EXPECT_EQ(SK_ColorGREEN, controller_->GetProminentColor(
-                               {color_utils::LumaRange::DARK,
-                                color_utils::SaturationRange::VIBRANT}));
-  EXPECT_EQ(k_means_color, controller_->GetKMeanColor());
-}
-
-TEST_P(WallpaperControllerTest, ProminentColor_ClearedBetweenUsers) {
-  if (IsJellyEnabled()) {
-    // Prominent colors only apply when jelly is disabled.
-    return;
-  }
-  // Setup prominent colors for account 1.
-  base::FilePath relative_path = PrecacheWallpapers(kAccountId1);
-  WallpaperInfo info = InfoWithType(WallpaperType::kCustomized);
-  info.location = relative_path.value();
-  ASSERT_TRUE(pref_manager_->SetLocalWallpaperInfo(kAccountId1, info));
-
-  const std::vector<SkColor> prominent_colors = {SK_ColorGREEN, SK_ColorRED,
-                                                 SK_ColorBLUE,  SK_ColorWHITE,
-                                                 SK_ColorWHITE, SK_ColorWHITE};
-  pref_manager_->CacheProminentColors(relative_path.value(), prominent_colors);
-  const SkColor k_means_color = SK_ColorLTGRAY;
-  pref_manager_->CacheKMeanColor(relative_path.value(), k_means_color);
-
-  // Set a wallpaper for account 2.
-  WallpaperInfo info2 = InfoWithType(WallpaperType::kDefault);
-  ASSERT_TRUE(pref_manager_->SetLocalWallpaperInfo(kAccountId2, info2));
-
-  // Reset to login screen.
-  GetSessionControllerClient()->RequestSignOut();
-
-  TestWallpaperControllerObserver observer(controller_);
-
-  // No notifications should have occurred yet.
-  EXPECT_EQ(0, observer.colors_changed_count());
-
-  // Show wallpaper for account 1.
-  controller_->ShowUserWallpaper(kAccountId1,
-                                 user_manager::UserType::USER_TYPE_REGULAR);
-  task_environment()->RunUntilIdle();
-
-  // Should have received a notification for the cached colors.
-  EXPECT_EQ(1, observer.colors_changed_count());
-
-  // Verify that we can retrieve the prominent color.
-  EXPECT_EQ(SK_ColorGREEN, controller_->GetProminentColor(
-                               {color_utils::LumaRange::DARK,
-                                color_utils::SaturationRange::VIBRANT}));
-
-  // Show wallpaper for account 2.
-  controller_->ShowUserWallpaper(kAccountId2,
-                                 user_manager::UserType::USER_TYPE_REGULAR);
-  task_environment()->RunUntilIdle();
-  // Since account 2 has not cached colors and wallpaper decoding is disabled,
-  // the prominent color should be invalid.
-  EXPECT_EQ(
-      kInvalidWallpaperColor,
-      controller_->GetProminentColor({color_utils::LumaRange::DARK,
-                                      color_utils::SaturationRange::VIBRANT}));
-  // We got one notification for the first user but nothing after because the
-  // wallpaper color hasn't been computed yet.
-  EXPECT_EQ(1, observer.colors_changed_count());
-}
-
 TEST_P(WallpaperControllerTest,
        OnWallpaperColorsChangedAlwaysCalledOnFirstUpdate) {
   TestWallpaperControllerObserver observer(controller_);
@@ -3989,7 +3829,6 @@
     const bool oobe_jelly_modal = std::get<2>(GetParam());
     scoped_feature_list_.InitWithFeatureStates(
         {{features::kFeatureManagementOobeSimon, boot_animation},
-         {chromeos::features::kJelly, oobe_jelly},
          {features::kOobeJelly, oobe_jelly},
          {features::kOobeJellyModal, oobe_jelly_modal}});
   }
@@ -5910,9 +5749,8 @@
     // Empty to simplify gtest output
     ,
     WallpaperControllerDailyRefreshSchedulerTest,
-    ::testing::Values(JellyFeatureCombination::kDisabled,
-                      JellyFeatureCombination::kEnabled,
-                      JellyFeatureCombination::kEnabledWithTimeOfDay),
+    ::testing::Values(TimeOfDayFeatureCombination::kDisabled,
+                      TimeOfDayFeatureCombination::kTimeOfDay),
     WallpaperControllerTest::PrintToStringParamName());
 
 TEST_P(WallpaperControllerDailyRefreshSchedulerTest,
diff --git a/ash/wallpaper/wallpaper_pref_manager.cc b/ash/wallpaper/wallpaper_pref_manager.cc
index a160c82b..3b4e0657 100644
--- a/ash/wallpaper/wallpaper_pref_manager.cc
+++ b/ash/wallpaper/wallpaper_pref_manager.cc
@@ -392,15 +392,6 @@
     absl::optional<std::vector<SkColor>> cached_colors =
         GetCachedProminentColors(location);
     absl::optional<SkColor> cached_k_mean_color = GetCachedKMeanColor(location);
-    if (!chromeos::features::IsJellyEnabled()) {
-      if (cached_colors.has_value() && cached_k_mean_color.has_value()) {
-        return WallpaperCalculatedColors(cached_colors.value(),
-                                         cached_k_mean_color.value(),
-                                         SK_ColorTRANSPARENT);
-      }
-
-      return absl::nullopt;
-    }
 
     absl::optional<SkColor> cached_celebi_color = GetCelebiColor(location);
     if (cached_k_mean_color.has_value() && cached_celebi_color.has_value()) {
diff --git a/ash/wallpaper/wallpaper_pref_manager_unittest.cc b/ash/wallpaper/wallpaper_pref_manager_unittest.cc
index bbf0fdd..5a75a82 100644
--- a/ash/wallpaper/wallpaper_pref_manager_unittest.cc
+++ b/ash/wallpaper/wallpaper_pref_manager_unittest.cc
@@ -17,9 +17,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/user_manager/user_type.h"
@@ -217,12 +215,7 @@
 
 class WallpaperPrefManagerTest : public WallpaperPrefManagerTestBase {
  public:
-  WallpaperPrefManagerTest() {
-    scoped_feature_list_.InitAndEnableFeature(chromeos::features::kJelly);
-  }
-
- protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  WallpaperPrefManagerTest() = default;
 };
 
 TEST_F(WallpaperPrefManagerTest, GetWallpaperInfo_Normal) {
@@ -547,58 +540,5 @@
                                                  /*is_oobe=*/true));
 }
 
-class WallpaperPrefManagerJellyDisabledTest
-    : public WallpaperPrefManagerTestBase {
- public:
-  WallpaperPrefManagerJellyDisabledTest() {
-    scoped_feature_list_.InitAndDisableFeature(chromeos::features::kJelly);
-  }
-
- protected:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(WallpaperPrefManagerJellyDisabledTest, SetCalculatedColors) {
-  const char location[] = "location";
-
-  // Cache a prominent and KMean color entry
-  const std::vector<SkColor> prominent_colors = {
-      SK_ColorGREEN, SK_ColorGREEN, SK_ColorGREEN,
-      SkColorSetRGB(0xAB, 0xBC, 0xEF)};
-  pref_manager_->CacheProminentColors(location, prominent_colors);
-
-  const SkColor k_mean_color = SkColorSetRGB(0xAB, 0xBC, 0xEF);
-  pref_manager_->CacheKMeanColor(location, k_mean_color);
-
-  absl::optional<WallpaperCalculatedColors> actual_colors =
-      pref_manager_->GetCachedWallpaperColors(location);
-  ASSERT_TRUE(actual_colors);
-  EXPECT_THAT(actual_colors->prominent_colors,
-              testing::ContainerEq(prominent_colors));
-  EXPECT_EQ(actual_colors->k_mean_color, k_mean_color);
-}
-
-TEST_F(WallpaperPrefManagerJellyDisabledTest,
-       CalculatedColorsEmptyIfKMeanMissing) {
-  const char location[] = "location";
-
-  const std::vector<SkColor> prominent_colors = {
-      SK_ColorGREEN, SK_ColorGREEN, SK_ColorGREEN,
-      SkColorSetRGB(0xAB, 0xBC, 0xEF)};
-  pref_manager_->CacheProminentColors(location, prominent_colors);
-
-  EXPECT_FALSE(pref_manager_->GetCachedWallpaperColors(location));
-}
-
-TEST_F(WallpaperPrefManagerJellyDisabledTest,
-       CalculatedColorsEmptyIfProminentColorsMissing) {
-  const char location[] = "location";
-
-  const SkColor k_mean_color = SkColorSetRGB(0xAB, 0xBC, 0xEF);
-  pref_manager_->CacheKMeanColor(location, k_mean_color);
-
-  EXPECT_FALSE(pref_manager_->GetCachedWallpaperColors(location));
-}
-
 }  // namespace
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator.cc b/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator.cc
index c84dc2f..a7fdecbe 100644
--- a/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator.cc
+++ b/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator.cc
@@ -99,9 +99,7 @@
       /*find_closest=*/true);
 
   // Compute result with with the improved clustering algorithm.
-  SkColor celebi_color = chromeos::features::IsJellyEnabled()
-                             ? ComputeWallpaperSeedColor(resized_image)
-                             : SK_ColorTRANSPARENT;
+  SkColor celebi_color = ComputeWallpaperSeedColor(resized_image);
 
   DVLOG(2) << __func__ << " image_size=" << image.size().ToString()
            << " time=" << base::TimeTicks::Now() - start_time;
diff --git a/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator_unittest.cc b/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator_unittest.cc
index 6c4dc1f..7e698a3 100644
--- a/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator_unittest.cc
+++ b/ash/wallpaper/wallpaper_utils/wallpaper_color_calculator_unittest.cc
@@ -32,8 +32,6 @@
 namespace ash {
 namespace {
 
-const SkColor kDefaultColor = SK_ColorTRANSPARENT;
-
 const SkColor kGray = SkColorSetRGB(10, 10, 10);
 
 const SkColor kVibrantGreen = SkColorSetRGB(25, 200, 25);
@@ -145,28 +143,7 @@
   EXPECT_TRUE(notified);
 }
 
-TEST_F(WallPaperColorCalculatorAsyncTest, ColorUpdatedOnSuccessfulCalculation) {
-  base::test::ScopedFeatureList features;
-  features.InitAndDisableFeature(chromeos::features::kJelly);
-
-  std::vector<SkColor> colors = {kDefaultColor};
-  SkColor k_mean_color = kDefaultColor;
-  calculator_->set_calculated_colors_for_test(
-      WallpaperCalculatedColors(colors, k_mean_color, kDefaultColor));
-
-  base::RunLoop run_loop;
-  EXPECT_TRUE(calculator_->StartCalculation(Wrap(run_loop.QuitClosure())));
-
-  run_loop.Run();
-  ASSERT_TRUE(calculator_->get_calculated_colors());
-  EXPECT_NE(kDefaultColor,
-            calculator_->get_calculated_colors()->prominent_colors[0]);
-  EXPECT_EQ(kGray, calculator_->get_calculated_colors()->k_mean_color);
-}
-
-TEST_F(WallPaperColorCalculatorAsyncTest, CelebiCalculatedWhenJellyEnabled) {
-  base::test::ScopedFeatureList features(chromeos::features::kJelly);
-
+TEST_F(WallPaperColorCalculatorAsyncTest, CelebiCalculated) {
   base::RunLoop run_loop;
   EXPECT_TRUE(calculator_->StartCalculation(Wrap(run_loop.QuitClosure())));
 
diff --git a/ash/webui/common/resources/auth_setup/set_local_password_input.html b/ash/webui/common/resources/auth_setup/set_local_password_input.html
index 5116227..987cb0e 100644
--- a/ash/webui/common/resources/auth_setup/set_local_password_input.html
+++ b/ash/webui/common/resources/auth_setup/set_local_password_input.html
@@ -34,6 +34,7 @@
     type="[[getPasswordInputType(isFirstPasswordVisible_)]]"
     required
     placeholder="[[i18nDynamic(locale, 'setLocalPasswordPlaceholder')]]"
+    aria-label="[[firstInputAriaLabel]]"
     on-input="onInput"
     on-blur="onBlur"
     on-keyup="onKeyup"
diff --git a/ash/webui/common/resources/auth_setup/set_local_password_input.ts b/ash/webui/common/resources/auth_setup/set_local_password_input.ts
index 05241f2..98709a08a 100644
--- a/ash/webui/common/resources/auth_setup/set_local_password_input.ts
+++ b/ash/webui/common/resources/auth_setup/set_local_password_input.ts
@@ -88,6 +88,14 @@
         value: '',
       },
 
+      /**
+       * Aria label to apply to the first input.
+       */
+      firstInputAriaLabel: {
+        type: String,
+        value: null,
+      },
+
       firstInputValidity_: {
         type: String,
         value: null,
diff --git a/ash/webui/common/resources/network/apn_list.html b/ash/webui/common/resources/network/apn_list.html
index 3ce0502..d47a87a 100644
--- a/ash/webui/common/resources/network/apn_list.html
+++ b/ash/webui/common/resources/network/apn_list.html
@@ -97,7 +97,8 @@
         should-disallow-enabling="[[shouldDisallowEnabling_(item)]]"
         guid="[[guid]]"
         item-index="[[index]]"
-        list-size="[[apns_.length]]">
+        list-size="[[apns_.length]]"
+        portal-state="[[portalState]]">
     </apn-list-item>
   </template>
 </iron-list>
diff --git a/ash/webui/common/resources/network/apn_list.js b/ash/webui/common/resources/network/apn_list.js
index 37d5938..c1061cc 100644
--- a/ash/webui/common/resources/network/apn_list.js
+++ b/ash/webui/common/resources/network/apn_list.js
@@ -20,6 +20,7 @@
 import {afterNextRender, mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {ApnDetailDialogMode, ApnEventData} from 'chrome://resources/ash/common/network/cellular_utils.js';
 import {ApnProperties, ApnState, ApnType, ManagedCellularProperties} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
+import {PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
 
 import {getTemplate} from './apn_list.html.js';
 
@@ -55,6 +56,11 @@
 
       errorState: String,
 
+      /** @type {?PortalState} */
+      portalState: {
+        type: Object,
+      },
+
       shouldOmitLinks: {
         type: Boolean,
         value: false,
diff --git a/ash/webui/common/resources/network/apn_list_item.html b/ash/webui/common/resources/network/apn_list_item.html
index 2ffcc76..91a2e60 100644
--- a/ash/webui/common/resources/network/apn_list_item.html
+++ b/ash/webui/common/resources/network/apn_list_item.html
@@ -35,6 +35,10 @@
     color: var(--cros-text-color-positive);
   }
 
+  #subLabel[warning] {
+    color: var(--cros-text-color-warning);
+  }
+
   #autoDetected {
     color: var(--cr-secondary-text-color);
     margin-inline-start: 10px;
@@ -49,8 +53,9 @@
     </div>
   </div>
   <div id="subLabel" class="cr-secondary-text" aria-hidden="true"
-      hidden="[[!isConnected]]">
-    [[i18n('OncConnected')]]
+      hidden="[[!isConnected]]"
+      warning$="[[isPortalStateNoInternet_(portalState)]]">
+    [[getSublabel_(isConnected, portalState)]]
   </div>
 </div>
 <cr-icon-button id="actionMenuButton" class="icon-more-vert"
diff --git a/ash/webui/common/resources/network/apn_list_item.js b/ash/webui/common/resources/network/apn_list_item.js
index 457d82c4f..80a1fd9 100644
--- a/ash/webui/common/resources/network/apn_list_item.js
+++ b/ash/webui/common/resources/network/apn_list_item.js
@@ -15,6 +15,7 @@
 import {ApnDetailDialogMode, ApnEventData, getApnDisplayName} from 'chrome://resources/ash/common/network/cellular_utils.js';
 import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
 import {ApnProperties, ApnState, CrosNetworkConfigInterface} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
+import {PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
 
 import {getTemplate} from './apn_list_item.html.js';
 
@@ -67,6 +68,11 @@
        */
       listSize: Number,
 
+      /** @type {?PortalState} */
+      portalState: {
+        type: Object,
+      },
+
       /** @private */
       isDisabled_: {
         reflectToAttribute: true,
@@ -92,6 +98,25 @@
   }
 
   /**
+   * @return {string}
+   * @private
+   */
+  getSublabel_() {
+    if (this.isPortalStateNoInternet_()) {
+      return this.i18n('networkListItemConnectedNoConnectivity');
+    }
+    return this.i18n('OncConnected');
+  }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  isPortalStateNoInternet_() {
+    return !!this.portalState && this.portalState === PortalState.kNoInternet;
+  }
+
+  /**
    * Opens the three dots menu.
    * @private
    */
diff --git a/ash/webui/personalization_app/mojom/personalization_app.mojom b/ash/webui/personalization_app/mojom/personalization_app.mojom
index 9a3017c7..2ad1e93f 100644
--- a/ash/webui/personalization_app/mojom/personalization_app.mojom
+++ b/ash/webui/personalization_app/mojom/personalization_app.mojom
@@ -358,6 +358,9 @@
     // Cancels the wallpaper preview and reverts to the user wallpaper. Must be
     // called in preview mode.
     CancelPreviewWallpaper();
+
+    // Determines whether to show the time of day wallpaper dialog.
+    ShouldShowTimeOfDayWallpaperDialog() => (bool should_show_dialog);
 };
 
 // Static color options used to generate the system UI's color palette. These
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index bb3ef30..59ca2e59 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -348,7 +348,17 @@
       {"timeOfDayBannerDescriptionNoScreensaver",
        IDS_PERSONALIZATION_APP_TIME_OF_DAY_BANNER_DESCRIPTION_NO_SCREENSAVER},
       {"timeOfDayWallpaperCollectionSublabel",
-       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_COLLECTION_SUBLABEL}};
+       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_COLLECTION_SUBLABEL},
+
+      // TODO(b/309715996): Add finalized strings.
+      {"timeOfDayWallpaperDialogTitle",
+       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_TITLE},
+      {"timeOfDayWallpaperDialogContent",
+       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONTENT},
+      {"timeOfDayWallpaperDialogBackButton",
+       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_BACK_BUTTON},
+      {"timeOfDayWallpaperDialogConfirmButton",
+       IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONFIRM_BUTTON}};
 
   source->AddLocalizedStrings(kLocalizedStrings);
 
@@ -516,6 +526,9 @@
   source->AddBoolean("isTimeOfDayWallpaperEnabled",
                      features::IsTimeOfDayWallpaperEnabled());
 
+  source->AddBoolean("isTimeOfDayWallpaperForcedAutoScheduleEnabled",
+                     features::IsTimeOfDayWallpaperForcedAutoScheduleEnabled());
+
   source->AddBoolean(
       "isSeaPenEnabled",
       features::IsSeaPenEnabled() && manta::features::IsMantaServiceEnabled());
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index b5da99b..3c68700 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -90,6 +90,7 @@
     "js/wallpaper/sea_pen/sea_pen_images_element.ts",
     "js/wallpaper/sea_pen/sea_pen_recent_wallpapers_element.ts",
     "js/wallpaper/sea_pen/sea_pen_templates_element.ts",
+    "js/wallpaper/time_of_day_wallpaper_dialog_element.ts",
     "js/wallpaper/wallpaper_collections_element.ts",
     "js/wallpaper/wallpaper_error_element.ts",
     "js/wallpaper/wallpaper_fullscreen_element.ts",
diff --git a/ash/webui/personalization_app/resources/js/load_time_booleans.ts b/ash/webui/personalization_app/resources/js/load_time_booleans.ts
index e89db04..c09bd0d0 100644
--- a/ash/webui/personalization_app/resources/js/load_time_booleans.ts
+++ b/ash/webui/personalization_app/resources/js/load_time_booleans.ts
@@ -51,6 +51,11 @@
   return loadTimeData.getBoolean('isTimeOfDayWallpaperEnabled');
 }
 
+export function isTimeOfDayWallpaperForcedAutoScheduleEnabled() {
+  return loadTimeData.getBoolean(
+      'isTimeOfDayWallpaperForcedAutoScheduleEnabled');
+}
+
 export function isSeaPenEnabled() {
   return loadTimeData.getBoolean('isSeaPenEnabled');
 }
diff --git a/ash/webui/personalization_app/resources/js/personalization_app.ts b/ash/webui/personalization_app/resources/js/personalization_app.ts
index e9a79f1f..27d224a1 100644
--- a/ash/webui/personalization_app/resources/js/personalization_app.ts
+++ b/ash/webui/personalization_app/resources/js/personalization_app.ts
@@ -135,6 +135,7 @@
 export {WallpaperSubpageElement} from './wallpaper/wallpaper_subpage_element.js';
 export {WallpaperSubpageTopElement} from './wallpaper/wallpaper_subpage_top_element.js';
 export {DailyRefreshType} from './wallpaper/wallpaper_state.js';
+export {TimeOfDayAcceptEvent, TimeOfDayWallpaperDialogElement} from './wallpaper/time_of_day_wallpaper_dialog_element.js';
 
 PersonalizationStore.getInstance().init(emptyState());
 const link = document.querySelector('link[rel=\'icon\']') as HTMLLinkElement;
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/index.ts b/ash/webui/personalization_app/resources/js/wallpaper/index.ts
index 810c834..af89f94 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/index.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/index.ts
@@ -8,6 +8,7 @@
 import './google_photos_photos_element.js';
 import './google_photos_zero_state_element.js';
 import './local_images_element.js';
+import './time_of_day_wallpaper_dialog_element.js';
 import './wallpaper_grid_item_element.js';
 import './wallpaper_collections_element.js';
 import './wallpaper_error_element.js';
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/time_of_day_wallpaper_dialog_element.html b/ash/webui/personalization_app/resources/js/wallpaper/time_of_day_wallpaper_dialog_element.html
new file mode 100644
index 0000000..b46b6558
--- /dev/null
+++ b/ash/webui/personalization_app/resources/js/wallpaper/time_of_day_wallpaper_dialog_element.html
@@ -0,0 +1,24 @@
+<style include="common cros-button-style">
+  h3,
+  p {
+    margin: 0;
+  }
+
+  cr-button + cr-button {
+    margin-inline-start: 8px;
+  }
+</style>
+<cr-dialog id="dialog" show-on-attach>
+  <h3 slot="title">$i18n{timeOfDayWallpaperDialogTitle}</h3>
+  <div slot="body">
+    <p>$i18n{timeOfDayWallpaperDialogContent}</p>
+  </div>
+  <div slot="button-container">
+    <cr-button id="close" on-click="onClickClose_">
+      <span>$i18n{timeOfDayWallpaperDialogBackButton}</span>
+    </cr-button>
+    <cr-button id="accept" on-click="onClickAccept_" class="action-button">
+      <span>$i18n{timeOfDayWallpaperDialogConfirmButton}</span>
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/time_of_day_wallpaper_dialog_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/time_of_day_wallpaper_dialog_element.ts
new file mode 100644
index 0000000..54314be
--- /dev/null
+++ b/ash/webui/personalization_app/resources/js/wallpaper/time_of_day_wallpaper_dialog_element.ts
@@ -0,0 +1,66 @@
+// 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.
+
+/**
+ * @fileoverview Displays a dialog asking the user to whether enable auto dark
+ * light mode or not before setting the time of day wallpaper.
+ */
+
+import '../../css/common.css.js';
+import '../../css/cros_button_style.css.js';
+
+import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js';
+import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {getTemplate} from './time_of_day_wallpaper_dialog_element.html.js';
+
+export class TimeOfDayAcceptEvent extends CustomEvent<null> {
+  static readonly EVENT_NAME = 'time-of-day-wallpaper-dialog-accept';
+
+  constructor() {
+    super(
+        TimeOfDayAcceptEvent.EVENT_NAME,
+        {
+          bubbles: true,
+          composed: true,
+          detail: null,
+        },
+    );
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'time-of-day-wallpaper-dialog': TimeOfDayWallpaperDialogElement;
+  }
+}
+
+export interface TimeOfDayWallpaperDialogElement {
+  $: {dialog: CrDialogElement};
+}
+
+export class TimeOfDayWallpaperDialogElement extends PolymerElement {
+  static get is() {
+    return 'time-of-day-wallpaper-dialog';
+  }
+
+  static get template() {
+    return getTemplate();
+  }
+
+  static get properties() {
+    return {};
+  }
+
+  private onClickAccept_() {
+    this.dispatchEvent(new TimeOfDayAcceptEvent());
+  }
+
+  private onClickClose_() {
+    this.$.dialog.cancel();
+  }
+}
+
+customElements.define(
+    TimeOfDayWallpaperDialogElement.is, TimeOfDayWallpaperDialogElement);
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_actions.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_actions.ts
index 86c2d7bef..3dd32ba 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_actions.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_actions.ts
@@ -50,6 +50,8 @@
   SET_SELECTED_IMAGE = 'set_selected_image',
   SET_UPDATED_DAILY_REFRESH_IMAGE = 'set_updated_daily_refreshed_image',
   SET_FULLSCREEN_ENABLED = 'set_fullscreen_enabled',
+  SET_SHOULD_SHOW_TIME_OF_DAY_WALLPAPER_DIALOG =
+      'set_shoud_show_time_of_day_wallpaper_dialog',
   SET_IMAGE_THUMBNAILS = 'set_image_thumbnails',
   SET_RECENT_WALLPAPER_IMAGES = 'set_recent_wallpaper_images',
 }
@@ -69,7 +71,7 @@
     SetDefaultImageThumbnailAction|SetLocalImageDataAction|SetLocalImagesAction|
     SetUpdatedDailyRefreshImageAction|SetSelectedImageAction|
     SetFullscreenEnabledAction|SetSeaPenThumbnailsAction|
-    SetRecentWallpaperImagesAction;
+    SetRecentWallpaperImagesAction|SetShouldShowTimeOfDayWallpaperDialog;
 
 export interface AppendGooglePhotosAlbumAction extends Action {
   name: WallpaperActionName.APPEND_GOOGLE_PHOTOS_ALBUM;
@@ -557,6 +559,25 @@
 
 
 
+export interface SetShouldShowTimeOfDayWallpaperDialog extends Action {
+  name: WallpaperActionName.SET_SHOULD_SHOW_TIME_OF_DAY_WALLPAPER_DIALOG;
+  shouldShowDialog: boolean;
+}
+
+
+/**
+ * Sets the boolean that determines whether to show the time of day wallpaper
+ * dialog.
+ */
+export function setShouldShowTimeOfDayWallpaperDialog(
+    shouldShowDialog: boolean): SetShouldShowTimeOfDayWallpaperDialog {
+  assert(typeof shouldShowDialog === 'boolean');
+  return {
+    name: WallpaperActionName.SET_SHOULD_SHOW_TIME_OF_DAY_WALLPAPER_DIALOG,
+    shouldShowDialog,
+  };
+}
+
 export interface SetFullscreenEnabledAction extends Action {
   name: WallpaperActionName.SET_FULLSCREEN_ENABLED;
   enabled: boolean;
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts
index 5cb1fb0..ad16f66c 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_controller.ts
@@ -557,6 +557,16 @@
   provider.makeOpaque();
 }
 
+export async function getShouldShowTimeOfDayWallpaperDialog(
+    provider: WallpaperProviderInterface, store: PersonalizationStore) {
+  const {shouldShowDialog} =
+      await provider.shouldShowTimeOfDayWallpaperDialog();
+
+  // Dispatch action to set the should show dialog boolean.
+  store.dispatch(
+      action.setShouldShowTimeOfDayWallpaperDialog(shouldShowDialog));
+}
+
 /**
  * Fetches list of collections, then fetches list of images for each
  * collection.
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html
index bb60569..a500cf7 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.html
@@ -37,4 +37,10 @@
       </template>
     </iron-list>
   </template>
+  <template is="dom-if" if="[[pendingTimeOfDayWallpaper_]]" restamp>
+    <time-of-day-wallpaper-dialog
+        on-time-of-day-wallpaper-dialog-accept="onConfirmTimeOfDayDialog_"
+        on-cancel="onCloseTimeOfDayDialog_">
+    </time-of-day-wallpaper-dialog>
+  </template>
 </main>
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts
index 871a210..d5423e0 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_images_element.ts
@@ -16,14 +16,17 @@
 
 import {CurrentWallpaper, OnlineImageType, WallpaperCollection, WallpaperImage, WallpaperType} from '../../personalization_app.mojom-webui.js';
 import {dismissTimeOfDayBanner} from '../ambient/ambient_controller.js';
+import {isTimeOfDayWallpaperForcedAutoScheduleEnabled} from '../load_time_booleans.js';
 import {PersonalizationRouterElement} from '../personalization_router_element.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
+import {setColorModeAutoSchedule} from '../theme/theme_controller.js';
+import {getThemeProvider} from '../theme/theme_interface_provider.js';
 import {ThemeObserver} from '../theme/theme_observer.js';
 import {isNonEmptyArray} from '../utils.js';
 
 import {ImageTile} from './constants.js';
 import {getLoadingPlaceholderAnimationDelay, getLoadingPlaceholders, isWallpaperImage} from './utils.js';
-import {selectWallpaper} from './wallpaper_controller.js';
+import {getShouldShowTimeOfDayWallpaperDialog, selectWallpaper} from './wallpaper_controller.js';
 import {WallpaperGridItemSelectedEvent} from './wallpaper_grid_item_element';
 import {getTemplate} from './wallpaper_images_element.html.js';
 import {getWallpaperProvider} from './wallpaper_interface_provider.js';
@@ -168,6 +171,15 @@
             'computeTiles_(images_, imagesLoading_, collectionId, isDarkModeActive)',
         observer: 'onTilesChanged_',
       },
+
+      /**
+       * The pending ToD wallpaper to be set when the dialog is displayed.
+       */
+      pendingTimeOfDayWallpaper_: Object,
+
+      colorModeAutoScheduleEnabled_: Boolean,
+
+      showTimeOfDayWallpaperDialog_: Boolean,
     };
   }
 
@@ -181,6 +193,9 @@
   private pendingSelectedUnitId_: bigint|null;
   private hasError_: boolean;
   private tiles_: ImageTile[];
+  private pendingTimeOfDayWallpaper_: WallpaperImage|null;
+  private colorModeAutoScheduleEnabled_: boolean|null;
+  private showTimeOfDayWallpaperDialog_: boolean;
 
   override connectedCallback() {
     super.connectedCallback();
@@ -200,6 +215,12 @@
         state => isWallpaperImage(state.wallpaper.pendingSelected) ?
             state.wallpaper.pendingSelected.unitId :
             null);
+    this.watch<WallpaperImagesElement['colorModeAutoScheduleEnabled_']>(
+        'colorModeAutoScheduleEnabled_',
+        state => state.theme.colorModeAutoScheduleEnabled);
+    this.watch<WallpaperImagesElement['showTimeOfDayWallpaperDialog_']>(
+        'showTimeOfDayWallpaperDialog_',
+        state => state.wallpaper.shouldShowTimeOfDayWallpaperDialog);
     this.updateFromStore();
   }
 
@@ -311,17 +332,48 @@
     return this.isImageTile_(tile) && !!tile.isTimeOfDayWallpaper;
   }
 
-  private onImageSelected_(e: WallpaperGridItemSelectedEvent&
-                           {model: {item: ImageTile}}) {
+  private async onImageSelected_(e: WallpaperGridItemSelectedEvent&
+                                 {model: {item: ImageTile}}) {
     const unitId = e.model.item.unitId;
     assert(unitId && typeof unitId === 'bigint', 'unitId not found');
     const images = this.images_[this.collectionId]!;
     assert(isNonEmptyArray(images));
     const selectedImage = images.find(choice => choice.unitId === unitId);
     assert(selectedImage, 'could not find selected image');
+    if (await this.shouldShowTimeOfDayWallpaperDialog_(e.model.item)) {
+      this.pendingTimeOfDayWallpaper_ = selectedImage;
+      return;
+    }
     selectWallpaper(selectedImage, getWallpaperProvider(), this.getStore());
   }
 
+  private async shouldShowTimeOfDayWallpaperDialog_(tile: ImageTile):
+      Promise<boolean> {
+    if (isTimeOfDayWallpaperForcedAutoScheduleEnabled()) {
+      await getShouldShowTimeOfDayWallpaperDialog(
+          getWallpaperProvider(), this.getStore());
+    }
+    return this.isTimeOfDayWallpaper_(tile) &&
+        this.showTimeOfDayWallpaperDialog_ &&
+        !this.colorModeAutoScheduleEnabled_;
+  }
+
+  private onCloseTimeOfDayDialog_() {
+    assert(
+        this.pendingTimeOfDayWallpaper_,
+        'could not find the time of day wallpaper');
+    selectWallpaper(
+        this.pendingTimeOfDayWallpaper_, getWallpaperProvider(),
+        this.getStore());
+    this.pendingTimeOfDayWallpaper_ = null;
+  }
+
+  private onConfirmTimeOfDayDialog_() {
+    setColorModeAutoSchedule(
+        /*enabled=*/ true, getThemeProvider(), this.getStore());
+    this.onCloseTimeOfDayDialog_();
+  }
+
   private getAriaLabel_(tile: number|ImageTile): string {
     if (this.isLoadingTile_(tile)) {
       return this.i18n('ariaLabelLoading');
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts
index 1e6f285..2ca3530 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_reducers.ts
@@ -427,6 +427,16 @@
   }
 }
 
+function shouldShowTimeOfDayWallpaperDialogReducer(
+    state: boolean, action: Actions, _: PersonalizationState): boolean {
+  switch (action.name) {
+    case WallpaperActionName.SET_SHOULD_SHOW_TIME_OF_DAY_WALLPAPER_DIALOG:
+      return action.shouldShowDialog;
+    default:
+      return state;
+  }
+}
+
 function googlePhotosReducer(
     state: WallpaperState['googlePhotos'], action: Actions,
     _: PersonalizationState): WallpaperState['googlePhotos'] {
@@ -662,6 +672,8 @@
       pendingSelected: pendingSelectedReducer,
       dailyRefresh: dailyRefreshReducer,
       fullscreen: fullscreenReducer,
+      shouldShowTimeOfDayWallpaperDialog:
+          shouldShowTimeOfDayWallpaperDialogReducer,
       googlePhotos: googlePhotosReducer,
       seaPen: seaPenReducer,
     };
diff --git a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_state.ts b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_state.ts
index 8e8b216..c63c83b 100644
--- a/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_state.ts
+++ b/ash/webui/personalization_app/resources/js/wallpaper/wallpaper_state.ts
@@ -127,6 +127,7 @@
   pendingSelected: DisplayableImage|null;
   dailyRefresh: DailyRefreshState|null;
   fullscreen: boolean;
+  shouldShowTimeOfDayWallpaperDialog: boolean;
   googlePhotos: GooglePhotosState;
   seaPen: SeaPenState;
 }
@@ -158,6 +159,7 @@
     pendingSelected: null,
     dailyRefresh: null,
     fullscreen: false,
+    shouldShowTimeOfDayWallpaperDialog: false,
     googlePhotos: {
       enabled: undefined,
       albums: undefined,
diff --git a/ash/webui/personalization_app/test/personalization_app_mojom_banned_mocha_test_base.cc b/ash/webui/personalization_app/test/personalization_app_mojom_banned_mocha_test_base.cc
index 2cf3dbc..bb42893 100644
--- a/ash/webui/personalization_app/test/personalization_app_mojom_banned_mocha_test_base.cc
+++ b/ash/webui/personalization_app/test/personalization_app_mojom_banned_mocha_test_base.cc
@@ -279,6 +279,10 @@
               (override));
   MOCK_METHOD(void, ConfirmPreviewWallpaper, (), (override));
   MOCK_METHOD(void, CancelPreviewWallpaper, (), (override));
+  MOCK_METHOD(void,
+              ShouldShowTimeOfDayWallpaperDialog,
+              (ShouldShowTimeOfDayWallpaperDialogCallback callback),
+              (override));
 };
 
 class MockPersonalizationAppUserProvider
diff --git a/ash/wm/default_state.cc b/ash/wm/default_state.cc
index a3e43b6c..bff7fd17 100644
--- a/ash/wm/default_state.cc
+++ b/ash/wm/default_state.cc
@@ -41,10 +41,6 @@
 
 using ::chromeos::WindowStateType;
 
-// This specifies how much percent (30%) of a window rect
-// must be visible when the window is added to the workspace.
-const float kMinimumPercentOnScreenArea = 0.3f;
-
 // When a window that has restore bounds at least as large as a work area is
 // unmaximized, inset the bounds slightly so that they are not exactly the same.
 // This makes it easier to resize the window.
diff --git a/ash/wm/desks/templates/saved_desk_dialog_controller.cc b/ash/wm/desks/templates/saved_desk_dialog_controller.cc
index c16ee35..10caca6 100644
--- a/ash/wm/desks/templates/saved_desk_dialog_controller.cc
+++ b/ash/wm/desks/templates/saved_desk_dialog_controller.cc
@@ -7,7 +7,6 @@
 #include "ash/accessibility/accessibility_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/style/ash_color_provider.h"
 #include "ash/style/system_dialog_delegate_view.h"
 #include "ash/style/typography.h"
 #include "ash/wm/desks/templates/saved_desk_grid_view.h"
@@ -19,17 +18,11 @@
 #include "ash/wm/overview/overview_grid.h"
 #include "ash/wm/window_properties.h"
 #include "base/memory/raw_ptr.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "ui/aura/env.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/metadata/metadata_header_macros.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/chromeos/styles/cros_tokens_color_mappings.h"
-#include "ui/strings/grit/ui_strings.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/layout/layout_provider.h"
-#include "ui/views/window/dialog_delegate.h"
 
 namespace ash {
 
@@ -43,80 +36,6 @@
 
 }  // namespace
 
-// The client view of the dialog. Contains a label which is a description, and
-// optionally a couple images of unsupported apps.
-class SavedDeskDialog : public views::DialogDelegateView {
- public:
-  METADATA_HEADER(SavedDeskDialog);
-
-  SavedDeskDialog() {
-    SetButtonLabel(ui::DIALOG_BUTTON_CANCEL,
-                   l10n_util::GetStringUTF16(IDS_APP_CANCEL));
-
-    auto* layout_provider = views::LayoutProvider::Get();
-    set_fixed_width(layout_provider->GetDistanceMetric(
-        views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
-    SetLayoutManager(std::make_unique<views::BoxLayout>(
-        views::BoxLayout::Orientation::kVertical,
-        layout_provider->GetInsetsMetric(views::InsetsMetric::INSETS_DIALOG),
-        layout_provider->GetDistanceMetric(
-            views::DISTANCE_RELATED_CONTROL_VERTICAL)));
-
-    // Add the description for the dialog.
-    AddChildView(
-        views::Builder<views::Label>()
-            .CopyAddressTo(&description_label_)
-            .SetFontList(gfx::FontList({"Roboto"}, gfx::Font::NORMAL, 14,
-                                       gfx::Font::Weight::NORMAL))
-            .SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
-                AshColorProvider::ContentLayerType::kTextColorPrimary))
-            .SetMultiLine(true)
-            .SetHorizontalAlignment(gfx::ALIGN_LEFT)
-            .Build());
-  }
-  SavedDeskDialog(const SavedDeskDialog&) = delete;
-  SavedDeskDialog& operator=(const SavedDeskDialog&) = delete;
-  ~SavedDeskDialog() override = default;
-
-  void SetTitleText(int message_id) {
-    SetTitle(l10n_util::GetStringUTF16(message_id));
-  }
-
-  void SetConfirmButtonText(int message_id) {
-    SetButtonLabel(ui::DIALOG_BUTTON_OK, l10n_util::GetStringUTF16(message_id));
-  }
-
-  void SetDescriptionText(const std::u16string& text) {
-    DCHECK(description_label_);
-    description_label_->SetText(text);
-  }
-
-  void SetDescriptionAccessibleName(const std::u16string& accessible_name) {
-    DCHECK(description_label_);
-    description_label_->SetAccessibleName(accessible_name);
-  }
-
- private:
-  raw_ptr<views::Label, ExperimentalAsh> description_label_ = nullptr;
-};
-
-BEGIN_VIEW_BUILDER(/* no export */, SavedDeskDialog, views::DialogDelegateView)
-VIEW_BUILDER_PROPERTY(int, TitleText)
-VIEW_BUILDER_PROPERTY(int, ConfirmButtonText)
-VIEW_BUILDER_PROPERTY(std::u16string, DescriptionText)
-VIEW_BUILDER_PROPERTY(std::u16string, DescriptionAccessibleName)
-END_VIEW_BUILDER
-
-BEGIN_METADATA(SavedDeskDialog, views::DialogDelegateView)
-END_METADATA
-
-}  // namespace ash
-
-// Must be in global namespace and defined before usage.
-DEFINE_VIEW_BUILDER(/* no export */, ash::SavedDeskDialog)
-
-namespace ash {
-
 //-----------------------------------------------------------------------------
 // SavedDeskDialogController:
 
@@ -155,83 +74,46 @@
         IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_INCOGNITO_DIALOG_DESCRIPTION;
   }
 
-  if (chromeos::features::IsJellyEnabled()) {
-    auto unsupported_apps_view = std::make_unique<views::BoxLayoutView>();
-    unsupported_apps_view->SetOrientation(
-        views::BoxLayout::Orientation::kVertical);
-    unsupported_apps_view->SetBetweenChildSpacing(kUnsupportedAppsViewSpacing);
+  auto unsupported_apps_view = std::make_unique<views::BoxLayoutView>();
+  unsupported_apps_view->SetOrientation(
+      views::BoxLayout::Orientation::kVertical);
+  unsupported_apps_view->SetBetweenChildSpacing(kUnsupportedAppsViewSpacing);
 
-    auto label_view = std::make_unique<views::Label>();
-    label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosButton2,
-                                          *label_view);
-    label_view->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);
-    label_view->SetText(l10n_util::GetStringUTF16(
-        IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_DIALOG_HEADER));
-    unsupported_apps_view->AddChildView(std::move(label_view));
+  auto label_view = std::make_unique<views::Label>();
+  label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosButton2,
+                                        *label_view);
+  label_view->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);
+  label_view->SetText(l10n_util::GetStringUTF16(
+      IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_DIALOG_HEADER));
+  unsupported_apps_view->AddChildView(std::move(label_view));
 
-    auto icon_container_view = std::make_unique<SavedDeskIconContainer>();
-    icon_container_view->PopulateIconContainerFromWindows(unsupported_apps);
-    unsupported_apps_view->AddChildView(std::move(icon_container_view));
-    auto dialog =
-        views::Builder<SystemDialogDelegateView>()
-            .SetTitleText(l10n_util::GetStringUTF16(
-                unsupported_apps_template_->type() ==
-                        DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_IN_TEMPLATE_DIALOG_TITLE
-                    : IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_IN_DESK_DIALOG_TITLE))
-            .SetAcceptButtonText(l10n_util::GetStringUTF16(
-                IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_DIALOG_CONFIRM_BUTTON))
-            .SetDescription(l10n_util::GetStringUTF16(app_description_id))
-            .SetCancelCallback(base::BindOnce(
-                &SavedDeskDialogController::OnUserCanceledUnsupportedAppsDialog,
-                weak_ptr_factory_.GetWeakPtr()))
-            .SetCloseCallback(base::BindOnce(
-                &SavedDeskDialogController::OnUserCanceledUnsupportedAppsDialog,
-                weak_ptr_factory_.GetWeakPtr()))
-            .SetAcceptCallback(base::BindOnce(
-                &SavedDeskDialogController::OnUserAcceptedUnsupportedAppsDialog,
-                weak_ptr_factory_.GetWeakPtr()))
-            .Build();
-    dialog->SetMiddleContentView(std::move(unsupported_apps_view));
-    dialog->SetMiddleContentAlignment(views::LayoutAlignment::kStart);
-    CreateDialogWidget(std::move(dialog), root_window);
-  } else {
-    auto dialog =
-        views::Builder<SavedDeskDialog>()
-            .SetTitleText(
-                unsupported_apps_template_->type() ==
-                        DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_IN_TEMPLATE_DIALOG_TITLE
-                    : IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_IN_DESK_DIALOG_TITLE)
-            .SetConfirmButtonText(
-                IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_DIALOG_CONFIRM_BUTTON)
-            .SetDescriptionText(l10n_util::GetStringUTF16(app_description_id))
-            .SetCancelCallback(base::BindOnce(
-                &SavedDeskDialogController::OnUserCanceledUnsupportedAppsDialog,
-                weak_ptr_factory_.GetWeakPtr()))
-            .SetCloseCallback(base::BindOnce(
-                &SavedDeskDialogController::OnUserCanceledUnsupportedAppsDialog,
-                weak_ptr_factory_.GetWeakPtr()))
-            .SetAcceptCallback(base::BindOnce(
-                &SavedDeskDialogController::OnUserAcceptedUnsupportedAppsDialog,
-                weak_ptr_factory_.GetWeakPtr()))
-            .AddChildren(
-                views::Builder<views::Label>()
-                    .SetHorizontalAlignment(gfx::ALIGN_LEFT)
-                    .SetFontList(gfx::FontList({"Roboto"}, gfx::Font::NORMAL,
-                                               14, gfx::Font::Weight::MEDIUM))
-                    .SetEnabledColor(
-                        AshColorProvider::Get()->GetContentLayerColor(
-                            AshColorProvider::ContentLayerType::
-                                kTextColorPrimary))
-                    .SetText(l10n_util::GetStringUTF16(
-                        IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_DIALOG_HEADER)),
-                views::Builder<SavedDeskIconContainer>()
-                    .PopulateIconContainerFromWindows(unsupported_apps))
-            .Build();
-    CreateDialogWidget(std::move(dialog), root_window);
-  }
+  auto icon_container_view = std::make_unique<SavedDeskIconContainer>();
+  icon_container_view->PopulateIconContainerFromWindows(unsupported_apps);
+  unsupported_apps_view->AddChildView(std::move(icon_container_view));
+  auto dialog =
+      views::Builder<SystemDialogDelegateView>()
+          .SetTitleText(l10n_util::GetStringUTF16(
+              unsupported_apps_template_->type() == DeskTemplateType::kTemplate
+                  ? IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_IN_TEMPLATE_DIALOG_TITLE
+                  : IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_IN_DESK_DIALOG_TITLE))
+          .SetAcceptButtonText(l10n_util::GetStringUTF16(
+              IDS_ASH_DESKS_TEMPLATES_UNSUPPORTED_APPS_DIALOG_CONFIRM_BUTTON))
+          .SetDescription(l10n_util::GetStringUTF16(app_description_id))
+          .SetCancelCallback(base::BindOnce(
+              &SavedDeskDialogController::OnUserCanceledUnsupportedAppsDialog,
+              weak_ptr_factory_.GetWeakPtr()))
+          .SetCloseCallback(base::BindOnce(
+              &SavedDeskDialogController::OnUserCanceledUnsupportedAppsDialog,
+              weak_ptr_factory_.GetWeakPtr()))
+          .SetAcceptCallback(base::BindOnce(
+              &SavedDeskDialogController::OnUserAcceptedUnsupportedAppsDialog,
+              weak_ptr_factory_.GetWeakPtr()))
+          .Build();
+  dialog->SetMiddleContentView(std::move(unsupported_apps_view));
+  dialog->SetMiddleContentAlignment(views::LayoutAlignment::kStart);
+  CreateDialogWidget(std::move(dialog), root_window);
+
   RecordUnsupportedAppDialogShowHistogram(unsupported_apps_template_->type());
 }
 
@@ -244,53 +126,28 @@
   if (!CanShowDialog()) {
     return;
   }
-  if (chromeos::features::IsJellyEnabled()) {
-    auto dialog =
-        views::Builder<SystemDialogDelegateView>()
-            .SetTitleText(l10n_util::GetStringUTF16(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_TITLE
-                    : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_TITLE))
-            .SetAcceptButtonText(l10n_util::GetStringUTF16(
-                IDS_ASH_DESKS_TEMPLATES_REPLACE_DIALOG_CONFIRM_BUTTON))
-            .SetDescription(l10n_util::GetStringFUTF16(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_DESCRIPTION
-                    : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_DESCRIPTION,
-                GetStringWithQuotes(template_name)))
-            .SetDescriptionAccessibleName(l10n_util::GetStringFUTF16(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_DESCRIPTION
-                    : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_DESCRIPTION,
-                template_name))
-            .SetAcceptCallback(std::move(on_accept_callback))
-            .SetCancelCallback(std::move(on_cancel_callback))
-            .Build();
-    CreateDialogWidget(std::move(dialog), root_window);
-  } else {
-    auto dialog =
-        views::Builder<SavedDeskDialog>()
-            .SetTitleText(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_TITLE
-                    : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_TITLE)
-            .SetConfirmButtonText(
-                IDS_ASH_DESKS_TEMPLATES_REPLACE_DIALOG_CONFIRM_BUTTON)
-            .SetDescriptionText(l10n_util::GetStringFUTF16(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_DESCRIPTION
-                    : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_DESCRIPTION,
-                GetStringWithQuotes(template_name)))
-            .SetDescriptionAccessibleName(l10n_util::GetStringFUTF16(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_DESCRIPTION
-                    : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_DESCRIPTION,
-                template_name))
-            .SetAcceptCallback(std::move(on_accept_callback))
-            .SetCancelCallback(std::move(on_cancel_callback))
-            .Build();
-    CreateDialogWidget(std::move(dialog), root_window);
-  }
+  auto dialog =
+      views::Builder<SystemDialogDelegateView>()
+          .SetTitleText(l10n_util::GetStringUTF16(
+              template_type == DeskTemplateType::kTemplate
+                  ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_TITLE
+                  : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_TITLE))
+          .SetAcceptButtonText(l10n_util::GetStringUTF16(
+              IDS_ASH_DESKS_TEMPLATES_REPLACE_DIALOG_CONFIRM_BUTTON))
+          .SetDescription(l10n_util::GetStringFUTF16(
+              template_type == DeskTemplateType::kTemplate
+                  ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_DESCRIPTION
+                  : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_DESCRIPTION,
+              GetStringWithQuotes(template_name)))
+          .SetDescriptionAccessibleName(l10n_util::GetStringFUTF16(
+              template_type == DeskTemplateType::kTemplate
+                  ? IDS_ASH_DESKS_TEMPLATES_REPLACE_TEMPLATE_DIALOG_DESCRIPTION
+                  : IDS_ASH_DESKS_TEMPLATES_REPLACE_DESK_DIALOG_DESCRIPTION,
+              template_name))
+          .SetAcceptCallback(std::move(on_accept_callback))
+          .SetCancelCallback(std::move(on_cancel_callback))
+          .Build();
+  CreateDialogWidget(std::move(dialog), root_window);
 }
 
 void SavedDeskDialogController::ShowDeleteDialog(
@@ -301,45 +158,22 @@
   if (!CanShowDialog()) {
     return;
   }
-  if (chromeos::features::IsJellyEnabled()) {
-    auto dialog =
-        views::Builder<SystemDialogDelegateView>()
-            .SetTitleText(l10n_util::GetStringUTF16(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_DELETE_TEMPLATE_DIALOG_TITLE
-                    : IDS_ASH_DESKS_TEMPLATES_DELETE_DESK_DIALOG_TITLE))
-            .SetDescription(l10n_util::GetStringFUTF16(
-                IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_DESCRIPTION,
-                GetStringWithQuotes(template_name)))
-            .SetAcceptButtonText(l10n_util::GetStringUTF16(
-                IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_CONFIRM_BUTTON))
-            .SetDescriptionAccessibleName(l10n_util::GetStringFUTF16(
-                IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_DESCRIPTION,
-                template_name))
-            .SetAcceptCallback(std::move(on_accept_callback))
-            .Build();
-    CreateDialogWidget(std::move(dialog), root_window);
-  } else {
-    auto dialog =
-        views::Builder<SavedDeskDialog>()
-            .SetTitleText(
-                template_type == DeskTemplateType::kTemplate
-                    ? IDS_ASH_DESKS_TEMPLATES_DELETE_TEMPLATE_DIALOG_TITLE
-                    : IDS_ASH_DESKS_TEMPLATES_DELETE_DESK_DIALOG_TITLE)
-            .SetButtonLabel(
-                ui::DIALOG_BUTTON_OK,
-                l10n_util::GetStringUTF16(
-                    IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_CONFIRM_BUTTON))
-            .SetDescriptionText(l10n_util::GetStringFUTF16(
-                IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_DESCRIPTION,
-                GetStringWithQuotes(template_name)))
-            .SetDescriptionAccessibleName(l10n_util::GetStringFUTF16(
-                IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_DESCRIPTION,
-                template_name))
-            .SetAcceptCallback(std::move(on_accept_callback))
-            .Build();
-    CreateDialogWidget(std::move(dialog), root_window);
-  }
+  auto dialog =
+      views::Builder<SystemDialogDelegateView>()
+          .SetTitleText(l10n_util::GetStringUTF16(
+              template_type == DeskTemplateType::kTemplate
+                  ? IDS_ASH_DESKS_TEMPLATES_DELETE_TEMPLATE_DIALOG_TITLE
+                  : IDS_ASH_DESKS_TEMPLATES_DELETE_DESK_DIALOG_TITLE))
+          .SetDescription(l10n_util::GetStringFUTF16(
+              IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_DESCRIPTION,
+              GetStringWithQuotes(template_name)))
+          .SetAcceptButtonText(l10n_util::GetStringUTF16(
+              IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_CONFIRM_BUTTON))
+          .SetDescriptionAccessibleName(l10n_util::GetStringFUTF16(
+              IDS_ASH_DESKS_TEMPLATES_DELETE_DIALOG_DESCRIPTION, template_name))
+          .SetAcceptCallback(std::move(on_accept_callback))
+          .Build();
+  CreateDialogWidget(std::move(dialog), root_window);
 }
 
 void SavedDeskDialogController::OnWidgetDestroying(views::Widget* widget) {
@@ -374,21 +208,14 @@
   dialog->SetModalType(ui::MODAL_TYPE_SYSTEM);
   dialog->SetShowCloseButton(false);
 
-  if (chromeos::features::IsJellyEnabled()) {
-    views::Widget::InitParams params;
-    params.type = views::Widget::InitParams::Type::TYPE_WINDOW_FRAMELESS;
-    params.context = root_window;
-    params.init_properties_container.SetProperty(kOverviewUiKey, true);
-    params.delegate = dialog.release();
+  views::Widget::InitParams params;
+  params.type = views::Widget::InitParams::Type::TYPE_WINDOW_FRAMELESS;
+  params.context = root_window;
+  params.init_properties_container.SetProperty(kOverviewUiKey, true);
+  params.delegate = dialog.release();
 
-    dialog_widget_ = new views::Widget();
-    dialog_widget_->Init(std::move(params));
-  } else {
-    dialog_widget_ = views::DialogDelegate::CreateDialogWidget(
-        std::move(dialog),
-        /*context=*/root_window, /*parent=*/nullptr);
-    dialog_widget_->GetNativeWindow()->SetProperty(kOverviewUiKey, true);
-  }
+  dialog_widget_ = new views::Widget();
+  dialog_widget_->Init(std::move(params));
   dialog_widget_->GetNativeWindow()->SetName("TemplateDialogForTesting");
   dialog_widget_->Show();
   dialog_widget_observation_.Observe(dialog_widget_.get());
@@ -404,7 +231,6 @@
 
 const SystemDialogDelegateView*
 SavedDeskDialogController::GetSystemDialogViewForTesting() const {
-  CHECK(chromeos::features::IsJellyEnabled());
   CHECK(dialog_widget_);
   return static_cast<SystemDialogDelegateView*>(
       dialog_widget_->GetContentsView());
diff --git a/ash/wm/desks/templates/saved_desk_dialog_controller.h b/ash/wm/desks/templates/saved_desk_dialog_controller.h
index 0b68f4e..88c8edd 100644
--- a/ash/wm/desks/templates/saved_desk_dialog_controller.h
+++ b/ash/wm/desks/templates/saved_desk_dialog_controller.h
@@ -62,7 +62,7 @@
   // views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
 
-  // Helper function to get the system dialog view for Jelly tests.
+  // Helper function to get the system dialog view.
   const SystemDialogDelegateView* GetSystemDialogViewForTesting() const;
 
  private:
diff --git a/ash/wm/desks/templates/saved_desk_library_view.cc b/ash/wm/desks/templates/saved_desk_library_view.cc
index d8b1f9d..4e296aaf 100644
--- a/ash/wm/desks/templates/saved_desk_library_view.cc
+++ b/ash/wm/desks/templates/saved_desk_library_view.cc
@@ -7,7 +7,6 @@
 #include "ash/controls/rounded_scroll_bar.h"
 #include "ash/controls/scroll_view_gradient_helper.h"
 #include "ash/public/cpp/desk_template.h"
-#include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -25,11 +24,9 @@
 #include "ash/wm/overview/overview_grid_event_handler.h"
 #include "ash/wm/window_properties.h"
 #include "base/functional/callback_helpers.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "ui/aura/window_targeter.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/event_handler.h"
 #include "ui/gfx/geometry/insets.h"
@@ -47,10 +44,6 @@
 // Grids use landscape mode if the available width is greater or equal to this.
 constexpr int kLandscapeMinWidth = 756;
 
-// Section label dimensions.
-constexpr gfx::Size kLabelSizeLandscape = {708, 24};
-constexpr gfx::Size kLabelSizePortrait = {464, 24};
-
 // "No items" label dimensions.
 constexpr gfx::Size kNoItemsLabelPadding = {16, 8};
 constexpr int kNoItemsLabelHeight = 32;
@@ -64,9 +57,6 @@
 // The size of the gradient applied to the top and bottom of the scroll view.
 constexpr int kScrollViewGradientSize = 32;
 
-// Elevation for the grid label text's shadow.
-constexpr int kLabelTextShadowElevation = 4;
-
 // Insets of Library page scroll content view. Note: the bottom inset is there
 // to slightly adjust the otherwise vertically centered scroll content up a tad.
 constexpr gfx::Insets kLibraryPageScrollContentsInsets =
@@ -130,19 +120,6 @@
   return group_contents;
 }
 
-std::unique_ptr<views::Label> MakeGridLabel(int label_string_id) {
-  auto label = std::make_unique<views::Label>(
-      l10n_util::GetStringUTF16(label_string_id));
-  gfx::ShadowValues shadows =
-      gfx::ShadowValue::MakeChromeOSSystemUIShadowValues(
-          kLabelTextShadowElevation);
-  label->SetShadows(shadows);
-  TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosTitle1, *label);
-  label->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);
-  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  return label;
-}
-
 }  // namespace
 
 // -----------------------------------------------------------------------------
@@ -305,8 +282,6 @@
   // Create grids depending on which features are enabled.
   if (saved_desk_util::AreDesksTemplatesEnabled()) {
     auto group_contents = GetLabelAndGridGroupContents();
-    grid_labels_.push_back(group_contents->AddChildView(
-        MakeGridLabel(IDS_ASH_DESKS_TEMPLATES_LIBRARY_TEMPLATES_GRID_LABEL)));
     desk_template_grid_view_ =
         group_contents->AddChildView(std::make_unique<SavedDeskGridView>());
     grid_views_.push_back(desk_template_grid_view_);
@@ -315,8 +290,6 @@
   }
   if (saved_desk_util::IsSavedDesksEnabled()) {
     auto group_contents = GetLabelAndGridGroupContents();
-    grid_labels_.push_back(group_contents->AddChildView(MakeGridLabel(
-        IDS_ASH_DESKS_TEMPLATES_LIBRARY_SAVE_AND_RECALL_GRID_LABEL)));
     save_and_recall_grid_view_ =
         group_contents->AddChildView(std::make_unique<SavedDeskGridView>());
     grid_views_.push_back(save_and_recall_grid_view_);
@@ -555,27 +528,12 @@
     return;
 
   const bool landscape = width() >= kLandscapeMinWidth;
+  size_t total_saved_desks = 0;
   for (auto* grid_view : grid_views()) {
     grid_view->set_layout_mode(landscape
                                    ? SavedDeskGridView::LayoutMode::LANDSCAPE
                                    : SavedDeskGridView::LayoutMode::PORTRAIT);
-  }
-
-  size_t total_saved_desks = 0;
-
-  DCHECK_EQ(grid_views_.size(), grid_labels_.size());
-  for (size_t i = 0; i != grid_views_.size(); ++i) {
-    if (chromeos::features::IsJellyEnabled()) {
-      // Set label to be invisible to improve Jelly appearance(b/284210964).
-      grid_labels_[i]->SetVisible(false);
-    } else {
-      // Make the grid label invisible if the corresponding grid view is
-      // empty. This will exclude it from the box layout.
-      grid_labels_[i]->SetVisible(!grid_views_[i]->grid_items().empty());
-    }
-    grid_labels_[i]->SetPreferredSize(landscape ? kLabelSizeLandscape
-                                                : kLabelSizePortrait);
-    total_saved_desks += grid_views_[i]->grid_items().size();
+    total_saved_desks += grid_view->grid_items().size();
   }
 
   no_items_label_->SetVisible(total_saved_desks == 0);
diff --git a/ash/wm/desks/templates/saved_desk_library_view.h b/ash/wm/desks/templates/saved_desk_library_view.h
index 9eaeae63..840455a4 100644
--- a/ash/wm/desks/templates/saved_desk_library_view.h
+++ b/ash/wm/desks/templates/saved_desk_library_view.h
@@ -6,7 +6,6 @@
 #define ASH_WM_DESKS_TEMPLATES_SAVED_DESK_LIBRARY_VIEW_H_
 
 #include <memory>
-#include <string>
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
@@ -120,10 +119,6 @@
   // Holds the active ones, for convenience.
   std::vector<SavedDeskGridView*> grid_views_;
 
-  // Owned by views hierarchy. Section headers above grids. Will match size and
-  // order of items in `grid_views_`.
-  std::vector<views::Label*> grid_labels_;
-
   // Label that shows up when the library has no items.
   raw_ptr<views::Label, ExperimentalAsh> no_items_label_ = nullptr;
 
diff --git a/ash/wm/desks/templates/saved_desk_save_desk_button.cc b/ash/wm/desks/templates/saved_desk_save_desk_button.cc
index 6a1a8a7..663d0af2 100644
--- a/ash/wm/desks/templates/saved_desk_save_desk_button.cc
+++ b/ash/wm/desks/templates/saved_desk_save_desk_button.cc
@@ -7,7 +7,6 @@
 #include "ash/style/style_util.h"
 #include "ash/wm/desks/templates/saved_desk_constants.h"
 #include "ash/wm/overview/overview_utils.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/views/controls/focus_ring.h"
@@ -39,9 +38,7 @@
 
   SetBorder(std::make_unique<views::HighlightBorder>(
       kSaveDeskCornerRadius,
-      chromeos::features::IsJellyrollEnabled()
-          ? views::HighlightBorder::Type::kHighlightBorderNoShadow
-          : views::HighlightBorder::Type::kHighlightBorder2));
+      views::HighlightBorder::Type::kHighlightBorderNoShadow));
 
   SetEnableBackgroundBlur(true);
 }
diff --git a/ash/wm/desks/templates/saved_desk_test_util.cc b/ash/wm/desks/templates/saved_desk_test_util.cc
index dfb4982..423800b 100644
--- a/ash/wm/desks/templates/saved_desk_test_util.cc
+++ b/ash/wm/desks/templates/saved_desk_test_util.cc
@@ -6,25 +6,20 @@
 
 #include "ash/shell.h"
 #include "ash/style/icon_button.h"
-#include "ash/wm/desks/expanded_desks_bar_button.h"
 #include "ash/wm/desks/legacy_desk_bar_view.h"
 #include "ash/wm/desks/templates/saved_desk_controller.h"
 #include "ash/wm/desks/templates/saved_desk_dialog_controller.h"
 #include "ash/wm/desks/templates/saved_desk_icon_container.h"
 #include "ash/wm/desks/templates/saved_desk_presenter.h"
 #include "ash/wm/desks/templates/saved_desk_util.h"
-#include "ash/wm/desks/zero_state_button.h"
 #include "ash/wm/overview/overview_grid.h"
-#include "ash/wm/overview/overview_test_util.h"
 #include "ash/wm/overview/overview_utils.h"
 #include "base/memory/raw_ref.h"
 #include "base/test/bind.h"
-#include "chromeos/constants/chromeos_features.h"
 #include "ui/compositor/layer.h"
 #include "ui/views/animation/bounds_animator.h"
 #include "ui/views/animation/bounds_animator_observer.h"
 #include "ui/views/widget/widget_delegate.h"
-#include "ui/views/window/dialog_delegate.h"
 
 namespace ash {
 
@@ -254,20 +249,11 @@
 }
 
 const views::Button* GetSavedDeskDialogAcceptButton() {
-  if (chromeos::features::IsJellyEnabled()) {
-    const SystemDialogDelegateView* dialog_widget_view =
-        saved_desk_util::GetSavedDeskDialogController()
-            ->GetSystemDialogViewForTesting();
-    if (!dialog_widget_view) {
-      return nullptr;
-    }
+  if (auto* dialog_widget_view = saved_desk_util::GetSavedDeskDialogController()
+                                     ->GetSystemDialogViewForTesting()) {
     return dialog_widget_view->GetAcceptButtonForTesting();
   }
-  const views::Widget* dialog_widget =
-      saved_desk_util::GetSavedDeskDialogController()->dialog_widget();
-  if (!dialog_widget)
-    return nullptr;
-  return dialog_widget->widget_delegate()->AsDialogDelegate()->GetOkButton();
+  return nullptr;
 }
 
 void WaitForSavedDeskUI() {
diff --git a/ash/wm/desks/templates/saved_desk_unittest.cc b/ash/wm/desks/templates/saved_desk_unittest.cc
index 81be8631..2fe9f7f 100644
--- a/ash/wm/desks/templates/saved_desk_unittest.cc
+++ b/ash/wm/desks/templates/saved_desk_unittest.cc
@@ -72,6 +72,7 @@
 #include "components/app_restore/window_properties.h"
 #include "components/desks_storage/core/local_desk_data_manager.h"
 #include "ui/aura/client/aura_constants.h"
+#include "ui/aura/test/test_window_delegate.h"
 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
@@ -2375,6 +2376,42 @@
   EXPECT_EQ(test_window.get(), window_util::GetActiveWindow());
 }
 
+// Tests that we can open the saved desk library if the current active window
+// prior to entering overview is one that activates on occlusion change.
+// Regression test for http://b/307963349.
+TEST_F(SavedDeskTest, ShowLibraryWithWindowActivatingOnOcclusion) {
+  // Need at least one entry to show the saved desk library.
+  AddEntry(base::Uuid::GenerateRandomV4(), "template name", base::Time::Now(),
+           DeskTemplateType::kTemplate);
+
+  // Create a test window that has similar properties to a browser window with
+  // the print preview showing. Its occlusion state is tracked, and when
+  // occlusion state changes, the window gets activated.
+  auto window = CreateToplevelTestWindow(gfx::Rect(400, 300));
+  window->TrackOcclusionState();
+  window->Show();
+
+  auto* window_delegate =
+      static_cast<aura::test::TestWindowDelegate*>(window->delegate());
+  window_delegate->set_on_occlusion_changed(
+      base::BindLambdaForTesting([&]() { wm::ActivateWindow(window.get()); }));
+
+  // Entering overview also pauses the occlusion tracker. Wait a bit for it to
+  // unpause other occlusion won't try to be recomputed when showing the
+  // library.
+  ToggleOverview();
+  base::RunLoop loop;
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE, loop.QuitClosure(), base::Milliseconds(200));
+  loop.Run();
+
+  // Show the saved desk library. We should stay in overview, and there should
+  // be no u-a-f.
+  ShowSavedDeskLibrary();
+  WaitForSavedDeskLibrary();
+  EXPECT_TRUE(InOverviewSession());
+}
+
 TEST_F(SavedDeskTest, TemplateNameBounds) {
   AddEntry(base::Uuid::GenerateRandomV4(), "template name", base::Time::Now(),
            DeskTemplateType::kTemplate);
diff --git a/ash/wm/overview/overview_controller.cc b/ash/wm/overview/overview_controller.cc
index 93da2b6..d653eaa 100644
--- a/ash/wm/overview/overview_controller.cc
+++ b/ash/wm/overview/overview_controller.cc
@@ -296,6 +296,32 @@
   return windows;
 }
 
+bool OverviewController::CanEnterOverview() const {
+  if (!DesksController::Get()->CanEnterOverview()) {
+    return false;
+  }
+
+  if (SnapGroupController* snap_group_controller = SnapGroupController::Get();
+      snap_group_controller && !snap_group_controller->CanEnterOverview()) {
+    return false;
+  }
+
+  // Prevent entering overview while the divider is dragged or animated.
+  if (IsSplitViewDividerDraggedOrAnimated()) {
+    return false;
+  }
+
+  // Don't allow a window overview if the user session is not active (e.g.
+  // locked or in user-adding screen) or a modal dialog is open or running in
+  // kiosk app session.
+  SessionControllerImpl* session_controller =
+      Shell::Get()->session_controller();
+  return session_controller->GetSessionState() ==
+             session_manager::SessionState::ACTIVE &&
+         !Shell::IsSystemModalWindowOpen() &&
+         !Shell::Get()->screen_pinning_controller()->IsPinned();
+}
+
 void OverviewController::ToggleOverview(OverviewEnterExitType type) {
   // Pause raster scale updates while the overview is being toggled. This is to
   // handle the case where a mirror view is deleted then recreated when
@@ -505,33 +531,7 @@
   }
 }
 
-bool OverviewController::CanEnterOverview() {
-  if (!DesksController::Get()->CanEnterOverview()) {
-    return false;
-  }
-
-  if (SnapGroupController* snap_group_controller = SnapGroupController::Get();
-      snap_group_controller && !snap_group_controller->CanEnterOverview()) {
-    return false;
-  }
-
-  // Prevent entering overview while the divider is dragged or animated.
-  if (IsSplitViewDividerDraggedOrAnimated()) {
-    return false;
-  }
-
-  // Don't allow a window overview if the user session is not active (e.g.
-  // locked or in user-adding screen) or a modal dialog is open or running in
-  // kiosk app session.
-  SessionControllerImpl* session_controller =
-      Shell::Get()->session_controller();
-  return session_controller->GetSessionState() ==
-             session_manager::SessionState::ACTIVE &&
-         !Shell::IsSystemModalWindowOpen() &&
-         !Shell::Get()->screen_pinning_controller()->IsPinned();
-}
-
-bool OverviewController::CanEndOverview(OverviewEnterExitType type) {
+bool OverviewController::CanEndOverview(OverviewEnterExitType type) const {
   // Prevent ending overview while the divider is dragged or animated.
   if (IsSplitViewDividerDraggedOrAnimated())
     return false;
diff --git a/ash/wm/overview/overview_controller.h b/ash/wm/overview/overview_controller.h
index 5410465..07d5083e 100644
--- a/ash/wm/overview/overview_controller.h
+++ b/ash/wm/overview/overview_controller.h
@@ -125,6 +125,11 @@
   // overview mode is active for testing.
   std::vector<aura::Window*> GetWindowsListInOverviewGridsForTest();
 
+  // Returns true if it's possible to enter overview mode in the current
+  // configuration. This can be false at certain times, such as when the lock
+  // screen is visible we can't overview mode.
+  bool CanEnterOverview() const;
+
  private:
   friend class SavedDeskTest;
 
@@ -137,11 +142,10 @@
   void ToggleOverview(
       OverviewEnterExitType type = OverviewEnterExitType::kNormal);
 
-  // Returns true if it's possible to enter or exit overview mode in the current
-  // configuration. This can be false at certain times, such as when the lock
-  // screen is visible we can't overview mode.
-  bool CanEnterOverview();
-  bool CanEndOverview(OverviewEnterExitType type);
+  // Returns true if it's possible to exit overview mode in the current
+  // configuration. This can be false at certain times, such as when the divider
+  // or desks are animating.
+  bool CanEndOverview(OverviewEnterExitType type) const;
 
   void OnStartingAnimationComplete(bool canceled);
   void OnEndingAnimationComplete(bool canceled);
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index 47f02e8..3c17571 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -1890,8 +1890,8 @@
         /*desks=*/DesksController::Get()->desks(),
         /*notify_when_destroyed=*/true);
 
-    for (auto& overview_mode_item : window_list_) {
-      overview_mode_item->HideForSavedDeskLibrary(/*animate=*/true);
+    for (auto& overview_item : window_list_) {
+      overview_item->HideForSavedDeskLibrary(/*animate=*/true);
     }
   }
 
@@ -1905,8 +1905,7 @@
   saved_desk_library_widget_->Show();
 
   // Fade in the widget from its current opacity.
-  PerformFadeInLayer(saved_desk_library_widget_->GetLayer(),
-                     /*animate=*/true);
+  PerformFadeInLayer(saved_desk_library_widget_->GetLayer(), /*animate=*/true);
 
   UpdateSaveDeskButtons();
 
diff --git a/ash/wm/overview/overview_group_item.cc b/ash/wm/overview/overview_group_item.cc
index 7727be84..2ab4514 100644
--- a/ash/wm/overview/overview_group_item.cc
+++ b/ash/wm/overview/overview_group_item.cc
@@ -148,7 +148,11 @@
   return nullptr;
 }
 
-void OverviewGroupItem::RestoreWindow(bool reset_transform, bool animate) {}
+void OverviewGroupItem::RestoreWindow(bool reset_transform, bool animate) {
+  for (const auto& item : overview_items_) {
+    item->RestoreWindow(reset_transform, animate);
+  }
+}
 
 void OverviewGroupItem::SetBounds(const gfx::RectF& target_bounds,
                                   OverviewAnimationType animation_type) {
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index c0ee54a..c5c59ceb 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -1113,6 +1113,10 @@
     const base::Uuid& item_to_focus,
     const std::u16string& saved_desk_name,
     aura::Window* const root_window) {
+  // Some windows such as the print preview may request activation due to
+  // occlusion computations. These should not cause use to exit overview.
+  base::AutoReset<bool> ignore(&ignore_activations_, true);
+
   if (Shell::Get()->tablet_mode_controller()->InTabletMode() ||
       IsShowingSavedDeskLibrary()) {
     return;
@@ -1173,11 +1177,9 @@
 }
 
 void OverviewSession::HideSavedDeskLibrary() {
-  // Before hiding the saved desk library, we need to explicitly activate the
-  // focus window. Otherwise, some other window may get activated as the saved
-  // desk library is hidden, and this could in turn lead to exiting overview
-  // mode.
-  wm::ActivateWindow(GetOverviewFocusWindow());
+  // Some windows such as the print preview may request activation due to
+  // occlusion computations. These should not cause use to exit overview.
+  base::AutoReset<bool> ignore(&ignore_activations_, true);
 
   for (auto& grid : grid_list_)
     grid->HideSavedDeskLibrary(/*exit_overview=*/false);
diff --git a/ash/wm/snap_group/snap_group_unittest.cc b/ash/wm/snap_group/snap_group_unittest.cc
index 6cdb8bf..f6191f0b 100644
--- a/ash/wm/snap_group/snap_group_unittest.cc
+++ b/ash/wm/snap_group/snap_group_unittest.cc
@@ -1171,6 +1171,24 @@
   EXPECT_EQ(new_primary_window, split_view_controller()->secondary_window());
 }
 
+TEST_F(SnapGroupTest, DontAutoSnapNewWindowOutsideSplitViewOverview) {
+  std::unique_ptr<aura::Window> w1(CreateAppWindow());
+  std::unique_ptr<aura::Window> w2(CreateAppWindow());
+  SnapTwoTestWindows(w1.get(), w2.get());
+  EXPECT_TRUE(
+      SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
+  EXPECT_FALSE(
+      RootWindowController::ForWindow(w1.get())->split_view_overview_session());
+  EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
+
+  // Open a third window. Test it does *not* snap.
+  std::unique_ptr<aura::Window> w3(CreateAppWindow());
+  EXPECT_FALSE(WindowState::Get(w3.get())->IsSnapped());
+  EXPECT_TRUE(
+      SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
+  EXPECT_TRUE(split_view_divider());
+}
+
 // Tests that removing a display during split view overview session doesn't
 // crash.
 TEST_F(SnapGroupTest, RemoveDisplay) {
@@ -1613,6 +1631,52 @@
   }
 }
 
+// Tests that when one of the window in snap group gets destroyed in overview,
+// the other window will restore its bounds properly when been activated to exit
+// overview.
+TEST_F(SnapGroupTest, RemainingWindowBoundsRestoreAfterDestructionInOverview) {
+  std::unique_ptr<aura::Window> w1(CreateAppWindow());
+  std::unique_ptr<aura::Window> w2(CreateAppWindow());
+  std::unique_ptr<aura::Window> w3(CreateAppWindow());
+  SnapTwoTestWindows(w1.get(), w2.get());
+  ASSERT_TRUE(split_view_divider());
+  const gfx::Size w1_size_before_overview = w1->GetBoundsInScreen().size();
+
+  OverviewController* overview_controller = OverviewController::Get();
+  overview_controller->StartOverview(OverviewStartAction::kTests);
+  ASSERT_TRUE(overview_controller->InOverviewSession());
+  EXPECT_FALSE(w1->transform().IsIdentity());
+  EXPECT_FALSE(w2->transform().IsIdentity());
+
+  const auto* overview_grid =
+      GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
+  ASSERT_TRUE(overview_grid);
+  ASSERT_EQ(overview_grid->window_list().size(), 2u);
+
+  // On one window in snap group destroying, the group item will host the other
+  // window.
+  w2.reset();
+  ASSERT_TRUE(overview_grid);
+  EXPECT_EQ(overview_grid->window_list().size(), 2u);
+
+  auto* event_generator = GetEventGenerator();
+  event_generator->MoveMouseTo(gfx::ToRoundedPoint(
+      GetOverviewItemForWindow(w1.get())->target_bounds().CenterPoint()));
+  event_generator->ClickLeftButton();
+  EXPECT_FALSE(overview_controller->InOverviewSession());
+  const gfx::Size w1_size_after_overview = w1->GetBoundsInScreen().size();
+
+  // Verify that the size of `w1` on overview exit is equal to that of before
+  // entering overview plus `kSplitviewDividerShortSideLength / 2`.
+  EXPECT_EQ(
+      w1_size_before_overview.width() + kSplitviewDividerShortSideLength / 2,
+      w1_size_after_overview.width());
+  EXPECT_EQ(w1_size_before_overview.height(), w1_size_after_overview.height());
+
+  // Verify that the transform is identity.
+  EXPECT_TRUE(w1->transform().IsIdentity());
+}
+
 // Tests that the individual items within the same group will be hosted by the
 // same overview group item.
 TEST_F(SnapGroupTest, OverviewItemTest) {
diff --git a/ash/wm/window_positioning_utils.h b/ash/wm/window_positioning_utils.h
index 3a298904..87d222e 100644
--- a/ash/wm/window_positioning_utils.h
+++ b/ash/wm/window_positioning_utils.h
@@ -24,7 +24,11 @@
 namespace ash {
 
 // We force at least this many DIPs for any window on the screen.
-const int kMinimumOnScreenArea = 25;
+inline constexpr int kMinimumOnScreenArea = 25;
+
+// This specifies how much percent (30%) of a window rect must be visible when
+// the window is added to the workspace.
+inline constexpr float kMinimumPercentOnScreenArea = 0.3f;
 
 // In clamshell mode, users can snap left/right for horizontal display and
 // top/bottom for vertical display. For primary-landscape-oriented display,
diff --git a/ash/wm/window_restore/window_restore_controller.cc b/ash/wm/window_restore/window_restore_controller.cc
index b4857b2..b10ac75 100644
--- a/ash/wm/window_restore/window_restore_controller.cc
+++ b/ash/wm/window_restore/window_restore_controller.cc
@@ -104,7 +104,15 @@
   if (display_area.Contains(current_bounds))
     return;
 
-  AdjustBoundsToEnsureMinimumWindowVisibility(display_area, &current_bounds);
+  // Adjust the bounds so that at least 30% of the window bounds is visible.
+  auto get_minimum_length = [](int length) -> int {
+    return std::max(
+        kMinimumOnScreenArea,
+        static_cast<int>(std::round(length * kMinimumPercentOnScreenArea)));
+  };
+  AdjustBoundsToEnsureWindowVisibility(
+      display_area, get_minimum_length(current_bounds.width()),
+      get_minimum_length(current_bounds.height()), &current_bounds);
 
   auto* window_state = WindowState::Get(window);
   if (window_state->HasRestoreBounds()) {
@@ -337,9 +345,6 @@
   // If the restored bounds are out of the screen, move the window to the bounds
   // manually as most widget types force windows to be within the work area on
   // creation.
-  // TODO(sammiequon): The Files app uses async Mojo calls to activate
-  // and set its bounds, making this approach not work. In the future, we'll
-  // need to address the Files app.
   MaybeRestoreOutOfBoundsWindows(window);
 }
 
diff --git a/ash/wm/window_restore/window_restore_controller_unittest.cc b/ash/wm/window_restore/window_restore_controller_unittest.cc
index fd0746c..8de62d4 100644
--- a/ash/wm/window_restore/window_restore_controller_unittest.cc
+++ b/ash/wm/window_restore/window_restore_controller_unittest.cc
@@ -20,6 +20,7 @@
 #include "ash/wm/overview/overview_test_util.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
+#include "ash/wm/window_positioning_utils.h"
 #include "ash/wm/window_state.h"
 #include "base/cancelable_callback.h"
 #include "base/containers/contains.h"
@@ -1382,4 +1383,34 @@
   EXPECT_EQ(window_bounds, arc_window_info->arc_extra_info->bounds_in_root);
 }
 
+// Tests that a window whose bounds are offscreen (were on a disconnected
+// display), are restored such that at least 30% of the window is visible.
+TEST_F(WindowRestoreControllerTest, WindowsMinimumVisibleArea) {
+  UpdateDisplay("800x600");
+
+  // Create a Window Restore'd browser that is was previously on a monitor at
+  // the bottom right of the current display.
+  const int window_length = 200;
+  AddEntryToFakeFile(
+      /*restore_id=*/1, gfx::Rect(900, 700, window_length, window_length),
+      chromeos::WindowStateType::kNormal);
+  auto* restored_window = CreateTestWindowRestoredWidgetFromRestoreId(
+                              /*restore_id=*/1, AppType::BROWSER,
+                              /*is_taskless_arc_app=*/false)
+                              ->GetNativeWindow();
+  const gfx::Rect& bounds_in_screen = restored_window->GetBoundsInScreen();
+
+  // Check the intersection of the display bounds and the window bounds in
+  // screen. The intersection should be non-empty (window is partially on the
+  // display) and width and height should at least 60 (30% of the window is
+  // visible).
+  const gfx::Rect intersection =
+      gfx::IntersectRects(gfx::Rect(0, 0, 800, 600), bounds_in_screen);
+  const int minimum_length =
+      std::round(kMinimumPercentOnScreenArea * window_length);
+  EXPECT_FALSE(intersection.IsEmpty());
+  EXPECT_GE(intersection.size().width(), minimum_length);
+  EXPECT_GE(intersection.size().height(), minimum_length);
+}
+
 }  // namespace ash
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 2beb9d2..8c976b4 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -824,6 +824,8 @@
     "task/thread_pool/worker_thread_observer.h",
     "task/thread_pool/worker_thread_set.cc",
     "task/thread_pool/worker_thread_set.h",
+    "task/thread_pool/worker_thread_waitable_event.cc",
+    "task/thread_pool/worker_thread_waitable_event.h",
     "task/updateable_sequenced_task_runner.h",
     "template_util.h",
     "test/scoped_logging_settings.h",
@@ -3378,7 +3380,7 @@
     "task/thread_pool/thread_pool_impl_unittest.cc",
     "task/thread_pool/tracked_ref_unittest.cc",
     "task/thread_pool/worker_thread_set_unittest.cc",
-    "task/thread_pool/worker_thread_unittest.cc",
+    "task/thread_pool/worker_thread_waitable_event_unittest.cc",
     "task/thread_pool_unittest.cc",
     "template_util_unittest.cc",
     "test/gmock_callback_support_unittest.cc",
diff --git a/base/containers/span.h b/base/containers/span.h
index 9c0fe37..77f5740 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -488,7 +488,8 @@
 
 // [span.objectrep], views of object representation
 template <typename T, size_t X>
-span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+inline span<const uint8_t,
+           (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
 as_bytes(span<T, X> s) noexcept {
   return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
 }
@@ -496,7 +497,7 @@
 template <typename T,
           size_t X,
           typename = std::enable_if_t<!std::is_const_v<T>>>
-span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+inline span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
 as_writable_bytes(span<T, X> s) noexcept {
   return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
 }
diff --git a/base/files/file_path_watcher_inotify.cc b/base/files/file_path_watcher_inotify.cc
index 2fe86b4..021b8a6e 100644
--- a/base/files/file_path_watcher_inotify.cc
+++ b/base/files/file_path_watcher_inotify.cc
@@ -324,7 +324,7 @@
     for (size_t i = 0; i < static_cast<size_t>(bytes_read);) {
       inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
       size_t event_size = sizeof(inotify_event) + event->len;
-      DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
+      DUMP_WILL_BE_CHECK_LE(i + event_size, static_cast<size_t>(bytes_read));
       g_inotify_reader.Get().OnInotifyEvent(event);
       i += event_size;
     }
@@ -440,7 +440,8 @@
 FilePathWatcherImpl::FilePathWatcherImpl() = default;
 
 FilePathWatcherImpl::~FilePathWatcherImpl() {
-  DCHECK(!task_runner() || task_runner()->RunsTasksInCurrentSequence());
+  DUMP_WILL_BE_CHECK(!task_runner() ||
+                     task_runner()->RunsTasksInCurrentSequence());
 }
 
 void FilePathWatcherImpl::OnFilePathChanged(
@@ -449,9 +450,9 @@
     FilePathWatcher::ChangeInfo change_info,
     bool created,
     bool deleted) {
-  DCHECK(task_runner()->RunsTasksInCurrentSequence());
-  DCHECK(!watches_.empty());
-  DCHECK(HasValidWatchVector());
+  DUMP_WILL_BE_CHECK(task_runner()->RunsTasksInCurrentSequence());
+  DUMP_WILL_BE_CHECK(!watches_.empty());
+  DUMP_WILL_BE_CHECK(HasValidWatchVector());
 
   // Used below to avoid multiple recursive updates.
   bool did_update = false;
@@ -570,7 +571,7 @@
 }
 
 bool FilePathWatcherImpl::WouldExceedWatchLimit() const {
-  DCHECK(task_runner()->RunsTasksInCurrentSequence());
+  DUMP_WILL_BE_CHECK(task_runner()->RunsTasksInCurrentSequence());
 
   // `watches_` contains inotify watches of all dir components of `target_`.
   // `recursive_paths_by_watch_` contains inotify watches for sub dirs under
@@ -585,7 +586,7 @@
 }
 
 InotifyReader::WatcherEntry FilePathWatcherImpl::GetWatcherEntry() {
-  DCHECK(task_runner()->RunsTasksInCurrentSequence());
+  DUMP_WILL_BE_CHECK(task_runner()->RunsTasksInCurrentSequence());
   return {task_runner(), weak_factory_.GetWeakPtr()};
 }
 
@@ -612,7 +613,7 @@
     const FilePath& path,
     const WatchOptions& options,
     const FilePathWatcher::CallbackWithChangeInfo& callback) {
-  DCHECK(target_.empty());
+  DUMP_WILL_BE_CHECK(target_.empty());
 
   set_task_runner(SequencedTaskRunner::GetCurrentDefault());
   callback_ = callback;
@@ -621,7 +622,7 @@
   report_modified_path_ = options.report_modified_path;
 
   std::vector<FilePath::StringType> comps = target_.GetComponents();
-  DCHECK(!comps.empty());
+  DUMP_WILL_BE_CHECK(!comps.empty());
   for (size_t i = 1; i < comps.size(); ++i) {
     watches_.emplace_back(comps[i]);
   }
@@ -643,8 +644,8 @@
     return;
   }
 
-  DCHECK(task_runner()->RunsTasksInCurrentSequence());
-  DCHECK(!is_cancelled());
+  DUMP_WILL_BE_CHECK(task_runner()->RunsTasksInCurrentSequence());
+  DUMP_WILL_BE_CHECK(!is_cancelled());
 
   set_cancelled();
   callback_.Reset();
@@ -659,8 +660,8 @@
 bool FilePathWatcherImpl::UpdateWatches() {
   // Ensure this runs on the task_runner() exclusively in order to avoid
   // concurrency issues.
-  DCHECK(task_runner()->RunsTasksInCurrentSequence());
-  DCHECK(HasValidWatchVector());
+  DUMP_WILL_BE_CHECK(task_runner()->RunsTasksInCurrentSequence());
+  DUMP_WILL_BE_CHECK(HasValidWatchVector());
 
   // Walk the list of watches and update them as we go.
   FilePath path(FILE_PATH_LITERAL("/"));
@@ -692,7 +693,7 @@
 bool FilePathWatcherImpl::UpdateRecursiveWatches(
     InotifyReader::Watch fired_watch,
     bool is_dir) {
-  DCHECK(HasValidWatchVector());
+  DUMP_WILL_BE_CHECK(HasValidWatchVector());
 
   if (type_ != Type::kRecursive)
     return true;
@@ -747,9 +748,9 @@
 }
 
 bool FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) {
-  DCHECK_EQ(type_, Type::kRecursive);
-  DCHECK(!path.empty());
-  DCHECK(DirectoryExists(path));
+  DUMP_WILL_BE_CHECK_EQ(type_, Type::kRecursive);
+  DUMP_WILL_BE_CHECK(!path.empty());
+  DUMP_WILL_BE_CHECK(DirectoryExists(path));
 
   // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored
   // rather than followed. Following symlinks can easily lead to the undesirable
@@ -759,7 +760,7 @@
       FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS);
   for (FilePath current = enumerator.Next(); !current.empty();
        current = enumerator.Next()) {
-    DCHECK(enumerator.GetInfo().IsDirectory());
+    DUMP_WILL_BE_CHECK(enumerator.GetInfo().IsDirectory());
 
     // Check `recursive_watches_by_path_` as a heuristic to determine if this
     // needs to be an add or update operation.
@@ -781,7 +782,7 @@
     } else {
       // Update existing watches.
       InotifyReader::Watch old_watch = recursive_watches_by_path_[current];
-      DCHECK_NE(InotifyReader::kInvalidWatch, old_watch);
+      DUMP_WILL_BE_CHECK_NE(InotifyReader::kInvalidWatch, old_watch);
       InotifyReader::Watch watch =
           g_inotify_reader.Get().AddWatch(current, this);
       if (watch == InotifyReader::kWatchLimitExceeded)
@@ -799,15 +800,15 @@
 
 void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch,
                                                  const FilePath& path) {
-  DCHECK_EQ(type_, Type::kRecursive);
-  DCHECK(!path.empty());
-  DCHECK(target_.IsParent(path));
+  DUMP_WILL_BE_CHECK_EQ(type_, Type::kRecursive);
+  DUMP_WILL_BE_CHECK(!path.empty());
+  DUMP_WILL_BE_CHECK(target_.IsParent(path));
 
   if (watch == InotifyReader::kInvalidWatch)
     return;
 
-  DCHECK(!Contains(recursive_paths_by_watch_, watch));
-  DCHECK(!Contains(recursive_watches_by_path_, path));
+  DUMP_WILL_BE_CHECK(!Contains(recursive_paths_by_watch_, watch));
+  DUMP_WILL_BE_CHECK(!Contains(recursive_watches_by_path_, path));
   recursive_paths_by_watch_[watch] = path;
   recursive_watches_by_path_[path] = watch;
 }
@@ -829,12 +830,12 @@
   // Fuchsia does not support symbolic links.
   return false;
 #else   // BUILDFLAG(IS_FUCHSIA)
-  DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch);
+  DUMP_WILL_BE_CHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch);
   absl::optional<FilePath> link = ReadSymbolicLinkAbsolute(path);
   if (!link) {
     return true;
   }
-  DCHECK(link->IsAbsolute());
+  DUMP_WILL_BE_CHECK(link->IsAbsolute());
 
   // Try watching symlink target directory. If the link target is "/", then we
   // shouldn't get here in normal situations and if we do, we'd watch "/" for
@@ -891,7 +892,7 @@
 
 ScopedMaxNumberOfInotifyWatchesOverrideForTest::
     ScopedMaxNumberOfInotifyWatchesOverrideForTest(size_t override_max) {
-  DCHECK_EQ(g_override_max_inotify_watches, 0u);
+  DUMP_WILL_BE_CHECK_EQ(g_override_max_inotify_watches, 0u);
   g_override_max_inotify_watches = override_max;
 }
 
diff --git a/base/functional/callback.h b/base/functional/callback.h
index 84a21f9..f6a52fc 100644
--- a/base/functional/callback.h
+++ b/base/functional/callback.h
@@ -206,30 +206,38 @@
     return *this;
   }
 
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr OnceCallback(internal::DoNothingCallbackTag)
       : OnceCallback(BindOnce([](Args... args) {})) {}
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   constexpr OnceCallback& operator=(internal::DoNothingCallbackTag) {
     *this = BindOnce([](Args... args) {});
     return *this;
   }
 
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr OnceCallback(internal::DoNothingCallbackTag::WithSignature<RunType>)
       : OnceCallback(internal::DoNothingCallbackTag()) {}
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   constexpr OnceCallback& operator=(
       internal::DoNothingCallbackTag::WithSignature<RunType>) {
     *this = internal::DoNothingCallbackTag();
     return *this;
   }
 
-  template <typename... BoundArgs>
+  template <typename... BoundArgs,
+            typename S = R,
+            typename = std::enable_if_t<std::is_void_v<S>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr OnceCallback(
       internal::DoNothingCallbackTag::WithBoundArguments<BoundArgs...> tag)
       : OnceCallback(
             internal::ToDoNothingCallback<true, R, Args...>(std::move(tag))) {}
-  template <typename... BoundArgs>
+  template <typename... BoundArgs,
+            typename S = R,
+            typename = std::enable_if_t<std::is_void_v<S>>>
   constexpr OnceCallback& operator=(
       internal::DoNothingCallbackTag::WithBoundArguments<BoundArgs...> tag) {
     *this = internal::ToDoNothingCallback<true, R, Args...>(std::move(tag));
@@ -408,31 +416,39 @@
     return *this;
   }
 
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr RepeatingCallback(internal::DoNothingCallbackTag)
       : RepeatingCallback(BindRepeating([](Args... args) {})) {}
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   constexpr RepeatingCallback& operator=(internal::DoNothingCallbackTag) {
     *this = BindRepeating([](Args... args) {});
     return *this;
   }
 
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr RepeatingCallback(
       internal::DoNothingCallbackTag::WithSignature<RunType>)
       : RepeatingCallback(internal::DoNothingCallbackTag()) {}
+  template <typename S = R, typename = std::enable_if_t<std::is_void_v<S>>>
   constexpr RepeatingCallback& operator=(
       internal::DoNothingCallbackTag::WithSignature<RunType>) {
     *this = internal::DoNothingCallbackTag();
     return *this;
   }
 
-  template <typename... BoundArgs>
+  template <typename... BoundArgs,
+            typename S = R,
+            typename = std::enable_if_t<std::is_void_v<S>>>
   // NOLINTNEXTLINE(google-explicit-constructor)
   constexpr RepeatingCallback(
       internal::DoNothingCallbackTag::WithBoundArguments<BoundArgs...> tag)
       : RepeatingCallback(
             internal::ToDoNothingCallback<false, R, Args...>(std::move(tag))) {}
-  template <typename... BoundArgs>
+  template <typename... BoundArgs,
+            typename S = R,
+            typename = std::enable_if_t<std::is_void_v<S>>>
   constexpr RepeatingCallback& operator=(
       internal::DoNothingCallbackTag::WithBoundArguments<BoundArgs...> tag) {
     *this = internal::ToDoNothingCallback<false, R, Args...>(std::move(tag));
diff --git a/base/functional/callback_helpers_unittest.cc b/base/functional/callback_helpers_unittest.cc
index 194475f..30e1838 100644
--- a/base/functional/callback_helpers_unittest.cc
+++ b/base/functional/callback_helpers_unittest.cc
@@ -14,6 +14,94 @@
 
 namespace {
 
+struct BadArg {};
+
+template <typename TagType, typename CallbackType>
+struct TestConversionAndAssignmentImpl {
+  static constexpr bool kSupportsConversion =
+      std::is_convertible_v<TagType, CallbackType>;
+  static constexpr bool kSupportsAssignment =
+      std::is_assignable_v<CallbackType, TagType>;
+  static_assert(kSupportsConversion == kSupportsAssignment);
+
+  static constexpr bool kValue = kSupportsConversion;
+};
+
+template <typename T, typename U>
+constexpr bool TestConversionAndAssignment =
+    TestConversionAndAssignmentImpl<T, U>::kValue;
+
+#define VOID_RETURN_CALLBACK_TAG_TEST(CallbackType, Sig, BadSig, BoundArg)   \
+  static_assert(TestConversionAndAssignment<decltype(base::NullCallback()),  \
+                                            CallbackType<Sig>>);             \
+  static_assert(                                                             \
+      TestConversionAndAssignment<decltype(base::NullCallbackAs<Sig>()),     \
+                                  CallbackType<Sig>>);                       \
+  static_assert(TestConversionAndAssignment<decltype(base::DoNothing()),     \
+                                            CallbackType<Sig>>);             \
+  static_assert(                                                             \
+      TestConversionAndAssignment<decltype(base::DoNothingAs<Sig>()),        \
+                                  CallbackType<Sig>>);                       \
+  static_assert(TestConversionAndAssignment<                                 \
+                decltype(base::DoNothingWithBoundArgs(BoundArg)),            \
+                CallbackType<Sig>>);                                         \
+                                                                             \
+  static_assert(                                                             \
+      !TestConversionAndAssignment<decltype(base::NullCallbackAs<BadSig>()), \
+                                   CallbackType<Sig>>);                      \
+  static_assert(                                                             \
+      !TestConversionAndAssignment<decltype(base::DoNothingAs<BadSig>()),    \
+                                   CallbackType<Sig>>);                      \
+  static_assert(TestConversionAndAssignment<                                 \
+                decltype(base::DoNothingWithBoundArgs(BadArg())),            \
+                CallbackType<Sig>>)
+
+#define NON_VOID_RETURN_CALLBACK_TAG_TEST(CallbackType, Sig, BadSig, BoundArg) \
+  static_assert(TestConversionAndAssignment<decltype(base::NullCallback()),    \
+                                            CallbackType<Sig>>);               \
+  static_assert(                                                               \
+      TestConversionAndAssignment<decltype(base::NullCallbackAs<Sig>()),       \
+                                  CallbackType<Sig>>);                         \
+                                                                               \
+  /* Unlike callbacks that return void, callbacks that return non-void      */ \
+  /* should not be implicitly convertible from DoNothingCallbackTag since   */ \
+  /* this would require guessing what the callback should return.           */ \
+  static_assert(!TestConversionAndAssignment<decltype(base::DoNothing()),      \
+                                             CallbackType<Sig>>);              \
+  static_assert(                                                               \
+      !TestConversionAndAssignment<decltype(base::DoNothingAs<Sig>()),         \
+                                   CallbackType<Sig>>);                        \
+  static_assert(!TestConversionAndAssignment<                                  \
+                decltype(base::DoNothingWithBoundArgs(BoundArg)),              \
+                CallbackType<Sig>>);                                           \
+                                                                               \
+  static_assert(                                                               \
+      !TestConversionAndAssignment<decltype(base::NullCallbackAs<BadSig>()),   \
+                                   CallbackType<Sig>>);                        \
+  static_assert(                                                               \
+      !TestConversionAndAssignment<decltype(base::DoNothingAs<BadSig>()),      \
+                                   CallbackType<Sig>>);                        \
+  static_assert(!TestConversionAndAssignment<                                  \
+                decltype(base::DoNothingWithBoundArgs(BadArg())),              \
+                CallbackType<Sig>>)
+
+VOID_RETURN_CALLBACK_TAG_TEST(base::OnceCallback, void(), void(char), );
+VOID_RETURN_CALLBACK_TAG_TEST(base::OnceCallback, void(int), void(char), 8);
+NON_VOID_RETURN_CALLBACK_TAG_TEST(base::OnceCallback, int(int), char(int), 8);
+
+VOID_RETURN_CALLBACK_TAG_TEST(base::RepeatingCallback, void(), void(char), );
+VOID_RETURN_CALLBACK_TAG_TEST(base::RepeatingCallback,
+                              void(int),
+                              void(char),
+                              8);
+NON_VOID_RETURN_CALLBACK_TAG_TEST(base::RepeatingCallback,
+                                  int(int),
+                                  char(int),
+                                  8);
+
+#undef VOID_RETURN_CALLBACK_TAG_TEST
+#undef NON_VOID_RETURN_CALLBACK_TAG_TEST
+
 TEST(CallbackHelpersTest, IsBaseCallback) {
   // Check that base::{Once,Repeating}Closures and references to them are
   // considered base::{Once,Repeating}Callbacks.
diff --git a/base/nix/mime_util_xdg.h b/base/nix/mime_util_xdg.h
index b3182fd..cbb0372 100644
--- a/base/nix/mime_util_xdg.h
+++ b/base/nix/mime_util_xdg.h
@@ -5,6 +5,8 @@
 #ifndef BASE_NIX_MIME_UTIL_XDG_H_
 #define BASE_NIX_MIME_UTIL_XDG_H_
 
+#include <stdint.h>
+
 #include <map>
 #include <string>
 
diff --git a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
index e309cdf..0ddba47 100644
--- a/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
+++ b/base/task/thread_pool/pooled_single_thread_task_runner_manager.cc
@@ -27,7 +27,7 @@
 #include "base/task/thread_pool/task.h"
 #include "base/task/thread_pool/task_source.h"
 #include "base/task/thread_pool/task_tracker.h"
-#include "base/task/thread_pool/worker_thread.h"
+#include "base/task/thread_pool/worker_thread_waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -101,7 +101,7 @@
   PlatformThreadRef thread_ref_;
 };
 
-class WorkerThreadDelegate : public WorkerThread::Delegate {
+class WorkerThreadDelegate : public WorkerThreadWaitableEvent::Delegate {
  public:
   WorkerThreadDelegate(const std::string& thread_name,
                        WorkerThread::ThreadLabel thread_label,
@@ -112,7 +112,7 @@
   WorkerThreadDelegate(const WorkerThreadDelegate&) = delete;
   WorkerThreadDelegate& operator=(const WorkerThreadDelegate&) = delete;
 
-  void set_worker(WorkerThread* worker) {
+  void set_worker(WorkerThreadWaitableEvent* worker) {
     DCHECK(!worker_);
     worker_ = worker;
   }
@@ -141,15 +141,38 @@
     return task_source;
   }
 
-  void DidProcessTask(RegisteredTaskSource task_source) override {
+  RegisteredTaskSource SwapProcessedTask(RegisteredTaskSource task_source,
+                                         WorkerThread* worker) override {
+    std::optional<RegisteredTaskSourceAndTransaction>
+        task_source_with_transaction;
     if (task_source) {
-      auto task_source_with_transaction =
+      task_source_with_transaction.emplace(
           RegisteredTaskSourceAndTransaction::FromTaskSource(
-              std::move(task_source));
-      task_source_with_transaction.task_source.WillReEnqueue(
-          TimeTicks::Now(), &task_source_with_transaction.transaction);
-      EnqueueTaskSource(std::move(task_source_with_transaction));
+              std::move(task_source)));
+      task_source_with_transaction->task_source.WillReEnqueue(
+          TimeTicks::Now(), &task_source_with_transaction->transaction);
     }
+    CheckedAutoLock auto_lock(lock_);
+    if (task_source_with_transaction.has_value()) {
+      EnqueueTaskSourceLockRequired(std::move(*task_source_with_transaction));
+    }
+
+    // Calling WakeUp() guarantees that this WorkerThread will run Tasks from
+    // TaskSources returned by the GetWork() method of |delegate_| until it
+    // returns nullptr. Resetting |wake_up_event_| here doesn't break this
+    // invariant and avoids a useless loop iteration before going to sleep if
+    // WakeUp() is called while this WorkerThread is awake.
+    wake_up_event_.Reset();
+
+    auto new_task_source = GetWorkLockRequired(worker);
+    if (!new_task_source) {
+      // The worker will sleep after this returns nullptr.
+      worker_awake_ = false;
+      return nullptr;
+    }
+    auto run_status = new_task_source.WillRunTask();
+    DCHECK_NE(run_status, TaskSource::RunStatus::kDisallowed);
+    return new_task_source;
   }
 
   TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
@@ -171,10 +194,15 @@
       return false;
     transaction.PushImmediateTask(std::move(task));
     if (task_source) {
-      bool should_wakeup =
-          EnqueueTaskSource({std::move(task_source), std::move(transaction)});
-      if (should_wakeup)
+      bool should_wakeup;
+      {
+        CheckedAutoLock auto_lock(lock_);
+        should_wakeup = EnqueueTaskSourceLockRequired(
+            {std::move(task_source), std::move(transaction)});
+      }
+      if (should_wakeup) {
         worker_->WakeUp();
+      }
     }
     return true;
   }
@@ -225,9 +253,9 @@
   // Enqueues a task source in this single-threaded worker's priority queue.
   // Returns true iff the worker must wakeup, i.e. task source is allowed to run
   // and the worker was not awake.
-  bool EnqueueTaskSource(
-      RegisteredTaskSourceAndTransaction transaction_with_task_source) {
-    CheckedAutoLock auto_lock(lock_);
+  bool EnqueueTaskSourceLockRequired(
+      RegisteredTaskSourceAndTransaction transaction_with_task_source)
+      EXCLUSIVE_LOCKS_REQUIRED(lock_) {
     auto sort_key = transaction_with_task_source.task_source->GetSortKey();
     // When moving |task_source| into |priority_queue_|, it may be destroyed
     // on another thread as soon as |lock_| is released, since we're no longer
@@ -255,7 +283,7 @@
   // The WorkerThread that has |this| as a delegate. Must be set before
   // starting or posting a task to the WorkerThread, because it's used in
   // OnMainEntry() and PostTaskNow().
-  raw_ptr<WorkerThread> worker_ = nullptr;
+  raw_ptr<WorkerThreadWaitableEvent> worker_ = nullptr;
 
   PriorityQueue priority_queue_ GUARDED_BY(lock_);
 
@@ -277,7 +305,7 @@
   WorkerThreadCOMDelegate& operator=(const WorkerThreadCOMDelegate&) = delete;
   ~WorkerThreadCOMDelegate() override { DCHECK(!scoped_com_initializer_); }
 
-  // WorkerThread::Delegate:
+  // WorkerThreadWaitableEvent::Delegate:
   void OnMainEntry(WorkerThread* worker) override {
     WorkerThreadDelegate::OnMainEntry(worker);
 
@@ -353,12 +381,11 @@
     scoped_com_initializer_.reset();
   }
 
-  void WaitForWork(WaitableEvent* wake_up_event) override {
-    DCHECK(wake_up_event);
+  void WaitForWork() override {
     const TimeDelta sleep_time = GetSleepTimeout();
     const DWORD milliseconds_wait = checked_cast<DWORD>(
         sleep_time.is_max() ? INFINITE : sleep_time.InMilliseconds());
-    const HANDLE wake_up_event_handle = wake_up_event->handle();
+    const HANDLE wake_up_event_handle = wake_up_event_.handle();
     MsgWaitForMultipleObjectsEx(1, &wake_up_event_handle, milliseconds_wait,
                                 QS_ALLINPUT, 0);
   }
@@ -419,7 +446,7 @@
   // lifetime of a dedicated |worker| for |traits|.
   PooledSingleThreadTaskRunner(PooledSingleThreadTaskRunnerManager* const outer,
                                const TaskTraits& traits,
-                               WorkerThread* worker,
+                               WorkerThreadWaitableEvent* worker,
                                SingleThreadTaskRunnerThreadMode thread_mode)
       : outer_(outer),
         worker_(worker),
@@ -532,7 +559,8 @@
                 DisableDanglingPtrDetection>
       outer_;
 
-  const raw_ptr<WorkerThread, AcrossTasksDanglingUntriaged> worker_;
+  const raw_ptr<WorkerThreadWaitableEvent, AcrossTasksDanglingUntriaged>
+      worker_;
   const SingleThreadTaskRunnerThreadMode thread_mode_;
   const scoped_refptr<Sequence> sequence_;
 };
@@ -592,7 +620,7 @@
   // PooledSingleThreadTaskRunner::PostTaskNow(). As a result, it's
   // unnecessary to call WakeUp() for each worker (in fact, an extraneous
   // WakeUp() would be racy and wrong - see https://crbug.com/862582).
-  for (scoped_refptr<WorkerThread> worker : workers_to_start) {
+  for (scoped_refptr<WorkerThreadWaitableEvent> worker : workers_to_start) {
     worker->Start(io_thread_task_runner_, worker_thread_observer_);
   }
 }
@@ -656,8 +684,8 @@
   // SingleThreadTaskRunnerThreadMode. In DEDICATED, the scoped_refptr is backed
   // by a local variable and in SHARED, the scoped_refptr is backed by a member
   // variable.
-  WorkerThread* dedicated_worker = nullptr;
-  WorkerThread*& worker =
+  WorkerThreadWaitableEvent* dedicated_worker = nullptr;
+  WorkerThreadWaitableEvent*& worker =
       thread_mode == SingleThreadTaskRunnerThreadMode::DEDICATED
           ? dedicated_worker
           : GetSharedWorkerThreadForTraits<DelegateType>(traits);
@@ -743,7 +771,7 @@
 #endif  // BUILDFLAG(IS_WIN)
 
 template <typename DelegateType>
-WorkerThread*
+WorkerThreadWaitableEvent*
 PooledSingleThreadTaskRunnerManager::CreateAndRegisterWorkerThread(
     const std::string& name,
     SingleThreadTaskRunnerThreadMode thread_mode,
@@ -752,15 +780,17 @@
   std::unique_ptr<WorkerThreadDelegate> delegate =
       CreateWorkerThreadDelegate<DelegateType>(name, id, thread_mode);
   WorkerThreadDelegate* delegate_raw = delegate.get();
-  scoped_refptr<WorkerThread> worker = MakeRefCounted<WorkerThread>(
-      thread_type_hint, std::move(delegate), task_tracker_, workers_.size());
+  scoped_refptr<WorkerThreadWaitableEvent> worker =
+      MakeRefCounted<WorkerThreadWaitableEvent>(thread_type_hint,
+                                                std::move(delegate),
+                                                task_tracker_, workers_.size());
   delegate_raw->set_worker(worker.get());
   workers_.emplace_back(std::move(worker));
   return workers_.back().get();
 }
 
 template <>
-WorkerThread*&
+WorkerThreadWaitableEvent*&
 PooledSingleThreadTaskRunnerManager::GetSharedWorkerThreadForTraits<
     WorkerThreadDelegate>(const TaskTraits& traits) {
   return shared_worker_threads_[GetEnvironmentIndexForTraits(traits)]
@@ -769,7 +799,7 @@
 
 #if BUILDFLAG(IS_WIN)
 template <>
-WorkerThread*&
+WorkerThreadWaitableEvent*&
 PooledSingleThreadTaskRunnerManager::GetSharedWorkerThreadForTraits<
     WorkerThreadCOMDelegate>(const TaskTraits& traits) {
   return shared_com_worker_threads_[GetEnvironmentIndexForTraits(traits)]
@@ -778,9 +808,9 @@
 #endif  // BUILDFLAG(IS_WIN)
 
 void PooledSingleThreadTaskRunnerManager::UnregisterWorkerThread(
-    WorkerThread* worker) {
+    WorkerThreadWaitableEvent* worker) {
   // Cleanup uses a CheckedLock, so call Cleanup() after releasing |lock_|.
-  scoped_refptr<WorkerThread> worker_to_destroy;
+  scoped_refptr<WorkerThreadWaitableEvent> worker_to_destroy;
   {
     CheckedAutoLock auto_lock(lock_);
 
diff --git a/base/task/thread_pool/pooled_single_thread_task_runner_manager.h b/base/task/thread_pool/pooled_single_thread_task_runner_manager.h
index c4ddb43..bb02bc3 100644
--- a/base/task/thread_pool/pooled_single_thread_task_runner_manager.h
+++ b/base/task/thread_pool/pooled_single_thread_task_runner_manager.h
@@ -28,7 +28,7 @@
 namespace internal {
 
 class DelayedTaskManager;
-class WorkerThread;
+class WorkerThreadWaitableEvent;
 class TaskTracker;
 
 namespace {
@@ -117,15 +117,16 @@
       SingleThreadTaskRunnerThreadMode thread_mode);
 
   template <typename DelegateType>
-  WorkerThread* CreateAndRegisterWorkerThread(
+  WorkerThreadWaitableEvent* CreateAndRegisterWorkerThread(
       const std::string& name,
       SingleThreadTaskRunnerThreadMode thread_mode,
       ThreadType thread_type_hint) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   template <typename DelegateType>
-  WorkerThread*& GetSharedWorkerThreadForTraits(const TaskTraits& traits);
+  WorkerThreadWaitableEvent*& GetSharedWorkerThreadForTraits(
+      const TaskTraits& traits);
 
-  void UnregisterWorkerThread(WorkerThread* worker);
+  void UnregisterWorkerThread(WorkerThreadWaitableEvent* worker);
 
   void ReleaseSharedWorkerThreads();
 
@@ -139,7 +140,8 @@
   raw_ptr<WorkerThreadObserver> worker_thread_observer_ = nullptr;
 
   CheckedLock lock_;
-  std::vector<scoped_refptr<WorkerThread>> workers_ GUARDED_BY(lock_);
+  std::vector<scoped_refptr<WorkerThreadWaitableEvent>> workers_
+      GUARDED_BY(lock_);
   int next_worker_id_ GUARDED_BY(lock_) = 0;
 
   // Workers for SingleThreadTaskRunnerThreadMode::SHARED tasks. It is
@@ -147,11 +149,11 @@
   // CONTINUE_ON_SHUTDOWN to avoid being in a situation where a
   // CONTINUE_ON_SHUTDOWN task effectively blocks shutdown by preventing a
   // BLOCK_SHUTDOWN task to be scheduled. https://crbug.com/829786
-  WorkerThread* shared_worker_threads_[ENVIRONMENT_COUNT]
-                                      [CONTINUE_ON_SHUTDOWN_COUNT] GUARDED_BY(
-                                          lock_) = {};
+  WorkerThreadWaitableEvent*
+      shared_worker_threads_[ENVIRONMENT_COUNT]
+                            [CONTINUE_ON_SHUTDOWN_COUNT] GUARDED_BY(lock_) = {};
 #if BUILDFLAG(IS_WIN)
-  WorkerThread* shared_com_worker_threads_
+  WorkerThreadWaitableEvent* shared_com_worker_threads_
       [ENVIRONMENT_COUNT][CONTINUE_ON_SHUTDOWN_COUNT] GUARDED_BY(lock_) = {};
 #endif  // BUILDFLAG(IS_WIN)
 
diff --git a/base/task/thread_pool/thread_group_impl.cc b/base/task/thread_pool/thread_group_impl.cc
index 0cec4b8..3841ca5c9 100644
--- a/base/task/thread_pool/thread_group_impl.cc
+++ b/base/task/thread_pool/thread_group_impl.cc
@@ -29,6 +29,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool/task_tracker.h"
+#include "base/task/thread_pool/worker_thread_waitable_event.h"
 #include "base/threading/platform_thread.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/threading/scoped_blocking_call_internal.h"
@@ -78,10 +79,11 @@
 constexpr TimeDelta kBackgroundBlockedWorkersPoll = Seconds(12);
 
 // Only used in DCHECKs.
-bool ContainsWorker(const std::vector<scoped_refptr<WorkerThread>>& workers,
-                    const WorkerThread* worker) {
-  auto it =
-      ranges::find_if(workers, [worker](const scoped_refptr<WorkerThread>& i) {
+bool ContainsWorker(
+    const std::vector<scoped_refptr<WorkerThreadWaitableEvent>>& workers,
+    const WorkerThreadWaitableEvent* worker) {
+  auto it = ranges::find_if(
+      workers, [worker](const scoped_refptr<WorkerThreadWaitableEvent>& i) {
         return i.get() == worker;
       });
   return it != workers.end();
@@ -100,11 +102,11 @@
   ScopedCommandsExecutor& operator=(const ScopedCommandsExecutor&) = delete;
   ~ScopedCommandsExecutor() { FlushImpl(); }
 
-  void ScheduleWakeUp(scoped_refptr<WorkerThread> worker) {
+  void ScheduleWakeUp(scoped_refptr<WorkerThreadWaitableEvent> worker) {
     workers_to_wake_up_.AddWorker(std::move(worker));
   }
 
-  void ScheduleStart(scoped_refptr<WorkerThread> worker) {
+  void ScheduleStart(scoped_refptr<WorkerThreadWaitableEvent> worker) {
     workers_to_start_.AddWorker(std::move(worker));
   }
 
@@ -130,7 +132,7 @@
     WorkerContainer(const WorkerContainer&) = delete;
     WorkerContainer& operator=(const WorkerContainer&) = delete;
 
-    void AddWorker(scoped_refptr<WorkerThread> worker) {
+    void AddWorker(scoped_refptr<WorkerThreadWaitableEvent> worker) {
       if (!worker)
         return;
       if (!first_worker_)
@@ -143,8 +145,10 @@
     void ForEachWorker(Action action) {
       if (first_worker_) {
         action(first_worker_.get());
-        for (scoped_refptr<WorkerThread> worker : additional_workers_)
+        for (scoped_refptr<WorkerThreadWaitableEvent> worker :
+             additional_workers_) {
           action(worker.get());
+        }
       } else {
         DCHECK(additional_workers_.empty());
       }
@@ -160,8 +164,8 @@
    private:
     // The purpose of |first_worker| is to avoid a heap allocation by the vector
     // in the case where there is only one worker in the container.
-    scoped_refptr<WorkerThread> first_worker_;
-    std::vector<scoped_refptr<WorkerThread>> additional_workers_;
+    scoped_refptr<WorkerThreadWaitableEvent> first_worker_;
+    std::vector<scoped_refptr<WorkerThreadWaitableEvent>> additional_workers_;
   };
 
   void FlushImpl() {
@@ -169,12 +173,12 @@
 
     // Wake up workers.
     workers_to_wake_up_.ForEachWorker(
-        [](WorkerThread* worker) { worker->WakeUp(); });
+        [](WorkerThreadWaitableEvent* worker) { worker->WakeUp(); });
 
     // Start workers. Happens after wake ups to prevent the case where a worker
     // enters its main function, is descheduled because it wasn't woken up yet,
     // and is woken up immediately after.
-    workers_to_start_.ForEachWorker([&](WorkerThread* worker) {
+    workers_to_start_.ForEachWorker([&](WorkerThreadWaitableEvent* worker) {
       worker->Start(outer_->after_start().service_thread_task_runner,
                     outer_->after_start().worker_thread_observer);
       if (outer_->worker_started_for_testing_)
@@ -192,8 +196,9 @@
   bool must_schedule_adjust_max_tasks_ = false;
 };
 
-class ThreadGroupImpl::WorkerThreadDelegateImpl : public WorkerThread::Delegate,
-                                                  public BlockingObserver {
+class ThreadGroupImpl::WorkerThreadDelegateImpl
+    : public WorkerThreadWaitableEvent::Delegate,
+      public BlockingObserver {
  public:
   // |outer| owns the worker for which this delegate is constructed. If
   // |is_excess| is true, this worker will be eligible for reclaim.
@@ -206,11 +211,12 @@
   // can thereafter safely be deleted from any thread.
   ~WorkerThreadDelegateImpl() override = default;
 
-  // WorkerThread::Delegate:
+  // WorkerThreadWaitableEvent::Delegate:
   WorkerThread::ThreadLabel GetThreadLabel() const override;
   void OnMainEntry(WorkerThread* worker) override;
   RegisteredTaskSource GetWork(WorkerThread* worker) override;
-  void DidProcessTask(RegisteredTaskSource task_source) override;
+  RegisteredTaskSource SwapProcessedTask(RegisteredTaskSource task_source,
+                                         WorkerThread* worker) override;
   TimeDelta GetSleepTimeout() override;
   void OnMainExit(WorkerThread* worker) override;
   void RecordUnnecessaryWakeup() override;
@@ -228,7 +234,7 @@
   // Returns true iff the worker can get work. Cleans up the worker or puts it
   // on the idle set if it can't get work.
   bool CanGetWorkLockRequired(ScopedCommandsExecutor* executor,
-                              WorkerThread* worker)
+                              WorkerThreadWaitableEvent* worker)
       EXCLUSIVE_LOCKS_REQUIRED(outer_->lock_);
 
   // Increments max [best effort] tasks iff this worker has been within a
@@ -255,25 +261,29 @@
  private:
   // Returns true if |worker| is allowed to cleanup and remove itself from the
   // thread group. Called from GetWork() when no work is available.
-  bool CanCleanupLockRequired(const WorkerThread* worker) const
+  bool CanCleanupLockRequired(const WorkerThreadWaitableEvent* worker) const
       EXCLUSIVE_LOCKS_REQUIRED(outer_->lock_);
 
   // Calls cleanup on |worker| and removes it from the thread group. Called from
   // GetWork() when no work is available and CanCleanupLockRequired() returns
   // true.
   void CleanupLockRequired(ScopedCommandsExecutor* executor,
-                           WorkerThread* worker)
+                           WorkerThreadWaitableEvent* worker)
       EXCLUSIVE_LOCKS_REQUIRED(outer_->lock_);
 
   // Called in GetWork() when a worker becomes idle.
   void OnWorkerBecomesIdleLockRequired(ScopedCommandsExecutor* executor,
-                                       WorkerThread* worker)
+                                       WorkerThreadWaitableEvent* worker)
+      EXCLUSIVE_LOCKS_REQUIRED(outer_->lock_);
+
+  RegisteredTaskSource GetWorkLockRequired(ScopedCommandsExecutor* executor,
+                                           WorkerThreadWaitableEvent* worker)
       EXCLUSIVE_LOCKS_REQUIRED(outer_->lock_);
 
   // Accessed only from the worker thread.
   struct WorkerOnly {
     // Associated WorkerThread, if any, initialized in OnMainEntry().
-    raw_ptr<WorkerThread> worker_thread_;
+    raw_ptr<WorkerThreadWaitableEvent> worker_thread_;
 
 #if BUILDFLAG(IS_WIN)
     std::unique_ptr<win::ScopedWindowsThreadEnvironment> win_thread_environment;
@@ -293,8 +303,8 @@
     TimeTicks blocking_start_time;
 
     // Whether the worker is currently running a task (i.e. GetWork() has
-    // returned a non-empty task source and DidProcessTask() hasn't been called
-    // yet).
+    // returned a non-empty task source and SwapProcessedTask() hasn't been
+    // called yet).
     bool is_running_task() const { return !!current_shutdown_behavior; }
   } write_worker_read_any_;
 
@@ -479,7 +489,7 @@
     worker_cleanup_disallowed_for_testing_ = true;
 
     // Make a copy of the WorkerThreads so that we can call
-    // WorkerThread::JoinForTesting() without holding |lock_| since
+    // WorkerThreadWaitableEvent::JoinForTesting() without holding |lock_| since
     // WorkerThreads may need to access |workers_|.
     workers_copy = workers_;
   }
@@ -532,7 +542,8 @@
   {
 #if DCHECK_IS_ON()
     CheckedAutoLock auto_lock(outer_->lock_);
-    DCHECK(ContainsWorker(outer_->workers_, worker));
+    DCHECK(ContainsWorker(outer_->workers_,
+                          static_cast<WorkerThreadWaitableEvent*>(worker)));
 #endif
   }
 
@@ -545,7 +556,8 @@
       StringPrintf("ThreadPool%sWorker", outer_->thread_group_label_.c_str()));
 
   outer_->BindToCurrentThread();
-  worker_only().worker_thread_ = worker;
+  worker_only().worker_thread_ =
+      static_cast<WorkerThreadWaitableEvent*>(worker);
   SetBlockingObserverForCurrentThread(this);
 
   if (outer_->worker_started_for_testing_) {
@@ -557,15 +569,10 @@
   }
 }
 
-RegisteredTaskSource ThreadGroupImpl::WorkerThreadDelegateImpl::GetWork(
-    WorkerThread* worker) {
-  DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
-  DCHECK(!read_worker().current_task_priority);
-  DCHECK(!read_worker().current_shutdown_behavior);
-
-  ScopedCommandsExecutor executor(outer_.get());
-  CheckedAutoLock auto_lock(outer_->lock_);
-
+RegisteredTaskSource
+ThreadGroupImpl::WorkerThreadDelegateImpl::GetWorkLockRequired(
+    ScopedCommandsExecutor* executor,
+    WorkerThreadWaitableEvent* worker) {
   DCHECK(ContainsWorker(outer_->workers_, worker));
 
   // Use this opportunity, before assigning work to this worker, to create/wake
@@ -575,11 +582,12 @@
   // Note: FlushWorkerCreation() below releases |outer_->lock_|. It is thus
   // important that all other operations come after it to keep this method
   // transactional.
-  outer_->EnsureEnoughWorkersLockRequired(&executor);
-  executor.FlushWorkerCreation(&outer_->lock_);
+  outer_->EnsureEnoughWorkersLockRequired(executor);
+  executor->FlushWorkerCreation(&outer_->lock_);
 
-  if (!CanGetWorkLockRequired(&executor, worker))
+  if (!CanGetWorkLockRequired(executor, worker)) {
     return nullptr;
+  }
 
   RegisteredTaskSource task_source;
   TaskPriority priority;
@@ -594,10 +602,10 @@
       break;
     }
 
-    task_source = outer_->TakeRegisteredTaskSource(&executor);
+    task_source = outer_->TakeRegisteredTaskSource(executor);
   }
   if (!task_source) {
-    OnWorkerBecomesIdleLockRequired(&executor, worker);
+    OnWorkerBecomesIdleLockRequired(executor, worker);
     return nullptr;
   }
 
@@ -610,8 +618,24 @@
   return task_source;
 }
 
-void ThreadGroupImpl::WorkerThreadDelegateImpl::DidProcessTask(
-    RegisteredTaskSource task_source) {
+RegisteredTaskSource ThreadGroupImpl::WorkerThreadDelegateImpl::GetWork(
+    WorkerThread* worker_base) {
+  DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
+  DCHECK(!read_worker().current_task_priority);
+  DCHECK(!read_worker().current_shutdown_behavior);
+  WorkerThreadWaitableEvent* worker =
+      static_cast<WorkerThreadWaitableEvent*>(worker_base);
+
+  ScopedCommandsExecutor executor(outer_.get());
+  CheckedAutoLock auto_lock(outer_->lock_);
+
+  return GetWorkLockRequired(&executor, worker);
+}
+
+RegisteredTaskSource
+ThreadGroupImpl::WorkerThreadDelegateImpl::SwapProcessedTask(
+    RegisteredTaskSource task_source,
+    WorkerThread* worker_thread) {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
   DCHECK(read_worker().current_task_priority);
   DCHECK(read_worker().current_shutdown_behavior);
@@ -627,6 +651,13 @@
             std::move(task_source)));
   }
 
+  // Calling WakeUp() guarantees that this WorkerThread will run Tasks from
+  // TaskSources returned by the GetWork() method of |delegate_| until it
+  // returns nullptr. Resetting |wake_up_event_| here doesn't break this
+  // invariant and avoids a useless loop iteration before going to sleep if
+  // WakeUp() is called while this WorkerThread is awake.
+  wake_up_event_.Reset();
+
   ScopedCommandsExecutor workers_executor(outer_.get());
   ScopedReenqueueExecutor reenqueue_executor;
   CheckedAutoLock auto_lock(outer_->lock_);
@@ -658,6 +689,10 @@
         &workers_executor, &reenqueue_executor,
         std::move(transaction_with_task_source.value()));
   }
+
+  return GetWorkLockRequired(
+      &workers_executor,
+      static_cast<WorkerThreadWaitableEvent*>(worker_thread));
 }
 
 TimeDelta ThreadGroupImpl::WorkerThreadDelegateImpl::GetSleepTimeout() {
@@ -696,7 +731,7 @@
 }
 
 bool ThreadGroupImpl::WorkerThreadDelegateImpl::CanCleanupLockRequired(
-    const WorkerThread* worker) const {
+    const WorkerThreadWaitableEvent* worker) const {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
   if (!is_excess())
     return false;
@@ -710,7 +745,7 @@
 
 void ThreadGroupImpl::WorkerThreadDelegateImpl::CleanupLockRequired(
     ScopedCommandsExecutor* executor,
-    WorkerThread* worker) {
+    WorkerThreadWaitableEvent* worker) {
   DCHECK(!outer_->join_for_testing_started_);
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
 
@@ -727,7 +762,7 @@
 
 void ThreadGroupImpl::WorkerThreadDelegateImpl::OnWorkerBecomesIdleLockRequired(
     ScopedCommandsExecutor* executor,
-    WorkerThread* worker) {
+    WorkerThreadWaitableEvent* worker) {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
   DCHECK(!outer_->idle_workers_set_.Contains(worker));
 
@@ -738,10 +773,12 @@
 }
 
 void ThreadGroupImpl::WorkerThreadDelegateImpl::OnMainExit(
-    WorkerThread* worker) {
+    WorkerThread* worker_base) {
   DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
 
 #if DCHECK_IS_ON()
+  WorkerThreadWaitableEvent* worker =
+      static_cast<WorkerThreadWaitableEvent*>(worker_base);
   {
     bool shutdown_complete = outer_->task_tracker_->IsShutdownComplete();
     CheckedAutoLock auto_lock(outer_->lock_);
@@ -888,7 +925,7 @@
     return;
   // Workers running a CONTINUE_ON_SHUTDOWN tasks are replaced by incrementing
   // max_tasks/max_best_effort_tasks. The effect is reverted in
-  // DidProcessTask().
+  // SwapProcessedTask().
   if (*read_any().current_shutdown_behavior ==
       TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) {
     incremented_max_tasks_for_shutdown_ = true;
@@ -898,7 +935,7 @@
 
 bool ThreadGroupImpl::WorkerThreadDelegateImpl::CanGetWorkLockRequired(
     ScopedCommandsExecutor* executor,
-    WorkerThread* worker) {
+    WorkerThreadWaitableEvent* worker) {
   const bool is_on_idle_workers_set = outer_->IsOnIdleSetLockRequired(worker);
   DCHECK_EQ(is_on_idle_workers_set, outer_->idle_workers_set_.Contains(worker));
 
@@ -972,13 +1009,13 @@
   if (workers_.size() >= max_tasks_)
     return;
 
-  scoped_refptr<WorkerThread> new_worker =
+  scoped_refptr<WorkerThreadWaitableEvent> new_worker =
       CreateAndRegisterWorkerLockRequired(executor);
   DCHECK(new_worker);
   idle_workers_set_.Insert(new_worker.get());
 }
 
-scoped_refptr<WorkerThread>
+scoped_refptr<WorkerThreadWaitableEvent>
 ThreadGroupImpl::CreateAndRegisterWorkerLockRequired(
     ScopedCommandsExecutor* executor) {
   DCHECK(!join_for_testing_started_);
@@ -989,14 +1026,15 @@
   // WorkerThread needs |lock_| as a predecessor for its thread lock because in
   // GetWork(), |lock_| is first acquired and then the thread lock is acquired
   // when GetLastUsedTime() is called on the worker by CanGetWorkLockRequired().
-  scoped_refptr<WorkerThread> worker = MakeRefCounted<WorkerThread>(
-      thread_type_hint_,
-      std::make_unique<WorkerThreadDelegateImpl>(
-          tracked_ref_factory_.GetTrackedRef(),
-          /* is_excess=*/after_start().no_worker_reclaim
-              ? workers_.size() >= after_start().initial_max_tasks
-              : true),
-      task_tracker_, worker_sequence_num_++, &lock_);
+  scoped_refptr<WorkerThreadWaitableEvent> worker =
+      MakeRefCounted<WorkerThreadWaitableEvent>(
+          thread_type_hint_,
+          std::make_unique<WorkerThreadDelegateImpl>(
+              tracked_ref_factory_.GetTrackedRef(),
+              /* is_excess=*/after_start().no_worker_reclaim
+                  ? workers_.size() >= after_start().initial_max_tasks
+                  : true),
+          task_tracker_, worker_sequence_num_++, &lock_);
 
   workers_.push_back(worker);
   executor->ScheduleStart(worker);
@@ -1052,7 +1090,7 @@
     return;
 
   // Start a MAY_BLOCK scope on each worker that is already running a task.
-  for (scoped_refptr<WorkerThread>& worker : workers_) {
+  for (scoped_refptr<WorkerThreadWaitableEvent>& worker : workers_) {
     // The delegates of workers inside a ThreadGroupImpl should be
     // WorkerThreadDelegateImpls.
     WorkerThreadDelegateImpl* delegate =
@@ -1085,7 +1123,7 @@
   // Wake up the appropriate number of workers.
   for (size_t i = 0; i < num_workers_to_wake_up; ++i) {
     MaintainAtLeastOneIdleWorkerLockRequired(executor);
-    WorkerThread* worker_to_wakeup = idle_workers_set_.Take();
+    WorkerThreadWaitableEvent* worker_to_wakeup = idle_workers_set_.Take();
     DCHECK(worker_to_wakeup);
     executor->ScheduleWakeUp(worker_to_wakeup);
   }
@@ -1116,7 +1154,7 @@
 
   // Increment max tasks for each worker that has been within a MAY_BLOCK
   // ScopedBlockingCall for more than may_block_threshold.
-  for (scoped_refptr<WorkerThread> worker : workers_) {
+  for (scoped_refptr<WorkerThreadWaitableEvent> worker : workers_) {
     // The delegates of workers inside a ThreadGroupImpl should be
     // WorkerThreadDelegateImpls.
     WorkerThreadDelegateImpl* delegate =
@@ -1188,7 +1226,8 @@
   }
 }
 
-bool ThreadGroupImpl::IsOnIdleSetLockRequired(WorkerThread* worker) const {
+bool ThreadGroupImpl::IsOnIdleSetLockRequired(
+    WorkerThreadWaitableEvent* worker) const {
   // To avoid searching through the idle set : use GetLastUsedTime() not being
   // null (or being directly on top of the idle set) as a proxy for being on
   // the idle set.
diff --git a/base/task/thread_pool/thread_group_impl.h b/base/task/thread_pool/thread_group_impl.h
index 4c6de67f..cc5009b 100644
--- a/base/task/thread_pool/thread_group_impl.h
+++ b/base/task/thread_pool/thread_group_impl.h
@@ -157,7 +157,7 @@
 
   // Creates a worker, adds it to the thread group, schedules its start and
   // returns it. Cannot be called before Start().
-  scoped_refptr<WorkerThread> CreateAndRegisterWorkerLockRequired(
+  scoped_refptr<WorkerThreadWaitableEvent> CreateAndRegisterWorkerLockRequired(
       ScopedCommandsExecutor* executor) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   // Returns the number of workers that are awake (i.e. not on the idle set).
@@ -204,7 +204,7 @@
   // or when a new task is added to |priority_queue_|.
   void UpdateMinAllowedPriorityLockRequired() EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
-  bool IsOnIdleSetLockRequired(WorkerThread* worker) const
+  bool IsOnIdleSetLockRequired(WorkerThreadWaitableEvent* worker) const
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
   // Increments/decrements the number of tasks of |priority| that are currently
@@ -275,7 +275,8 @@
   const ThreadType thread_type_hint_;
 
   // All workers owned by this thread group.
-  std::vector<scoped_refptr<WorkerThread>> workers_ GUARDED_BY(lock_);
+  std::vector<scoped_refptr<WorkerThreadWaitableEvent>> workers_
+      GUARDED_BY(lock_);
   size_t worker_sequence_num_ GUARDED_BY(lock_) = 0;
 
   bool shutdown_started_ GUARDED_BY(lock_) = false;
diff --git a/base/task/thread_pool/worker_thread.cc b/base/task/thread_pool/worker_thread.cc
index 1fd13ad..f98ae150 100644
--- a/base/task/thread_pool/worker_thread.cc
+++ b/base/task/thread_pool/worker_thread.cc
@@ -20,7 +20,6 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task/task_features.h"
 #include "base/task/thread_pool/environment_config.h"
-#include "base/task/thread_pool/task_tracker.h"
 #include "base/task/thread_pool/worker_thread_observer.h"
 #include "base/threading/hang_watcher.h"
 #include "base/time/time.h"
@@ -44,58 +43,9 @@
 
 namespace base::internal {
 
-namespace {
-
-#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
-    PA_CONFIG(THREAD_CACHE_SUPPORTED)
-// Returns the desired sleep time before the worker has to wake up to purge
-// the cache thread or reclaim itself. |min_sleep_time| contains the minimal
-// acceptable amount of time to sleep.
-TimeDelta GetSleepTimeBeforePurge(TimeDelta min_sleep_time) {
-  const TimeTicks now = TimeTicks::Now();
-
-  // Do not wake up to purge within the first minute of process lifetime. In
-  // short lived processes this will avoid waking up to try and save memory
-  // for a heap that will be going away soon. For longer lived processes this
-  // should allow for better performance at process startup since even if a
-  // worker goes to sleep for kPurgeThreadCacheIdleDelay it's very likely it
-  // will be needed soon after because of heavy startup workloads.
-  constexpr TimeDelta kFirstSleepLength = Minutes(1);
-
-  // Use the first time a worker goes to sleep in this process as an
-  // approximation of the process creation time.
-  static const TimeTicks first_sleep_time = now;
-
-  // Align wakeups for purges to reduce the chances of taking the CPU out of
-  // sleep multiple times for these operations.
-  constexpr TimeDelta kPurgeThreadCacheIdleDelay = Seconds(1);
-
-  // A sleep that occurs within `kFirstSleepLength` of the
-  // first sleep lasts at least `kFirstSleepLength`.
-  if (now <= first_sleep_time + kFirstSleepLength) {
-    min_sleep_time = std::max(kFirstSleepLength, min_sleep_time);
-  }
-
-  const TimeTicks snapped_wake =
-      (now + min_sleep_time)
-          .SnappedToNextTick(TimeTicks(), kPurgeThreadCacheIdleDelay);
-
-  return snapped_wake - now;
-}
-#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
-        // PA_CONFIG(THREAD_CACHE_SUPPORTED)
-
-bool IsDelayFirstWorkerSleepEnabled() {
-  static bool state = FeatureList::IsEnabled(kDelayFirstWorkerWake);
-  return state;
-}
-
-}  // namespace
-
 constexpr TimeDelta WorkerThread::Delegate::kPurgeThreadCacheIdleDelay;
 
-void WorkerThread::Delegate::WaitForWork(WaitableEvent* wake_up_event) {
-  DCHECK(wake_up_event);
+void WorkerThread::Delegate::WaitForWork() {
   const TimeDelta sleep_time = GetSleepTimeout();
 
   // When a thread goes to sleep, the memory retained by its thread cache is
@@ -116,10 +66,11 @@
     PA_CONFIG(THREAD_CACHE_SUPPORTED)
   TimeDelta min_sleep_time = std::min(sleep_time, kPurgeThreadCacheIdleDelay);
 
-  if (IsDelayFirstWorkerSleepEnabled())
+  if (IsDelayFirstWorkerSleepEnabled()) {
     min_sleep_time = GetSleepTimeBeforePurge(min_sleep_time);
+  }
 
-  const bool was_signaled = wake_up_event->TimedWait(min_sleep_time);
+  const bool was_signaled = TimedWait(min_sleep_time);
   // Timed out.
   if (!was_signaled) {
     partition_alloc::ThreadCache::PurgeCurrentThread();
@@ -127,34 +78,73 @@
     // The thread woke up to purge before its standard reclaim time. Sleep for
     // what's remaining until then.
     if (sleep_time > min_sleep_time) {
-      wake_up_event->TimedWait(
-          sleep_time.is_max() ? TimeDelta::Max() : sleep_time - min_sleep_time);
+      TimedWait(sleep_time.is_max() ? TimeDelta::Max()
+                                    : sleep_time - min_sleep_time);
     }
   }
 #else
-  wake_up_event->TimedWait(sleep_time);
+  TimedWait(sleep_time);
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
         // PA_CONFIG(THREAD_CACHE_SUPPORTED)
 }
 
+bool WorkerThread::Delegate::IsDelayFirstWorkerSleepEnabled() {
+  static bool state = FeatureList::IsEnabled(kDelayFirstWorkerWake);
+  return state;
+}
+
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    PA_CONFIG(THREAD_CACHE_SUPPORTED)
+// Returns the desired sleep time before the worker has to wake up to purge
+// the cache thread or reclaim itself. |min_sleep_time| contains the minimal
+// acceptable amount of time to sleep.
+TimeDelta WorkerThread::Delegate::GetSleepTimeBeforePurge(
+    TimeDelta min_sleep_time) {
+  const TimeTicks now = TimeTicks::Now();
+
+  // Do not wake up to purge within the first minute of process lifetime. In
+  // short lived processes this will avoid waking up to try and save memory
+  // for a heap that will be going away soon. For longer lived processes this
+  // should allow for better performance at process startup since even if a
+  // worker goes to sleep for kPurgeThreadCacheIdleDelay it's very likely it
+  // will be needed soon after because of heavy startup workloads.
+  constexpr TimeDelta kFirstSleepLength = Minutes(1);
+
+  // Use the first time a worker goes to sleep in this process as an
+  // approximation of the process creation time.
+  static const TimeTicks first_sleep_time = now;
+
+  // A sleep that occurs within `kFirstSleepLength` of the
+  // first sleep lasts at least `kFirstSleepLength`.
+  if (now <= first_sleep_time + kFirstSleepLength) {
+    min_sleep_time = std::max(kFirstSleepLength, min_sleep_time);
+  }
+
+  // Align wakeups for purges to reduce the chances of taking the CPU out of
+  // sleep multiple times for these operations.
+  const TimeTicks snapped_wake =
+      (now + min_sleep_time)
+          .SnappedToNextTick(TimeTicks(), kPurgeThreadCacheIdleDelay);
+
+  return snapped_wake - now;
+}
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
+        // PA_CONFIG(THREAD_CACHE_SUPPORTED)
+
 WorkerThread::WorkerThread(ThreadType thread_type_hint,
-                           std::unique_ptr<Delegate> delegate,
                            TrackedRef<TaskTracker> task_tracker,
                            size_t sequence_num,
                            const CheckedLock* predecessor_lock)
     : thread_lock_(predecessor_lock),
-      delegate_(std::move(delegate)),
       task_tracker_(std::move(task_tracker)),
       thread_type_hint_(thread_type_hint),
       current_thread_type_(GetDesiredThreadType()),
       sequence_num_(sequence_num) {
-  DCHECK(delegate_);
   DCHECK(task_tracker_);
   DCHECK(CanUseBackgroundThreadTypeForWorkerThread() ||
          thread_type_hint_ != ThreadType::kBackground);
   DCHECK(CanUseUtilityThreadTypeForWorkerThread() ||
          thread_type_hint != ThreadType::kUtility);
-  wake_up_event_.declare_only_used_while_idle();
 }
 
 bool WorkerThread::Start(
@@ -174,7 +164,7 @@
   // Note 2: This is done on Start instead of in the constructor as construction
   // happens under a ThreadGroupImpl lock which precludes calling into
   // FeatureList (as that can also use a lock).
-  IsDelayFirstWorkerSleepEnabled();
+  delegate()->IsDelayFirstWorkerSleepEnabled();
 
   CheckedAutoLock auto_lock(thread_lock_);
   DCHECK(thread_handle_.is_null());
@@ -204,41 +194,6 @@
   return true;
 }
 
-void WorkerThread::WakeUp() {
-  // Signalling an event can deschedule the current thread. Since being
-  // descheduled while holding a lock is undesirable (https://crbug.com/890978),
-  // assert that no lock is held by the current thread.
-  CheckedLock::AssertNoLockHeldOnCurrentThread();
-  // Calling WakeUp() after Cleanup() or Join() is wrong because the
-  // WorkerThread cannot run more tasks.
-  DCHECK(!join_called_for_testing_.IsSet());
-  DCHECK(!should_exit_.IsSet());
-  TRACE_EVENT_INSTANT("wakeup.flow", "WorkerThread::WakeUp",
-                      perfetto::Flow::FromPointer(this));
-  wake_up_event_.Signal();
-}
-
-void WorkerThread::JoinForTesting() {
-  DCHECK(!join_called_for_testing_.IsSet());
-  join_called_for_testing_.Set();
-  wake_up_event_.Signal();
-
-  PlatformThreadHandle thread_handle;
-
-  {
-    CheckedAutoLock auto_lock(thread_lock_);
-
-    if (thread_handle_.is_null())
-      return;
-
-    thread_handle = thread_handle_;
-    // Reset |thread_handle_| so it isn't joined by the destructor.
-    thread_handle_ = PlatformThreadHandle();
-  }
-
-  PlatformThread::Join(thread_handle);
-}
-
 bool WorkerThread::ThreadAliveForTesting() const {
   CheckedAutoLock auto_lock(thread_lock_);
   return !thread_handle_.is_null();
@@ -254,12 +209,6 @@
   }
 }
 
-void WorkerThread::Cleanup() {
-  DCHECK(!should_exit_.IsSet());
-  should_exit_.Set();
-  wake_up_event_.Signal();
-}
-
 void WorkerThread::MaybeUpdateThreadType() {
   UpdateThreadType(GetDesiredThreadType());
 }
@@ -313,7 +262,7 @@
 #endif
 
   if (thread_type_hint_ == ThreadType::kBackground) {
-    switch (delegate_->GetThreadLabel()) {
+    switch (delegate()->GetThreadLabel()) {
       case ThreadLabel::POOLED:
         RunBackgroundPooledWorker();
         return;
@@ -334,7 +283,7 @@
     }
   }
 
-  switch (delegate_->GetThreadLabel()) {
+  switch (delegate()->GetThreadLabel()) {
     case ThreadLabel::POOLED:
       RunPooledWorker();
       return;
@@ -412,10 +361,11 @@
   TRACE_EVENT_INSTANT0("base", "WorkerThread born", TRACE_EVENT_SCOPE_THREAD);
   TRACE_EVENT_BEGIN0("base", "WorkerThread active");
 
-  if (worker_thread_observer_)
+  if (worker_thread_observer_) {
     worker_thread_observer_->OnWorkerThreadMainEntry();
+  }
 
-  delegate_->OnMainEntry(this);
+  delegate()->OnMainEntry(this);
 
   // Background threads can take an arbitrary amount of time to complete, do not
   // watch them for hangs. Ignore priority boosting for now.
@@ -430,84 +380,80 @@
         base::HangWatcher::ThreadType::kThreadPoolThread);
   }
 
-  // A WorkerThread starts out waiting for work.
-  {
-    TRACE_EVENT_END0("base", "WorkerThread active");
-    // TODO(crbug.com/1021571): Remove this once fixed.
-    PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
-    delegate_->WaitForWork(&wake_up_event_);
-    TRACE_EVENT_BEGIN("base", "WorkerThread active",
-                      perfetto::TerminatingFlow::FromPointer(this));
-  }
-  bool got_work_this_wakeup = false;
   while (!ShouldExit()) {
 #if BUILDFLAG(IS_APPLE)
     apple::ScopedNSAutoreleasePool autorelease_pool;
 #endif
     absl::optional<WatchHangsInScope> hang_watch_scope;
-    if (watch_for_hangs)
-      hang_watch_scope.emplace();
 
-    UpdateThreadType(GetDesiredThreadType());
+    TRACE_EVENT_END0("base", "WorkerThread active");
+    // TODO(crbug.com/1021571): Remove this once fixed.
+    PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
+    hang_watch_scope.reset();
+    delegate()->WaitForWork();
+    TRACE_EVENT_BEGIN("base", "WorkerThread active",
+                      perfetto::TerminatingFlow::FromPointer(this));
 
-    // Get the task source containing the next task to execute.
-    RegisteredTaskSource task_source = delegate_->GetWork(this);
-    if (!task_source) {
-      // Exit immediately if GetWork() resulted in detaching this worker.
-      if (ShouldExit())
-        break;
-
-      // If this is the first time we called GetWork and the worker's still
-      // alive, record that this is an unnecessary wakeup.
-      if (!got_work_this_wakeup)
-        delegate_->RecordUnnecessaryWakeup();
-
-      TRACE_EVENT_END0("base", "WorkerThread active");
-      // TODO(crbug.com/1021571): Remove this once fixed.
-      PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
-      hang_watch_scope.reset();
-      delegate_->WaitForWork(&wake_up_event_);
-      got_work_this_wakeup = false;
-
-      TRACE_EVENT_BEGIN("base", "WorkerThread active",
-                        perfetto::TerminatingFlow::FromPointer(this));
-      continue;
+    // Don't GetWork() in the case where we woke up for Cleanup().
+    if (ShouldExit()) {
+      break;
     }
 
-    got_work_this_wakeup = true;
+    if (watch_for_hangs) {
+      hang_watch_scope.emplace();
+    }
 
-    // Alias pointer for investigation of memory corruption. crbug.com/1218384
-    TaskSource* task_source_before_run = task_source.get();
-    base::debug::Alias(&task_source_before_run);
+    // Thread type needs to be updated before GetWork.
+    UpdateThreadType(GetDesiredThreadType());
 
-    task_source = task_tracker_->RunAndPopNextTask(std::move(task_source));
+    // Get the task source containing the first task to execute.
+    RegisteredTaskSource task_source = delegate()->GetWork(this);
 
-    // Alias pointer for investigation of memory corruption. crbug.com/1218384
-    TaskSource* task_source_before_move = task_source.get();
-    base::debug::Alias(&task_source_before_move);
+    // If acquiring work failed and the worker's still alive,
+    // record that this is an unnecessary wakeup.
+    if (!task_source && !ShouldExit()) {
+      delegate()->RecordUnnecessaryWakeup();
+    }
 
-    delegate_->DidProcessTask(std::move(task_source));
+    while (task_source) {
+      // Alias pointer for investigation of memory corruption. crbug.com/1218384
+      TaskSource* task_source_before_run = task_source.get();
+      base::debug::Alias(&task_source_before_run);
 
-    // Check that task_source is always cleared, to help investigation of memory
-    // corruption where task_source is non-null after being moved.
-    // crbug.com/1218384
-    CHECK(!task_source);
+      task_source = task_tracker_->RunAndPopNextTask(std::move(task_source));
 
-    // Calling WakeUp() guarantees that this WorkerThread will run Tasks from
-    // TaskSources returned by the GetWork() method of |delegate_| until it
-    // returns nullptr. Resetting |wake_up_event_| here doesn't break this
-    // invariant and avoids a useless loop iteration before going to sleep if
-    // WakeUp() is called while this WorkerThread is awake.
-    wake_up_event_.Reset();
+      // Alias pointer for investigation of memory corruption. crbug.com/1218384
+      TaskSource* task_source_before_move = task_source.get();
+      base::debug::Alias(&task_source_before_move);
+
+      // We emplace the hang_watch_scope here so that each hang watch scope
+      // covers one GetWork (or SwapProcessedTask) as well as one
+      // RunAndPopNextTask.
+      if (watch_for_hangs) {
+        hang_watch_scope.emplace();
+      }
+
+      RegisteredTaskSource new_task_source =
+          delegate()->SwapProcessedTask(std::move(task_source), this);
+
+      UpdateThreadType(GetDesiredThreadType());
+
+      // Check that task_source is always cleared, to help investigation of
+      // memory corruption where task_source is non-null after being moved.
+      // crbug.com/1218384
+      CHECK(!task_source);
+      task_source = std::move(new_task_source);
+    }
   }
 
   // Important: It is unsafe to access unowned state (e.g. |task_tracker_|)
   // after invoking OnMainExit().
 
-  delegate_->OnMainExit(this);
+  delegate()->OnMainExit(this);
 
-  if (worker_thread_observer_)
+  if (worker_thread_observer_) {
     worker_thread_observer_->OnWorkerThreadMainExit();
+  }
 
   // Release the self-reference to |this|. This can result in deleting |this|
   // and as such no more member accesses should be made after this point.
diff --git a/base/task/thread_pool/worker_thread.h b/base/task/thread_pool/worker_thread.h
index 8fea0ce4..1548d208 100644
--- a/base/task/thread_pool/worker_thread.h
+++ b/base/task/thread_pool/worker_thread.h
@@ -5,8 +5,6 @@
 #ifndef BASE_TASK_THREAD_POOL_WORKER_THREAD_H_
 #define BASE_TASK_THREAD_POOL_WORKER_THREAD_H_
 
-#include <memory>
-
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
@@ -15,6 +13,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/task/common/checked_lock.h"
 #include "base/task/thread_pool/task_source.h"
+#include "base/task/thread_pool/task_tracker.h"
 #include "base/task/thread_pool/tracked_ref.h"
 #include "base/thread_annotations.h"
 #include "base/threading/platform_thread.h"
@@ -44,7 +43,7 @@
  public:
   // Labels this WorkerThread's association. This doesn't affect any logic
   // but will add a stack frame labeling this thread for ease of stack trace
-  // identification.
+  // identification
   enum class ThreadLabel {
     POOLED,
     SHARED,
@@ -71,21 +70,29 @@
     // Called by |worker|'s thread to get a TaskSource from which to run a Task.
     virtual RegisteredTaskSource GetWork(WorkerThread* worker) = 0;
 
-    // Called by the WorkerThread after it ran a Task. If the Task's
-    // TaskSource should be reenqueued, it is passed to |task_source|.
-    // Otherwise, |task_source| is nullptr.
-    virtual void DidProcessTask(RegisteredTaskSource task_source) = 0;
+    // // Called by the WorkerThread after it ran a Task. Must only be passed a
+    // // task when ShouldExit() returns true, meaning that this worker should
+    // quit
+    // // immediately and relinquish any task sources it owns. If a task source
+    // // just run by this WorkerThread contains more work after
+    // // RunAndPopNextTask() and the worker is running as usual,
+    // // SwapProcessedTask() should be used instead.
+    // virtual void DidProcessTask(RegisteredTaskSource task_source) = 0;
+
+    // Called by the worker thread to swap the task source that has just run for
+    // another one, if available. |task_source| must not be null. The worker can
+    // then run the task returned as if it was acquired via GetWork().
+    virtual RegisteredTaskSource SwapProcessedTask(
+        RegisteredTaskSource task_source,
+        WorkerThread* worker) = 0;
 
     // Called to determine how long to sleep before the next call to GetWork().
     // GetWork() may be called before this timeout expires if the worker's
     // WakeUp() method is called.
     virtual TimeDelta GetSleepTimeout() = 0;
 
-    // Called by the WorkerThread's thread to wait for work. Override this
-    // method if the thread in question needs special handling to go to sleep.
-    // |wake_up_event| is a manually resettable event and is signaled on
-    // WorkerThread::WakeUp()
-    virtual void WaitForWork(WaitableEvent* wake_up_event);
+    // Called by the WorkerThread's thread to wait for work.
+    virtual void WaitForWork();
 
     // Called by |worker|'s thread right before the main function exits. The
     // Delegate is free to release any associated resources in this call. It is
@@ -98,20 +105,35 @@
     virtual void RecordUnnecessaryWakeup() {}
 
     static constexpr TimeDelta kPurgeThreadCacheIdleDelay = Seconds(1);
+
+   protected:
+    friend WorkerThread;
+    static bool IsDelayFirstWorkerSleepEnabled();
+
+    // Called in WaitForWork() to hide the worker's synchronization
+    // mechanism. Returns |true| if signaled, and |false| if the call timed out.
+    virtual bool TimedWait(TimeDelta timeout) = 0;
+
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    PA_CONFIG(THREAD_CACHE_SUPPORTED)
+    // Returns the desired sleep time before the worker has to wake up to purge
+    // the cache thread or reclaim itself. |min_sleep_time| contains the minimal
+    // acceptable amount of time to sleep.
+    static TimeDelta GetSleepTimeBeforePurge(TimeDelta min_sleep_time);
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) &&
+        // PA_CONFIG(THREAD_CACHE_SUPPORTED)
   };
 
   // Creates a WorkerThread that runs Tasks from TaskSources returned by
-  // |delegate|. No actual thread will be created for this WorkerThread
-  // before Start() is called. |thread_type_hint| is the preferred thread type;
-  // the actual thread type depends on shutdown state and platform
-  // capabilities. |task_tracker| is used to handle shutdown behavior of Tasks.
-  // |sequence_num| is an index that helps identifying this WorkerThread.
-  // |predecessor_lock| is a lock that is allowed to be held when calling
-  // methods on this WorkerThread. |backward_compatibility| indicates
-  // whether backward compatibility is enabled. Either JoinForTesting() or
-  // Cleanup() must be called before releasing the last external reference.
+  // |delegate()|. No actual thread will be created for this WorkerThread before
+  // Start() is called. |thread_type_hint| is the preferred thread type; the
+  // actual thread type depends on shutdown state and platform
+  // capabilities. |task_tracker| is used to handle shutdown behavior of
+  // Tasks. |sequence_num| is an index that helps identifying this
+  // WorkerThread. |predecessor_lock| is a lock that is allowed to be held when
+  // calling methods on this WorkerThread.  Either JoinForTesting() or Cleanup()
+  // must be called before releasing the last external reference.
   WorkerThread(ThreadType thread_type_hint,
-               std::unique_ptr<Delegate> delegate,
                TrackedRef<TaskTracker> task_tracker,
                size_t sequence_num,
                const CheckedLock* predecessor_lock = nullptr);
@@ -130,14 +152,7 @@
   bool Start(scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner_,
              WorkerThreadObserver* worker_thread_observer = nullptr);
 
-  // Wakes up this WorkerThread if it wasn't already awake. After this is
-  // called, this WorkerThread will run Tasks from TaskSources returned by
-  // the GetWork() method of its delegate until it returns nullptr. No-op if
-  // Start() wasn't called. DCHECKs if called after Start() has failed or after
-  // Cleanup() has been called.
-  void WakeUp();
 
-  WorkerThread::Delegate* delegate() { return delegate_.get(); }
 
   // Joins this WorkerThread. If a Task is already running, it will be
   // allowed to complete its execution. This can only be called once.
@@ -145,7 +160,7 @@
   // Note: A thread that detaches before JoinForTesting() is called may still be
   // running after JoinForTesting() returns. However, it can't run tasks after
   // JoinForTesting() returns.
-  void JoinForTesting();
+  virtual void JoinForTesting() = 0;
 
   // Returns true if the worker is alive.
   bool ThreadAliveForTesting() const;
@@ -159,7 +174,9 @@
   //   scoped_refptr<WorkerThread> worker_ = /* Existing Worker */
   //   worker_->Cleanup();
   //   worker_ = nullptr;
-  void Cleanup();
+  virtual void Cleanup() = 0;
+
+  virtual Delegate* delegate() = 0;
 
   // Possibly updates the thread type to the appropriate type based on the
   // thread type hint, current shutdown state, and platform capabilities.
@@ -176,7 +193,7 @@
 
   size_t sequence_num() const { return sequence_num_; }
 
- private:
+ protected:
   friend class RefCountedThreadSafe<WorkerThread>;
   class Thread;
 
@@ -231,14 +248,9 @@
   // stand as a required idle thread).
   TimeTicks last_used_time_ GUARDED_BY(thread_lock_);
 
-  // Event to wake up the thread managed by |this|.
-  WaitableEvent wake_up_event_{WaitableEvent::ResetPolicy::AUTOMATIC,
-                               WaitableEvent::InitialState::NOT_SIGNALED};
-
   // Whether the thread should exit. Set by Cleanup().
   AtomicFlag should_exit_;
 
-  const std::unique_ptr<Delegate> delegate_;
   const TrackedRef<TaskTracker> task_tracker_;
 
   // Optional observer notified when a worker enters and exits its main
diff --git a/base/task/thread_pool/worker_thread_set.cc b/base/task/thread_pool/worker_thread_set.cc
index 3b43f40..825703b 100644
--- a/base/task/thread_pool/worker_thread_set.cc
+++ b/base/task/thread_pool/worker_thread_set.cc
@@ -7,12 +7,13 @@
 #include "base/check_op.h"
 #include "base/containers/contains.h"
 #include "base/ranges/algorithm.h"
-#include "base/task/thread_pool/worker_thread.h"
+#include "base/task/thread_pool/worker_thread_waitable_event.h"
 
 namespace base::internal {
 
-bool WorkerThreadSet::Compare::operator()(const WorkerThread* a,
-                                          const WorkerThread* b) const {
+bool WorkerThreadSet::Compare::operator()(
+    const WorkerThreadWaitableEvent* a,
+    const WorkerThreadWaitableEvent* b) const {
   return a->sequence_num() < b->sequence_num();
 }
 
@@ -20,7 +21,7 @@
 
 WorkerThreadSet::~WorkerThreadSet() = default;
 
-void WorkerThreadSet::Insert(WorkerThread* worker) {
+void WorkerThreadSet::Insert(WorkerThreadWaitableEvent* worker) {
   DCHECK(!Contains(worker)) << "WorkerThread already on stack";
   auto old_first = set_.begin();
   set_.insert(worker);
@@ -30,30 +31,30 @@
     (*old_first)->BeginUnusedPeriod();
 }
 
-WorkerThread* WorkerThreadSet::Take() {
+WorkerThreadWaitableEvent* WorkerThreadSet::Take() {
   if (IsEmpty())
     return nullptr;
-  WorkerThread* const worker = *set_.begin();
+  WorkerThreadWaitableEvent* const worker = *set_.begin();
   set_.erase(set_.begin());
   if (!IsEmpty())
     (*set_.begin())->EndUnusedPeriod();
   return worker;
 }
 
-WorkerThread* WorkerThreadSet::Peek() const {
+WorkerThreadWaitableEvent* WorkerThreadSet::Peek() const {
   if (IsEmpty())
     return nullptr;
   return *set_.begin();
 }
 
-bool WorkerThreadSet::Contains(const WorkerThread* worker) const {
-  return set_.count(const_cast<WorkerThread*>(worker)) > 0;
+bool WorkerThreadSet::Contains(const WorkerThreadWaitableEvent* worker) const {
+  return set_.count(const_cast<WorkerThreadWaitableEvent*>(worker)) > 0;
 }
 
-void WorkerThreadSet::Remove(const WorkerThread* worker) {
+void WorkerThreadSet::Remove(const WorkerThreadWaitableEvent* worker) {
   DCHECK(!IsEmpty());
   DCHECK_NE(worker, *set_.begin());
-  auto it = set_.find(const_cast<WorkerThread*>(worker));
+  auto it = set_.find(const_cast<WorkerThreadWaitableEvent*>(worker));
   DCHECK(it != set_.end());
   DCHECK_NE(TimeTicks(), (*it)->GetLastUsedTime());
   set_.erase(it);
diff --git a/base/task/thread_pool/worker_thread_set.h b/base/task/thread_pool/worker_thread_set.h
index 42773f9..9137a90 100644
--- a/base/task/thread_pool/worker_thread_set.h
+++ b/base/task/thread_pool/worker_thread_set.h
@@ -14,7 +14,7 @@
 namespace base {
 namespace internal {
 
-class WorkerThread;
+class WorkerThreadWaitableEvent;
 
 // An ordered set of WorkerThreads which has custom logic to treat the worker at
 // the front of the set as being "in-use" (so its time in that position doesn't
@@ -24,7 +24,8 @@
 // O(log(n)). This class is NOT thread-safe.
 class BASE_EXPORT WorkerThreadSet {
   struct Compare {
-    bool operator()(const WorkerThread* a, const WorkerThread* b) const;
+    bool operator()(const WorkerThreadWaitableEvent* a,
+                    const WorkerThreadWaitableEvent* b) const;
   };
 
  public:
@@ -34,24 +35,24 @@
   ~WorkerThreadSet();
 
   // Inserts |worker| in the set. |worker| must not already be on the set. Flags
-  // the WorkerThread previously at the front of the set, if it changed, or
-  // |worker| as unused.
-  void Insert(WorkerThread* worker);
+  // the WorkerThreadWaitableEvent previously at the front of the set, if it
+  // changed, or |worker| as unused.
+  void Insert(WorkerThreadWaitableEvent* worker);
 
   // Removes the front WorkerThread from the set and returns it. Returns nullptr
   // if the set is empty. Flags the WorkerThread now at the front of the set, if
   // any, as being in-use.
-  WorkerThread* Take();
+  WorkerThreadWaitableEvent* Take();
 
   // Returns the front WorkerThread from the set, nullptr if empty.
-  WorkerThread* Peek() const;
+  WorkerThreadWaitableEvent* Peek() const;
 
   // Returns true if |worker| is already in the set.
-  bool Contains(const WorkerThread* worker) const;
+  bool Contains(const WorkerThreadWaitableEvent* worker) const;
 
   // Removes |worker| from the set. Must not be invoked for the first worker
   // on the set.
-  void Remove(const WorkerThread* worker);
+  void Remove(const WorkerThreadWaitableEvent* worker);
 
   // Returns the number of WorkerThreads on the set.
   size_t Size() const { return set_.size(); }
@@ -60,7 +61,7 @@
   bool IsEmpty() const { return set_.empty(); }
 
  private:
-  std::set<WorkerThread*, Compare> set_;
+  std::set<WorkerThreadWaitableEvent*, Compare> set_;
 };
 
 }  // namespace internal
diff --git a/base/task/thread_pool/worker_thread_set_unittest.cc b/base/task/thread_pool/worker_thread_set_unittest.cc
index b95af33b..08c697a 100644
--- a/base/task/thread_pool/worker_thread_set_unittest.cc
+++ b/base/task/thread_pool/worker_thread_set_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/task/thread_pool/task_source.h"
 #include "base/task/thread_pool/task_tracker.h"
 #include "base/task/thread_pool/worker_thread.h"
+#include "base/task/thread_pool/worker_thread_waitable_event.h"
 #include "base/test/gtest_util.h"
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
@@ -18,7 +19,7 @@
 
 namespace {
 
-class MockWorkerThreadDelegate : public WorkerThread::Delegate {
+class MockWorkerThreadDelegate : public WorkerThreadWaitableEvent::Delegate {
  public:
   WorkerThread::ThreadLabel GetThreadLabel() const override {
     return WorkerThread::ThreadLabel::DEDICATED;
@@ -27,8 +28,10 @@
   RegisteredTaskSource GetWork(WorkerThread* worker) override {
     return nullptr;
   }
-  void DidProcessTask(RegisteredTaskSource task_source) override {
-    ADD_FAILURE() << "Unexpected call to DidRunTask()";
+  RegisteredTaskSource SwapProcessedTask(RegisteredTaskSource task_source,
+                                         WorkerThread* worker) override {
+    ADD_FAILURE() << "Unexpected call to SwapProcessedTask()";
+    return nullptr;
   }
   TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
 };
@@ -36,15 +39,15 @@
 class ThreadPoolWorkerSetTest : public testing::Test {
  protected:
   void SetUp() override {
-    worker_a_ = MakeRefCounted<WorkerThread>(
+    worker_a_ = MakeRefCounted<WorkerThreadWaitableEvent>(
         ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(),
         task_tracker_.GetTrackedRef(), 0);
     ASSERT_TRUE(worker_a_);
-    worker_b_ = MakeRefCounted<WorkerThread>(
+    worker_b_ = MakeRefCounted<WorkerThreadWaitableEvent>(
         ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(),
         task_tracker_.GetTrackedRef(), 1);
     ASSERT_TRUE(worker_b_);
-    worker_c_ = MakeRefCounted<WorkerThread>(
+    worker_c_ = MakeRefCounted<WorkerThreadWaitableEvent>(
         ThreadType::kDefault, std::make_unique<MockWorkerThreadDelegate>(),
         task_tracker_.GetTrackedRef(), 2);
     ASSERT_TRUE(worker_c_);
@@ -54,9 +57,9 @@
   TaskTracker task_tracker_;
 
  protected:
-  scoped_refptr<WorkerThread> worker_a_;
-  scoped_refptr<WorkerThread> worker_b_;
-  scoped_refptr<WorkerThread> worker_c_;
+  scoped_refptr<WorkerThreadWaitableEvent> worker_a_;
+  scoped_refptr<WorkerThreadWaitableEvent> worker_b_;
+  scoped_refptr<WorkerThreadWaitableEvent> worker_c_;
 };
 
 }  // namespace
@@ -81,7 +84,7 @@
   EXPECT_FALSE(set.IsEmpty());
   EXPECT_EQ(3U, set.Size());
 
-  WorkerThread* idle_worker = set.Take();
+  WorkerThreadWaitableEvent* idle_worker = set.Take();
   EXPECT_EQ(idle_worker, worker_a_.get());
   EXPECT_FALSE(set.IsEmpty());
   EXPECT_EQ(2U, set.Size());
@@ -126,7 +129,7 @@
   EXPECT_FALSE(set.IsEmpty());
   EXPECT_EQ(3U, set.Size());
 
-  WorkerThread* idle_worker = set.Take();
+  WorkerThreadWaitableEvent* idle_worker = set.Take();
   EXPECT_EQ(worker_a_.get(), idle_worker);
   EXPECT_EQ(worker_b_.get(), set.Peek());
   EXPECT_FALSE(set.IsEmpty());
@@ -166,7 +169,7 @@
   EXPECT_TRUE(set.Contains(worker_b_.get()));
   EXPECT_TRUE(set.Contains(worker_c_.get()));
 
-  WorkerThread* idle_worker = set.Take();
+  WorkerThreadWaitableEvent* idle_worker = set.Take();
   EXPECT_EQ(idle_worker, worker_a_.get());
   EXPECT_FALSE(set.Contains(worker_a_.get()));
   EXPECT_TRUE(set.Contains(worker_b_.get()));
diff --git a/base/task/thread_pool/worker_thread_waitable_event.cc b/base/task/thread_pool/worker_thread_waitable_event.cc
new file mode 100644
index 0000000..cd127fa
--- /dev/null
+++ b/base/task/thread_pool/worker_thread_waitable_event.cc
@@ -0,0 +1,93 @@
+// 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 "base/task/thread_pool/worker_thread_waitable_event.h"
+
+#include "base/debug/alias.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/thread_pool/task_tracker.h"
+#include "base/task/thread_pool/worker_thread_observer.h"
+#include "base/time/time.h"
+#include "base/trace_event/base_tracing.h"
+
+#if BUILDFLAG(IS_APPLE)
+#include "base/apple/scoped_nsautorelease_pool.h"
+#endif
+
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && \
+    PA_CONFIG(THREAD_CACHE_SUPPORTED)
+#include "base/allocator/partition_allocator/src/partition_alloc/thread_cache.h"
+#endif
+
+namespace base::internal {
+
+bool WorkerThreadWaitableEvent::Delegate::TimedWait(TimeDelta timeout) {
+  return wake_up_event_.TimedWait(timeout);
+}
+
+WorkerThreadWaitableEvent::WorkerThreadWaitableEvent(
+    ThreadType thread_type_hint,
+    std::unique_ptr<Delegate> delegate,
+    TrackedRef<TaskTracker> task_tracker,
+    size_t sequence_num,
+    const CheckedLock* predecessor_lock)
+    : WorkerThread(thread_type_hint,
+                   task_tracker,
+                   sequence_num,
+                   predecessor_lock),
+      delegate_(std::move(delegate)) {
+  DCHECK(delegate_);
+  delegate_->wake_up_event_.declare_only_used_while_idle();
+}
+
+WorkerThreadWaitableEvent::~WorkerThreadWaitableEvent() = default;
+
+void WorkerThreadWaitableEvent::JoinForTesting() {
+  DCHECK(!join_called_for_testing_.IsSet());
+  join_called_for_testing_.Set();
+  delegate_->wake_up_event_.Signal();
+
+  PlatformThreadHandle thread_handle;
+
+  {
+    CheckedAutoLock auto_lock(thread_lock_);
+
+    if (thread_handle_.is_null()) {
+      return;
+    }
+
+    thread_handle = thread_handle_;
+    // Reset |thread_handle_| so it isn't joined by the destructor.
+    thread_handle_ = PlatformThreadHandle();
+  }
+
+  PlatformThread::Join(thread_handle);
+}
+
+void WorkerThreadWaitableEvent::Cleanup() {
+  DCHECK(!should_exit_.IsSet());
+  should_exit_.Set();
+  delegate_->wake_up_event_.Signal();
+}
+
+void WorkerThreadWaitableEvent::WakeUp() {
+  // Signalling an event can deschedule the current thread. Since being
+  // descheduled while holding a lock is undesirable (https://crbug.com/890978),
+  // assert that no lock is held by the current thread.
+  CheckedLock::AssertNoLockHeldOnCurrentThread();
+  // Calling WakeUp() after Cleanup() or Join() is wrong because the
+  // WorkerThread cannot run more tasks.
+  DCHECK(!join_called_for_testing_.IsSet());
+  DCHECK(!should_exit_.IsSet());
+  TRACE_EVENT_INSTANT("wakeup.flow", "WorkerThreadWaitableEvent::WakeUp",
+                      perfetto::Flow::FromPointer(this));
+
+  delegate_->wake_up_event_.Signal();
+}
+
+WorkerThreadWaitableEvent::Delegate* WorkerThreadWaitableEvent::delegate() {
+  return delegate_.get();
+}
+
+}  // namespace base::internal
diff --git a/base/task/thread_pool/worker_thread_waitable_event.h b/base/task/thread_pool/worker_thread_waitable_event.h
new file mode 100644
index 0000000..5da2f9c
--- /dev/null
+++ b/base/task/thread_pool/worker_thread_waitable_event.h
@@ -0,0 +1,64 @@
+// 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 BASE_TASK_THREAD_POOL_WORKER_THREAD_WAITABLE_EVENT_H_
+#define BASE_TASK_THREAD_POOL_WORKER_THREAD_WAITABLE_EVENT_H_
+
+#include "base/task/thread_pool/worker_thread.h"
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/common/checked_lock.h"
+#include "base/task/thread_pool/tracked_ref.h"
+
+namespace base {
+
+namespace internal {
+
+class BASE_EXPORT WorkerThreadWaitableEvent : public WorkerThread {
+ public:
+  class BASE_EXPORT Delegate : public WorkerThread::Delegate {
+   protected:
+    friend WorkerThreadWaitableEvent;
+    bool TimedWait(TimeDelta timeout) override;
+    // Event to wake up the thread managed by the WorkerThread whose delegate
+    // this is.
+    WaitableEvent wake_up_event_{WaitableEvent::ResetPolicy::AUTOMATIC,
+                                 WaitableEvent::InitialState::NOT_SIGNALED};
+  };
+
+  // Everything is passed to WorkerThread's constructor, except the Delegate.
+  WorkerThreadWaitableEvent(ThreadType thread_type_hint,
+                            std::unique_ptr<Delegate> delegate,
+                            TrackedRef<TaskTracker> task_tracker,
+                            size_t sequence_num,
+                            const CheckedLock* predecessor_lock = nullptr);
+
+  WorkerThreadWaitableEvent(const WorkerThread&) = delete;
+  WorkerThreadWaitableEvent& operator=(const WorkerThread&) = delete;
+
+  // Wakes up this WorkerThreadWaitableEvent if it wasn't already awake. After
+  // this is called, this WorkerThreadWaitableEvent will run Tasks from
+  // TaskSources returned by the GetWork() method of its delegate until it
+  // returns nullptr. No-op if Start() wasn't called. DCHECKs if called after
+  // Start() has failed or after Cleanup() has been called.
+  void WakeUp();
+
+  // WorkerThread:
+  void JoinForTesting() override;
+  void Cleanup() override;
+  Delegate* delegate() override;
+
+ private:
+  const std::unique_ptr<Delegate> delegate_;
+
+  ~WorkerThreadWaitableEvent() override;
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_TASK_THREAD_POOL_WORKER_THREAD_WAITABLE_EVENT_H_
diff --git a/base/task/thread_pool/worker_thread_unittest.cc b/base/task/thread_pool/worker_thread_waitable_event_unittest.cc
similarity index 93%
rename from base/task/thread_pool/worker_thread_unittest.cc
rename to base/task/thread_pool/worker_thread_waitable_event_unittest.cc
index 327af8a7..43caa18 100644
--- a/base/task/thread_pool/worker_thread_unittest.cc
+++ b/base/task/thread_pool/worker_thread_waitable_event_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/task/thread_pool/worker_thread.h"
+#include "base/task/thread_pool/worker_thread_waitable_event.h"
 
 #include <stddef.h>
 
@@ -58,14 +58,14 @@
 
 const size_t kNumSequencesPerTest = 150;
 
-class WorkerThreadDefaultDelegate : public WorkerThread::Delegate {
+class WorkerThreadDefaultDelegate : public WorkerThreadWaitableEvent::Delegate {
  public:
   WorkerThreadDefaultDelegate() = default;
   WorkerThreadDefaultDelegate(const WorkerThreadDefaultDelegate&) = delete;
   WorkerThreadDefaultDelegate& operator=(const WorkerThreadDefaultDelegate&) =
       delete;
 
-  // WorkerThread::Delegate:
+  // WorkerThreadWaitableEvent::Delegate:
   WorkerThread::ThreadLabel GetThreadLabel() const override {
     return WorkerThread::ThreadLabel::DEDICATED;
   }
@@ -73,8 +73,10 @@
   RegisteredTaskSource GetWork(WorkerThread* worker) override {
     return nullptr;
   }
-  void DidProcessTask(RegisteredTaskSource task_source) override {
-    ADD_FAILURE() << "Unexpected call to DidRunTask()";
+  RegisteredTaskSource SwapProcessedTask(RegisteredTaskSource task_source,
+                                         WorkerThread* worker) override {
+    ADD_FAILURE() << "Unexpected call to SwapProcessedTask()";
+    return nullptr;
   }
   TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); }
 };
@@ -93,8 +95,8 @@
   }
 
   void SetUp() override {
-    worker_ = MakeRefCounted<WorkerThread>(
-        ThreadType::kDefault, std::make_unique<TestWorkerThreadDelegate>(this),
+    worker_ = MakeRefCounted<WorkerThreadWaitableEvent>(
+        ThreadType::kDefault, std::make_unique<TestWorkerThreadWaitableEventDelegate>(this),
         task_tracker_.GetTrackedRef(), 0);
     ASSERT_TRUE(worker_);
     worker_->Start(service_thread_.task_runner());
@@ -144,19 +146,19 @@
     return did_run_task_sources_;
   }
 
-  scoped_refptr<WorkerThread> worker_;
+  scoped_refptr<WorkerThreadWaitableEvent> worker_;
   Thread service_thread_ = Thread("ServiceThread");
 
  private:
-  class TestWorkerThreadDelegate : public WorkerThreadDefaultDelegate {
+  class TestWorkerThreadWaitableEventDelegate : public WorkerThreadDefaultDelegate {
    public:
-    explicit TestWorkerThreadDelegate(ThreadPoolWorkerTest* outer)
+    explicit TestWorkerThreadWaitableEventDelegate(ThreadPoolWorkerTest* outer)
         : outer_(outer) {}
-    TestWorkerThreadDelegate(const TestWorkerThreadDelegate&) = delete;
-    TestWorkerThreadDelegate& operator=(const TestWorkerThreadDelegate&) =
+    TestWorkerThreadWaitableEventDelegate(const TestWorkerThreadWaitableEventDelegate&) = delete;
+    TestWorkerThreadWaitableEventDelegate& operator=(const TestWorkerThreadWaitableEventDelegate&) =
         delete;
 
-    ~TestWorkerThreadDelegate() override {
+    ~TestWorkerThreadWaitableEventDelegate() override {
       EXPECT_FALSE(IsCallToDidProcessTaskExpected());
     }
 
@@ -223,11 +225,9 @@
       return registered_task_source;
     }
 
-    // This override verifies that |task_source| has the expected number of
-    // Tasks and adds it to |did_run_task_sources_|. Unlike a normal
-    // DidProcessTask() implementation, it doesn't add |task_source| to a queue
-    // for further execution.
-    void DidProcessTask(RegisteredTaskSource registered_task_source) override {
+    RegisteredTaskSource SwapProcessedTask(
+        RegisteredTaskSource registered_task_source,
+        WorkerThread* worker) override {
       {
         CheckedAutoLock auto_lock(expect_did_run_task_lock_);
         EXPECT_TRUE(expect_did_run_task_);
@@ -264,6 +264,7 @@
                     outer_->created_sequences_.size());
         }
       }
+      return GetWork(worker);
     }
 
    private:
@@ -501,7 +502,9 @@
     return registered_task_source;
   }
 
-  void DidProcessTask(RegisteredTaskSource) override {}
+  RegisteredTaskSource SwapProcessedTask(RegisteredTaskSource task_source, WorkerThread* worker) override {
+    return GetWork(worker);
+  }
 
   void OnMainExit(WorkerThread* worker) override {
     controls_->exited_.Signal();
@@ -561,7 +564,7 @@
   controls->set_can_cleanup(true);
   EXPECT_CALL(*delegate, OnMainEntry(_));
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, WrapUnique(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, WrapUnique(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   worker->Start(service_thread.task_runner());
   worker->WakeUp();
@@ -589,7 +592,7 @@
   controls->HaveWorkBlock();
 
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   worker->Start(service_thread.task_runner());
   worker->WakeUp();
@@ -616,7 +619,7 @@
       delegate->controls();
 
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   worker->Start(service_thread.task_runner());
   worker->WakeUp();
@@ -644,7 +647,7 @@
   controls->HaveWorkBlock();
 
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   worker->Start(service_thread.task_runner());
   worker->WakeUp();
@@ -674,7 +677,7 @@
   controls->set_expect_get_work(false);
 
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
 
   worker->Cleanup();
@@ -687,7 +690,7 @@
 
 class CallJoinFromDifferentThread : public SimpleThread {
  public:
-  explicit CallJoinFromDifferentThread(WorkerThread* worker_to_join)
+  explicit CallJoinFromDifferentThread(WorkerThreadWaitableEvent* worker_to_join)
       : SimpleThread("WorkerThreadJoinThread"),
         worker_to_join_(worker_to_join) {}
 
@@ -704,7 +707,7 @@
   void WaitForRunToStart() { run_started_event_.Wait(); }
 
  private:
-  raw_ptr<WorkerThread> worker_to_join_;
+  raw_ptr<WorkerThreadWaitableEvent> worker_to_join_;
   TestWaitableEvent run_started_event_;
 };
 
@@ -728,7 +731,7 @@
   controls->HaveWorkBlock();
 
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   worker->Start(service_thread.task_runner());
   worker->WakeUp();
@@ -816,7 +819,7 @@
   ExpectThreadTypeDelegate* delegate_raw = delegate.get();
   delegate_raw->SetExpectedThreadType(ThreadType::kBackground);
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kBackground, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kBackground, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   worker->Start(service_thread.task_runner());
 
@@ -871,7 +874,7 @@
   service_thread.StartWithOptions(std::move(service_thread_options));
   auto delegate = std::make_unique<VerifyCallsToObserverDelegate>(&observer);
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   EXPECT_CALL(observer, OnWorkerThreadMainEntry());
   worker->Start(service_thread.task_runner(), &observer);
@@ -891,7 +894,7 @@
 
 class WorkerThreadThreadCacheDelegate : public WorkerThreadDefaultDelegate {
  public:
-  void WaitForWork(WaitableEvent* wake_up_event) override {
+  void WaitForWork() override {
     // Fill several buckets before going to sleep.
     for (size_t size = 8;
          size < partition_alloc::ThreadCache::kDefaultSizeThreshold; size++) {
@@ -905,7 +908,7 @@
 
     size_t cached_memory_before =
         partition_alloc::ThreadCache::Get()->CachedMemory();
-    WorkerThreadDefaultDelegate::WaitForWork(wake_up_event);
+    WorkerThreadDefaultDelegate::WaitForWork();
     size_t cached_memory_after =
         partition_alloc::ThreadCache::Get()->CachedMemory();
 
@@ -944,7 +947,7 @@
   auto delegate = std::make_unique<WorkerThreadThreadCacheDelegate>();
   auto* delegate_raw = delegate.get();
   auto worker =
-      MakeRefCounted<WorkerThread>(ThreadType::kDefault, std::move(delegate),
+      MakeRefCounted<WorkerThreadWaitableEvent>(ThreadType::kDefault, std::move(delegate),
                                    task_tracker.GetTrackedRef(), 0);
   // Wake up before the thread is started to make sure the first sleep is short.
   worker->WakeUp();
diff --git a/cc/input/browser_controls_offset_manager.cc b/cc/input/browser_controls_offset_manager.cc
index 4c6bc64..ae8de83 100644
--- a/cc/input/browser_controls_offset_manager.cc
+++ b/cc/input/browser_controls_offset_manager.cc
@@ -720,6 +720,33 @@
       std::make_pair(std::min(from, to), std::max(from, to));
 }
 
+double BrowserControlsOffsetManager::PredictViewportBoundsDelta(
+    double current_bounds_delta,
+    gfx::Vector2dF scroll_distance) {
+  double adjustment = current_bounds_delta;
+  if (scroll_distance.y() > 0 && adjustment > 0) {
+    // We're scrolling down and started to hide controls. Let's assume they're
+    // going to be fully hidden by the end of the fling.
+    if (TopControlsShownRatio() < 1) {
+      adjustment += ContentTopOffset();
+    }
+    if (BottomControlsShownRatio() < 1) {
+      adjustment += ContentBottomOffset();
+    }
+  }
+  if (scroll_distance.y() < 0 && adjustment < 0) {
+    // We're scrolling up and started to show controls. Let's assume they're
+    // going to be fully shown by the end of the fling.
+    if (TopControlsShownRatio() > 0) {
+      adjustment -= TopControlsHeight() - ContentTopOffset();
+    }
+    if (BottomControlsShownRatio() > 0) {
+      adjustment -= BottomControlsHeight() - ContentBottomOffset();
+    }
+  }
+  return adjustment;
+}
+
 // class Animation
 
 BrowserControlsOffsetManager::Animation::Animation() {}
diff --git a/cc/input/browser_controls_offset_manager.h b/cc/input/browser_controls_offset_manager.h
index 501082f..700e8be 100644
--- a/cc/input/browser_controls_offset_manager.h
+++ b/cc/input/browser_controls_offset_manager.h
@@ -114,6 +114,12 @@
 
   gfx::Vector2dF Animate(base::TimeTicks monotonic_time);
 
+  // Predict what the outer viewport container bounds delta will be as browser
+  // controls are shown or hidden during a scroll gesture before the Blink
+  // WebView is resized to reflect the new state.
+  double PredictViewportBoundsDelta(double current_bounds_delta,
+                                    gfx::Vector2dF scroll_distance);
+
  protected:
   BrowserControlsOffsetManager(BrowserControlsOffsetManagerClient* client,
                                float controls_show_threshold,
diff --git a/cc/input/input_handler.cc b/cc/input/input_handler.cc
index 9966414..2b1112b1 100644
--- a/cc/input/input_handler.cc
+++ b/cc/input/input_handler.cc
@@ -834,6 +834,25 @@
   return original == fling ? std::nullopt : std::make_optional(fling);
 }
 
+double InputHandler::PredictViewportBoundsDelta(
+    gfx::Vector2dF scroll_distance) {
+  // This adjustment is just an estimate. If we're wrong about where to aim a
+  // snap fling curve, SnapAtScrollEnd will probably take us to a good place.
+  // And if all else fails, the main thread will fix things in SnapAfterLayout
+  // which runs after cc has stopped scrolling. But it does look nicer when no
+  // corrections are needed, so we try to achieve that in the common cases.
+
+  // The outer_viewport_container_bounds_delta is how much the true viewport
+  // size currently differs from what Blink thinks it is.
+  double current_bounds_delta = GetScrollTree()
+                                    .property_trees()
+                                    ->outer_viewport_container_bounds_delta()
+                                    .y();
+  return compositor_delegate_->GetImplDeprecated()
+      .browser_controls_manager()
+      ->PredictViewportBoundsDelta(current_bounds_delta, scroll_distance);
+}
+
 bool InputHandler::GetSnapFlingInfoAndSetAnimatingSnapTarget(
     const gfx::Vector2dF& current_delta,
     const gfx::Vector2dF& natural_displacement_in_viewport,
@@ -844,7 +863,7 @@
       snap_fling_state_ == kNativeFling) {
     return false;
   }
-  const SnapContainerData& data = scroll_node->snap_container_data.value();
+  SnapContainerData& data = scroll_node->snap_container_data.value();
 
   float scale_factor = ActiveTree().page_scale_factor_for_scroll();
   gfx::Vector2dF current_delta_in_content =
@@ -869,7 +888,12 @@
       SnapSelectionStrategy::CreateForEndAndDirection(
           current_offset, snap_displacement, use_fractional_offsets);
 
-  SnapPositionData snap = data.FindSnapPosition(*strategy);
+  double snapport_height_adjustment =
+      scroll_node->scrolls_outer_viewport
+          ? PredictViewportBoundsDelta(snap_displacement)
+          : 0;
+  SnapPositionData snap = data.FindSnapPositionWithViewportAdjustment(
+      *strategy, snapport_height_adjustment);
   if (snap.type == SnapPositionData::Type::kNone) {
     snap_fling_state_ = kNativeFling;
     return false;
@@ -912,7 +936,7 @@
     SetNeedsCommit();
   }
   scroll_animating_snap_target_ids_ = TargetSnapAreaElementIds();
-  ScrollEnd(false /* should_snap */);
+  ScrollEnd(true /* should_snap */);
 }
 
 void InputHandler::NotifyInputEvent() {
@@ -1853,7 +1877,12 @@
         did_scroll_y_for_scroll_gesture_);
   }
 
-  SnapPositionData snap = data.FindSnapPosition(*strategy);
+  double snapport_height_adjustment =
+      scroll_node->scrolls_outer_viewport
+          ? PredictViewportBoundsDelta(gfx::Vector2dF())
+          : 0;
+  SnapPositionData snap = data.FindSnapPositionWithViewportAdjustment(
+      *strategy, snapport_height_adjustment);
   if (snap.type == SnapPositionData::Type::kNone) {
     return false;
   }
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 29d96c936..1d06ddc 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -636,6 +636,11 @@
 
   std::optional<gfx::PointF> ConstrainFling(gfx::PointF original);
 
+  // Estimate how to adjust the height of the snapport rect based on the state
+  // of browser controls that are being shown or hidden during a scroll gesture
+  // before the Blink WebView is resized to reflect the new state.
+  double PredictViewportBoundsDelta(gfx::Vector2dF scroll_distance);
+
   // The input handler is owned by the delegate so their lifetimes are tied
   // together.
   const raw_ref<CompositorDelegateForInput> compositor_delegate_;
diff --git a/cc/input/scroll_snap_data.cc b/cc/input/scroll_snap_data.cc
index 5a828307..0f70fc4645 100644
--- a/cc/input/scroll_snap_data.cc
+++ b/cc/input/scroll_snap_data.cc
@@ -9,6 +9,7 @@
 #include <limits>
 #include <memory>
 
+#include "base/auto_reset.h"
 #include "base/check.h"
 #include "base/notreached.h"
 #include "cc/base/features.h"
@@ -205,6 +206,14 @@
   snap_area_list_.push_back(snap_area_data);
 }
 
+SnapPositionData SnapContainerData::FindSnapPositionWithViewportAdjustment(
+    const SnapSelectionStrategy& strategy,
+    double snapport_height_adjustment) {
+  base::AutoReset<double> resetter{&snapport_height_adjustment_,
+                                   snapport_height_adjustment};
+  return FindSnapPosition(strategy);
+}
+
 SnapPositionData SnapContainerData::FindSnapPosition(
     const SnapSelectionStrategy& strategy,
     const ElementId& active_element_id) const {
@@ -358,7 +367,7 @@
 
     // Preferentially minimize block scrolling distance. Ties in block scrolling
     // distance are resolved by considering inline scrolling distance.
-    gfx::Vector2dF distance = DistanceFromCorridor(dx, dy, rect_);
+    gfx::Vector2dF distance = DistanceFromCorridor(dx, dy, snapport());
     if (distance.y() < smallest_distance.y() ||
         (distance.y() == smallest_distance.y() &&
          distance.x() < smallest_distance.x())) {
@@ -402,7 +411,7 @@
       auto aligned_result = GetSnapSearchResult(axis, area);
       if (base::FeatureList::IsEnabled(
               features::kScrollSnapPreferCloserCovering) &&
-          CanCoverSnapportOnAxis(axis, rect_, area.rect)) {
+          CanCoverSnapportOnAxis(axis, snapport(), area.rect)) {
         // This code path handles snapping after layout changes. If the
         // target snap area is larger than the snapport, we need to consider
         // snap areas nested within it, which may themselves be large snap areas
@@ -559,7 +568,7 @@
     evaluate(candidate);
     if (should_consider_covering &&
         (base::FeatureList::IsEnabled(features::kScrollSnapPreferCloserCovering)
-             ? CanCoverSnapportOnAxis(axis, rect_, area.rect)
+             ? CanCoverSnapportOnAxis(axis, snapport(), area.rect)
              : IsSnapportCoveredOnAxis(axis, intended_position, area.rect))) {
       if (std::optional<SnapSearchResult> covering =
               FindCoveringCandidate(area, axis, candidate, intended_position)) {
@@ -592,40 +601,41 @@
     SearchAxis axis,
     const SnapAreaData& area) const {
   SnapSearchResult result;
+  gfx::RectF rect = snapport();
   if (axis == SearchAxis::kX) {
-    result.set_visible_range(gfx::RangeF(area.rect.y() - rect_.bottom(),
-                                         area.rect.bottom() - rect_.y()));
+    result.set_visible_range(gfx::RangeF(area.rect.y() - rect.bottom(),
+                                         area.rect.bottom() - rect.y()));
     // https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-align
     // Snap alignment has been normalized for a horizontal left to right and top
     // to bottom writing mode.
     switch (area.scroll_snap_align.alignment_inline) {
       case SnapAlignment::kStart:
-        result.set_snap_offset(area.rect.x() - rect_.x());
+        result.set_snap_offset(area.rect.x() - rect.x());
         break;
       case SnapAlignment::kCenter:
         result.set_snap_offset(area.rect.CenterPoint().x() -
-                               rect_.CenterPoint().x());
+                               rect.CenterPoint().x());
         break;
       case SnapAlignment::kEnd:
-        result.set_snap_offset(area.rect.right() - rect_.right());
+        result.set_snap_offset(area.rect.right() - rect.right());
         break;
       default:
         NOTREACHED();
     }
     result.Clip(max_position_.x(), max_position_.y());
   } else {
-    result.set_visible_range(gfx::RangeF(area.rect.x() - rect_.right(),
-                                         area.rect.right() - rect_.x()));
+    result.set_visible_range(gfx::RangeF(area.rect.x() - rect.right(),
+                                         area.rect.right() - rect.x()));
     switch (area.scroll_snap_align.alignment_block) {
       case SnapAlignment::kStart:
-        result.set_snap_offset(area.rect.y() - rect_.y());
+        result.set_snap_offset(area.rect.y() - rect.y());
         break;
       case SnapAlignment::kCenter:
         result.set_snap_offset(area.rect.CenterPoint().y() -
-                               rect_.CenterPoint().y());
+                               rect.CenterPoint().y());
         break;
       case SnapAlignment::kEnd:
-        result.set_snap_offset(area.rect.bottom() - rect_.bottom());
+        result.set_snap_offset(area.rect.bottom() - rect.bottom());
         break;
       default:
         NOTREACHED();
@@ -642,8 +652,9 @@
     const SnapSearchResult& aligned_candidate,
     float intended_position) const {
   bool horiz = axis == SearchAxis::kX;
-  float scroll_padding = horiz ? rect_.x() : rect_.y();
-  float snapport_size = horiz ? rect_.width() : rect_.height();
+  gfx::RectF rect = snapport();
+  float scroll_padding = horiz ? rect.x() : rect.y();
+  float snapport_size = horiz ? rect.width() : rect.height();
   SnapAlignment alignment = horiz ? area.scroll_snap_align.alignment_inline
                                   : area.scroll_snap_align.alignment_block;
   gfx::RangeF area_range = horiz
@@ -765,18 +776,21 @@
   // int coming from ScrollTree::ClampScrollOffsetToLimits which uses
   // ScrollNode::bounds which is a gfx::Size which stores ints.
   // See crbug.com/1468412.
+  gfx::RectF rect = snapport();
   if (axis == SearchAxis::kX) {
-    if (area_rect.width() < rect_.width())
+    if (area_rect.width() < rect.width()) {
       return false;
-    float left = area_rect.x() - rect_.x();
-    float right = area_rect.right() - rect_.right();
+    }
+    float left = area_rect.x() - rect.x();
+    float right = area_rect.right() - rect.right();
     return current_offset >= left - kSnapportCoveredTolerance &&
            current_offset <= right + kSnapportCoveredTolerance;
   } else {
-    if (area_rect.height() < rect_.height())
+    if (area_rect.height() < rect.height()) {
       return false;
-    float top = area_rect.y() - rect_.y();
-    float bottom = area_rect.bottom() - rect_.bottom();
+    }
+    float top = area_rect.y() - rect.y();
+    float bottom = area_rect.bottom() - rect.bottom();
     return current_offset >= top - kSnapportCoveredTolerance &&
            current_offset <= bottom + kSnapportCoveredTolerance;
   }
@@ -827,6 +841,18 @@
   return false;
 }
 
+gfx::RectF SnapContainerData::snapport() const {
+  if (!snapport_height_adjustment_) {
+    return rect_;
+  }
+
+  gfx::RectF adjusted = rect_;
+  // The top visible point is not changed by showing / hiding the top controls;
+  // they only expand the visible rect from that anchor point.
+  adjusted.set_height(adjusted.height() + snapport_height_adjustment_);
+  return adjusted;
+}
+
 SnappedTargetData::SnappedTargetData() = default;
 SnappedTargetData::SnappedTargetData(const SnappedTargetData& other) = default;
 SnappedTargetData::~SnappedTargetData() = default;
diff --git a/cc/input/scroll_snap_data.h b/cc/input/scroll_snap_data.h
index 1d8844f..711234f 100644
--- a/cc/input/scroll_snap_data.h
+++ b/cc/input/scroll_snap_data.h
@@ -280,6 +280,10 @@
     return !(*this == other);
   }
 
+  SnapPositionData FindSnapPositionWithViewportAdjustment(
+      const SnapSelectionStrategy& strategy,
+      double snapport_height_adjustment);
+
   SnapPositionData FindSnapPosition(
       const SnapSelectionStrategy& strategy,
       const ElementId& active_element_id = ElementId()) const;
@@ -378,12 +382,16 @@
   bool IsSnappedToArea(const SnapAreaData& area,
                        const gfx::PointF& scroll_offset) const;
 
+  gfx::RectF snapport() const;
+
   // Specifies whether a scroll container is a scroll snap container, how
   // strictly it snaps, and which axes are considered.
   // See https://www.w3.org/TR/css-scroll-snap-1/#scroll-snap-type for details.
   ScrollSnapType scroll_snap_type_;
 
-  // The rect of the snap_container relative to its boundary.
+  // The rect of the snap_container relative to its boundary.  This is the
+  // snapport supplied by Blink; it is subject to browser controls adjustment
+  // through snapport_height_adjustment_.
   gfx::RectF rect_;
 
   // The maximal scroll position of the SnapContainer, in the same coordinate
@@ -404,6 +412,11 @@
   // container is not snapped to a position.
   TargetSnapAreaElementIds target_snap_area_element_ids_;
 
+  // Transient adjustment to the height of the snapport (rect_) to account for
+  // showing or hiding browser controls during a scroll gesture.  This is only
+  // set while a call to FindSnapPosition is executing.
+  double snapport_height_adjustment_ = 0;
+
   FRIEND_TEST_ALL_PREFIXES(ScrollSnapDataTest, SnapToFocusedElementHorizontal);
   FRIEND_TEST_ALL_PREFIXES(ScrollSnapDataTest, SnapToFocusedElementVertical);
   FRIEND_TEST_ALL_PREFIXES(ScrollSnapDataTest, SnapToFocusedElementBoth);
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 14baffe..4eea38f 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -145,15 +145,13 @@
 class HudGpuBacking : public ResourcePool::GpuBacking {
  public:
   ~HudGpuBacking() override {
-    if (!shared_image) {
+    if (mailbox.IsZero()) {
       return;
     }
     if (returned_sync_token.HasData())
-      shared_image_interface->DestroySharedImage(returned_sync_token,
-                                                 std::move(shared_image));
+      shared_image_interface->DestroySharedImage(returned_sync_token, mailbox);
     else if (mailbox_sync_token.HasData())
-      shared_image_interface->DestroySharedImage(mailbox_sync_token,
-                                                 std::move(shared_image));
+      shared_image_interface->DestroySharedImage(mailbox_sync_token, mailbox);
   }
 
   void OnMemoryDump(
@@ -161,11 +159,11 @@
       const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
       uint64_t tracing_process_id,
       int importance) const override {
-    if (!shared_image) {
+    if (mailbox.IsZero()) {
       return;
     }
 
-    auto tracing_guid = shared_image->GetGUIDForTracing();
+    auto tracing_guid = gpu::GetSharedImageGUIDForTracing(mailbox);
     pmd->CreateSharedGlobalAllocatorDump(tracing_guid);
     pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
   }
@@ -302,12 +300,13 @@
       if (backing->overlay_candidate) {
         flags |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
       }
-      backing->shared_image = sii->CreateSharedImage(
+      auto client_shared_image = sii->CreateSharedImage(
           pool_resource.format(), pool_resource.size(),
           pool_resource.color_space(), kTopLeft_GrSurfaceOrigin,
           kPremul_SkAlphaType, flags, "HeadsUpDisplayLayer",
           gpu::kNullSurfaceHandle);
-      CHECK(backing->shared_image);
+      CHECK(client_shared_image);
+      backing->mailbox = client_shared_image->mailbox();
       auto* ri = raster_context_provider->RasterInterface();
       ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
       pool_resource.set_gpu_backing(std::move(backing));
@@ -364,8 +363,7 @@
       ri->BeginRasterCHROMIUM(background_color, needs_clear, msaa_sample_count,
                               gpu::raster::kNoMSAA, can_use_lcd_text,
                               /*visible=*/true, gfx::ColorSpace::CreateSRGB(),
-                              /*hdr_headroom=*/1.f,
-                              backing->shared_image->mailbox().name);
+                              /*hdr_headroom=*/1.f, backing->mailbox.name);
       constexpr gfx::Vector2dF post_translate(0.f, 0.f);
       constexpr gfx::Vector2dF post_scale(1.f, 1.f);
       DummyImageProvider image_provider;
@@ -397,8 +395,7 @@
       SkPixmap pixmap;
       staging_surface_->peekPixels(&pixmap);
 
-      ri->WritePixels(backing->shared_image->mailbox(), /*dst_x_offset=*/0,
-                      /*dst_y_offset=*/0,
+      ri->WritePixels(backing->mailbox, /*dst_x_offset=*/0, /*dst_y_offset=*/0,
                       /*dst_plane_index=*/0, backing->texture_target, pixmap);
     }
 
diff --git a/cc/layers/viewport.cc b/cc/layers/viewport.cc
index 0c71734..ed91344 100644
--- a/cc/layers/viewport.cc
+++ b/cc/layers/viewport.cc
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/memory/ptr_util.h"
 #include "cc/input/browser_controls_offset_manager.h"
+#include "cc/input/snap_selection_strategy.h"
 #include "cc/trees/layer_tree_host_impl.h"
 #include "cc/trees/layer_tree_impl.h"
 #include "cc/trees/scroll_node.h"
@@ -93,6 +94,33 @@
   return result;
 }
 
+void Viewport::SnapIfNeeded() {
+  ScrollNode* scroll_node = OuterScrollNode();
+  if (!scroll_node || !scroll_node->snap_container_data.has_value()) {
+    return;
+  }
+
+  if (scroll_node == scroll_tree().CurrentlyScrollingNode()) {
+    // If there is an in-progress scroll gesture, InputHandler will take care of
+    // snapping at the end.
+    return;
+  }
+
+  SnapContainerData& data = scroll_node->snap_container_data.value();
+  gfx::PointF current_position = TotalScrollOffset();
+
+  SnapPositionData snap = data.FindSnapPosition(
+      *SnapSelectionStrategy::CreateForTargetElement(current_position));
+  if (snap.type == SnapPositionData::Type::kNone) {
+    return;
+  }
+
+  gfx::Vector2dF delta = snap.position - current_position;
+  delta.Scale(host_impl_->active_tree()->page_scale_factor_for_scroll());
+
+  ScrollBy(delta, gfx::Point(), false, false, true);
+}
+
 gfx::Vector2dF Viewport::ComputeClampedDelta(
     const gfx::Vector2dF& scroll_delta) const {
   // When clamping for the outer viewport, we need to distribute the scroll
diff --git a/cc/layers/viewport.h b/cc/layers/viewport.h
index 6d4aa7b6..e3671a8 100644
--- a/cc/layers/viewport.h
+++ b/cc/layers/viewport.h
@@ -106,6 +106,10 @@
   // inner viewport where content is visible.
   gfx::SizeF GetInnerViewportSizeExcludingScrollbars() const;
 
+  // Performs an instant snap if the viewport is a snap container and no scroll
+  // gesture is in progress.
+  void SnapIfNeeded();
+
  private:
   explicit Viewport(LayerTreeHostImpl* host_impl);
 
diff --git a/cc/raster/bitmap_raster_buffer_provider.cc b/cc/raster/bitmap_raster_buffer_provider.cc
index a0cee8c..50165b3 100644
--- a/cc/raster/bitmap_raster_buffer_provider.cc
+++ b/cc/raster/bitmap_raster_buffer_provider.cc
@@ -57,7 +57,7 @@
   ~BitmapSoftwareBacking() override {
     if (frame_sink->shared_image_interface()) {
       frame_sink->shared_image_interface()->DestroySharedImage(
-          gpu::SyncToken(), std::move(shared_image));
+          gpu::SyncToken(), shared_bitmap_id);
     } else {
       frame_sink->DidDeleteSharedBitmap(shared_bitmap_id);
     }
@@ -73,7 +73,6 @@
   }
 
   raw_ptr<LayerTreeFrameSink> frame_sink;
-  scoped_refptr<gpu::ClientSharedImage> shared_image;
   base::WritableSharedMemoryMapping mapping;
 
   base::UnsafeSharedMemoryRegion unsafe_region;
@@ -168,14 +167,15 @@
           size.width(), gfx::BufferFormat::RGBA_8888, 0));
       handle.region = backing->unsafe_region.Duplicate();
 
-      backing->shared_image =
+      auto client_shared_image =
           frame_sink_->shared_image_interface()->CreateSharedImage(
               viz::SinglePlaneFormat::kRGBA_8888, size, color_space,
               kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType,
               gpu::SHARED_IMAGE_USAGE_CPU_WRITE, kDebugLabel,
               std::move(handle));
-      CHECK(backing->shared_image);
-      backing->shared_bitmap_id = backing->shared_image->mailbox();
+      CHECK(client_shared_image);
+      backing->shared_bitmap_id = client_shared_image->mailbox();
+
     } else {
       backing->shared_bitmap_id = viz::SharedBitmap::GenerateId();
       base::MappedReadOnlyRegion shm =
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index 4e6ef4f6..11711b4 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -50,14 +50,14 @@
     : public ResourcePool::GpuBacking {
  public:
   ~GpuRasterBacking() override {
-    if (!shared_image) {
+    if (mailbox.IsZero()) {
       return;
     }
     auto* sii = worker_context_provider->SharedImageInterface();
     if (returned_sync_token.HasData())
-      sii->DestroySharedImage(returned_sync_token, std::move(shared_image));
+      sii->DestroySharedImage(returned_sync_token, mailbox);
     else if (mailbox_sync_token.HasData())
-      sii->DestroySharedImage(mailbox_sync_token, std::move(shared_image));
+      sii->DestroySharedImage(mailbox_sync_token, mailbox);
   }
 
   void OnMemoryDump(
@@ -65,11 +65,11 @@
       const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
       uint64_t tracing_process_id,
       int importance) const override {
-    if (!shared_image) {
+    if (mailbox.IsZero()) {
       return;
     }
 
-    auto tracing_guid = shared_image->GetGUIDForTracing();
+    auto tracing_guid = gpu::GetSharedImageGUIDForTracing(mailbox);
     pmd->CreateSharedGlobalAllocatorDump(tracing_guid);
     pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
   }
@@ -370,7 +370,7 @@
   gpu::raster::RasterInterface* ri =
       client_->worker_context_provider_->RasterInterface();
   bool mailbox_needs_clear = false;
-  if (!backing_->shared_image) {
+  if (backing_->mailbox.IsZero()) {
     DCHECK(!backing_->returned_sync_token.HasData());
     auto* sii = client_->worker_context_provider_->SharedImageInterface();
     uint32_t flags = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
@@ -383,11 +383,12 @@
     } else if (client_->is_using_raw_draw_) {
       flags |= gpu::SHARED_IMAGE_USAGE_RAW_DRAW;
     }
-    backing_->shared_image = sii->CreateSharedImage(
+    auto client_shared_image = sii->CreateSharedImage(
         shared_image_format_, resource_size_, color_space_,
         kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, flags, "GpuRasterTile",
         gpu::kNullSurfaceHandle);
-    CHECK(backing_->shared_image);
+    CHECK(client_shared_image);
+    backing_->mailbox = client_shared_image->mailbox();
     mailbox_needs_clear = true;
     ri->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
   } else {
@@ -419,7 +420,7 @@
       raster_source->background_color(), mailbox_needs_clear,
       playback_settings.msaa_sample_count, msaa_mode, use_lcd_text,
       playback_settings.visible, color_space_, playback_settings.hdr_headroom,
-      backing_->shared_image->mailbox().name);
+      backing_->mailbox.name);
 
   gfx::Vector2dF recording_to_raster_scale = transform.scale();
   recording_to_raster_scale.InvScale(raster_source->recording_scale_factor());
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index c496e1c9..6a4f366 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -57,14 +57,14 @@
     : public ResourcePool::GpuBacking {
  public:
   ~OneCopyGpuBacking() override {
-    if (!shared_image) {
+    if (mailbox.IsZero()) {
       return;
     }
     auto* sii = worker_context_provider->SharedImageInterface();
     if (returned_sync_token.HasData())
-      sii->DestroySharedImage(returned_sync_token, std::move(shared_image));
+      sii->DestroySharedImage(returned_sync_token, mailbox);
     else if (mailbox_sync_token.HasData())
-      sii->DestroySharedImage(mailbox_sync_token, std::move(shared_image));
+      sii->DestroySharedImage(mailbox_sync_token, mailbox);
   }
 
   void OnMemoryDump(
@@ -72,11 +72,11 @@
       const base::trace_event::MemoryAllocatorDumpGuid& buffer_dump_guid,
       uint64_t tracing_process_id,
       int importance) const override {
-    if (!shared_image) {
+    if (mailbox.IsZero()) {
       return;
     }
 
-    auto tracing_guid = shared_image->GetGUIDForTracing();
+    auto tracing_guid = gpu::GetSharedImageGUIDForTracing(mailbox);
     pmd->CreateSharedGlobalAllocatorDump(tracing_guid);
     pmd->AddOwnershipEdge(buffer_dump_guid, tracing_guid, importance);
   }
@@ -98,7 +98,7 @@
       color_space_(in_use_resource.color_space()),
       previous_content_id_(previous_content_id),
       before_raster_sync_token_(backing->returned_sync_token),
-      shared_image_(backing->shared_image),
+      mailbox_(backing->mailbox),
       mailbox_texture_target_(backing->texture_target),
       mailbox_texture_is_overlay_candidate_(backing->overlay_candidate) {}
 
@@ -111,7 +111,7 @@
     // happened if the |after_raster_sync_token_| was set.
     backing_->returned_sync_token = gpu::SyncToken();
   }
-  backing_->shared_image = std::move(shared_image_);
+  backing_->mailbox = mailbox_;
 }
 
 void OneCopyRasterBufferProvider::RasterBufferImpl::Playback(
@@ -128,11 +128,10 @@
   // returns another SyncToken generated on the worker thread to synchronize
   // with after the raster is complete.
   after_raster_sync_token_ = client_->PlaybackAndCopyOnWorkerThread(
-      shared_image_, mailbox_texture_target_,
-      mailbox_texture_is_overlay_candidate_, before_raster_sync_token_,
-      raster_source, raster_full_rect, raster_dirty_rect, transform,
-      resource_size_, format_, color_space_, playback_settings,
-      previous_content_id_, new_content_id);
+      &mailbox_, mailbox_texture_target_, mailbox_texture_is_overlay_candidate_,
+      before_raster_sync_token_, raster_source, raster_full_rect,
+      raster_dirty_rect, transform, resource_size_, format_, color_space_,
+      playback_settings, previous_content_id_, new_content_id);
 }
 
 bool OneCopyRasterBufferProvider::RasterBufferImpl::
@@ -277,7 +276,7 @@
 }
 
 gpu::SyncToken OneCopyRasterBufferProvider::PlaybackAndCopyOnWorkerThread(
-    scoped_refptr<gpu::ClientSharedImage>& shared_image,
+    gpu::Mailbox* mailbox,
     GLenum mailbox_texture_target,
     bool mailbox_texture_is_overlay_candidate,
     const gpu::SyncToken& sync_token,
@@ -307,16 +306,17 @@
   if (put_data_in_staging_buffer) {
     sync_token_after_upload = CopyOnWorkerThread(
         staging_buffer.get(), raster_source, raster_full_rect, format,
-        resource_size, shared_image, mailbox_texture_target,
+        resource_size, mailbox, mailbox_texture_target,
         mailbox_texture_is_overlay_candidate, sync_token, color_space);
   } else {
     // If we failed to put data in the staging buffer
     // (https://crbug.com/554541), then we don't have anything to give to copy
     // into the resource. We report a zero mailbox that will result in
     // checkerboarding, and be treated as OOM which should retry.
-    if (shared_image) {
+    if (!mailbox->IsZero()) {
       worker_context_provider_->SharedImageInterface()->DestroySharedImage(
-          sync_token, std::move(shared_image));
+          sync_token, *mailbox);
+      mailbox->SetZero();
     }
   }
 
@@ -438,7 +438,7 @@
     const gfx::Rect& rect_to_copy,
     viz::SharedImageFormat format,
     const gfx::Size& resource_size,
-    scoped_refptr<gpu::ClientSharedImage>& shared_image,
+    gpu::Mailbox* mailbox,
     GLenum mailbox_texture_target,
     bool mailbox_texture_is_overlay_candidate,
     const gpu::SyncToken& sync_token,
@@ -454,16 +454,17 @@
 
   bool needs_clear = false;
 
-  if (!shared_image) {
+  if (mailbox->IsZero()) {
     uint32_t usage =
         gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | gpu::SHARED_IMAGE_USAGE_RASTER;
     if (mailbox_texture_is_overlay_candidate)
       usage |= gpu::SHARED_IMAGE_USAGE_SCANOUT;
-    shared_image = sii->CreateSharedImage(
+    auto client_shared_image = sii->CreateSharedImage(
         format, resource_size, color_space, kTopLeft_GrSurfaceOrigin,
         kPremul_SkAlphaType, usage, "OneCopyRasterTile",
         gpu::kNullSurfaceHandle);
-    CHECK(shared_image);
+    CHECK(client_shared_image);
+    *mailbox = client_shared_image->mailbox();
     // Clear the resource if we're not going to initialize it fully from the
     // copy due to non-exact resource reuse.  See https://crbug.com/1313091
     needs_clear = rect_to_copy.size() != resource_size;
@@ -529,9 +530,9 @@
       // SkBitmap.cpp doesn't yet have an interface for SkColor4fs
       // https://bugs.chromium.org/p/skia/issues/detail?id=13329
       bitmap.eraseColor(raster_source->background_color().toSkColor());
-      ri->WritePixels(
-          shared_image->mailbox(), /*dst_x_offset=*/0, /*dst_y_offset=*/0,
-          /*dst_plane_index=*/0, mailbox_texture_target, bitmap.pixmap());
+      ri->WritePixels(*mailbox, /*dst_x_offset=*/0, /*dst_y_offset=*/0,
+                      /*dst_plane_index=*/0, mailbox_texture_target,
+                      bitmap.pixmap());
     }
   }
 
@@ -549,9 +550,9 @@
     DCHECK_GT(rows_to_copy, 0);
 
     ri->CopySharedImage(
-        staging_buffer->mailbox, shared_image->mailbox(),
-        mailbox_texture_target, 0, y, 0, y, rect_to_copy.width(), rows_to_copy,
-        false /* unpack_flip_y */, false /* unpack_premultiply_alpha */);
+        staging_buffer->mailbox, *mailbox, mailbox_texture_target, 0, y, 0, y,
+        rect_to_copy.width(), rows_to_copy, false /* unpack_flip_y */,
+        false /* unpack_premultiply_alpha */);
     y += rows_to_copy;
 
     // Increment |bytes_scheduled_since_last_flush_| by the amount of memory
diff --git a/cc/raster/one_copy_raster_buffer_provider.h b/cc/raster/one_copy_raster_buffer_provider.h
index f56f3b1..978ef70 100644
--- a/cc/raster/one_copy_raster_buffer_provider.h
+++ b/cc/raster/one_copy_raster_buffer_provider.h
@@ -73,7 +73,7 @@
 
   // Playback raster source and copy result into |resource|.
   gpu::SyncToken PlaybackAndCopyOnWorkerThread(
-      scoped_refptr<gpu::ClientSharedImage>& shared_image,
+      gpu::Mailbox* mailbox,
       GLenum mailbox_texture_target,
       bool mailbox_texture_is_overlay_candidate,
       const gpu::SyncToken& sync_token,
@@ -127,7 +127,7 @@
     const gfx::ColorSpace color_space_;
     const uint64_t previous_content_id_;
     const gpu::SyncToken before_raster_sync_token_;
-    scoped_refptr<gpu::ClientSharedImage> shared_image_;
+    gpu::Mailbox mailbox_;
     const GLenum mailbox_texture_target_;
     const bool mailbox_texture_is_overlay_candidate_;
     // A SyncToken to be returned from the worker thread, and waited on before
@@ -147,17 +147,16 @@
       const RasterSource::PlaybackSettings& playback_settings,
       uint64_t previous_content_id,
       uint64_t new_content_id);
-  gpu::SyncToken CopyOnWorkerThread(
-      StagingBuffer* staging_buffer,
-      const RasterSource* raster_source,
-      const gfx::Rect& rect_to_copy,
-      viz::SharedImageFormat format,
-      const gfx::Size& resource_size,
-      scoped_refptr<gpu::ClientSharedImage>& shared_image,
-      GLenum mailbox_texture_target,
-      bool mailbox_texture_is_overlay_candidate,
-      const gpu::SyncToken& sync_token,
-      const gfx::ColorSpace& color_space);
+  gpu::SyncToken CopyOnWorkerThread(StagingBuffer* staging_buffer,
+                                    const RasterSource* raster_source,
+                                    const gfx::Rect& rect_to_copy,
+                                    viz::SharedImageFormat format,
+                                    const gfx::Size& resource_size,
+                                    gpu::Mailbox* mailbox,
+                                    GLenum mailbox_texture_target,
+                                    bool mailbox_texture_is_overlay_candidate,
+                                    const gpu::SyncToken& sync_token,
+                                    const gfx::ColorSpace& color_space);
 
   const raw_ptr<viz::RasterContextProvider> compositor_context_provider_;
   const raw_ptr<viz::RasterContextProvider> worker_context_provider_;
diff --git a/cc/raster/zero_copy_raster_buffer_provider.cc b/cc/raster/zero_copy_raster_buffer_provider.cc
index 7b832cb..95f43f4 100644
--- a/cc/raster/zero_copy_raster_buffer_provider.cc
+++ b/cc/raster/zero_copy_raster_buffer_provider.cc
@@ -42,15 +42,13 @@
 class ZeroCopyGpuBacking : public ResourcePool::GpuBacking {
  public:
   ~ZeroCopyGpuBacking() override {
-    if (!shared_image) {
+    if (mailbox.IsZero()) {
       return;
     }
     if (returned_sync_token.HasData())
-      shared_image_interface->DestroySharedImage(returned_sync_token,
-                                                 std::move(shared_image));
+      shared_image_interface->DestroySharedImage(returned_sync_token, mailbox);
     else if (mailbox_sync_token.HasData())
-      shared_image_interface->DestroySharedImage(mailbox_sync_token,
-                                                 std::move(shared_image));
+      shared_image_interface->DestroySharedImage(mailbox_sync_token, mailbox);
   }
 
   void OnMemoryDump(
@@ -59,11 +57,10 @@
       uint64_t tracing_process_id,
       int importance) const override {
     if (base::FeatureList::IsEnabled(kAlwaysUseMappableSIForZeroCopyRaster)) {
-      if (!shared_image) {
+      if (mailbox.IsZero()) {
         return;
       }
-      auto mapping =
-          shared_image_interface->MapSharedImage(shared_image->mailbox());
+      auto mapping = shared_image_interface->MapSharedImage(mailbox);
       if (!mapping) {
         return;
       }
@@ -110,12 +107,12 @@
     // checkerboarding.
     if (base::FeatureList::IsEnabled(kAlwaysUseMappableSIForZeroCopyRaster)) {
       CHECK(!gpu_memory_buffer_);
-      if (!backing_->shared_image) {
+      if (backing_->mailbox.IsZero()) {
         return;
       }
     } else {
       if (!gpu_memory_buffer_) {
-        DCHECK(!backing_->shared_image);
+        DCHECK(backing_->mailbox.IsZero());
         return;
       }
     }
@@ -126,21 +123,21 @@
     // TODO(danakj): This could be done with the worker context in Playback. Do
     // we need to do things in IsResourceReadyToDraw() and OrderingBarrier then?
     gpu::SharedImageInterface* sii = backing_->shared_image_interface;
-    if (!backing_->shared_image) {
+    if (backing_->mailbox.IsZero()) {
       CHECK(
           !base::FeatureList::IsEnabled(kAlwaysUseMappableSIForZeroCopyRaster));
       uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
                        gpu::SHARED_IMAGE_USAGE_SCANOUT;
       // Make a mailbox for export of the GpuMemoryBuffer to the display
       // compositor.
-      backing_->shared_image = sii->CreateSharedImage(
+      auto client_shared_image = sii->CreateSharedImage(
           format_, resource_size_, resource_color_space_,
           kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
           "ZeroCopyRasterTile", gpu_memory_buffer_->CloneHandle());
-      CHECK(backing_->shared_image);
+      CHECK(client_shared_image);
+      backing_->mailbox = client_shared_image->mailbox();
     } else {
-      sii->UpdateSharedImage(backing_->returned_sync_token,
-                             backing_->shared_image->mailbox());
+      sii->UpdateSharedImage(backing_->returned_sync_token, backing_->mailbox);
     }
 
     backing_->mailbox_sync_token = sii->GenUnverifiedSyncToken();
@@ -169,24 +166,25 @@
       gpu::SharedImageInterface* sii = backing_->shared_image_interface;
 
       // Create a MappableSI if necessary.
-      if (!backing_->shared_image) {
+      if (backing_->mailbox.IsZero()) {
         uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
                          gpu::SHARED_IMAGE_USAGE_SCANOUT;
-        backing_->shared_image = sii->CreateSharedImage(
+        auto client_shared_image = sii->CreateSharedImage(
             format_, resource_size_, resource_color_space_,
             kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
             "ZeroCopyRasterTile", gpu::kNullSurfaceHandle, kBufferUsage);
-        if (!backing_->shared_image) {
+        if (!client_shared_image) {
           LOG(ERROR) << "Creation of MappableSharedImage failed.";
           return;
         }
+        backing_->mailbox = client_shared_image->mailbox();
       }
 
-      mapping = sii->MapSharedImage(backing_->shared_image->mailbox());
+      mapping = sii->MapSharedImage(backing_->mailbox);
       if (!mapping) {
         LOG(ERROR) << "MapSharedImage Failed.";
-        sii->DestroySharedImage(gpu::SyncToken(),
-                                std::move(backing_->shared_image));
+        sii->DestroySharedImage(gpu::SyncToken(), backing_->mailbox);
+        backing_->mailbox.SetZero();
         return;
       }
       memory = mapping->Memory(0);
diff --git a/cc/resources/resource_pool.cc b/cc/resources/resource_pool.cc
index c5a5553..027d92a2 100644
--- a/cc/resources/resource_pool.cc
+++ b/cc/resources/resource_pool.cc
@@ -27,7 +27,6 @@
 #include "components/viz/client/client_resource_provider.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "components/viz/common/resources/shared_image_format_utils.h"
-#include "gpu/command_buffer/client/client_shared_image.h"
 #include "gpu/command_buffer/client/context_support.h"
 #include "gpu/command_buffer/common/capabilities.h"
 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
@@ -37,10 +36,6 @@
 using base::trace_event::MemoryDumpLevelOfDetail;
 
 namespace cc {
-
-ResourcePool::GpuBacking::GpuBacking() = default;
-ResourcePool::GpuBacking::~GpuBacking() = default;
-
 namespace {
 
 // Process-unique number for each resource pool.
@@ -321,7 +316,7 @@
   viz::TransferableResource transferable;
   if (resource->gpu_backing()) {
     GpuBacking* gpu_backing = resource->gpu_backing();
-    if (!gpu_backing->shared_image) {
+    if (gpu_backing->mailbox.IsZero()) {
       // This can happen if we failed to allocate a GpuMemoryBuffer. Avoid
       // sending an invalid resource to the parent in that case, and avoid
       // caching/reusing the resource.
@@ -330,7 +325,7 @@
       return false;
     }
     transferable = viz::TransferableResource::MakeGpu(
-        gpu_backing->shared_image->mailbox(), gpu_backing->texture_target,
+        gpu_backing->mailbox, gpu_backing->texture_target,
         gpu_backing->mailbox_sync_token, resource->size(), resource->format(),
         gpu_backing->overlay_candidate, resource_source);
     if (gpu_backing->wait_on_fence_required)
diff --git a/cc/resources/resource_pool.h b/cc/resources/resource_pool.h
index e0c4db1..eb3708e 100644
--- a/cc/resources/resource_pool.h
+++ b/cc/resources/resource_pool.h
@@ -38,10 +38,6 @@
 class SingleThreadTaskRunner;
 }
 
-namespace gpu {
-class ClientSharedImage;
-}
-
 namespace viz {
 class ClientResourceProvider;
 class RasterContextProvider;
@@ -60,10 +56,9 @@
 
   // A base class to hold ownership of gpu backed PoolResources. Allows the
   // client to define destruction semantics.
-  class CC_EXPORT GpuBacking {
+  class GpuBacking {
    public:
-    GpuBacking();
-    virtual ~GpuBacking();
+    virtual ~GpuBacking() = default;
 
     // Dumps information about the memory backing the GpuBacking to |pmd|.
     // The memory usage is attributed to |buffer_dump_guid|.
@@ -77,7 +72,7 @@
         uint64_t tracing_process_id,
         int importance) const = 0;
 
-    scoped_refptr<gpu::ClientSharedImage> shared_image;
+    gpu::Mailbox mailbox;
     gpu::SyncToken mailbox_sync_token;
     GLenum texture_target = 0;
     bool overlay_candidate = false;
diff --git a/cc/resources/resource_pool_unittest.cc b/cc/resources/resource_pool_unittest.cc
index d26ed47..5cea93f 100644
--- a/cc/resources/resource_pool_unittest.cc
+++ b/cc/resources/resource_pool_unittest.cc
@@ -20,7 +20,6 @@
 #include "components/viz/test/test_context_provider.h"
 #include "components/viz/test/test_context_support.h"
 #include "components/viz/test/test_shared_bitmap_manager.h"
-#include "gpu/command_buffer/client/client_shared_image.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -64,7 +63,7 @@
 
   void SetBackingOnResource(const ResourcePool::InUsePoolResource& resource) {
     auto backing = std::make_unique<StubGpuBacking>();
-    backing->shared_image = gpu::ClientSharedImage::CreateForTesting();
+    backing->mailbox = gpu::Mailbox::GenerateForSharedImage();
     backing->mailbox_sync_token.Set(
         gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(1), 1);
     resource.set_gpu_backing(std::move(backing));
@@ -701,6 +700,8 @@
             viz::SinglePlaneSharedImageFormatToBufferFormat(format));
   gfx::ColorSpace color_space = gfx::ColorSpace::CreateSRGB();
   uint32_t target = 5;
+  gpu::Mailbox mailbox;
+  mailbox.name[0] = 'a';
   gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
                             gpu::CommandBufferId::FromUnsafeValue(0x123), 7);
 
@@ -709,8 +710,7 @@
   SetBackingOnResource(resource);
 
   // More non-default values.
-  resource.gpu_backing()->shared_image =
-      gpu::ClientSharedImage::CreateForTesting();
+  resource.gpu_backing()->mailbox = mailbox;
   resource.gpu_backing()->mailbox_sync_token = sync_token;
   resource.gpu_backing()->texture_target = target;
   resource.gpu_backing()->wait_on_fence_required = true;
@@ -730,8 +730,7 @@
 
   ASSERT_EQ(transfer.size(), 1u);
   EXPECT_EQ(transfer[0].id, resource.resource_id_for_export());
-  EXPECT_EQ(transfer[0].mailbox_holder.mailbox,
-            resource.gpu_backing()->shared_image->mailbox());
+  EXPECT_EQ(transfer[0].mailbox_holder.mailbox, mailbox);
   EXPECT_EQ(transfer[0].mailbox_holder.sync_token, sync_token);
   EXPECT_EQ(transfer[0].mailbox_holder.texture_target, target);
   EXPECT_EQ(transfer[0].format, format);
diff --git a/cc/test/fake_raster_buffer_provider.cc b/cc/test/fake_raster_buffer_provider.cc
index 7627d9d..4a0ac23 100644
--- a/cc/test/fake_raster_buffer_provider.cc
+++ b/cc/test/fake_raster_buffer_provider.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "cc/resources/resource_pool.h"
-#include "gpu/command_buffer/client/client_shared_image.h"
 
 namespace cc {
 
@@ -33,7 +32,7 @@
     bool depends_on_hardware_accelerated_jpeg_candidates,
     bool depends_on_hardware_accelerated_webp_candidates) {
   auto backing = std::make_unique<StubGpuBacking>();
-  backing->shared_image = gpu::ClientSharedImage::CreateForTesting();
+  backing->mailbox = gpu::Mailbox::GenerateForSharedImage();
   resource.set_gpu_backing(std::move(backing));
   return nullptr;
 }
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index 7366c68..7b1e6685 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -39,7 +39,6 @@
 #include "cc/tiles/tile_draw_info.h"
 #include "cc/tiles/tile_manager_settings.h"
 #include "cc/tiles/tile_task_manager.h"
-#include "gpu/command_buffer/client/client_shared_image.h"
 #include "ui/gfx/display_color_spaces.h"
 #include "url/gurl.h"
 
@@ -237,8 +236,8 @@
       // The raster here never really happened, cuz tests. So just add an
       // arbitrary sync token.
       if (resource.gpu_backing()) {
-        resource.gpu_backing()->shared_image =
-            gpu::ClientSharedImage::CreateForTesting();
+        resource.gpu_backing()->mailbox =
+            gpu::Mailbox::GenerateForSharedImage();
         resource.gpu_backing()->mailbox_sync_token.Set(
             gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(1), 1);
       }
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 75adec33..19b8102 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -4293,6 +4293,12 @@
                       /*is_wheel_scroll=*/false,
                       /*affect_browser_controls=*/false,
                       /*scroll_outer_viewport=*/true);
+
+  // If the viewport has scroll snap styling, we may need to snap after
+  // scrolling it. Browser controls animations may happen after scrollend, so
+  // it is too late for InputHandler to do the snapping.
+  viewport().SnapIfNeeded();
+
   client_->SetNeedsCommitOnImplThread();
   client_->RenewTreePriority();
   return true;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 6ab1da4d..52431f1 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -3043,6 +3043,10 @@
   EXPECT_EQ(TargetSnapAreaElementIds(),
             GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
 
+  GetInputHandler().ScrollUpdate(UpdateState(pointer_position,
+                                             gfx::Vector2dF(6, 6),
+                                             ui::ScrollInputType::kWheel)
+                                     .get());
   GetInputHandler().ScrollEndForSnapFling(true /* did_finish */);
   EXPECT_FALSE(GetInputHandler().animating_for_snap_for_testing());
   EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(10)),
@@ -3085,7 +3089,7 @@
 
   // The snap targets should not be set if the snap fling did not finish.
   GetInputHandler().ScrollEndForSnapFling(false /* did_finish */);
-  EXPECT_FALSE(GetInputHandler().animating_for_snap_for_testing());
+  EXPECT_TRUE(GetInputHandler().animating_for_snap_for_testing());
   EXPECT_EQ(TargetSnapAreaElementIds(),
             GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds());
 }
@@ -7246,6 +7250,69 @@
   GetInputHandler().ScrollEnd();
 }
 
+TEST_F(LayerTreeHostImplBrowserControlsTest,
+       HidingBrowserControlsAdjustsSnapFling) {
+  gfx::Size view_size(100, 100);
+  gfx::Size content_size(100, 1000);
+  gfx::RectF snap_area_1(0, 0, 100, 700);
+  SetupBrowserControlsAndScrollLayerWithVirtualViewport(view_size, view_size,
+                                                        content_size);
+
+  SnapContainerData container(
+      ScrollSnapType(false, SnapAxis::kY, SnapStrictness::kMandatory),
+      gfx::RectF(0, 0, 100, 100), gfx::PointF(0, 900));
+  ScrollSnapAlign start = ScrollSnapAlign(SnapAlignment::kStart);
+  container.AddSnapAreaData(
+      SnapAreaData(start, snap_area_1, false, ElementId(10)));
+  host_impl_->OuterViewportScrollNode()->snap_container_data.emplace(container);
+
+  DrawFrame();
+  EXPECT_VIEWPORT_GEOMETRIES(1.0f);
+
+  LayerTreeImpl* active_tree = host_impl_->active_tree();
+  PropertyTrees* property_trees = active_tree->property_trees();
+  LayerImpl* outer_scroll_layer = OuterViewportScrollLayer();
+  InputHandler& handler = GetInputHandler();
+  gfx::PointF initial_offset, target_offset;
+  gfx::Point position(50, 50);
+  ui::ScrollInputType type = ui::ScrollInputType::kTouchscreen;
+
+  handler.ScrollBegin(&*BeginState(position, gfx::Vector2dF(0, 15), type),
+                      type);
+  handler.ScrollUpdate(&*UpdateState(position, gfx::Vector2dF(0, 15), type));
+
+  // The browser controls, now partially hidden, are consuming all of the scroll
+  // delta so far.
+  EXPECT_FLOAT_EQ(0.7, active_tree->CurrentTopControlsShownRatio());
+  EXPECT_EQ(gfx::Vector2dF(0, 15),
+            property_trees->outer_viewport_container_bounds_delta());
+  EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(outer_scroll_layer));
+
+  // Enter "constrained native fling" mode inside snap_area_1.
+  EXPECT_FALSE(handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
+      gfx::Vector2dF(0, 30), gfx::Vector2dF(0, 600), &initial_offset,
+      &target_offset));
+
+  // Finish hiding the browser controls and start scrolling the content.
+  handler.ScrollUpdate(&*UpdateState(position, gfx::Vector2dF(0, 435), type));
+  EXPECT_POINTF_EQ(gfx::PointF(0, 400),
+                   CurrentScrollOffset(outer_scroll_layer));
+
+  // Try to scroll past the bottom of snap_area_1.
+  EXPECT_TRUE(handler.GetSnapFlingInfoAndSetAnimatingSnapTarget(
+      gfx::Vector2dF(0, 300), gfx::Vector2dF(0, 1000), &initial_offset,
+      &target_offset));
+
+  // The fling constraint should take us to 550, which aligns with the bottom of
+  // snap_area_1 using the expanded viewport size of 100x150 from hiding browser
+  // controls, even though SnapContainerData is still based on 100x100 viewport.
+  EXPECT_TRUE(handler.animating_for_snap_for_testing());
+  EXPECT_POINTF_EQ(gfx::PointF(0, 400), initial_offset);
+  EXPECT_POINTF_EQ(gfx::PointF(0, 550), target_offset);
+
+  handler.ScrollEnd();
+}
+
 // Ensure that moving the browser controls (i.e. omnibox/url-bar on mobile) on
 // pages with a non-1 minimum page scale factor (e.g. legacy desktop page)
 // correctly scales the clipping adjustment performed to show the newly exposed
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
index 33988b93..8a53f83 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromePhone.java
@@ -106,8 +106,7 @@
     @Override
     public void onTabsAllClosing(boolean incognito) {
         if (getActiveLayout() == mStaticLayout && !incognito) {
-            startShowing(mTabSwitcherLayout != null ? mTabSwitcherLayout : mOverviewLayout,
-                    /* animate= */ false);
+            showLayout(LayoutType.TAB_SWITCHER, /* animate= */ false);
         }
         super.onTabsAllClosing(incognito);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java
index a0fc8a62..16940d8ea 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java
@@ -82,9 +82,8 @@
         // Cache the result so it will not change during the same browser session.
         if (sShowQueryTilesOnNTP != null) return sShowQueryTilesOnNTP;
         boolean queryTileEnabled =
-                (ChromeFeatureList.isEnabled(ChromeFeatureList.QUERY_TILES)
-                        && ChromeFeatureList.isEnabled(ChromeFeatureList.QUERY_TILES_IN_NTP))
-                || QueryTileUtilsJni.get().isQueryTilesEnabled();
+                ChromeFeatureList.isEnabled(ChromeFeatureList.QUERY_TILES)
+                        && ChromeFeatureList.isEnabled(ChromeFeatureList.QUERY_TILES_IN_NTP);
         sShowQueryTilesOnNTP = queryTileEnabled && shouldShowQueryTiles();
         return sShowQueryTilesOnNTP;
     }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
index cc7de40..a4131d5 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
@@ -47,6 +47,7 @@
 import org.chromium.chrome.browser.commerce.ShoppingServiceFactory;
 import org.chromium.chrome.browser.enterprise.util.ManagedBrowserUtils;
 import org.chromium.chrome.browser.enterprise.util.ManagedBrowserUtilsJni;
+import org.chromium.chrome.browser.feed.FeedFeatures;
 import org.chromium.chrome.browser.feed.webfeed.WebFeedSnackbarController;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthController;
@@ -57,6 +58,7 @@
 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
+import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.readaloud.ReadAloudController;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
@@ -77,6 +79,7 @@
 import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridgeJni;
 import org.chromium.components.commerce.core.ShoppingService;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
+import org.chromium.components.prefs.PrefService;
 import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.components.webapps.AppBannerManager;
 import org.chromium.components.webapps.AppBannerManagerJni;
@@ -146,6 +149,7 @@
     @Mock private ShoppingService mShoppingService;
     @Mock private AppBannerManager.Natives mAppBannerManagerJniMock;
     @Mock private ReadAloudController mReadAloudController;
+    @Mock private PrefService mPrefService;
 
     private OneshotSupplierImpl<LayoutStateProvider> mLayoutStateProviderSupplier =
             new OneshotSupplierImpl<>();
@@ -195,6 +199,7 @@
         IdentityServicesProvider.setInstanceForTests(mIdentityService);
         FeatureList.setTestCanUseDefaultsForTesting();
         PageZoomCoordinator.setShouldShowMenuItemForTesting(false);
+        FeedFeatures.setFakePrefsForTest(mPrefService);
         jniMocker.mock(AppBannerManagerJni.TEST_HOOKS, mAppBannerManagerJniMock);
         Mockito.when(mAppBannerManagerJniMock.getInstallableWebAppManifestId(any()))
                 .thenReturn(null);
@@ -418,6 +423,45 @@
                 mTabbedAppMenuPropertiesDelegate.getFooterResourceId());
     }
 
+    @Test
+    @EnableFeatures(ChromeFeatureList.NEW_TAB_SEARCH_ENGINE_URL_ANDROID)
+    public void getFooterResourceId_dseOff_doesNotReturnWebFeedMenuItem() {
+        setUpMocksForWebFeedFooter();
+        when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(true);
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(false);
+
+        assertNotEquals(
+                "Footer Resource ID should not be web_feed_main_menu_item.",
+                R.layout.web_feed_main_menu_item,
+                mTabbedAppMenuPropertiesDelegate.getFooterResourceId());
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.NEW_TAB_SEARCH_ENGINE_URL_ANDROID)
+    public void getFooterResourceId_dseOn_returnsWebFeedMenuItem() {
+        setUpMocksForWebFeedFooter();
+        when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(true);
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
+
+        assertEquals(
+                "Footer Resource ID should be web_feed_main_menu_item.",
+                R.layout.web_feed_main_menu_item,
+                mTabbedAppMenuPropertiesDelegate.getFooterResourceId());
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.NEW_TAB_SEARCH_ENGINE_URL_ANDROID)
+    public void getFooterResourceId_signedOutUser_dseOn_doesNotReturnWebFeedMenuItem() {
+        setUpMocksForWebFeedFooter();
+        when(mIdentityManager.hasPrimaryAccount(anyInt())).thenReturn(false);
+        when(mPrefService.getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE)).thenReturn(true);
+
+        assertNotEquals(
+                "Footer Resource ID should not be web_feed_main_menu_item.",
+                R.layout.web_feed_main_menu_item,
+                mTabbedAppMenuPropertiesDelegate.getFooterResourceId());
+    }
+
     private void setUpMocksForWebFeedFooter() {
         when(mActivityTabProvider.get()).thenReturn(mTab);
         when(mTab.isIncognito()).thenReturn(false);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 7e691909..a70068b1 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -16351,47 +16351,6 @@
       You have enabled testing third-party cookie phaseout. This cannot be overridden by the settings page. If you want to re-enable third-party cookies, relaunch Chrome with this feature disabled.
     </message>
 
-    <!-- Experimental enterprise plus_addresses feature strings -->
-    <message name="IDS_PLUS_ADDRESS_MODAL_TITLE" desc="Title for the experimental enterprise plus addresses feature modal" translateable="false">
-      Lorem Ipsum
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_LABEL" desc="Label for the upper portion of the plus addresses modal" translateable="false">
-      Lorem Ipsum
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PROPOSED_PLUS_ADDRESS_AND_COPY" desc="The plus address with some descriptive text around it." translateable="false">
-      Use <ph name="PLUS_ADDRESS">$1<ex>plus@plus.plus</ex>?</ph>
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_LINK_TEXT" desc="The text of the link shown on the Plus Address modal." translateable="false">
-      Settings
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION_START" desc="The start of the plus address description that has a placeholder for a link." translateable="false">
-      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua <ph name="LINK">$1</ph>. Ut enim ad minim veniam...
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION_END" desc="The end of the plus address description that has a placeholder for the primary email address." translateable="false">
-      Lorem <ph name="REGULAR_ADDRESS">$1</ph>.
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_ERROR_MESSAGE" desc="The error message shown on the modal." translateable="false">
-      Lorem ipsum or whatever
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PROPOSED_PLUS_ADDRESS_PLACEHOLDER" desc="A placeholder for the plus address" translateable="false">
-      Lorem Ipsum or whatever
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_REGULAR_ADDRESS_LABEL" desc="A label for the regular address in the plus address modal" translateable="false">
-      For <ph name="REGULAR_ADDRESS">$1<ex>test@example.test</ex></ph>
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION" desc="An illustrative description of the plus addresses modal" translateable="false">
-      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_CANCEL_TEXT" desc="Cancel button text for the experimental enterprise plus addresses feature modal" translateable="false">
-      Dolor
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_OK_TEXT" desc="Ok button text for the experimental enterprise plus addresses feature modal" translateable="false">
-      Sit
-    </message>
-    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION_V2" desc="An illustrative description of the plus addresses modal" translateable="false">
-      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua <ph name="BEGIN_LINK">&lt;link&gt;</ph>Ut enim ad minim veniam<ph name="END_LINK">&lt;/link&gt;</ph> for <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="REGULAR_ADDRESS">$1<ex>test@example.test</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph>.
-    </message>
-
     <!-- Responsive Toolbar -->
     <message name="IDS_OVERFLOW_MENU_ITEM_TEXT_PROFILE" desc="Overflow menu item text of profile button">
       Profile
@@ -16469,6 +16428,27 @@
     <message name="IDS_IWA_INSTALLER_SHOW_METADATA_APP_VERSION_LABEL" desc="Label before the Isolated Web App version in the pre-install app info summary screen">
       Version: <ph name="APP_VERSION">$1<ex>1.0.0</ex></ph>
     </message>
+    <message name="IDS_IWA_INSTALLER_CONFIRM_TITLE" desc="Title of the confirmation popup that appears immediately before Isolated Web App installation">
+      Do you want to continue and allow this software to make changes to your device?
+    </message>
+    <message name="IDS_IWA_INSTALLER_CONFIRM_SUBTITLE" desc="Subtitle of the confirmation popup that appears immediately before Isolated Web App installation. $1 = IDS_IWA_INSTALLER_CONFIRM_LEARN_MORE">
+      To keep your device secure, you should only run and install software from trusted sources and developers. <ph name="LEARN_MORE">$1<ex>Learn more</ex></ph>
+    </message>
+    <message name="IDS_IWA_INSTALLER_CONFIRM_LEARN_MORE" desc="Link to a learn more page shown in the confirmation popup that appears immediately before Isolated Web App installation">
+      Learn more
+    </message>
+    <message name="IDS_IWA_INSTALLER_CONFIRM_CONTINUE" desc="Text of the continue button in the confirmation popup that appears immediately before Isolated Web App installation">
+      Continue
+    </message>
+    <message name="IDS_IWA_INSTALLER_SUCCESS_SUBTITLE" desc="Subtitle of the page shown after an Isolated Web App has successfully been installed.">
+      Congrats! <ph name="APP_NAME">$1<ex>Google Maps</ex></ph> has been successfully installed on your device
+    </message>
+    <message name="IDS_IWA_INSTALLER_SUCCESS_FINISH" desc="Text of the close button shown on the page after an Isolated Web App has been successfully installed">
+      Finish
+    </message>
+    <message name="IDS_IWA_INSTALLER_SUCCESS_LAUNCH_APPLICATION" desc="Text of the launch app button shown on the page after an Isolated Web App has been successfully installed">
+      Launch application
+    </message>
     <message name="IDS_IWA_INSTALLER_VERIFICATION_TITLE" desc="Title of the verification step of the IWA installation">
       Chrome is verifying the installation bundle
     </message>
diff --git a/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_CONTINUE.png.sha1 b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_CONTINUE.png.sha1
new file mode 100644
index 0000000..0664e3c6
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_CONTINUE.png.sha1
@@ -0,0 +1 @@
+ffef3c9665cc71657d17348c0d0beddb930ab228
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_LEARN_MORE.png.sha1 b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_LEARN_MORE.png.sha1
new file mode 100644
index 0000000..55d51b18
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_LEARN_MORE.png.sha1
@@ -0,0 +1 @@
+dae0d0a8e7e4480b497edda07321b7b1bc26b52f
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_SUBTITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_SUBTITLE.png.sha1
new file mode 100644
index 0000000..d397657
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+627fede2ad1131a5a111e267317ee924aff33114
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_TITLE.png.sha1
new file mode 100644
index 0000000..ca00617
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_CONFIRM_TITLE.png.sha1
@@ -0,0 +1 @@
+463b5618e84d42d27a4cedca7a2048dda6f948a5
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_FINISH.png.sha1 b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_FINISH.png.sha1
new file mode 100644
index 0000000..7c56d280
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_FINISH.png.sha1
@@ -0,0 +1 @@
+17b35a53e1ad380fbc7d423a55445310e15febbd
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_LAUNCH_APPLICATION.png.sha1 b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_LAUNCH_APPLICATION.png.sha1
new file mode 100644
index 0000000..2a254b5
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_LAUNCH_APPLICATION.png.sha1
@@ -0,0 +1 @@
+889a46f60e12fe5c18dd69ac9f3f9a26444ebf1d
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_SUBTITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_SUBTITLE.png.sha1
new file mode 100644
index 0000000..41ff7609
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_IWA_INSTALLER_SUCCESS_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+93491199a8b3d1e641ad961e5859d0ead860f9a1
\ No newline at end of file
diff --git a/chrome/app/nearby_share_strings.grdp b/chrome/app/nearby_share_strings.grdp
index 33be25f..a9cc5e93 100644
--- a/chrome/app/nearby_share_strings.grdp
+++ b/chrome/app/nearby_share_strings.grdp
@@ -34,6 +34,12 @@
       other {# contacts are not available. To use Nearby Share with them, add the email addresses associated with their Google Accounts to your contacts.}
     }
   </message>
+  <message name="IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE_PH" desc="Informational message indicating that there are contacts that are not connectable for sharing with the Nearby Share feature.">
+    {COUNT, plural,
+      =1 {# contact is not available. To use <ph name="FEATURE_NAME">$2<ex>Nearby Share</ex></ph> with them, add the email address associated with their Google Account to your contacts.}
+      other {# contacts are not available. To use <ph name="FEATURE_NAME">$2<ex>Nearby Share</ex></ph> with them, add the email addresses associated with their Google Accounts to your contacts.}
+    }
+  </message>
   <message name="IDS_NEARBY_CONTACT_VISIBILITY_OWN_ALL" desc="Describes who can see the local device when the Nearby Share visibility setting is set to 'All contacts'.">
     Your contacts can share with you when they are nearby. Transfers won't start until you accept.
   </message>
@@ -292,6 +298,9 @@
   <message name="IDS_NEARBY_ACCOUNT_ROW_LABEL" desc="Label to describe the account row which displays the user's full name and their email which will identify them when using the Nearby Share feature." is_accessibility_with_no_ui="true">
       Nearby Share settings for <ph name="USER_NAME">$1<ex>Jane Doe</ex></ph>'s device, sharing under the account <ph name="USER_EMAIL">$2<ex>example@gmail.com</ex></ph>.
   </message>
+  <message name="IDS_NEARBY_ACCOUNT_ROW_LABEL_PH" desc="Feature name placeholder: Label to describe the account row which displays the user's full name and their email which will identify them when using the Nearby Share feature." is_accessibility_with_no_ui="true">
+    <ph name="FEATURE_NAME">$1<ex>Nearby Share</ex></ph> settings for <ph name="USER_NAME">$2<ex>Jane Doe</ex></ph>'s device, sharing under the account <ph name="USER_EMAIL">$3<ex>example@gmail.com</ex></ph>.
+  </message>
   <message name="IDS_NEARBY_SETTINGS_HELP_CAPTION_TOP" desc="Brief explanation shown at the bottom of the settings page. Meant to give context on what conditions have to be met to use Nearby Share and how to toggle the high visibility state in the system tray. Provides a link with more detailed information.">
     Nearby Share uses Bluetooth scanning to find nearby devices.
   </message>
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_ACCOUNT_ROW_LABEL_PH.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_ACCOUNT_ROW_LABEL_PH.png.sha1
new file mode 100644
index 0000000..28e09692
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_ACCOUNT_ROW_LABEL_PH.png.sha1
@@ -0,0 +1 @@
+e822a7f2912d046b2fed42b49bd854ac951deec5
\ No newline at end of file
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE_PH.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE_PH.png.sha1
new file mode 100644
index 0000000..977615a
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE_PH.png.sha1
@@ -0,0 +1 @@
+07cbe06813e7dbe17b8009045d5ccdbf374ecfa8
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 052cc32d..37543ea 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -5975,8 +5975,6 @@
       "metrics/antivirus_metrics_provider_win.h",
       "metrics/google_update_metrics_provider_win.cc",
       "metrics/google_update_metrics_provider_win.h",
-      "metrics/jumplist_metrics_win.cc",
-      "metrics/jumplist_metrics_win.h",
       "net/chrome_mojo_proxy_resolver_win.cc",
       "net/chrome_mojo_proxy_resolver_win.h",
       "net/service_providers_win.cc",
diff --git a/chrome/browser/app_controller_mac_browsertest.mm b/chrome/browser/app_controller_mac_browsertest.mm
index 56d36c8..b138a3c1 100644
--- a/chrome/browser/app_controller_mac_browsertest.mm
+++ b/chrome/browser/app_controller_mac_browsertest.mm
@@ -1179,50 +1179,6 @@
   EXPECT_EQ(profile, new_browser->profile()->GetOriginalProfile());
 }
 
-// Tests opening a new window from dock menu while incognito browser is opened.
-// Regression test for https://crbug.com/1371923
-IN_PROC_BROWSER_TEST_F(AppControllerMainMenuBrowserTest,
-                       WhileIncognitoBrowserIsOpened_NewWindow) {
-  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
-
-  // Close the current browser.
-  Profile* profile = browser()->profile();
-  chrome::CloseAllBrowsers();
-  ui_test_utils::WaitForBrowserToClose();
-  EXPECT_TRUE(BrowserList::GetInstance()->empty());
-
-  // Create an incognito browser.
-  Browser* incognito_browser = CreateIncognitoBrowser(profile);
-  EXPECT_TRUE(incognito_browser->profile()->IsIncognitoProfile());
-  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
-  EXPECT_EQ(incognito_browser, chrome::GetLastActiveBrowser());
-  // Assure that `windowDidBecomeMain` is called even if this browser process
-  // lost focus because of other browser processes in other shards taking
-  // focus. It prevents flakiness.
-  // See: https://crbug.com/1450491
-  [[NSNotificationCenter defaultCenter]
-      postNotificationName:NSWindowDidBecomeMainNotification
-                    object:incognito_browser->window()
-                               ->GetNativeWindow()
-                               .GetNativeNSWindow()];
-
-  // Simulate click on "New Window".
-  ui_test_utils::BrowserChangeObserver browser_added_observer(
-      nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded);
-  AppController* app_controller = AppController.sharedController;
-  NSMenu* menu = [app_controller applicationDockMenu:NSApp];
-  ASSERT_TRUE(menu);
-  NSMenuItem* item = [menu itemWithTag:IDC_NEW_WINDOW];
-  ASSERT_TRUE(item);
-  [app_controller commandDispatch:item];
-
-  // Check that a new non-incognito browser is opened.
-  Browser* new_browser = browser_added_observer.Wait();
-  EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
-  EXPECT_TRUE(new_browser->profile()->IsRegularProfile());
-  EXPECT_EQ(profile, new_browser->profile());
-}
-
 class AppControllerIncognitoSwitchTest : public InProcessBrowserTest {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
diff --git a/chrome/browser/app_controller_mac_interactive_uitest.mm b/chrome/browser/app_controller_mac_interactive_uitest.mm
index 65804cd..ed58e0d 100644
--- a/chrome/browser/app_controller_mac_interactive_uitest.mm
+++ b/chrome/browser/app_controller_mac_interactive_uitest.mm
@@ -8,6 +8,7 @@
 
 #import "chrome/browser/app_controller_mac.h"
 #include "chrome/browser/history/history_service_factory.h"
+#include "chrome/browser/lifetime/application_lifetime_desktop.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
@@ -26,14 +27,17 @@
   [NSApp.delegate application:NSApp openURLs:@[ net::NSURLWithGURL(url) ]];
 }
 
+// -------------------AppControllerMainMenuInteractiveUITest-------------------
+
 class AppControllerMainMenuInteractiveUITest : public InProcessBrowserTest {
  protected:
   AppControllerMainMenuInteractiveUITest() = default;
 };
 
-// Note: This test interacts with SharedController which requires the browser's
+// Note: These tests interacts with SharedController which requires the browser's
 // focus. In browser_tests other tests that are running in parallel cause
 // flakiness to test test. See: https://crbug.com/1469960
+
 // Test switching from Regular to OTR profiles updates the history menu.
 IN_PROC_BROWSER_TEST_F(AppControllerMainMenuInteractiveUITest,
                        SwitchToIncognitoRemovesHistoryItems) {
@@ -75,4 +79,39 @@
   EXPECT_TRUE([app_controller historyMenuBridge]->service());
 }
 
+// Tests opening a new window from dock menu while incognito browser is opened.
+// Regression test for https://crbug.com/1371923
+IN_PROC_BROWSER_TEST_F(AppControllerMainMenuInteractiveUITest,
+                       WhileIncognitoBrowserIsOpened_NewWindow) {
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
+
+  // Close the current browser.
+  Profile* profile = browser()->profile();
+  chrome::CloseAllBrowsers();
+  ui_test_utils::WaitForBrowserToClose();
+  EXPECT_TRUE(BrowserList::GetInstance()->empty());
+
+  // Create an incognito browser.
+  Browser* incognito_browser = CreateIncognitoBrowser(profile);
+  EXPECT_TRUE(incognito_browser->profile()->IsIncognitoProfile());
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
+  EXPECT_EQ(incognito_browser, chrome::GetLastActiveBrowser());
+
+  // Simulate click on "New Window".
+  ui_test_utils::BrowserChangeObserver browser_added_observer(
+      nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded);
+  AppController* app_controller = AppController.sharedController;
+  NSMenu* menu = [app_controller applicationDockMenu:NSApp];
+  ASSERT_TRUE(menu);
+  NSMenuItem* item = [menu itemWithTag:IDC_NEW_WINDOW];
+  ASSERT_TRUE(item);
+  [app_controller commandDispatch:item];
+
+  // Check that a new non-incognito browser is opened.
+  Browser* new_browser = browser_added_observer.Wait();
+  EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
+  EXPECT_TRUE(new_browser->profile()->IsRegularProfile());
+  EXPECT_EQ(profile, new_browser->profile());
+}
+
 }  // namespace
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 72692db..1ab8628 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -2683,6 +2683,8 @@
     "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h",
     "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_reported_local_id_manager.cc",
     "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_reported_local_id_manager.h",
+    "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.cc",
+    "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h",
     "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_settings_for_test.cc",
     "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_settings_for_test.h",
     "policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_uploaded_crash_info_manager.cc",
diff --git a/chrome/browser/ash/arc/nearby_share/ui/low_disk_space_dialog_view.cc b/chrome/browser/ash/arc/nearby_share/ui/low_disk_space_dialog_view.cc
index e457080..03d5361 100644
--- a/chrome/browser/ash/arc/nearby_share/ui/low_disk_space_dialog_view.cc
+++ b/chrome/browser/ash/arc/nearby_share/ui/low_disk_space_dialog_view.cc
@@ -7,6 +7,9 @@
 #include <memory>
 
 #include "ash/frame/non_client_frame_view_ash.h"
+#include "base/i18n/message_formatter.h"
+#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
+#include "chrome/browser/nearby_sharing/common/nearby_share_resource_getter.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/text/bytes_formatting.h"
@@ -42,10 +45,22 @@
       },
       base::Unretained(this)));
 
-  AddDialogMessage(base::ReplaceStringPlaceholders(
-      l10n_util::GetPluralStringFUTF16(
-          IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_MESSAGE, file_count),
-      ui::FormatBytes(required_disk_space), /*offset=*/nullptr));
+  std::u16string low_disk_space_dialog_message;
+  if (features::IsNameEnabled()) {
+    low_disk_space_dialog_message =
+        base::i18n::MessageFormatter::FormatWithNumberedArgs(
+            l10n_util::GetStringUTF16(
+                IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_MESSAGE_PH),
+            file_count,
+            NearbyShareResourceGetter::GetInstance()->GetFeatureName(),
+            ui::FormatBytes(required_disk_space));
+  } else {
+    low_disk_space_dialog_message = base::ReplaceStringPlaceholders(
+        l10n_util::GetPluralStringFUTF16(
+            IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_MESSAGE, file_count),
+        ui::FormatBytes(required_disk_space), /*offset=*/nullptr);
+  }
+  AddDialogMessage(low_disk_space_dialog_message);
 }
 
 LowDiskSpaceDialogView::~LowDiskSpaceDialogView() = default;
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.cc
index 1003095..54ca98f8 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.cc
@@ -24,6 +24,7 @@
 #include "base/types/expected.h"
 #include "base/values.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_reported_local_id_manager.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_settings_for_test.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_uploaded_crash_info_manager.h"
 #include "chromeos/ash/services/cros_healthd/public/cpp/service_connection.h"
@@ -39,11 +40,6 @@
 
 namespace {
 
-constexpr std::string_view kDefaultReportedLocalIdSaveFilePath =
-    "/var/lib/reporting/crash_events/REPORTED_LOCAL_IDS";
-constexpr std::string_view kDefaultUploadedCrashInfoSaveFilePath =
-    "/var/lib/reporting/crash_events/UPLOADED_CRASH_INFO";
-
 // Get current user session.
 const ash::UserSession* GetCurrentUserSession() {
   return ash::Shell::Get()->session_controller()->GetPrimaryUserSession();
@@ -89,21 +85,19 @@
 }  // namespace
 
 FatalCrashEventsObserver::FatalCrashEventsObserver()
-    : FatalCrashEventsObserver(
-          base::FilePath(kDefaultReportedLocalIdSaveFilePath),
-          base::FilePath(kDefaultUploadedCrashInfoSaveFilePath),
-          /*reported_local_id_io_task_runner=*/nullptr,
-          /*uploaded_crash_info_io_task_runner=*/nullptr) {}
+    : FatalCrashEventsObserver(DefaultSaveFilePathsProvider::Get(),
+                               /*reported_local_id_io_task_runner=*/nullptr,
+                               /*uploaded_crash_info_io_task_runner=*/nullptr) {
+}
 
 FatalCrashEventsObserver::FatalCrashEventsObserver(
-    base::FilePath reported_local_id_save_file,
-    base::FilePath uploaded_crash_info_save_file,
+    const SaveFilePathsProviderInterface& save_file_paths_provider,
     scoped_refptr<base::SequencedTaskRunner> reported_local_id_io_task_runner,
     scoped_refptr<base::SequencedTaskRunner> uploaded_crash_info_io_task_runner)
     : MojoServiceEventsObserverBase<ash::cros_healthd::mojom::EventObserver>(
           this),
       reported_local_id_manager_{ReportedLocalIdManager::Create(
-          std::move(reported_local_id_save_file),
+          save_file_paths_provider.GetReportedLocalIdSaveFilePath(),
           // Don't BindPostTask here, because it would risk calling
           // `ProcessEventsBeforeSaveFilesLoaded` twice, once from
           // reported_local_id_manager_, once from uploaded_crash_info_manager_.
@@ -115,7 +109,7 @@
               base::Unretained(this)),
           std::move(reported_local_id_io_task_runner))},
       uploaded_crash_info_manager_{UploadedCrashInfoManager::Create(
-          std::move(uploaded_crash_info_save_file),
+          save_file_paths_provider.GetUploadedCrashInfoSaveFilePath(),
           // Don't BindPostTask here, because it would risk calling
           // `ProcessEventsBeforeSaveFilesLoaded` twice, once from
           // reported_local_id_manager_, once from uploaded_crash_info_manager_.
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h
index 951ffe8..ff978fc 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h
@@ -33,7 +33,8 @@
   // Chromium code style to not name it "TestSettings" because this struct is
   // also compiled in production code.
   struct SettingsForTest;
-  // A RAII class that setups the environment for testing this class.
+
+  // RAII class that set up the environment for testing this class.
   class TestEnvironment;
 
   // An entry corresponds to a crash that is saved in the save file of reported
@@ -65,6 +66,11 @@
   // specifies the path for the save file.
   friend class FatalCrashEventsObserver::TestEnvironment;
 
+  // Manages default save file paths. The defaults are changed in browser tests.
+  class SaveFilePathsProviderInterface;
+  // Production implementation of `SaveFilePathsProviderInterface`.
+  class DefaultSaveFilePathsProvider;
+
   // For `OnEvent`. Not let `TestEnvironment` be a proxy of `OnEvent` because it
   // is an exception to allow `SlowFileLoadingFieldsPassedThrough` to call
   // `OnEvent` directly. Using `TestEnvironment` as a proxy would expose
@@ -85,8 +91,7 @@
   // input parameters to accommodate the test environment. In production code,
   // they are always the default value specified in the default constructor.
   FatalCrashEventsObserver(
-      base::FilePath reported_local_id_save_file,
-      base::FilePath uploaded_crash_info_save_file,
+      const SaveFilePathsProviderInterface& save_file_paths_provider,
       scoped_refptr<base::SequencedTaskRunner> reported_local_id_io_task_runner,
       scoped_refptr<base::SequencedTaskRunner>
           uploaded_crash_info_io_task_runner);
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_browsertest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_browsertest.cc
new file mode 100644
index 0000000..b122bcf
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_browsertest.cc
@@ -0,0 +1,168 @@
+// 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 "base/functional/bind.h"
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/ash/policy/affiliation/affiliation_mixin.h"
+#include "chrome/browser/ash/policy/affiliation/affiliation_test_helper.h"
+#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h"
+#include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
+#include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
+#include "chrome/browser/policy/dm_token_utils.h"
+#include "chromeos/ash/components/settings/cros_settings_names.h"
+#include "chromeos/ash/services/cros_healthd/public/cpp/fake_cros_healthd.h"
+#include "chromeos/dbus/missive/missive_client_test_observer.h"
+#include "components/reporting/proto/synced/metric_data.pb.h"
+#include "components/reporting/proto/synced/record.pb.h"
+#include "components/reporting/proto/synced/record_constants.pb.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/test_launcher.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace reporting {
+namespace {
+constexpr std::string_view kTestLocalId = "a local ID";
+constexpr std::string_view kTestCrashReportId = "a crash report ID";
+
+using ::ash::cros_healthd::FakeCrosHealthd;
+using ::ash::cros_healthd::mojom::CrashEventInfo;
+using ::ash::cros_healthd::mojom::CrashUploadInfo;
+using ::ash::cros_healthd::mojom::EventCategoryEnum;
+using ::ash::cros_healthd::mojom::EventInfo;
+using ::testing::Eq;
+
+// Filter to determine if a record is a crash event as required by the browser
+// tests.
+bool IsRecordCrashEvent(const ::reporting::Record& record) {
+  MetricData record_data;
+  EXPECT_TRUE(record_data.ParseFromString(record.data()));
+  return
+      // Destination must be TELEMETRY_METRIC.
+      record.has_destination() &&
+      record.destination() == Destination::EVENT_METRIC &&
+      // Event type must be CRASH_FATALLY
+      record_data.has_event_data() && record_data.event_data().has_type() &&
+      record_data.event_data().type() == MetricEventType::CRASH_FATALLY;
+}
+
+// Browser test environment for fatal crash events.
+class BrowserTestEnvironment final
+    : public FatalCrashEventsObserver::TestEnvironment::SaveFilePathsProvider {
+ public:
+  BrowserTestEnvironment()
+      : original_default_save_file_paths_{g_default_save_file_paths_} {
+    g_default_save_file_paths_ = this;
+  }
+  ~BrowserTestEnvironment() override {
+    g_default_save_file_paths_ = original_default_save_file_paths_;
+  }
+  BrowserTestEnvironment(const BrowserTestEnvironment&) = delete;
+  BrowserTestEnvironment& operator=(const BrowserTestEnvironment&) = delete;
+
+ private:
+  // The original `SaveFilePathsProviderInterface` instance before
+  // entering the browser test environment.
+  const raw_ptr<const SaveFilePathsProviderInterface>
+      original_default_save_file_paths_;
+};
+
+class FatalCrashEventsBrowserTest
+    : public ::policy::DevicePolicyCrosBrowserTest,
+      public ::testing::WithParamInterface</*uploaded=*/bool> {
+ protected:
+  FatalCrashEventsBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(kEnableFatalCrashEventsObserver);
+    ::policy::SetDMTokenForTesting(
+        ::policy::DMToken::CreateValidToken("token"));
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    ::policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager(
+        command_line);
+    ::policy::DevicePolicyCrosBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  bool is_uploaded() const { return GetParam(); }
+
+  void EnablePolicy() {
+    scoped_testing_cros_settings_.device_settings()->SetBoolean(
+        ::ash::kReportDeviceCrashReportInfo, true);
+  }
+
+  // healthd emits a crash.
+  static void EmitCrash(bool is_uploaded) {
+    auto crash_event_info = CrashEventInfo::New();
+    crash_event_info->local_id = kTestLocalId;
+    if (is_uploaded) {
+      crash_event_info->upload_info = CrashUploadInfo::New();
+      crash_event_info->upload_info->crash_report_id = kTestCrashReportId;
+      // The default zero time is earlier than the UNIX epoch.
+      crash_event_info->upload_info->creation_time = base::Time::UnixEpoch();
+    }
+
+    FakeCrosHealthd::Get()->EmitEventForCategory(
+        EventCategoryEnum::kCrash,
+        EventInfo::NewCrashEventInfo(std::move(crash_event_info)));
+  }
+
+ private:
+  BrowserTestEnvironment fatal_crash_events_browser_test_environment_;
+  ::policy::DevicePolicyCrosTestHelper test_helper_;
+  // Set up device affiliation. No need to set up or log in as an affiliated
+  // user, because reporting fatal crash events is controlled by a device
+  // policy.
+  ::policy::AffiliationMixin affiliation_mixin_{&mixin_host_, &test_helper_};
+  ::ash::ScopedTestingCrosSettings scoped_testing_cros_settings_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(FatalCrashEventsBrowserTest,
+                       CrashOccursAndPolicyEnabled) {
+  EnablePolicy();
+  chromeos::MissiveClientTestObserver missive_event_observer(
+      base::BindRepeating(IsRecordCrashEvent));
+
+  EmitCrash(is_uploaded());
+
+  const auto [priority, record] =
+      missive_event_observer.GetNextEnqueuedRecord();
+  EXPECT_THAT(priority, Eq(Priority::SLOW_BATCH));
+  ASSERT_TRUE(record.has_source_info());
+  EXPECT_THAT(record.source_info().source(), Eq(SourceInfo::ASH));
+
+  MetricData metric_data;
+  ASSERT_TRUE(metric_data.ParseFromString(record.data()));
+
+  // Testing event found successfully. It is sufficient to test,
+  // local ID (present in both uploaded and unuploaded crashes) and crash report
+  // ID (uploaded crash only) in browser tests. Detailed tests of fields are
+  // covered in unit tests.
+  ASSERT_TRUE(metric_data.has_telemetry_data());
+  ASSERT_TRUE(metric_data.telemetry_data().has_fatal_crash_telemetry());
+  const auto& fatal_crash_telemetry =
+      metric_data.telemetry_data().fatal_crash_telemetry();
+
+  ASSERT_TRUE(fatal_crash_telemetry.has_local_id());
+  EXPECT_EQ(fatal_crash_telemetry.local_id(), kTestLocalId);
+
+  if (is_uploaded()) {
+    ASSERT_TRUE(fatal_crash_telemetry.has_crash_report_id());
+    EXPECT_EQ(fatal_crash_telemetry.crash_report_id(), kTestCrashReportId);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    FatalCrashEventsBrowserTests,
+    FatalCrashEventsBrowserTest,
+    ::testing::Bool(),
+    [](const testing::TestParamInfo<FatalCrashEventsBrowserTest::ParamType>&
+           info) { return info.param ? "uploaded" : "unuploaded"; });
+}  // namespace
+}  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.cc
new file mode 100644
index 0000000..3923225
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.cc
@@ -0,0 +1,39 @@
+// 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/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h"
+
+#include "base/files/file_path.h"
+
+namespace reporting {
+
+FatalCrashEventsObserver::DefaultSaveFilePathsProvider::
+    DefaultSaveFilePathsProvider() = default;
+FatalCrashEventsObserver::DefaultSaveFilePathsProvider::
+    ~DefaultSaveFilePathsProvider() = default;
+
+// static
+const FatalCrashEventsObserver::SaveFilePathsProviderInterface*
+    FatalCrashEventsObserver::SaveFilePathsProviderInterface::
+        g_default_save_file_paths_{nullptr};
+
+// static
+const FatalCrashEventsObserver::SaveFilePathsProviderInterface&
+FatalCrashEventsObserver::DefaultSaveFilePathsProvider::Get() {
+  if (!g_default_save_file_paths_) {
+    g_default_save_file_paths_ = new DefaultSaveFilePathsProvider();
+  }
+  return *g_default_save_file_paths_;
+}
+
+base::FilePath FatalCrashEventsObserver::DefaultSaveFilePathsProvider::
+    GetReportedLocalIdSaveFilePath() const {
+  return base::FilePath("/var/lib/reporting/crash_events/REPORTED_LOCAL_IDS");
+}
+
+base::FilePath FatalCrashEventsObserver::DefaultSaveFilePathsProvider::
+    GetUploadedCrashInfoSaveFilePath() const {
+  return base::FilePath("/var/lib/reporting/crash_events/UPLOADED_CRASH_INFO");
+}
+}  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h
new file mode 100644
index 0000000..627f745
--- /dev/null
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h
@@ -0,0 +1,48 @@
+// 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_POLICY_REPORTING_METRICS_REPORTING_FATAL_CRASH_FATAL_CRASH_EVENTS_OBSERVER_SAVE_FILE_PATHS_PROVIDER_H_
+#define CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_FATAL_CRASH_FATAL_CRASH_EVENTS_OBSERVER_SAVE_FILE_PATHS_PROVIDER_H_
+
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h"
+
+#include "base/files/file_path.h"
+
+namespace reporting {
+
+class FatalCrashEventsObserver::SaveFilePathsProviderInterface {
+ public:
+  // Gets the save file path for saving reported local IDs.
+  virtual base::FilePath GetReportedLocalIdSaveFilePath() const = 0;
+
+  // Gets the save file path for saving uploaded crash info.
+  virtual base::FilePath GetUploadedCrashInfoSaveFilePath() const = 0;
+
+ protected:
+  // The `SaveFilePathsProviderInterface` singleton. Use a raw pointer
+  // instead of `unique_ptr` or `raw_ptr` here, because we must avoid
+  // destructing it at exit time. `unique_ptr` would trigger the error of
+  // "declaration requires an exit-time destructor".
+  static const SaveFilePathsProviderInterface* g_default_save_file_paths_;
+};
+
+class FatalCrashEventsObserver::DefaultSaveFilePathsProvider final
+    : public FatalCrashEventsObserver::SaveFilePathsProviderInterface {
+ public:
+  static const SaveFilePathsProviderInterface& Get();
+  DefaultSaveFilePathsProvider(const DefaultSaveFilePathsProvider&) = delete;
+  DefaultSaveFilePathsProvider& operator=(const DefaultSaveFilePathsProvider&) =
+      delete;
+  ~DefaultSaveFilePathsProvider();
+
+ private:
+  DefaultSaveFilePathsProvider();
+
+  // SaveFilePathsProviderInterface:
+  base::FilePath GetReportedLocalIdSaveFilePath() const override;
+  base::FilePath GetUploadedCrashInfoSaveFilePath() const override;
+};
+}  // namespace reporting
+
+#endif  // CHROME_BROWSER_ASH_POLICY_REPORTING_METRICS_REPORTING_FATAL_CRASH_FATAL_CRASH_EVENTS_OBSERVER_SAVE_FILE_PATHS_PROVIDER_H_
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.cc
index 4a06d9d5..e8904a72 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.cc
@@ -31,8 +31,8 @@
     scoped_refptr<base::SequencedTaskRunner> uploaded_crash_info_io_task_runner)
     const {
   auto observer = base::WrapUnique(new FatalCrashEventsObserver(
-      GetReportedLocalIdSaveFilePath(), GetUploadedCrashInfoSaveFilePath(),
-      reported_local_id_io_task_runner, uploaded_crash_info_io_task_runner));
+      save_file_paths_provider_, reported_local_id_io_task_runner,
+      uploaded_crash_info_io_task_runner));
 
   DCHECK_CALLED_ON_VALID_SEQUENCE(observer->sequence_checker_);
   DCHECK_CALLED_ON_VALID_SEQUENCE(
@@ -55,16 +55,9 @@
   return observer;
 }
 
-const base::FilePath&
-FatalCrashEventsObserver::TestEnvironment::GetReportedLocalIdSaveFilePath()
-    const {
-  return reported_local_id_save_file_path_;
-}
-
-const base::FilePath&
-FatalCrashEventsObserver::TestEnvironment::GetUploadedCrashInfoSaveFilePath()
-    const {
-  return uploaded_crash_info_save_file_path_;
+const FatalCrashEventsObserver::TestEnvironment::SaveFilePathsProvider&
+FatalCrashEventsObserver::TestEnvironment::GetSaveFilePathsProvider() const {
+  return save_file_paths_provider_;
 }
 
 // static
@@ -132,6 +125,21 @@
   run_loop.Run();
 }
 
+FatalCrashEventsObserver::TestEnvironment::SaveFilePathsProvider::
+    SaveFilePathsProvider() = default;
+FatalCrashEventsObserver::TestEnvironment::SaveFilePathsProvider::
+    ~SaveFilePathsProvider() = default;
+
+base::FilePath FatalCrashEventsObserver::TestEnvironment::
+    SaveFilePathsProvider::GetReportedLocalIdSaveFilePath() const {
+  return temp_dir_.Append("REPORTED_LOCAL_IDS");
+}
+
+base::FilePath FatalCrashEventsObserver::TestEnvironment::
+    SaveFilePathsProvider::GetUploadedCrashInfoSaveFilePath() const {
+  return temp_dir_.Append("UPLOADED_CRASH_INFO");
+}
+
 FatalCrashEventsObserver::TestEnvironment::SequenceBlocker::SequenceBlocker(
     scoped_refptr<base::SequencedTaskRunner> task_runner) {
   task_runner->PostTask(FROM_HERE, base::BindOnce(
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.h b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.h
index 3e5d3bc..824f6776 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.h
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.h
@@ -14,6 +14,7 @@
 #include "base/test/test_file_util.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_reported_local_id_manager.h"
+#include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_save_file_paths_provider.h"
 #include "chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_uploaded_crash_info_manager.h"
 
 namespace reporting {
@@ -23,6 +24,24 @@
   using ShouldReportResult =
       FatalCrashEventsObserver::ReportedLocalIdManager::ShouldReportResult;
 
+  // Save file paths provider for tests.
+  class SaveFilePathsProvider
+      : public FatalCrashEventsObserver::SaveFilePathsProviderInterface {
+   public:
+    SaveFilePathsProvider();
+    SaveFilePathsProvider(const SaveFilePathsProvider&) = delete;
+    SaveFilePathsProvider& operator=(const SaveFilePathsProvider&) = delete;
+    virtual ~SaveFilePathsProvider();
+
+    // SaveFilePathsProviderInterface:
+    base::FilePath GetReportedLocalIdSaveFilePath() const override;
+    base::FilePath GetUploadedCrashInfoSaveFilePath() const override;
+
+   private:
+    // Temporary dir for storing save files.
+    base::FilePath temp_dir_{base::CreateUniqueTempDirectoryScopedToTest()};
+  };
+
   // Posts a task that blocks a sequence, and unblocks when requested. User must
   // ensure that the blocking task is cleared when this object is destroyed.
   class SequenceBlocker {
@@ -52,11 +71,8 @@
   TestEnvironment& operator=(const TestEnvironment&) = delete;
   ~TestEnvironment();
 
-  // Gets the path to the save file that contains reported local IDs.
-  const base::FilePath& GetReportedLocalIdSaveFilePath() const;
-
-  // Gets the path to the save file that contains uploaded crash info.
-  const base::FilePath& GetUploadedCrashInfoSaveFilePath() const;
+  // Gets the paths to the save files.
+  const SaveFilePathsProvider& GetSaveFilePathsProvider() const;
 
   // Creates a `FatalCrashEventsObserver` object that uses `save_file_path_` as
   // the save file and returns the pointer. If
@@ -94,11 +110,8 @@
       scoped_refptr<base::SequencedTaskRunner> task_runner);
 
  private:
-  const base::FilePath temp_dir_{base::CreateUniqueTempDirectoryScopedToTest()};
-  const base::FilePath reported_local_id_save_file_path_{
-      temp_dir_.Append("REPORTED_LOCAL_IDS")};
-  const base::FilePath uploaded_crash_info_save_file_path_{
-      temp_dir_.Append("UPLOADED_CRASH_INFO")};
+  // Save file paths used in unit tests.
+  const SaveFilePathsProvider save_file_paths_provider_;
 };
 }  // namespace reporting
 
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_unittest.cc
index 192c3db..534aa074 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_unittest.cc
@@ -637,8 +637,9 @@
   ~FatalCrashEventsObserverReportedLocalIdsTestBase() override = default;
 
   // Gets the path to the save file.
-  const base::FilePath& GetSaveFilePath() const {
-    return fatal_crash_test_environment_.GetReportedLocalIdSaveFilePath();
+  base::FilePath GetSaveFilePath() const {
+    return fatal_crash_test_environment_.GetSaveFilePathsProvider()
+        .GetReportedLocalIdSaveFilePath();
   }
 
   // Generates an uninteresting fatal crash event to alter the observer's state
@@ -1250,8 +1251,9 @@
   ~FatalCrashEventsObserverUploadedCrashTestBase() override = default;
 
   // Gets the path to the save file.
-  const base::FilePath& GetSaveFilePath() const {
-    return fatal_crash_test_environment_.GetUploadedCrashInfoSaveFilePath();
+  base::FilePath GetSaveFilePath() const {
+    return fatal_crash_test_environment_.GetSaveFilePathsProvider()
+        .GetUploadedCrashInfoSaveFilePath();
   }
 
   // Generates an uninteresting fatal crash event to alter the observer's state
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
index f15f55869..a67f0f9a 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
@@ -680,7 +680,7 @@
 
   if (base::FeatureList::IsEnabled(kEnableFatalCrashEventsObserver)) {
     event_observer_managers_.emplace_back(delegate_->CreateEventObserverManager(
-        FatalCrashEventsObserver::Create(), telemetry_report_queue_.get(),
+        FatalCrashEventsObserver::Create(), event_report_queue_.get(),
         &reporting_settings_, ash::kReportDeviceCrashReportInfo,
         metrics::kReportDeviceCrashReportInfoDefaultValue,
         /*collector_pool=*/this));
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.cc
index fc4521b..13e8025 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.cc
@@ -12,6 +12,8 @@
 #include <type_traits>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
+#include "ash/controls/contextual_tooltip.h"
 #include "ash/public/cpp/image_util.h"
 #include "ash/public/cpp/tablet_mode.h"
 #include "ash/public/cpp/wallpaper/google_photos_wallpaper_params.h"
@@ -22,6 +24,7 @@
 #include "ash/public/cpp/wallpaper/wallpaper_types.h"
 #include "ash/public/cpp/window_backdrop.h"
 #include "ash/wallpaper/wallpaper_constants.h"
+#include "ash/wallpaper/wallpaper_utils/wallpaper_online_variant_utils.h"
 #include "ash/wallpaper/wallpaper_utils/wallpaper_resizer.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
 #include "ash/webui/personalization_app/mojom/personalization_app_mojom_traits.h"
@@ -490,6 +493,15 @@
   DCHECK(client);
   client->RecordWallpaperSourceUMA(ash::WallpaperType::kOnline);
 
+  if (IsTimeOfDayWallpaper(collection_id) &&
+      features::IsTimeOfDayWallpaperForcedAutoScheduleEnabled()) {
+    // Records the display count of the time of day wallpaper dialog when the
+    // user selects one to determine whether to show it the next time.
+    contextual_tooltip::HandleGesturePerformed(
+        profile_->GetPrefs(),
+        contextual_tooltip::TooltipType::kTimeOfDayWallpaperDialog);
+  }
+
   wallpaper_controller->SetOnlineWallpaper(
       ash::OnlineWallpaperParams(
           GetAccountId(profile_), collection_id,
@@ -763,6 +775,17 @@
   SetMinimizedWindowStateForPreview(/*preview_mode=*/false);
 }
 
+void PersonalizationAppWallpaperProviderImpl::
+    ShouldShowTimeOfDayWallpaperDialog(
+        ShouldShowTimeOfDayWallpaperDialogCallback callback) {
+  std::move(callback).Run(
+      features::IsTimeOfDayWallpaperForcedAutoScheduleEnabled() &&
+      contextual_tooltip::ShouldShowNudge(
+          profile_->GetPrefs(),
+          contextual_tooltip::TooltipType::kTimeOfDayWallpaperDialog,
+          /*recheck_delay=*/nullptr));
+}
+
 wallpaper_handlers::GooglePhotosAlbumsFetcher*
 PersonalizationAppWallpaperProviderImpl::
     GetOrCreateGooglePhotosAlbumsFetcher() {
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.h b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.h
index 72ffee0..86eb1cc 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.h
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl.h
@@ -187,6 +187,9 @@
 
   void CancelPreviewWallpaper() override;
 
+  void ShouldShowTimeOfDayWallpaperDialog(
+      ShouldShowTimeOfDayWallpaperDialogCallback callback) override;
+
   wallpaper_handlers::GooglePhotosAlbumsFetcher*
   GetOrCreateGooglePhotosAlbumsFetcher();
 
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
index 6daa67f..20ba0d25 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_wallpaper_provider_impl_unittest.cc
@@ -9,10 +9,12 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/wallpaper/online_wallpaper_params.h"
 #include "ash/public/cpp/wallpaper/online_wallpaper_variant.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller_client.h"
 #include "ash/public/cpp/wallpaper/wallpaper_info.h"
+#include "ash/wallpaper/wallpaper_constants.h"
 #include "ash/wallpaper/wallpaper_pref_manager.h"
 #include "ash/webui/personalization_app/mojom/personalization_app.mojom.h"
 #include "base/functional/callback_helpers.h"
@@ -80,7 +82,6 @@
   ash::FakeChromeUserManager* user_manager =
       static_cast<ash::FakeChromeUserManager*>(
           user_manager::UserManager::Get());
-
   user_manager->AddUser(account_id);
   user_manager->LoginUser(account_id);
   user_manager->SwitchActiveUser(account_id);
@@ -442,6 +443,52 @@
             bad_message_observer.WaitForBadMessage());
 }
 
+TEST_F(PersonalizationAppWallpaperProviderImplTest,
+       ShouldShowTimeOfDayWallpaperDialog) {
+  test_wallpaper_controller()->ClearCounts();
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures({features::kFeatureManagementTimeOfDayWallpaper,
+                             features::kTimeOfDayWallpaperForcedAutoSchedule},
+                            {});
+
+  auto image_info = GetDefaultImageInfo();
+  image_info.collection_id =
+      wallpaper_constants::kTimeOfDayWallpaperCollectionId;
+  std::vector<ash::OnlineWallpaperVariant> variants;
+  variants.emplace_back(image_info.asset_id, image_info.image_url,
+                        backdrop::Image::IMAGE_TYPE_UNKNOWN);
+
+  AddWallpaperImage(image_info);
+
+  base::test::TestFuture<bool> should_show_dialog_future;
+  wallpaper_provider_remote()->ShouldShowTimeOfDayWallpaperDialog(
+      should_show_dialog_future.GetCallback());
+  // Expects to return true before time of day wallpaper is set.
+  EXPECT_TRUE(should_show_dialog_future.Take());
+
+  base::test::TestFuture<bool> success_future;
+  wallpaper_provider_remote()->SelectWallpaper(image_info.asset_id,
+                                               /*preview_mode=*/false,
+                                               success_future.GetCallback());
+  EXPECT_TRUE(success_future.Take());
+
+  EXPECT_EQ(1, test_wallpaper_controller()->set_online_wallpaper_count());
+  EXPECT_TRUE(
+      test_wallpaper_controller()->wallpaper_info().value().MatchesSelection(
+          ash::WallpaperInfo(
+              {GetTestAccountId(),
+               wallpaper_constants::kTimeOfDayWallpaperCollectionId,
+               ash::WallpaperLayout::WALLPAPER_LAYOUT_CENTER_CROPPED,
+               /*preview_mode=*/false, /*from_user=*/true,
+               /*daily_refresh_enabled=*/false, image_info.unit_id, variants},
+              variants.front())));
+
+  wallpaper_provider_remote()->ShouldShowTimeOfDayWallpaperDialog(
+      should_show_dialog_future.GetCallback());
+  // Expects to return false after time of day wallpaper is set.
+  EXPECT_FALSE(should_show_dialog_future.Take());
+}
+
 class PersonalizationAppWallpaperProviderImplGooglePhotosTest
     : public PersonalizationAppWallpaperProviderImplTest {
  protected:
diff --git a/chrome/browser/bad_message.h b/chrome/browser/bad_message.h
index bebb099..6739aea 100644
--- a/chrome/browser/bad_message.h
+++ b/chrome/browser/bad_message.h
@@ -31,6 +31,7 @@
   PVM_SCRIPTED_PRINT_FENCED_FRAME = 7,
   PVMB_SCRIPTED_PRINT_FENCED_FRAME = 8,
   SSI_CREATE_FENCED_FRAME = 9,
+  CCU_SUPERFLUOUS_BIND = 10,
 
   // Please add new elements here. The naming convention is abbreviated class
   // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index a5057b9..48b59a3d 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -533,6 +533,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/search/new_tab_page_navigation_throttle.h"
+#include "chrome/browser/ui/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h"
 #include "chrome/browser/ui/web_applications/tabbed_web_app_navigation_throttle.h"
 #include "chrome/browser/ui/web_applications/webui_web_app_navigation_throttle.h"
 #include "chrome/browser/ui/webui/chrome_content_browser_client_webui_part.h"
@@ -5321,6 +5322,13 @@
   }
 #endif
 
+#if !BUILDFLAG(IS_ANDROID)
+  if (features::IsReadAnythingEnabled()) {
+    MaybeAddThrottle(ReadAnythingSidePanelNavigationThrottle::CreateFor(handle),
+                     &throttles);
+  }
+#endif
+
 #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
   MaybeAddThrottle(
       offline_pages::OfflinePageNavigationThrottle::MaybeCreateThrottleFor(
diff --git a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
index b1bebac5..3e746fb 100644
--- a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
+++ b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
@@ -1257,8 +1257,8 @@
       "text-object.txt: ae52dd09-9746-4b7e-86a6-6ada5e2680c2");
 }
 
-// The trust-token-redemption Permissions Policy feature, which is enabled by
-// default, is required in order to execute a Trust Tokens
+// The private-state-token-redemption Permissions Policy feature, which is
+// enabled by default, is required in order to execute a Trust Tokens
 // (https://github.com/wicg/trust-token-api) redemption operation alongside a
 // subresource request. To enforce this requirement, the browser binds the
 // feature's value to a frame's subresource loader.
diff --git a/chrome/browser/extensions/extension_untrusted_webui_apitest.cc b/chrome/browser/extensions/extension_untrusted_webui_apitest.cc
index 52b30eff..74e1482 100644
--- a/chrome/browser/extensions/extension_untrusted_webui_apitest.cc
+++ b/chrome/browser/extensions/extension_untrusted_webui_apitest.cc
@@ -7,9 +7,9 @@
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/threading/thread_restrictions.h"
-#include "build/build_config.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/side_panel/side_panel_ui.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -91,17 +91,27 @@
       script = "(function(){'use strict';" + script + "}());";
     }
 
-    // Run the test.
+    // Run the test. Navigating to the URL will trigger the read anything
+    // navigation throttle and open the side panel instead of loading read
+    // anything in the main content area.
     EXPECT_TRUE(ui_test_utils::NavigateToURL(
-        browser(),
-        GURL("chrome-untrusted://read-anything-side-panel.top-chrome/")));
-    content::RenderFrameHost* webui = browser()
-                                          ->tab_strip_model()
-                                          ->GetActiveWebContents()
-                                          ->GetPrimaryMainFrame();
-    bool result = content::EvalJs(webui, script).ExtractBool();
-    return (result) ? testing::AssertionSuccess()
-                    : (testing::AssertionFailure() << "Check console output");
+        browser(), GURL(chrome::kChromeUIUntrustedReadAnythingSidePanelURL)));
+    // Get the side panel entry registry.
+    auto* side_panel_ui = SidePanelUI::GetSidePanelUIForBrowser(browser());
+    auto* side_panel_web_contents =
+        side_panel_ui->GetWebContentsForTest(SidePanelEntryId::kReadAnything);
+
+    if (!side_panel_web_contents) {
+      return testing::AssertionFailure() << "Failed to navigate to WebUI";
+    }
+    // Wait for the view to load before trying to run the test. This ensures
+    // that chrome.readingMode is set.
+    content::WaitForLoadStop(side_panel_web_contents);
+    // Eval the JS test.
+    bool result =
+        content::EvalJs(side_panel_web_contents, script).ExtractBool();
+    return result ? testing::AssertionSuccess()
+                  : (testing::AssertionFailure() << "Check console output");
   }
 
  private:
diff --git a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java
index 022698f..ca51c75 100644
--- a/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java
+++ b/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/FeedFeatures.java
@@ -36,9 +36,7 @@
      * change.
      */
     public static boolean isFeedEnabled() {
-        return FeedServiceBridge.isEnabled()
-                && (!ChromeFeatureList.sNewTabSearchEngineUrlAndroid.isEnabled()
-                        || getPrefService().getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE));
+        return FeedServiceBridge.isEnabled() && isFeedEnabledByDSE();
     }
 
     /**
@@ -49,10 +47,16 @@
         // TODO(b/197354832, b/188188861): change consent check to SIGNIN.
         return ChromeFeatureList.isEnabled(ChromeFeatureList.WEB_FEED)
                 && IdentityServicesProvider.get()
-                           .getSigninManager(Profile.getLastUsedRegularProfile())
-                           .getIdentityManager()
-                           .hasPrimaryAccount(ConsentLevel.SIGNIN)
-                && !Profile.getLastUsedRegularProfile().isChild();
+                        .getSigninManager(Profile.getLastUsedRegularProfile())
+                        .getIdentityManager()
+                        .hasPrimaryAccount(ConsentLevel.SIGNIN)
+                && !Profile.getLastUsedRegularProfile().isChild()
+                && isFeedEnabledByDSE();
+    }
+
+    private static boolean isFeedEnabledByDSE() {
+        return !ChromeFeatureList.sNewTabSearchEngineUrlAndroid.isEnabled()
+                || getPrefService().getBoolean(Pref.ENABLE_SNIPPETS_BY_DSE);
     }
 
     public static boolean shouldUseWebFeedAwarenessIPH() {
diff --git a/chrome/browser/metrics/jumplist_metrics_win.cc b/chrome/browser/metrics/jumplist_metrics_win.cc
deleted file mode 100644
index 5202ea33..0000000
--- a/chrome/browser/metrics/jumplist_metrics_win.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2014 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/metrics/jumplist_metrics_win.h"
-
-#include "base/metrics/histogram_macros.h"
-
-namespace jumplist {
-
-const char kMostVisitedCategory[] = "most-visited";
-const char kRecentlyClosedCategory[] = "recently-closed";
-
-void LogJumplistActionFromSwitchValue(const std::string& value) {
-  JumplistCategory metric = CATEGORY_UNKNOWN;
-  if (value == kMostVisitedCategory)
-    metric = MOST_VISITED_URL;
-  else if (value == kRecentlyClosedCategory)
-    metric = RECENTLY_CLOSED_URL;
-  DCHECK_NE(metric, CATEGORY_UNKNOWN);
-
-  UMA_HISTOGRAM_ENUMERATION(
-      "WinJumplist.Action", metric, NUM_JUMPLIST_CATEGORY_METRICS);
-}
-
-}  // namespace jumplist
diff --git a/chrome/browser/metrics/jumplist_metrics_win.h b/chrome/browser/metrics/jumplist_metrics_win.h
deleted file mode 100644
index 27f8c5f..0000000
--- a/chrome/browser/metrics/jumplist_metrics_win.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 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_METRICS_JUMPLIST_METRICS_WIN_H_
-#define CHROME_BROWSER_METRICS_JUMPLIST_METRICS_WIN_H_
-
-#include <string>
-
-namespace jumplist {
-
-// Enum for counting which category was clicked.
-// Note: UMA histogram enum - don't re-order or remove entries
-enum JumplistCategory {
-  RECENTLY_CLOSED_URL = 0,  // A URL from the "Recently Closed" category.
-  MOST_VISITED_URL,         // A URL from the "Most Visited" category.
-  SWITCH_TO_PROFILE,        // A profile name from the "People" category.
-  CATEGORY_UNKNOWN,         // An invalid category.
-  NUM_JUMPLIST_CATEGORY_METRICS
-};
-
-// Category types that can be logged with the --win-jumplist-action switch.
-extern const char kMostVisitedCategory[];
-extern const char kRecentlyClosedCategory[];
-
-// Logs a histogram for the JumplistCategory of the item that was clicked.
-void LogJumplistActionFromSwitchValue(const std::string& value);
-
-}  // namespace jumplist
-
-#endif  // CHROME_BROWSER_METRICS_JUMPLIST_METRICS_WIN_H_
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.cc b/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.cc
index caf0483..500516e 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.cc
+++ b/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.cc
@@ -23,6 +23,13 @@
   return instance.get();
 }
 
+std::u16string NearbyShareResourceGetter::GetFeatureName() {
+  // Caller ensures feature flag is enabled.
+  CHECK(features::IsNameEnabled());
+
+  return GetNearbyShareFeatureName();
+}
+
 std::u16string NearbyShareResourceGetter::GetStringWithFeatureName(
     int message_id) {
   // Caller ensures feature flag is enabled.
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.h b/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.h
index 3d459bc..9b3d822 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.h
+++ b/chrome/browser/nearby_sharing/common/nearby_share_resource_getter.h
@@ -18,6 +18,8 @@
   NearbyShareResourceGetter& operator=(const NearbyShareResourceGetter&) =
       delete;
 
+  std::u16string GetFeatureName();
+
   // Assumes that caller is passing a |message_id| with a placeholder for
   // the feature name at index 0 in the placeholder list.
   std::u16string GetStringWithFeatureName(int message_id);
diff --git a/chrome/browser/nearby_sharing/common/nearby_share_resource_getter_unittest.cc b/chrome/browser/nearby_sharing/common/nearby_share_resource_getter_unittest.cc
index dfcf5df..237aee1 100644
--- a/chrome/browser/nearby_sharing/common/nearby_share_resource_getter_unittest.cc
+++ b/chrome/browser/nearby_sharing/common/nearby_share_resource_getter_unittest.cc
@@ -40,3 +40,20 @@
             u"Nearby Share");
 }
 #endif  // !BUILDFLAG(GOOGLE_CHROME_BRANDING)
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+TEST_F(NearbyShareResourceGetterTest, GetFeatureNameOfficialBuild) {
+  base::test::ScopedFeatureList feature_list{features::kIsNameEnabled};
+
+  // Just enforce non empty string for official branded builds..
+  EXPECT_NE(NearbyShareResourceGetter::GetInstance()->GetFeatureName(), u"");
+}
+#else   // !BUILDFLAG(GOOGLE_CHROME_BRANDING)
+TEST_F(NearbyShareResourceGetterTest, GetFeatureNameWorksUnofficialBuild) {
+  base::test::ScopedFeatureList feature_list{features::kIsNameEnabled};
+
+  // Expect the feature name to be inserted into the string.
+  EXPECT_EQ(NearbyShareResourceGetter::GetInstance()->GetFeatureName(),
+            u"Nearby Share");
+}
+#endif  // !BUILDFLAG(GOOGLE_CHROME_BRANDING)
diff --git a/chrome/browser/net/cookie_policy_browsertest.cc b/chrome/browser/net/cookie_policy_browsertest.cc
index db779c5..940f70b 100644
--- a/chrome/browser/net/cookie_policy_browsertest.cc
+++ b/chrome/browser/net/cookie_policy_browsertest.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 "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/path_service.h"
@@ -170,7 +171,8 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-class CookiePolicyBrowser3pcAllowedTest : public CookiePolicyBrowserTest {
+// For test cases that only cover pre-3PCD logic.
+class CookiePolicyPre3pcdBrowserTest : public CookiePolicyBrowserTest {
  protected:
   std::vector<base::test::FeatureRef> DisabledFeatures() override {
     return {content_settings::features::kTrackingProtection3pcd};
@@ -178,8 +180,7 @@
 };
 
 // Visits a page that sets a first-party cookie.
-IN_PROC_BROWSER_TEST_F(CookiePolicyBrowser3pcAllowedTest,
-                       AllowFirstPartyCookies) {
+IN_PROC_BROWSER_TEST_F(CookiePolicyPre3pcdBrowserTest, AllowFirstPartyCookies) {
   SetBlockThirdPartyCookies(false);
 
   GURL url(https_server_.GetURL(kHostA, "/set-cookie?cookie1"));
@@ -216,7 +217,7 @@
 }
 
 // Third-Party Frame Tests
-IN_PROC_BROWSER_TEST_F(CookiePolicyBrowser3pcAllowedTest,
+IN_PROC_BROWSER_TEST_F(CookiePolicyPre3pcdBrowserTest,
                        ThirdPartyCookiesIFrameAllowSetting) {
   SetBlockThirdPartyCookies(false);
 
@@ -283,7 +284,7 @@
   EXPECT_EQ(content::GetCookies(browser()->profile(), GetURL(kHostB)), "");
 }
 
-IN_PROC_BROWSER_TEST_F(CookiePolicyBrowser3pcAllowedTest,
+IN_PROC_BROWSER_TEST_F(CookiePolicyPre3pcdBrowserTest,
                        ThirdPartyCookiesIFrameAllowReading) {
   SetBlockThirdPartyCookies(false);
 
@@ -585,7 +586,8 @@
     switch (ContextType()) {
       case ContextType::kFrame:
         storage::test::ExpectStorageForFrame(frame, expected_storage);
-        EXPECT_EQ(expected_cookie, content::EvalJs(frame, "hasCookie()"));
+        EXPECT_EQ(expected_cookie && !Is3pcd(),
+                  content::EvalJs(frame, "hasCookie()"));
         return;
       case ContextType::kWorker:
         storage::test::ExpectStorageForWorker(frame, expected_storage);
@@ -596,7 +598,7 @@
   void SetStorage(content::RenderFrameHost* frame) {
     switch (ContextType()) {
       case ContextType::kFrame:
-        storage::test::SetStorageForFrame(frame, /*include_cookies=*/true);
+        storage::test::SetStorageForFrame(frame, /*include_cookies=*/!Is3pcd());
         return;
       case ContextType::kWorker:
         storage::test::SetStorageForWorker(frame);
@@ -604,6 +606,11 @@
     }
   }
 
+  bool Is3pcd() {
+    return base::FeatureList::IsEnabled(
+        content_settings::features::kTrackingProtection3pcd);
+  }
+
   ContextType ContextType() const { return GetParam(); }
 };
 
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 188e2fb0..24bebc8 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -963,6 +963,11 @@
 constexpr char kWebRTCAllowLegacyTLSProtocols[] =
     "webrtc.allow_legacy_tls_protocols";
 
+// Deprecated 11/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+constexpr char kSystemTrayExpanded[] = "ash.system_tray.expanded";
+#endif
+
 // Register local state used only for migration (clearing or moving to a new
 // key).
 void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) {
@@ -1360,6 +1365,11 @@
 
   // Deprecated 11/2023.
   registry->RegisterBooleanPref(kWebRTCAllowLegacyTLSProtocols, false);
+
+// Deprecated 11/2023.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  registry->RegisterBooleanPref(kSystemTrayExpanded, true);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 void ClearSyncRequestedPrefAndMaybeMigrate(PrefService* profile_prefs) {
@@ -2550,6 +2560,11 @@
   // Added 11/2023.
   profile_prefs->ClearPref(kWebRTCAllowLegacyTLSProtocols);
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Added 11/2023.
+  profile_prefs->ClearPref(kSystemTrayExpanded);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // Please don't delete the following line. It is used by PRESUBMIT.py.
   // END_MIGRATE_OBSOLETE_PROFILE_PREFS
 
diff --git a/chrome/browser/resources/ash/settings/internet_page/apn_subpage.html b/chrome/browser/resources/ash/settings/internet_page/apn_subpage.html
index ab38a6f1..2d55b120 100644
--- a/chrome/browser/resources/ash/settings/internet_page/apn_subpage.html
+++ b/chrome/browser/resources/ash/settings/internet_page/apn_subpage.html
@@ -4,5 +4,6 @@
     id="apnList"
     managed-cellular-properties="[[managedProperties_.typeProperties.cellular]]"
     guid="[[guid_]]"
-    error-state="[[managedProperties_.errorState]]">
+    error-state="[[managedProperties_.errorState]]"
+    portal-state="[[managedProperties_.portalState]]">
 </apn-list>
\ No newline at end of file
diff --git a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html
index 0118432..454050d 100644
--- a/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html
+++ b/chrome/browser/resources/ash/settings/internet_page/internet_detail_subpage.html
@@ -64,7 +64,7 @@
     margin-inline-start: var(--cr-section-padding);
   }
 
-  cr-link-row {
+  cr-link-row:not([warning]) {
     --cr-secondary-text-color: var(--cros-text-color-positive);
   }
 
@@ -311,7 +311,8 @@
           label="$i18n{internetApnPageTitle}"
           sub-label="[[getApnRowSubLabel_(managedProperties_)]]"
           on-click="onApnRowClicked_"
-          role-description="$i18n{subpageArrowRoleDescription}">
+          role-description="$i18n{subpageArrowRoleDescription}"
+          warning$="[[showRestrictedConnectivity_(managedProperties_)]]">
       </cr-link-row>
     </template>
   </template>
diff --git a/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts b/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts
index 8346c928..e3d7c5d4 100644
--- a/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts
+++ b/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts
@@ -408,7 +408,9 @@
 
   private getAccountRowLabel(profileName: string, profileLabel: string):
       string {
-    return this.i18n('nearbyShareAccountRowLabel', profileName, profileLabel);
+    return this.i18n(
+        'nearbyShareAccountRowLabel', this.i18n('nearbyShareFeatureName'),
+        profileName, profileLabel);
   }
 
   private getEnabledToggleClassName_(): string {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path.js
index a7c0c263..ea7bc94 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path.js
@@ -222,7 +222,10 @@
  */
 ForcedActionPath.ActionInfo;
 
-// Represents an expected action.
+/**
+ * Represents an expected action.
+ * @abstract
+ */
 ForcedActionPath.Action = class {
   /**
    * Please see below for more information on arguments:
@@ -239,12 +242,13 @@
    *  beforeActionCallback: (function(): void|undefined),
    *  afterActionCallback: (function(): void|undefined)
    * }} params
+   * @protected
    */
   constructor(params) {
     /** @type {ActionType} */
     this.type = params.type;
     /** @type {string|!KeySequence} */
-    this.value = params.value;
+    this.value = this.typedValue(params.value);
     /** @type {boolean} */
     this.shouldPropagate =
         (params.shouldPropagate !== undefined) ? params.shouldPropagate : true;
@@ -321,13 +325,20 @@
       }
     };
 
-    return new ForcedActionPath.Action({
+    const params = {
       type,
       value,
       shouldPropagate,
       beforeActionCallback,
       afterActionCallback,
-    });
+    };
+
+    switch (type) {
+      case ActionType.KEY_SEQUENCE:
+        return new ForcedActionPath.KeySequenceAction(params);
+      default:
+        return new ForcedActionPath.StringAction(params);
+    }
   }
 
   /**
@@ -335,18 +346,16 @@
    * @return {boolean}
    */
   equals(other) {
-    if (this.type !== other.type) {
-      return false;
-    }
-
-    if (this.type === ActionType.KEY_SEQUENCE) {
-      // For KeySequences, use the built-in equals method.
-      return this.value.equals(/** @type {!KeySequence} */ (other.value));
-    }
-
-    return this.value === other.value;
+    return this.type === other.type;
   }
 
+  /**
+   * @param {string|Object} value
+   * @return {string|!KeySequence}
+   * @abstract
+   */
+  typedValue(value) {}
+
   // Static methods.
 
   /**
@@ -368,6 +377,40 @@
   }
 };
 
+ForcedActionPath.KeySequenceAction = class extends ForcedActionPath.Action {
+  /** @override */
+  equals(other) {
+    return super.equals(other) &&
+        this.value.equals(/**@type {!KeySequence} */ (other.value));
+  }
+
+  /** @override */
+  typedValue(value) {
+    if (!(value instanceof KeySequence)) {
+      throw new Error(
+          'ForcedActionPath: Must provide a KeySequence value for ' +
+          'Actions of type ActionType.KEY_SEQUENCE');
+    }
+    return /** @type {!KeySequence} */ (value);
+  }
+};
+
+ForcedActionPath.StringAction = class extends ForcedActionPath.Action {
+  /** @override */
+  equals(other) {
+    return super.equals(other) && this.value === other.value;
+  }
+
+  /** @override */
+  typedValue(value) {
+    if (typeof value !== 'string') {
+      throw new Error(`ForcedActionPath: Must provide string value for ${
+          this.type} actions`);
+    }
+    return String(value);
+  }
+};
+
 /** @type {ForcedActionPath} */
 ForcedActionPath.instance;
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path_test.js
index ce7c4191..eb4361c 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path_test.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/forced_action_path_test.js
@@ -89,14 +89,14 @@
   await this.runWithLoadedTree(this.simpleDoc);
   const keySequenceActionOne = ForcedActionPath.Action.fromActionInfo(
       {type: 'key_sequence', value: {keys: {keyCode: [KeyCode.SPACE]}}});
-  const keySequenceActionTwo = new ForcedActionPath.Action({
+  const keySequenceActionTwo = ForcedActionPath.Action.fromActionInfo({
     type: 'key_sequence',
     value: new KeySequence(TestUtils.createMockKeyEvent(KeyCode.A)),
   });
   const gestureActionOne = ForcedActionPath.Action.fromActionInfo(
       {type: 'gesture', value: Gesture.SWIPE_UP1});
-  const gestureActionTwo =
-      new ForcedActionPath.Action({type: 'gesture', value: Gesture.SWIPE_UP2});
+  const gestureActionTwo = ForcedActionPath.Action.fromActionInfo(
+      {type: 'gesture', value: Gesture.SWIPE_UP2});
 
   assertFalse(keySequenceActionOne.equals(keySequenceActionTwo));
   assertFalse(keySequenceActionOne.equals(gestureActionOne));
@@ -107,8 +107,8 @@
 
   const cloneKeySequenceActionOne = ForcedActionPath.Action.fromActionInfo(
       {type: 'key_sequence', value: {keys: {keyCode: [KeyCode.SPACE]}}});
-  const cloneGestureActionOne =
-      new ForcedActionPath.Action({type: 'gesture', value: Gesture.SWIPE_UP1});
+  const cloneGestureActionOne = ForcedActionPath.Action.fromActionInfo(
+      {type: 'gesture', value: Gesture.SWIPE_UP1});
   assertTrue(keySequenceActionOne.equals(cloneKeySequenceActionOne));
   assertTrue(gestureActionOne.equals(cloneGestureActionOne));
 });
@@ -134,19 +134,17 @@
     monitor = new ForcedActionPath([], onFinished);
     assertTrue(false);  // Shouldn't execute.
   } catch (error) {
-    assertEquals(
-        `ForcedActionPath: actionInfos can't be empty`, error.message);
+    assertTrue(/actionInfos can't be empty/.test(error.message));
     caught = true;
   }
   assertCaughtAndReset();
   try {
-    new ForcedActionPath.Action({type: 'key_sequence', value: 'invalid'});
+    ForcedActionPath.Action.fromActionInfo(
+        {type: 'key_sequence', value: 'invalid'});
     assertTrue(false);  // Shouldn't execute
   } catch (error) {
-    assertEquals(
-        'ForcedActionPath: Must provide a KeySequence value for Actions ' +
-            'of type ActionType.KEY_SEQUENCE',
-        error.message);
+    assertTrue(/Must provide.*KeySequence.*for.*ActionType.KEY_SEQUENCE/.test(
+        error.message));
     caught = true;
   }
   assertCaughtAndReset();
diff --git a/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.html b/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.html
index 18bed9c..7271a7d 100644
--- a/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.html
+++ b/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.html
@@ -97,7 +97,7 @@
   <span class="prefix">History: [[loggingStateToString(loggingState)]]</span>
   <cr-toggle checked="{{loggingState}}" on-change="onToggleChange"></cr-toggle>
   <span class="prefix"> Filter by: </span>
-  <select class="select-filter" value="{{selectedOption::change}}">
+  <select id="erpTableFilter" class="select-filter" value="[[selectedOption]]" on-change="onFilterChange">
     <template is="dom-repeat" items="[[filterOptions]]">
       <option value="[[item]]">[[item]]</option>
     </template>
diff --git a/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.ts b/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.ts
index 72d0d3c..dec907e 100644
--- a/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.ts
+++ b/chrome/browser/resources/chromeos/enterprise_reporting/reporting_history.ts
@@ -20,6 +20,7 @@
 export interface ReportingHistoryElement {
   $: {
     body: HTMLDivElement,
+    erpTableFilter: HTMLSelectElement,
   };
 }
 
@@ -40,6 +41,7 @@
     'Upload',
   ];
   private selectedOption: string = ReportingHistoryElement.allEvents;
+  private currentHistory: ErpHistoryData;
 
   static get is() {
     return 'reporting-history-element' as const;
@@ -78,6 +80,14 @@
     this.browserProxy.handler.recordDebugState(event.detail);
   }
 
+  onFilterChange() {
+    const currentSelection: string = this.$.erpTableFilter.value;
+    if (this.selectedOption != currentSelection) {
+      this.selectedOption = currentSelection;
+      this.updateErpTable();
+    }
+  }
+
   onDownloadButtonClick(): void {
     // Select the table and traverse through it.
     const tableRows = this.$.body.querySelectorAll('.erp-history-table tr');
@@ -121,8 +131,9 @@
     // Add a listener for the asynchronous 'setErpHistoryData' event
     // to be invoked by page handler and populate the table.
     this.browserProxy.callbackRouter.setErpHistoryData.addListener(
-        (history: ErpHistoryData) => {
-          this.updateErpTable(history);
+        (historyData: ErpHistoryData) => {
+          this.currentHistory = historyData;
+          this.updateErpTable();
         });
   }
 
@@ -138,7 +149,8 @@
     // Populate history upon page refresh.
     this.browserProxy.handler.getErpHistoryData().then(
         ({historyData}: {historyData: ErpHistoryData}) => {
-          this.updateErpTable(historyData);
+          this.currentHistory = historyData;
+          this.updateErpTable();
         });
   }
 
@@ -155,18 +167,17 @@
   }
 
   // Fills the passed table element with the given history.
-  private updateErpTable(history: ErpHistoryData) {
+  private updateErpTable() {
     // Reset table.
     this.$.body.replaceChildren();
 
     // If there are no events, present a placeholder.
-    if (history.events.length === 0) {
+    if (this.currentHistory.events.length === 0) {
       this.setEmptyErpTable();
       return;
     }
-
     // If there are events we filter them by the type of event.
-    const filteredEvents = history.events.filter(
+    const filteredEvents = this.currentHistory.events.filter(
         (event: ErpHistoryEvent) => event.call == this.selectedOption ||
             this.selectedOption == ReportingHistoryElement.allEvents ||
             (this.selectedOption == ReportingHistoryElement.allButUploads &&
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
index 1dc1dc9b..450b986 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.html
@@ -37,7 +37,7 @@
     height: var(--cr-section-two-line-min-height);
   }
 
-  #apnRowSublabel {
+  #apnRowSublabel:not([warning]) {
     color: var(--cros-text-color-positive);
   }
 
@@ -141,7 +141,9 @@
     <cr-expand-button id="apnRow" class="settings-box cr-row"
         expanded="{{apnExpanded_}}">
       <div id="apnRowTitle">$i18n{internetApnPageTitle}</div>
-      <div id="apnRowSublabel" class="cr-secondary-text">
+      <div id="apnRowSublabel"
+          class="cr-secondary-text"
+          warning$="[[showRestrictedConnectivity_(managedProperties_)]]">
         [[getApnRowSublabel_(managedProperties_, apnExpanded_)]]
       </div>
     </cr-expand-button>
@@ -151,6 +153,7 @@
           managed-cellular-properties="[[managedProperties_.typeProperties.cellular]]"
           guid="[[guid]]"
           error-state="[[managedProperties_.errorState]]"
+          portal-state="[[managedProperties_.portalState]]"
           should-omit-links>
       </apn-list>
       <div id="apnButtonTitle">
diff --git a/chrome/browser/resources/chromeos/login/screens/osauth/local_password_setup.html b/chrome/browser/resources/chromeos/login/screens/osauth/local_password_setup.html
index 6e80b1e..824f0d9 100644
--- a/chrome/browser/resources/chromeos/login/screens/osauth/local_password_setup.html
+++ b/chrome/browser/resources/chromeos/login/screens/osauth/local_password_setup.html
@@ -18,7 +18,9 @@
     [[titleText_(locale, isRecoveryFlow)]]
   </h1>
   <div slot="content" class="landscape-vertical-centered">
-    <set-local-password-input id="passwordInput" on-submit="onSubmit_">
+    <set-local-password-input id="passwordInput" class="focus-on-show"
+        first-input-aria-label="[[titleText_(locale, isRecoveryFow)]]"
+        on-submit="onSubmit_">
     </set-local-password-input>
   </div>
   <div slot="back-navigation">
diff --git a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts
index 9e2a6c05..f3181a0 100644
--- a/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts
+++ b/chrome/browser/resources/nearby_share/shared/nearby_contact_visibility.ts
@@ -639,7 +639,8 @@
         this.numUnreachable_)
         .then((labelTemplate) => {
           this.numUnreachableMessage_ = loadTimeData.substituteString(
-              labelTemplate, this.numUnreachable_);
+              labelTemplate, this.numUnreachable_,
+              this.i18n('nearbyShareFeatureName'));
         });
   }
 
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html b/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html
index b0cf096..12705ea 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html
+++ b/chrome/browser/resources/new_tab_page/modules/v2/history_clusters/header_tile.html
@@ -41,6 +41,12 @@
     width: 100%;
   }
 
+  ntp-module-header-v2 cr-icon-button {
+    --cr-icon-button-fill-color: var(--color-new-tab-page-primary-foreground);
+    --cr-icon-button-hover-background-color:
+        var(--color-new-tab-page-control-background-hovered);
+  }
+
   ntp-module-header-v2 > cr-icon-button {
     margin: 0;
   }
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
index 0f55093..f4cacc6 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -9,8 +9,7 @@
         display: none;
       }
     </style>
-    <template is="dom-if" if="[[showBasicPage_(
-        currentRoute_, inSearchMode, hasExpandedSection_)]]">
+    <template is="dom-if" if="[[showBasicPage_(currentRoute_, inSearchMode)]]">
       <div id="basicPage">
         <template is="dom-if" if="[[showResetProfileBanner_]]" restamp>
           <settings-reset-profile-banner on-close="onResetProfileBannerClosed_">
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.ts b/chrome/browser/resources/settings/basic_page/basic_page.ts
index d50c1437..da03850 100644
--- a/chrome/browser/resources/settings/basic_page/basic_page.ts
+++ b/chrome/browser/resources/settings/basic_page/basic_page.ts
@@ -306,9 +306,10 @@
    * @return A signal indicating that searching finished.
    */
   searchContents(query: string): Promise<SearchResult> {
+    const basicPage = this.shadowRoot!.querySelector<HTMLElement>('#basicPage');
+    assert(basicPage);
     const whenSearchDone = [
-      getSearchManager().search(
-          query, this.shadowRoot!.querySelector<HTMLElement>('#basicPage')!),
+      getSearchManager().search(query, basicPage),
     ];
 
     if (this.pageVisibility.advancedSettings !== false) {
@@ -372,13 +373,16 @@
   }
 
   /**
-   * @return Whether to show the basic page, taking into account both routing
-   *     and search state.
+   * @return Whether to show #basicPage. This is an optimization to lazy render
+   *     #basicPage only when a section/subpage within it is being shown, or
+   *     when in search mode.
    */
-  private showBasicPage_(
-      currentRoute: Route, _inSearchMode: boolean,
-      hasExpandedSection: boolean): boolean {
-    return !hasExpandedSection || routes.BASIC.contains(currentRoute);
+  private showBasicPage_(): boolean {
+    if (this.currentRoute_ === undefined) {
+      return false;
+    }
+
+    return this.inSearchMode || routes.BASIC.contains(this.currentRoute_);
   }
 
   /**
diff --git a/chrome/browser/ssl/https_first_mode_settings_tracker.cc b/chrome/browser/ssl/https_first_mode_settings_tracker.cc
index e3aa3b5..bce4483 100644
--- a/chrome/browser/ssl/https_first_mode_settings_tracker.cc
+++ b/chrome/browser/ssl/https_first_mode_settings_tracker.cc
@@ -74,6 +74,22 @@
     &features::kHttpsFirstModeV2ForTypicallySecureUsers,
     "min-total-site-engagement-score", 50};
 
+// Rolling window size in days to count recent navigations. Navigations within
+// this window will be counted to be used for the Typically Secure heuristic.
+// Navigations older than this many days will be discarded from the count.
+const base::FeatureParam<int> kNavigationCounterRollingWindowSizeInDays{
+    &features::kHttpsFirstModeV2ForTypicallySecureUsers,
+    "navigation-counts-rolling-window-size-in-days", 15};
+
+// Minimum number of main frame navigations counted in this profile during a
+// rolling window of kNavigationCounterDefaultRollingWindowSizeInDays for a user
+// to be considered typically secure. If the user doesn't have at least this
+// many navigations counted, they might not have used Chrome sufficiently for us
+// to auto-enable HFM.
+const base::FeatureParam<int> kMinRecentNavigationsForTypicallySecureUser{
+    &features::kHttpsFirstModeV2ForTypicallySecureUsers,
+    "min-recent-navigations", 100};
+
 // The key for the fallback events in the base preference.
 constexpr char kFallbackEventsKey[] = "fallback_events";
 
@@ -87,7 +103,6 @@
 // kFallbackEntriesRollingWindowSize.
 constexpr char kFallbackEventsPrefTimestampKey[] = "timestamp";
 
-constexpr int kNavigationCounterDefaultRollingWindowSizeInDays = 7;
 constexpr int kNavigationCounterDefaultSaveInterval = 10;
 
 namespace {
@@ -277,7 +292,7 @@
       profile_->GetPrefs()->GetDict(prefs::kHttpsUpgradeNavigations).Clone();
   navigation_counter_ = std::make_unique<DailyNavigationCounter>(
       &navigation_counts_dict_, clock_,
-      kNavigationCounterDefaultRollingWindowSizeInDays,
+      kNavigationCounterRollingWindowSizeInDays.Get(),
       kNavigationCounterDefaultSaveInterval);
 
   content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
@@ -445,7 +460,9 @@
       (recent_warning_count <= kMaxRecentFallbackEntryCount) &&
       (engagement_svc->GetTotalEngagementPoints() >=
        kMinTotalEngagementPointsForTypicallySecureUser.Get()) &&
-      (now - latest_fallback_timestamp > base::Days(1));
+      (now - latest_fallback_timestamp > base::Days(1)) &&
+      (static_cast<int>(GetRecentNavigationCount()) >=
+       kMinRecentNavigationsForTypicallySecureUser.Get());
 
   // Update the pref with the new fallback events.
   base::Value::Dict new_base_pref;
diff --git a/chrome/browser/ssl/https_first_mode_settings_tracker_unittest.cc b/chrome/browser/ssl/https_first_mode_settings_tracker_unittest.cc
index 3c5c37e2..5c1769fd 100644
--- a/chrome/browser/ssl/https_first_mode_settings_tracker_unittest.cc
+++ b/chrome/browser/ssl/https_first_mode_settings_tracker_unittest.cc
@@ -517,8 +517,10 @@
 class TypicallySecureUserTest : public HttpsFirstModeSettingsTrackerTest {
  public:
   TypicallySecureUserTest() {
-    feature_list_.InitAndEnableFeature(
-        features::kHttpsFirstModeV2ForTypicallySecureUsers);
+    base::FieldTrialParams feature_params;
+    feature_params["min-recent-navigations"] = "5";
+    feature_list_.InitAndEnableFeatureWithParameters(
+        features::kHttpsFirstModeV2ForTypicallySecureUsers, feature_params);
   }
 
  protected:
@@ -544,6 +546,11 @@
     hfm_service()->RecordHttpsUpgradeFallbackEvent();
     hfm_service()->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstMode();
   }
+  void IncrementRecentNavigationCount(size_t count) {
+    for (size_t i = 0; i < count; i++) {
+      hfm_service()->IncrementRecentNavigationCount();
+    }
+  }
 
   HttpsFirstModeService* hfm_service() { return hfm_service_; }
 
@@ -585,7 +592,6 @@
 // a week before enabling HFM pref.
 TEST_F(TypicallySecureUserTest, EnablePrefWhenObservedForLongEnough) {
   base::Time now = clock()->Now();
-
   EXPECT_FALSE(
       profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
   EXPECT_FALSE(
@@ -623,7 +629,18 @@
   hfm_service()->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstMode();
   EXPECT_EQ(2u, hfm_service()->GetFallbackEntryCountForTesting());
 
-  // Last fallback event is now a day old. HFM should be enabled now.
+  // Last fallback event is now a day old, but we don't have enough recent
+  // navigations. Don't enable yet.
+  EXPECT_FALSE(
+      hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
+  EXPECT_FALSE(
+      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
+  EXPECT_FALSE(
+      profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeAutoEnabled));
+
+  // Do lots of navigations. Should enable HFM now.
+  IncrementRecentNavigationCount(100u);
+  hfm_service()->CheckUserIsTypicallySecureAndMaybeEnableHttpsFirstMode();
   EXPECT_TRUE(
       hfm_service()->IsInterstitialEnabledByTypicallySecureUserHeuristic());
   EXPECT_TRUE(profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
@@ -674,6 +691,8 @@
 // from HTTPS-Upgrade fallbacks in production code. It then checks if the
 // HTTPS-First Mode pref is enabled.
 TEST_F(TypicallySecureUserTest, HFMEnabled) {
+  IncrementRecentNavigationCount(5u);
+
   base::Time now = clock()->Now();
   EXPECT_FALSE(
       profile()->GetPrefs()->HasPrefPath(prefs::kHttpsOnlyModeEnabled));
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc
index ecb86a28..a3f4d55 100644
--- a/chrome/browser/ssl/https_upgrades_browsertest.cc
+++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -1056,6 +1056,11 @@
 
   HttpsFirstModeService* hfm_service =
       HttpsFirstModeServiceFactory::GetForProfile(profile);
+  // Do lots of navigations so that Typically Secure User can kick in.
+  for (size_t i = 0; i < 100; i++) {
+    hfm_service->IncrementRecentNavigationCount();
+  }
+
   hfm_service->SetClockForTesting(&clock);
   // HFM service runs this on startup, but we can't set the test clock before it
   // runs, and we need to move the clock forward for this to work. So call it
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index b35c61dd..3e3f400 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -4751,6 +4751,7 @@
       "side_panel/history_clusters/history_clusters_tab_helper.cc",
       "side_panel/history_clusters/history_clusters_tab_helper.h",
       "side_panel/read_anything/read_anything_side_panel_controller_utils.h",
+      "side_panel/read_anything/read_anything_side_panel_navigation_throttle.h",
       "side_panel/read_anything/read_anything_tab_helper.cc",
       "side_panel/read_anything/read_anything_tab_helper.h",
       "side_panel/side_panel_entry_id.cc",
@@ -4768,6 +4769,8 @@
       "views/send_tab_to_self/send_tab_to_self_bubble_controller.h",
       "views/send_tab_to_self/send_tab_to_self_bubble_view.cc",
       "views/send_tab_to_self/send_tab_to_self_bubble_view.h",
+      "views/side_panel/read_anything/read_anything_side_panel_controller_utils.cc",
+      "views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc",
 
       # This test header is included because it contains forward declarations
       # needed for "friend" statements for use in tests.
@@ -5680,7 +5683,6 @@
       "views/side_panel/read_anything/read_anything_model.h",
       "views/side_panel/read_anything/read_anything_side_panel_controller.cc",
       "views/side_panel/read_anything/read_anything_side_panel_controller.h",
-      "views/side_panel/read_anything/read_anything_side_panel_controller_utils.cc",
       "views/side_panel/read_anything/read_anything_toolbar_view.cc",
       "views/side_panel/read_anything/read_anything_toolbar_view.h",
       "views/side_panel/read_later_side_panel_web_view.cc",
diff --git a/chrome/browser/ui/android/device_lock/java/res/drawable/missing_device_lock_illustration.xml b/chrome/browser/ui/android/device_lock/java/res/drawable/missing_device_lock_illustration.xml
index 18939de4..8a40c55 100644
--- a/chrome/browser/ui/android/device_lock/java/res/drawable/missing_device_lock_illustration.xml
+++ b/chrome/browser/ui/android/device_lock/java/res/drawable/missing_device_lock_illustration.xml
@@ -4,119 +4,112 @@
 Use of this source code is governed by a BSD-style license that can be
 found in the LICENSE file.
 -->
-
-<vector
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:width="462dp"
-    android:height="204dp"
-    android:viewportWidth="462"
-    android:viewportHeight="204"
-    tools:ignore="VectorRaster">
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="200dp"
+    android:height="100dp"
+    android:viewportWidth="200"
+    android:viewportHeight="100">
     <path
-        android:fillColor="@color/signin_header_animation_background"
-        android:pathData="M0,0h462v204h-462z"/>
+        android:pathData="M113.66,19.39H151.71C153.53,19.39 155,20.86 155,22.68C155,24.5 153.53,25.98 151.71,25.98H113.66C111.84,25.98 110.37,24.5 110.37,22.68C110.37,20.86 111.84,19.39 113.66,19.39Z"
+        android:strokeWidth="1.21951"
+        android:fillColor="#00000000"
+        android:strokeColor="@color/device_lock_illustration_background_polygon"/>
     <path
-        android:pathData="M309,10L203,194"
-        android:strokeWidth="2.47059"
+        android:pathData="M122.19,13.17L78.05,89.51"
+        android:strokeWidth="1.46341"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_background_polygon"
         android:strokeLineCap="round"/>
     <path
-        android:pathData="M243.41,49.12C247.21,42.47 256.8,42.47 260.59,49.12L309.05,134.17C312.8,140.75 308.05,148.94 300.46,148.94H203.54C195.95,148.94 191.2,140.75 194.95,134.17L243.41,49.12Z"
+        android:pathData="M95.81,25.62C97.67,22.38 102.33,22.38 104.18,25.62L125.15,62.3C126.98,65.51 124.66,69.51 120.96,69.51H79.04C75.34,69.51 73.02,65.51 74.85,62.3L95.81,25.62Z"
         android:fillColor="@color/device_lock_illustration_yellow"
-        android:fillAlpha="0.7"/>
+        android:fillAlpha="0.8"/>
     <path
-        android:pathData="M122,164.24L236,164.24A8.76,8.76 0,0 1,244.76 173L244.76,173A8.76,8.76 0,0 1,236 181.76L122,181.76A8.76,8.76 0,0 1,113.24 173L113.24,173A8.76,8.76 0,0 1,122 164.24z"
-        android:strokeWidth="2.47059"
+        android:pathData="M36.1,76.94L93.66,76.94A3.3,3.3 0,0 1,96.96 80.24L96.96,80.24A3.3,3.3 0,0 1,93.66 83.54L36.1,83.54A3.3,3.3 0,0 1,32.8 80.24L32.8,80.24A3.3,3.3 0,0 1,36.1 76.94z"
+        android:strokeWidth="1.20517"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_background_polygon"/>
-    <path
-        android:pathData="M282,24.24L344,24.24A9.76,9.76 0,0 1,353.76 34L353.76,34A9.76,9.76 0,0 1,344 43.76L282,43.76A9.76,9.76 0,0 1,272.23 34L272.23,34A9.76,9.76 0,0 1,282 24.24z"
-        android:strokeWidth="2.47059"
-        android:fillColor="#00000000"
-        android:strokeColor="@color/device_lock_illustration_top_polygon_border"/>
     <group>
         <clip-path
-            android:pathData="M311.91,6.88L198.26,203.29H-0V6.88H311.91Z"/>
+            android:pathData="M25.61,2.2H127.81L108.05,36.34H109.76L91.71,67.8H90L72.61,98.01H25.61V2.2Z"/>
         <path
-            android:pathData="M282,23L407,23A11,11 0,0 1,418 34L418,34A11,11 0,0 1,407 45L282,45A11,11 0,0 1,271 34L271,34A11,11 0,0 1,282 23z"
+            android:pathData="M113.66,18.78L151.71,18.78A3.9,3.9 0,0 1,155.61 22.68L155.61,22.68A3.9,3.9 0,0 1,151.71 26.59L113.66,26.59A3.9,3.9 0,0 1,109.76 22.68L109.76,22.68A3.9,3.9 0,0 1,113.66 18.78z"
             android:fillColor="@color/device_lock_illustration_top_polygon_fill"/>
         <path
-            android:pathData="M122,163L236,163A10,10 0,0 1,246 173L246,173A10,10 0,0 1,236 183L122,183A10,10 0,0 1,112 173L112,173A10,10 0,0 1,122 163z"
+            android:pathData="M36.1,76.34L93.66,76.34A3.9,3.9 0,0 1,97.56 80.24L97.56,80.24A3.9,3.9 0,0 1,93.66 84.15L36.1,84.15A3.9,3.9 0,0 1,32.2 80.24L32.2,80.24A3.9,3.9 0,0 1,36.1 76.34z"
             android:fillColor="@color/device_lock_illustration_background_polygon"/>
         <path
-            android:pathData="M243.41,49.12C247.21,42.47 256.8,42.47 260.59,49.12L309.05,134.17C312.8,140.75 308.05,148.94 300.46,148.94H203.54C195.95,148.94 191.2,140.75 194.95,134.17L243.41,49.12Z"
+            android:pathData="M95.81,25.62C97.67,22.38 102.33,22.38 104.19,25.62L125.15,62.3C126.98,65.51 124.66,69.51 120.96,69.51H79.04C75.34,69.51 73.02,65.51 74.85,62.3L95.81,25.62Z"
             android:fillColor="@color/device_lock_illustration_yellow"/>
     </group>
     <path
-        android:pathData="M245.02,50.04C248.1,44.64 255.9,44.64 258.98,50.04L307.44,135.08C310.49,140.44 306.63,147.09 300.46,147.09H203.54C197.38,147.09 193.51,140.44 196.56,135.08L245.02,50.04Z"
-        android:strokeWidth="3.70588"
+        android:pathData="M103.4,26.07L124.36,62.75C125.85,65.36 123.97,68.61 120.96,68.61H79.04C76.03,68.61 74.15,65.36 75.64,62.75L96.6,26.07C98.1,23.43 101.9,23.43 103.4,26.07Z"
+        android:strokeWidth="1.80775"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_yellow"/>
     <path
-        android:pathData="M72.59,97.24L120.15,97.24A12.35,12.35 0,0 1,132.5 109.59L132.5,109.59A12.35,12.35 0,0 1,120.15 121.94L72.59,121.94A12.35,12.35 0,0 1,60.24 109.59L60.24,109.59A12.35,12.35 0,0 1,72.59 97.24z"
-        android:strokeWidth="2.47059"
+        android:pathData="M12.2,49.63L38.05,49.63A4.27,4.27 0,0 1,42.32 53.9L42.32,53.9A4.27,4.27 0,0 1,38.05 58.17L12.2,58.17A4.27,4.27 0,0 1,7.93 53.9L7.93,53.9A4.27,4.27 0,0 1,12.2 49.63z"
+        android:strokeWidth="1.21951"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_red"/>
     <path
-        android:pathData="M252,65.56L252,65.56A6.79,6.79 0,0 1,258.79 72.35L258.79,104.47A6.79,6.79 0,0 1,252 111.26L252,111.26A6.79,6.79 0,0 1,245.21 104.47L245.21,72.35A6.79,6.79 0,0 1,252 65.56z"
-        android:strokeWidth="3.70588"
+        android:pathData="M100,35.44L100,35.44A2.86,2.86 0,0 1,102.86 38.29L102.86,51.46A2.86,2.86 0,0 1,100 54.32L100,54.32A2.86,2.86 0,0 1,97.14 51.46L97.14,38.29A2.86,2.86 0,0 1,100 35.44z"
+        android:strokeWidth="1.80775"
         android:fillColor="@color/device_lock_illustration_exclamation_point"
         android:strokeColor="@color/device_lock_illustration_yellow"/>
     <path
-        android:pathData="M252,114.97L252,114.97A8.03,8.03 0,0 1,260.03 123L260.03,123A8.03,8.03 0,0 1,252 131.03L252,131.03A8.03,8.03 0,0 1,243.97 123L243.97,123A8.03,8.03 0,0 1,252 114.97z"
-        android:strokeWidth="3.70588"
+        android:pathData="M100,55.44L100,55.44A3.34,3.34 0,0 1,103.34 58.78L103.34,58.78A3.34,3.34 0,0 1,100 62.12L100,62.12A3.34,3.34 0,0 1,96.66 58.78L96.66,58.78A3.34,3.34 0,0 1,100 55.44z"
+        android:strokeWidth="1.80775"
         android:fillColor="@color/device_lock_illustration_exclamation_point"
         android:strokeColor="@color/device_lock_illustration_yellow"/>
     <path
-        android:pathData="M390,24.24L390,24.24A9.76,9.76 0,0 1,399.76 34L399.76,34A9.76,9.76 0,0 1,390 43.76L390,43.76A9.76,9.76 0,0 1,380.23 34L380.23,34A9.76,9.76 0,0 1,390 24.24z"
-        android:strokeWidth="2.47059"
+        android:pathData="M169.27,19.39L169.27,19.39A3.29,3.29 0,0 1,172.56 22.68L172.56,22.68A3.29,3.29 0,0 1,169.27 25.98L169.27,25.98A3.29,3.29 0,0 1,165.98 22.68L165.98,22.68A3.29,3.29 0,0 1,169.27 19.39z"
+        android:strokeWidth="1.21951"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_top_polygon_border"/>
     <path
-        android:pathData="M297.71,164.71L297.71,164.71A8.65,8.65 0,0 1,306.35 173.35L306.35,173.35A8.65,8.65 0,0 1,297.71 182L297.71,182A8.65,8.65 0,0 1,289.06 173.35L289.06,173.35A8.65,8.65 0,0 1,297.71 164.71z"
-        android:strokeWidth="2.47059"
+        android:pathData="M117.07,76.95L117.07,76.95A3.29,3.29 0,0 1,120.37 80.24L120.37,80.24A3.29,3.29 0,0 1,117.07 83.54L117.07,83.54A3.29,3.29 0,0 1,113.78 80.24L113.78,80.24A3.29,3.29 0,0 1,117.07 76.95z"
+        android:strokeWidth="1.21951"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_background_polygon"/>
     <path
-        android:pathData="M182.82,18L182.82,18A16.06,16.06 0,0 1,198.88 34.06L198.88,34.06A16.06,16.06 0,0 1,182.82 50.12L182.82,50.12A16.06,16.06 0,0 1,166.76 34.06L166.76,34.06A16.06,16.06 0,0 1,182.82 18z"
-        android:strokeWidth="2.47059"
+        android:pathData="M64.88,16.95L64.88,16.95A4.27,4.27 0,0 1,69.15 21.22L69.15,21.22A4.27,4.27 0,0 1,64.88 25.49L64.88,25.49A4.27,4.27 0,0 1,60.61 21.22L60.61,21.22A4.27,4.27 0,0 1,64.88 16.95z"
+        android:strokeWidth="1.21951"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_green"/>
     <path
-        android:pathData="M383.52,100.67L385.99,104.95"
-        android:strokeWidth="2.47059"
+        android:pathData="M164.64,47.89L165.85,49.98"
+        android:strokeWidth="1.20517"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_burst"
         android:strokeLineCap="round"/>
     <path
-        android:pathData="M394.64,119.93L397.11,124.21"
-        android:strokeWidth="2.47059"
+        android:pathData="M170.07,57.28L171.27,59.37"
+        android:strokeWidth="1.20517"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_burst"
         android:strokeLineCap="round"/>
     <path
-        android:pathData="M397.11,100.67L394.64,104.95"
-        android:strokeWidth="2.47059"
+        android:pathData="M171.27,47.89L170.07,49.98"
+        android:strokeWidth="1.20517"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_burst"
         android:strokeLineCap="round"/>
     <path
-        android:pathData="M385.99,119.93L383.52,124.21"
-        android:strokeWidth="2.47059"
+        android:pathData="M165.85,57.28L164.64,59.37"
+        android:strokeWidth="1.20517"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_burst"
         android:strokeLineCap="round"/>
     <path
-        android:pathData="M376.22,112.74L381.16,112.74"
-        android:strokeWidth="2.47059"
+        android:pathData="M161.08,53.77L163.49,53.77"
+        android:strokeWidth="1.20517"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_burst"
         android:strokeLineCap="round"/>
     <path
-        android:pathData="M398.45,112.74L403.39,112.74"
-        android:strokeWidth="2.47059"
+        android:pathData="M171.93,53.77L174.34,53.77"
+        android:strokeWidth="1.20517"
         android:fillColor="#00000000"
         android:strokeColor="@color/device_lock_illustration_burst"
         android:strokeLineCap="round"/>
diff --git a/chrome/browser/ui/android/device_lock/java/res/layout/missing_device_lock_view.xml b/chrome/browser/ui/android/device_lock/java/res/layout/missing_device_lock_view.xml
index 4b5b029..99a685f 100644
--- a/chrome/browser/ui/android/device_lock/java/res/layout/missing_device_lock_view.xml
+++ b/chrome/browser/ui/android/device_lock/java/res/layout/missing_device_lock_view.xml
@@ -33,7 +33,7 @@
                 android:layout_height="136dp"
                 android:layout_alignParentTop="true"
                 android:adjustViewBounds="true"
-                android:scaleType="centerInside"
+                android:scaleType="fitCenter"
                 android:layout_centerHorizontal="true"
                 android:background="@color/signin_header_animation_background"
                 app:srcCompat="@drawable/missing_device_lock_illustration"
@@ -66,8 +66,8 @@
                 android:layout_below="@id/missing_device_lock_description"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginStart="36dp"
-                android:layout_marginEnd="48dp"
+                android:layout_marginStart="14dp"
+                android:layout_marginEnd="16dp"
                 android:layout_marginTop="24dp"
                 android:layout_marginBottom="40dp"
                 android:paddingStart="4dp"
diff --git a/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc b/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc
index ae9c0bf..e032ed9 100644
--- a/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc
+++ b/chrome/browser/ui/android/plus_addresses/plus_address_creation_view_android.cc
@@ -11,8 +11,8 @@
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/android/tab_model/tab_model_list.h"
 #include "chrome/browser/ui/plus_addresses/plus_address_creation_controller.h"
-#include "chrome/grit/generated_resources.h"
 #include "components/plus_addresses/plus_address_types.h"
+#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/android/view_android.h"
 #include "ui/android/window_android.h"
diff --git a/chrome/browser/ui/side_panel/read_anything/OWNERS b/chrome/browser/ui/side_panel/read_anything/OWNERS
new file mode 100644
index 0000000..0b1f181a
--- /dev/null
+++ b/chrome/browser/ui/side_panel/read_anything/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/views/side_panel/read_anything/OWNERS
diff --git a/chrome/browser/ui/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h b/chrome/browser/ui/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h
new file mode 100644
index 0000000..c53b8d0
--- /dev/null
+++ b/chrome/browser/ui/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h
@@ -0,0 +1,30 @@
+// 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_UI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_SIDE_PANEL_NAVIGATION_THROTTLE_H_
+#define CHROME_BROWSER_UI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_SIDE_PANEL_NAVIGATION_THROTTLE_H_
+
+#include "content/public/browser/navigation_throttle.h"
+
+// This class prevents users from opening reading mode in the main content area
+// by intercepting a request for the read anything page and instead showing the
+// side panel.
+class ReadAnythingSidePanelNavigationThrottle
+    : public content::NavigationThrottle {
+ public:
+  static std::unique_ptr<content::NavigationThrottle> CreateFor(
+      content::NavigationHandle* handle);
+
+  // NavigationThrottle overrides:
+  ThrottleCheckResult WillStartRequest() override;
+  const char* GetNameForLogging() override;
+
+ private:
+  explicit ReadAnythingSidePanelNavigationThrottle(
+      content::NavigationHandle* navigation_handle);
+
+  ThrottleCheckResult HandleSidePanelRequest();
+};
+
+#endif  // CHROME_BROWSER_UI_SIDE_PANEL_READ_ANYTHING_READ_ANYTHING_SIDE_PANEL_NAVIGATION_THROTTLE_H_
diff --git a/chrome/browser/ui/side_panel/side_panel_enums.h b/chrome/browser/ui/side_panel/side_panel_enums.h
index 9666827..46b14812 100644
--- a/chrome/browser/ui/side_panel/side_panel_enums.h
+++ b/chrome/browser/ui/side_panel/side_panel_enums.h
@@ -27,7 +27,8 @@
   kAppMenu = 13,
   kOpenedInNewTabFromSidePanel = 14,
   kReadAnythingOmniboxIcon = 15,
-  kMaxValue = kReadAnythingOmniboxIcon,
+  kReadAnythingNavigationThrottle = 16,
+  kMaxValue = kReadAnythingNavigationThrottle,
 };
 
 #endif  // CHROME_BROWSER_UI_SIDE_PANEL_SIDE_PANEL_ENUMS_H_
diff --git a/chrome/browser/ui/side_panel/side_panel_ui.h b/chrome/browser/ui/side_panel/side_panel_ui.h
index b5d50b2..75dbad96 100644
--- a/chrome/browser/ui/side_panel/side_panel_ui.h
+++ b/chrome/browser/ui/side_panel/side_panel_ui.h
@@ -12,6 +12,10 @@
 
 class Browser;
 
+namespace content {
+class WebContents;
+}  // namespace content
+
 // An abstract class of the side panel API.
 // The class is created in BrowserView for desktop Chrome. Get the instance of
 // this class by calling SidePanelUI::GetSidePanelUIForBrowser(browser);
@@ -74,6 +78,10 @@
   virtual bool IsSidePanelEntryShowing(
       const SidePanelEntryKey& entry_key) const = 0;
 
+  // Returns the content view for the given entry. Returns nullptr if the entry
+  // does not exist.
+  virtual content::WebContents* GetWebContentsForTest(SidePanelEntryId id) = 0;
+
  private:
   static const int kUserDataKey = 0;
 };
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc
index abe7d630..1a173a24 100644
--- a/chrome/browser/ui/startup/startup_browser_creator.cc
+++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -127,7 +127,6 @@
 #if BUILDFLAG(IS_WIN)
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/metrics/jumplist_metrics_win.h"
 #include "chrome/browser/notifications/notification_platform_bridge_win.h"
 #include "chrome/browser/notifications/win/notification_launch_id.h"
 #include "chrome/browser/ui/startup/credential_provider_signin_dialog_win.h"
@@ -1190,8 +1189,6 @@
     // `switches::kWinJumplistAction` is expected to be set together with a
     // URL to open and with a specific profile dir.
     if (profile_info.mode == StartupProfileMode::kBrowserWindow) {
-      jumplist::LogJumplistActionFromSwitchValue(
-          command_line.GetSwitchValueASCII(switches::kWinJumplistAction));
       // Use a non-NULL pointer to indicate JumpList has been used. We re-use
       // chrome::kJumpListIconDirname as the key to the data.
       privacy_safe_profile->SetUserData(
diff --git a/chrome/browser/ui/views/file_system_access/file_system_access_browsertest.cc b/chrome/browser/ui/views/file_system_access/file_system_access_browsertest.cc
index 3d4abf6..434fbea 100644
--- a/chrome/browser/ui/views/file_system_access/file_system_access_browsertest.cc
+++ b/chrome/browser/ui/views/file_system_access/file_system_access_browsertest.cc
@@ -208,6 +208,8 @@
   }
 }
 
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+// TODO(crbug/1499052): Re-enable the test after fixing on Lacros.
 IN_PROC_BROWSER_TEST_F(FileSystemAccessBrowserTest, FullscreenOpenFile) {
   const base::FilePath test_file = CreateTestFile("");
   const std::string file_contents = "file contents to write";
@@ -256,6 +258,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(IsFullscreen());
 }
+#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 class FileSystemAccessBrowserSlowLoadTest : public FileSystemAccessBrowserTest {
  public:
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc b/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
index 335768f..2608e9b6 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt.cc
@@ -265,8 +265,8 @@
 }
 
 void EmbeddedPermissionPrompt::StopAllowing() {
-  // TODO(crbug.com/1462930): Implement.
-  NOTREACHED();
+  delegate_->Deny();
+  delegate_->FinalizeCurrentRequests();
 }
 
 void EmbeddedPermissionPrompt::ShowSystemSettings() {
diff --git a/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc b/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc
index f5cea8c07..78ca66ba 100644
--- a/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc
+++ b/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc
@@ -10,8 +10,8 @@
 #include "chrome/browser/ui/plus_addresses/plus_address_creation_controller.h"
 #include "chrome/browser/ui/plus_addresses/plus_address_creation_controller_desktop.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/color/color_id.h"
diff --git a/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc
new file mode 100644
index 0000000..a480c4c
--- /dev/null
+++ b/chrome/browser/ui/views/side_panel/read_anything/read_anything_side_panel_navigation_throttle.cc
@@ -0,0 +1,54 @@
+// 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/ui/side_panel/read_anything/read_anything_side_panel_navigation_throttle.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/side_panel/read_anything/read_anything_side_panel_controller_utils.h"
+#include "chrome/browser/ui/side_panel/side_panel_enums.h"
+#include "chrome/common/webui_url_constants.h"
+#include "content/public/browser/navigation_handle.h"
+#include "ui/base/page_transition_types.h"
+
+// static
+std::unique_ptr<content::NavigationThrottle>
+ReadAnythingSidePanelNavigationThrottle::CreateFor(
+    content::NavigationHandle* handle) {
+  return base::WrapUnique(new ReadAnythingSidePanelNavigationThrottle(handle));
+}
+
+ReadAnythingSidePanelNavigationThrottle::ThrottleCheckResult
+ReadAnythingSidePanelNavigationThrottle::WillStartRequest() {
+  return HandleSidePanelRequest();
+}
+
+const char* ReadAnythingSidePanelNavigationThrottle::GetNameForLogging() {
+  return "ReadAnythingSidePanelNavigationThrottle";
+}
+
+ReadAnythingSidePanelNavigationThrottle::
+    ReadAnythingSidePanelNavigationThrottle(
+        content::NavigationHandle* navigation_handle)
+    : NavigationThrottle(navigation_handle) {}
+
+ReadAnythingSidePanelNavigationThrottle::ThrottleCheckResult
+ReadAnythingSidePanelNavigationThrottle::HandleSidePanelRequest() {
+  const auto& url = navigation_handle()->GetURL();
+  // Only cancel the request if a user initiated it. Otherwise when reading mode
+  // opens, it will hit this block and cause a stack overflow.
+  if (url != chrome::kChromeUIUntrustedReadAnythingSidePanelURL ||
+      !ui::PageTransitionCoreTypeIs(navigation_handle()->GetPageTransition(),
+                                    ui::PAGE_TRANSITION_TYPED)) {
+    return content::NavigationThrottle::PROCEED;
+  }
+  Browser* browser =
+      chrome::FindBrowserWithTab(navigation_handle()->GetWebContents());
+  CHECK(browser);
+  ShowReadAnythingSidePanel(
+      browser, SidePanelOpenTrigger::kReadAnythingNavigationThrottle);
+  return content::NavigationThrottle::CANCEL_AND_IGNORE;
+}
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
index ece3d7d7..cd3f6710 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/ui/views/side_panel/side_panel_content_proxy.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_header.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_toolbar_container.h"
+#include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h"
 #include "chrome/browser/ui/views/toolbar/pinned_toolbar_actions_container.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/pref_names.h"
@@ -61,6 +62,7 @@
 #include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/vector_icons.h"
+#include "ui/views/view.h"
 #include "ui/views/view_class_properties.h"
 
 namespace {
@@ -523,6 +525,20 @@
          current_entry_->key() == entry_key;
 }
 
+content::WebContents* SidePanelCoordinator::GetWebContentsForTest(
+    SidePanelEntryId id) {
+  if (auto* entry = GetEntryForKey(SidePanelEntryKey(id))) {
+    entry->CacheView(entry->GetContent());
+    if (entry->CachedView()) {
+      if (auto* view = entry->CachedView()->GetViewByID(
+              SidePanelWebUIView::kSidePanelWebViewId)) {
+        return (static_cast<views::WebView*>(view))->web_contents();
+      }
+    }
+  }
+  return nullptr;
+}
+
 SidePanelEntry::Id SidePanelCoordinator::GetComboboxDisplayedEntryIdForTesting()
     const {
   CHECK(combobox_model_);
@@ -614,7 +630,7 @@
 }
 
 SidePanelEntry* SidePanelCoordinator::GetEntryForKey(
-    const SidePanelEntry::Key& entry_key) {
+    const SidePanelEntry::Key& entry_key) const {
   if (auto* contextual_entry = GetActiveContextualEntryForKey(entry_key)) {
     return contextual_entry;
   }
@@ -623,7 +639,7 @@
 }
 
 SidePanelEntry* SidePanelCoordinator::GetActiveContextualEntryForKey(
-    const SidePanelEntry::Key& entry_key) {
+    const SidePanelEntry::Key& entry_key) const {
   return GetActiveContextualRegistry()
              ? GetActiveContextualRegistry()->GetEntryForKey(entry_key)
              : nullptr;
diff --git a/chrome/browser/ui/views/side_panel/side_panel_coordinator.h b/chrome/browser/ui/views/side_panel/side_panel_coordinator.h
index ffca94f..76252250 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_coordinator.h
+++ b/chrome/browser/ui/views/side_panel/side_panel_coordinator.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_COORDINATOR_H_
 #define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_SIDE_PANEL_COORDINATOR_H_
 
+#include <memory>
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
@@ -80,6 +81,9 @@
   bool IsSidePanelEntryShowing(
       const SidePanelEntry::Key& entry_key) const override;
 
+  // Returns the web contents in a side panel if one exists.
+  content::WebContents* GetWebContentsForTest(SidePanelEntryId id) override;
+
   // TODO(crbug.com/1341399): Move this method to `SidePanelUI` after decoupling
   // `SidePanelEntry` from views.
   bool IsSidePanelEntryShowing(const SidePanelEntry* entry) const;
@@ -141,10 +145,10 @@
   // Returns the corresponding entry for `entry_key` or a nullptr if this key is
   // not registered in the currently observed registries. This looks through the
   // active contextual registry first, then the global registry.
-  SidePanelEntry* GetEntryForKey(const SidePanelEntry::Key& entry_key);
+  SidePanelEntry* GetEntryForKey(const SidePanelEntry::Key& entry_key) const;
 
   SidePanelEntry* GetActiveContextualEntryForKey(
-      const SidePanelEntry::Key& entry_key);
+      const SidePanelEntry::Key& entry_key) const;
 
   // Returns the current loading entry or nullptr if none exists.
   SidePanelEntry* GetLoadingEntry() const;
diff --git a/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.cc b/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.cc
index f165594d..b828b4ab 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.cc
+++ b/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.cc
@@ -29,6 +29,7 @@
       contents_wrapper_(contents_wrapper) {
   SidePanelUtil::GetSidePanelContentProxy(this)->SetAvailable(false);
   SetVisible(false);
+  SetID(kSidePanelWebViewId);
   contents_wrapper_->SetHost(weak_factory_.GetWeakPtr());
   SetWebContents(contents_wrapper_->web_contents());
 }
diff --git a/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h b/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h
index f546d94..7d29f82 100644
--- a/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h
+++ b/chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h
@@ -28,6 +28,8 @@
 class SidePanelWebUIView : public views::WebView,
                            public BubbleContentsWrapper::Host {
  public:
+  static inline constexpr int kSidePanelWebViewId = 777;
+
   METADATA_HEADER(SidePanelWebUIView);
   SidePanelWebUIView(base::RepeatingClosure on_show_cb,
                      base::RepeatingClosure close_cb,
diff --git a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view.cc b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view.cc
index e37e30a2c..92b804ceb 100644
--- a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view.cc
+++ b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
@@ -82,6 +83,22 @@
   return ui::ImageModel::FromImageSkia(icon_image);
 }
 
+// Implicitly converts an id or raw string to a string. Used as an argument to
+// functions that need a string, but want to accept either ids or raw strings.
+class ToU16String {
+ public:
+  // NOLINTNEXTLINE(runtime/explicit)
+  ToU16String(int string_id) : string_(l10n_util::GetStringUTF16(string_id)) {}
+
+  // NOLINTNEXTLINE(runtime/explicit)
+  ToU16String(const std::u16string& string) : string_(string) {}
+
+  const std::u16string& get() const { return string_; }
+
+ private:
+  std::u16string string_;
+};
+
 // A View that displays key/value entries in a pane with a different
 // background color and a rounded border.
 class InfoPane : public views::BoxLayoutView {
@@ -123,25 +140,13 @@
  public:
   METADATA_HEADER(InstallerDialogView);
 
-  // The message_id of the link's body and a callback to run when it's clicked.
-  using LinkInfo = std::pair<int, base::RepeatingClosure>;
-
-  InstallerDialogView(const ui::ImageModel& icon_model,
-                      int title_id,
-                      int subtitle_id,
-                      absl::optional<LinkInfo> subtitle_link = absl::nullopt,
-                      std::unique_ptr<views::View> contents_view = nullptr)
-      : InstallerDialogView(icon_model,
-                            l10n_util::GetStringUTF16(title_id),
-                            subtitle_id,
-                            subtitle_link,
-                            std::move(contents_view)) {}
-
-  InstallerDialogView(const ui::ImageModel& icon_model,
-                      const std::u16string& title,
-                      int subtitle_id,
-                      absl::optional<LinkInfo> subtitle_link = absl::nullopt,
-                      std::unique_ptr<views::View> contents_view = nullptr) {
+  InstallerDialogView(
+      const ui::ImageModel& icon_model,
+      const ToU16String& title,
+      int subtitle_id,
+      absl::optional<ToU16String> subtitle_param = absl::nullopt,
+      absl::optional<base::RepeatingClosure> subtitle_link_callback =
+          absl::nullopt) {
     ConfigureBoxLayoutView(this);
 
     auto* icon = AddChildView(std::make_unique<NonAccessibleImageView>());
@@ -151,27 +156,30 @@
     views::StyledLabel* title_label =
         AddChildView(CreateLabelWithContextAndStyle(
             views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY));
-    title_label->SetText(title);
+    title_label->SetText(title.get());
 
     views::StyledLabel* subtitle = AddChildView(CreateLabelWithContextAndStyle(
         views::style::CONTEXT_LABEL, views::style::STYLE_SECONDARY));
-    if (subtitle_link.has_value()) {
-      std::u16string link_text =
-          l10n_util::GetStringUTF16(subtitle_link->first);
+    if (subtitle_param.has_value()) {
       size_t offset;
-      subtitle->SetText(
-          l10n_util::GetStringFUTF16(subtitle_id, link_text, &offset));
-      subtitle->AddStyleRange(gfx::Range(offset, offset + link_text.length()),
-                              views::StyledLabel::RangeStyleInfo::CreateForLink(
-                                  subtitle_link->second));
+      subtitle->SetText(l10n_util::GetStringFUTF16(
+          subtitle_id, subtitle_param->get(), &offset));
+      if (subtitle_link_callback.has_value()) {
+        subtitle->AddStyleRange(
+            gfx::Range(offset, offset + subtitle_param->get().length()),
+            views::StyledLabel::RangeStyleInfo::CreateForLink(
+                *subtitle_link_callback));
+      }
     } else {
       subtitle->SetText(l10n_util::GetStringUTF16(subtitle_id));
     }
+  }
 
-    if (contents_view) {
-      views::View* contents = AddChildView(std::move(contents_view));
-      SetFlexForView(contents, 1);
-    }
+  template <typename T>
+  T* SetContentsView(std::unique_ptr<T> contents_view) {
+    T* contents = AddChildView(std::move(contents_view));
+    SetFlexForView(contents, 1);
+    return contents;
   }
 };
 BEGIN_METADATA(InstallerDialogView, views::BoxLayoutView)
@@ -205,20 +213,24 @@
 IsolatedWebAppInstallerView::~IsolatedWebAppInstallerView() = default;
 
 void IsolatedWebAppInstallerView::ShowDisabledScreen() {
-  InstallerDialogView::LinkInfo link(
-      IDS_IWA_INSTALLER_DISABLED_CHANGE_PREFERENCE,
-      base::BindRepeating(&Delegate::OnSettingsLinkClicked,
-                          base::Unretained(delegate_)));
   ShowScreen(std::make_unique<InstallerDialogView>(
       CreateImageModelFromVector(vector_icons::kErrorOutlineIcon,
                                  ui::kColorAlertMediumSeverityIcon),
       IDS_IWA_INSTALLER_DISABLED_TITLE, IDS_IWA_INSTALLER_DISABLED_SUBTITLE,
-      link));
+      IDS_IWA_INSTALLER_DISABLED_CHANGE_PREFERENCE,
+      base::BindRepeating(&Delegate::OnSettingsLinkClicked,
+                          base::Unretained(delegate_))));
 }
 
 void IsolatedWebAppInstallerView::ShowGetMetadataScreen() {
-  auto progress_view = std::make_unique<views::BoxLayoutView>();
-  ConfigureBoxLayoutView(progress_view.get());
+  auto view = std::make_unique<InstallerDialogView>(
+      CreateImageModelFromVector(kFingerprintIcon, ui::kColorAccent),
+      IDS_IWA_INSTALLER_VERIFICATION_TITLE,
+      IDS_IWA_INSTALLER_VERIFICATION_SUBTITLE);
+
+  auto* progress_view =
+      view->SetContentsView(std::make_unique<views::BoxLayoutView>());
+  ConfigureBoxLayoutView(progress_view);
   progress_view->SetInsideBorderInsets(
       gfx::Insets::VH(0, kProgressViewHorizontalPadding));
 
@@ -229,12 +241,7 @@
       l10n_util::GetPluralStringFUTF16(IDS_IWA_INSTALLER_VERIFICATION_STATUS,
                                        0)));
 
-  ShowScreen(std::make_unique<InstallerDialogView>(
-                 CreateImageModelFromVector(kFingerprintIcon, ui::kColorAccent),
-                 IDS_IWA_INSTALLER_VERIFICATION_TITLE,
-                 IDS_IWA_INSTALLER_VERIFICATION_SUBTITLE,
-                 /*subtitle_link=*/absl::nullopt, std::move(progress_view)),
-             progress_bar);
+  ShowScreen(std::move(view), progress_bar);
 }
 
 void IsolatedWebAppInstallerView::UpdateGetMetadataProgress(
@@ -246,27 +253,33 @@
 
 void IsolatedWebAppInstallerView::ShowMetadataScreen(
     const SignedWebBundleMetadata& bundle_metadata) {
+  auto view = std::make_unique<InstallerDialogView>(
+      CreateImageModelFromBundleMetadata(bundle_metadata),
+      bundle_metadata.app_name(), IDS_IWA_INSTALLER_SHOW_METADATA_SUBTITLE,
+      IDS_IWA_INSTALLER_SHOW_METADATA_MANAGE_PROFILES,
+      base::BindRepeating(&Delegate::OnManageProfilesLinkClicked,
+                          base::Unretained(delegate_)));
+
   std::vector<std::pair<int, std::u16string>> info = {
       {IDS_IWA_INSTALLER_SHOW_METADATA_APP_NAME_LABEL,
        bundle_metadata.app_name()},
       {IDS_IWA_INSTALLER_SHOW_METADATA_APP_VERSION_LABEL,
        base::UTF8ToUTF16(bundle_metadata.version().GetString())},
   };
-  auto app_info = std::make_unique<InfoPane>(info);
-  InstallerDialogView::LinkInfo link(
-      IDS_IWA_INSTALLER_SHOW_METADATA_MANAGE_PROFILES,
-      base::BindRepeating(&Delegate::OnManageProfilesLinkClicked,
-                          base::Unretained(delegate_)));
-  ShowScreen(std::make_unique<InstallerDialogView>(
-      CreateImageModelFromBundleMetadata(bundle_metadata),
-      bundle_metadata.app_name(), IDS_IWA_INSTALLER_SHOW_METADATA_SUBTITLE,
-      link, std::move(app_info)));
+  view->SetContentsView(std::make_unique<InfoPane>(info));
+
+  ShowScreen(std::move(view));
 }
 
 void IsolatedWebAppInstallerView::ShowInstallScreen(
     const SignedWebBundleMetadata& bundle_metadata) {
-  auto progress_view = std::make_unique<views::BoxLayoutView>();
-  ConfigureBoxLayoutView(progress_view.get());
+  auto view = std::make_unique<InstallerDialogView>(
+      CreateImageModelFromBundleMetadata(bundle_metadata),
+      bundle_metadata.app_name(), IDS_IWA_INSTALLER_INSTALL_SUBTITLE);
+
+  auto* progress_view =
+      view->SetContentsView(std::make_unique<views::BoxLayoutView>());
+  ConfigureBoxLayoutView(progress_view);
   progress_view->SetInsideBorderInsets(
       gfx::Insets::VH(0, kProgressViewHorizontalPadding));
 
@@ -276,11 +289,7 @@
       views::style::CONTEXT_LABEL, views::style::STYLE_SECONDARY,
       l10n_util::GetStringUTF16(IDS_IWA_INSTALLER_INSTALL_PROGRESS)));
 
-  ShowScreen(std::make_unique<InstallerDialogView>(
-                 CreateImageModelFromBundleMetadata(bundle_metadata),
-                 bundle_metadata.app_name(), IDS_IWA_INSTALLER_INSTALL_SUBTITLE,
-                 /*subtitle_link=*/absl::nullopt, std::move(progress_view)),
-             progress_bar);
+  ShowScreen(std::move(view), progress_bar);
 }
 
 void IsolatedWebAppInstallerView::UpdateInstallProgress(double percent,
@@ -291,7 +300,10 @@
 
 void IsolatedWebAppInstallerView::ShowInstallSuccessScreen(
     const SignedWebBundleMetadata& bundle_metadata) {
-  // TODO(crbug.com/1479140): Implement
+  ShowScreen(std::make_unique<InstallerDialogView>(
+      CreateImageModelFromBundleMetadata(bundle_metadata),
+      bundle_metadata.app_name(), IDS_IWA_INSTALLER_SUCCESS_SUBTITLE,
+      bundle_metadata.app_name()));
 }
 
 void IsolatedWebAppInstallerView::ShowScreen(
@@ -323,16 +335,23 @@
           views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
   bubble_delegate->set_close_on_deactivate(false);
 
-  bubble_delegate->SetContentsView(std::make_unique<InstallerDialogView>(
+  ui::ImageModel image =
       dialog_content.is_error
           ? CreateImageModelFromVector(vector_icons::kErrorOutlineIcon,
                                        ui::kColorAlertMediumSeverityIcon)
-          : CreateImageModelFromVector(kSecurityIcon, ui::kColorAccent),
-      dialog_content.message, dialog_content.details,
-      dialog_content.details_link));
+          : CreateImageModelFromVector(kSecurityIcon, ui::kColorAccent);
+  if (dialog_content.details_link.has_value()) {
+    bubble_delegate->SetContentsView(std::make_unique<InstallerDialogView>(
+        image, dialog_content.message, dialog_content.details,
+        dialog_content.details_link->first,
+        dialog_content.details_link->second));
+  } else {
+    bubble_delegate->SetContentsView(std::make_unique<InstallerDialogView>(
+        image, dialog_content.message, dialog_content.details));
+  }
 
   SetDialogButtons(bubble_delegate.get(), IDS_APP_CLOSE,
-                   /*accept_button_label_id=*/absl::nullopt);
+                   dialog_content.accept_message);
 
   bubble_delegate->SetCancelCallback(base::BindOnce(
       &Delegate::OnChildDialogCanceled, base::Unretained(delegate_)));
diff --git a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_browsertest.cc b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_browsertest.cc
index 1527992a..7bda7a5 100644
--- a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/test/test_browser_dialog.h"
 #include "chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_model.h"
 #include "chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_location.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
 #include "chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.h"
 #include "chrome/browser/web_applications/test/web_app_icon_test_utils.h"
@@ -51,7 +52,8 @@
   return SignedWebBundleMetadata::CreateForTesting(
       IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(
           web_package::SignedWebBundleId::CreateRandomForDevelopment()),
-      u"Test Isolated Web App", base::Version("0.0.1"), icons);
+      DevModeBundle(base::FilePath()), u"Test Isolated Web App",
+      base::Version("0.0.1"), icons);
 }
 
 const TestParam kTestParam[] = {
@@ -59,6 +61,7 @@
     {.test_suffix = "GetMetadata", .step = Step::kGetMetadata},
     {.test_suffix = "ConfirmInstall", .step = Step::kConfirmInstall},
     {.test_suffix = "Install", .step = Step::kInstall},
+    {.test_suffix = "Success", .step = Step::kInstallSuccess},
 };
 
 class IsolatedWebAppInstallerViewUiPixelTest
diff --git a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.cc b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.cc
index bfe0c79f..b7537f2 100644
--- a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.cc
+++ b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.cc
@@ -11,7 +11,9 @@
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_model.h"
 #include "chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view.h"
+#include "chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.h"
+#include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
@@ -106,6 +108,69 @@
   view_ = view;
 }
 
+// static
+bool IsolatedWebAppInstallerViewController::OnAcceptWrapper(
+    base::WeakPtr<IsolatedWebAppInstallerViewController> controller) {
+  if (controller) {
+    return controller->OnAccept();
+  }
+  return true;
+}
+
+// Returns true if the dialog should be closed.
+bool IsolatedWebAppInstallerViewController::OnAccept() {
+  switch (model_->step()) {
+    case IsolatedWebAppInstallerModel::Step::kConfirmInstall: {
+      IsolatedWebAppInstallerModel::LinkInfo learn_more_link = {
+          IDS_IWA_INSTALLER_CONFIRM_LEARN_MORE,
+          base::BindRepeating(&IsolatedWebAppInstallerViewController::
+                                  OnConfirmInstallLearnMoreClicked,
+                              base::Unretained(this))};
+      model_->SetDialogContent(IsolatedWebAppInstallerModel::DialogContent(
+          /*is_error=*/false, IDS_IWA_INSTALLER_CONFIRM_TITLE,
+          IDS_IWA_INSTALLER_CONFIRM_SUBTITLE, learn_more_link,
+          IDS_IWA_INSTALLER_CONFIRM_CONTINUE));
+      OnModelChanged();
+      return false;
+    }
+
+    case IsolatedWebAppInstallerModel::Step::kInstallSuccess:
+      // TODO(crbug.com/1479140): Launch the app.
+      break;
+
+    default:
+      NOTREACHED();
+  }
+  return true;
+}
+
+void IsolatedWebAppInstallerViewController::OnComplete() {
+  view_ = nullptr;
+  dialog_delegate_ = nullptr;
+  std::move(callback_).Run();
+}
+
+void IsolatedWebAppInstallerViewController::Close() {
+  if (dialog_delegate_) {
+    dialog_delegate_->CancelDialog();
+  }
+}
+
+void IsolatedWebAppInstallerViewController::OnInstallComplete(
+    base::expected<InstallIsolatedWebAppCommandSuccess,
+                   InstallIsolatedWebAppCommandError> result) {
+  if (result.has_value()) {
+    model_->SetStep(IsolatedWebAppInstallerModel::Step::kInstallSuccess);
+    OnModelChanged();
+    return;
+  }
+  // TODO(crbug.com/1479140): Show error dialog
+}
+
+void IsolatedWebAppInstallerViewController::OnConfirmInstallLearnMoreClicked() {
+  // TODO(crbug.com/1479140): Implement
+}
+
 void IsolatedWebAppInstallerViewController::OnProfileShutdown() {
   Close();
 }
@@ -159,6 +224,10 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 }
 
+void IsolatedWebAppInstallerViewController::OnManageProfilesLinkClicked() {
+  // TODO(crbug.com/1479140): Implement
+}
+
 void IsolatedWebAppInstallerViewController::OnChildDialogCanceled() {
   // Currently all child dialogs should close the installer when closed.
   Close();
@@ -166,46 +235,26 @@
 
 void IsolatedWebAppInstallerViewController::OnChildDialogAccepted() {
   // TODO(crbug.com/1479140): Implement
-}
-
-void IsolatedWebAppInstallerViewController::OnManageProfilesLinkClicked() {
-  // TODO(crbug.com/1479140): Implement
-}
-
-// static
-bool IsolatedWebAppInstallerViewController::OnAcceptWrapper(
-    base::WeakPtr<IsolatedWebAppInstallerViewController> controller) {
-  if (controller) {
-    return controller->OnAccept();
-  }
-  return true;
-}
-
-// Returns true if the dialog should be closed.
-bool IsolatedWebAppInstallerViewController::OnAccept() {
-  // TODO(crbug.com/1479140): Implement
   switch (model_->step()) {
-    case IsolatedWebAppInstallerModel::Step::kConfirmInstall:
+    case IsolatedWebAppInstallerModel::Step::kConfirmInstall: {
       model_->SetStep(IsolatedWebAppInstallerModel::Step::kInstall);
+      model_->SetDialogContent(absl::nullopt);
       OnModelChanged();
-      return false;
+
+      const SignedWebBundleMetadata& metadata = model_->bundle_metadata();
+      web_app_provider_->scheduler().InstallIsolatedWebApp(
+          metadata.url_info(), metadata.location(), metadata.version(),
+          /*optional_keep_alive=*/nullptr,
+          /*optional_profile_keep_alive=*/nullptr,
+          base::BindOnce(
+              &IsolatedWebAppInstallerViewController::OnInstallComplete,
+              weak_ptr_factory_.GetWeakPtr()));
+      break;
+    }
 
     default:
       NOTREACHED();
   }
-  return true;
-}
-
-void IsolatedWebAppInstallerViewController::OnComplete() {
-  view_ = nullptr;
-  dialog_delegate_ = nullptr;
-  std::move(callback_).Run();
-}
-
-void IsolatedWebAppInstallerViewController::Close() {
-  if (dialog_delegate_) {
-    dialog_delegate_->CancelDialog();
-  }
 }
 
 void IsolatedWebAppInstallerViewController::OnModelChanged() {
@@ -213,7 +262,6 @@
     return;
   }
 
-  // TODO(crbug.com/1479140): Configure Install/Cancel buttons for all screens
   switch (model_->step()) {
     case IsolatedWebAppInstallerModel::Step::kDisabled:
       IsolatedWebAppInstallerView::SetDialogButtons(
@@ -243,6 +291,9 @@
       break;
 
     case IsolatedWebAppInstallerModel::Step::kInstallSuccess:
+      IsolatedWebAppInstallerView::SetDialogButtons(
+          dialog_delegate_, IDS_IWA_INSTALLER_SUCCESS_FINISH,
+          IDS_IWA_INSTALLER_SUCCESS_LAUNCH_APPLICATION);
       view_->ShowInstallSuccessScreen(model_->bundle_metadata());
       break;
   }
diff --git a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.h b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.h
index f2bb0df..c351d76f 100644
--- a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.h
+++ b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller.h
@@ -8,10 +8,13 @@
 #include <string>
 
 #include "base/functional/callback_forward.h"
+#include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/types/expected.h"
 #include "chrome/browser/ui/views/web_apps/isolated_web_apps/installability_checker.h"
 #include "chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view.h"
+#include "chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h"
 
 class Profile;
 
@@ -46,6 +49,13 @@
   void SetViewForTesting(IsolatedWebAppInstallerView* view);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(IsolatedWebAppInstallerViewControllerTest,
+                           InstallButtonLaunchesConfirmationDialog);
+  FRIEND_TEST_ALL_PREFIXES(IsolatedWebAppInstallerViewControllerTest,
+                           ConfirmationDialogMovesToInstallScreen);
+  FRIEND_TEST_ALL_PREFIXES(IsolatedWebAppInstallerViewControllerTest,
+                           SuccessfulInstallationMovesToSuccessScreen);
+
   // Handles returning a default value if the controller has been deleted.
   static bool OnAcceptWrapper(
       base::WeakPtr<IsolatedWebAppInstallerViewController> controller);
@@ -54,6 +64,12 @@
   void OnComplete();
   void Close();
 
+  void OnInstallComplete(
+      base::expected<InstallIsolatedWebAppCommandSuccess,
+                     InstallIsolatedWebAppCommandError> result);
+
+  void OnConfirmInstallLearnMoreClicked();
+
   // `InstallabilityChecker::Delegate`:
   void OnProfileShutdown() override;
   void OnBundleInvalid(const std::string& error) override;
diff --git a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc
index a2cbd63..b81b9379 100644
--- a/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc
+++ b/chrome/browser/ui/views/web_apps/isolated_web_apps/isolated_web_app_installer_view_controller_unittest.cc
@@ -80,6 +80,20 @@
   return IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(bundle.id);
 }
 
+SignedWebBundleMetadata CreateMetadata(const std::u16string& app_name,
+                                       const std::string& version) {
+  auto url_info = IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(
+      web_package::SignedWebBundleId::CreateRandomForDevelopment());
+  return SignedWebBundleMetadata::CreateForTesting(
+      url_info, DevModeBundle(base::FilePath()), app_name,
+      base::Version(version), IconBitmaps());
+}
+
+IsolatedWebAppInstallerModel::DialogContent CreateDummyDialog() {
+  return IsolatedWebAppInstallerModel::DialogContent(
+      /*is_error=*/false, /*message=*/0, /*details=*/0);
+}
+
 blink::mojom::ManifestPtr CreateDefaultManifest(const GURL& iwa_url,
                                                 const base::Version version) {
   auto manifest = blink::mojom::Manifest::New();
@@ -100,10 +114,10 @@
   return manifest;
 }
 
-class MockView : public IsolatedWebAppInstallerView,
-                 public IsolatedWebAppInstallerView::Delegate {
+class MockView : public IsolatedWebAppInstallerView {
  public:
-  MockView() : IsolatedWebAppInstallerView(this) {}
+  explicit MockView(IsolatedWebAppInstallerView::Delegate* delegate)
+      : IsolatedWebAppInstallerView(delegate) {}
 
   MOCK_METHOD(void, ShowDisabledScreen, (), (override));
   MOCK_METHOD(void, ShowGetMetadataScreen, (), (override));
@@ -132,14 +146,10 @@
       ShowDialog,
       (const IsolatedWebAppInstallerModel::DialogContent& dialog_content),
       (override));
-
-  // `IsolatedWebAppInstallerView::Delegate`:
-  MOCK_METHOD(void, OnSettingsLinkClicked, (), (override));
-  MOCK_METHOD(void, OnManageProfilesLinkClicked, (), (override));
-  MOCK_METHOD(void, OnChildDialogCanceled, (), (override));
-  MOCK_METHOD(void, OnChildDialogAccepted, (), (override));
 };
 
+}  // namespace
+
 class IsolatedWebAppInstallerViewControllerTest : public ::testing::Test {
  public:
   void SetUp() override {
@@ -167,7 +177,8 @@
         base::FilePath::FromASCII(bundle_filename));
   }
 
-  void MockIconAndPageState(const IsolatedWebAppUrlInfo& url_info) {
+  void MockIconAndPageState(const IsolatedWebAppUrlInfo& url_info,
+                            const std::string& version = "7.7.7") {
     GURL iwa_url = url_info.origin().GetURL();
     auto& fake_web_contents_manager = static_cast<FakeWebContentsManager&>(
         fake_provider()->web_contents_manager());
@@ -186,7 +197,7 @@
     page_state.manifest_url = iwa_url.Resolve("manifest.webmanifest");
     page_state.valid_manifest_for_web_app = true;
     page_state.opt_manifest =
-        CreateDefaultManifest(iwa_url, base::Version("7.7.7"));
+        CreateDefaultManifest(iwa_url, base::Version(version));
   }
 
  private:
@@ -206,7 +217,7 @@
   IsolatedWebAppInstallerModel model(bundle_path);
   IsolatedWebAppInstallerViewController controller(profile(), fake_provider(),
                                                    &model);
-  testing::StrictMock<MockView> view;
+  testing::StrictMock<MockView> view(&controller);
   controller.SetViewForTesting(&view);
 
   base::test::TestFuture<void> callback;
@@ -219,6 +230,7 @@
   controller.Start();
 
   EXPECT_TRUE(callback.Wait());
+  EXPECT_EQ(model.step(), IsolatedWebAppInstallerModel::Step::kConfirmInstall);
 }
 
 TEST_F(IsolatedWebAppInstallerViewControllerTest,
@@ -235,7 +247,7 @@
   IsolatedWebAppInstallerModel model(bundle_path);
   IsolatedWebAppInstallerViewController controller(profile(), fake_provider(),
                                                    &model);
-  testing::StrictMock<MockView> view;
+  testing::StrictMock<MockView> view(&controller);
   controller.SetViewForTesting(&view);
 
   base::test::TestFuture<void> callback;
@@ -249,7 +261,84 @@
   controller.Start();
 
   EXPECT_TRUE(callback.Wait());
+  EXPECT_EQ(model.step(), IsolatedWebAppInstallerModel::Step::kGetMetadata);
 }
 
-}  // namespace
+TEST_F(IsolatedWebAppInstallerViewControllerTest,
+       InstallButtonLaunchesConfirmationDialog) {
+  IsolatedWebAppInstallerModel model(CreateBundlePath("test_bundle.swbn"));
+  IsolatedWebAppInstallerViewController controller(profile(), fake_provider(),
+                                                   &model);
+  testing::StrictMock<MockView> view(&controller);
+  controller.SetViewForTesting(&view);
+
+  SignedWebBundleMetadata metadata = CreateMetadata(u"Test App", "0.0.1");
+  model.SetSignedWebBundleMetadata(metadata);
+  model.SetStep(IsolatedWebAppInstallerModel::Step::kConfirmInstall);
+
+  base::test::TestFuture<void> callback;
+  EXPECT_CALL(view, ShowMetadataScreen(metadata));
+  EXPECT_CALL(view, ShowDialog(WithContents(
+                        /*is_error=*/false, IDS_IWA_INSTALLER_CONFIRM_TITLE,
+                        IDS_IWA_INSTALLER_CONFIRM_SUBTITLE)))
+      .WillOnce(Invoke(&callback, &base::test::TestFuture<void>::SetValue));
+
+  controller.OnAccept();
+
+  EXPECT_TRUE(callback.Wait());
+}
+
+TEST_F(IsolatedWebAppInstallerViewControllerTest,
+       ConfirmationDialogMovesToInstallScreen) {
+  IsolatedWebAppInstallerModel model(CreateBundlePath("test_bundle.swbn"));
+  IsolatedWebAppInstallerViewController controller(profile(), fake_provider(),
+                                                   &model);
+  testing::StrictMock<MockView> view(&controller);
+  controller.SetViewForTesting(&view);
+
+  SignedWebBundleMetadata metadata = CreateMetadata(u"Test App", "0.0.1");
+  model.SetSignedWebBundleMetadata(metadata);
+  model.SetStep(IsolatedWebAppInstallerModel::Step::kConfirmInstall);
+  model.SetDialogContent(CreateDummyDialog());
+
+  base::test::TestFuture<void> callback;
+  EXPECT_CALL(view, ShowInstallScreen(metadata))
+      .WillOnce(Invoke(&callback, &base::test::TestFuture<void>::SetValue));
+
+  controller.OnChildDialogAccepted();
+
+  EXPECT_TRUE(callback.Wait());
+}
+
+TEST_F(IsolatedWebAppInstallerViewControllerTest,
+       SuccessfulInstallationMovesToSuccessScreen) {
+  base::FilePath bundle_path = CreateBundlePath("test_bundle.swbn");
+  IsolatedWebAppUrlInfo url_info = CreateAndWriteTestBundle(bundle_path, "1.0");
+  MockIconAndPageState(url_info, "1.0");
+
+  IsolatedWebAppInstallerModel model(bundle_path);
+  IsolatedWebAppInstallerViewController controller(profile(), fake_provider(),
+                                                   &model);
+  testing::StrictMock<MockView> view(&controller);
+  controller.SetViewForTesting(&view);
+
+  auto metadata = SignedWebBundleMetadata::CreateForTesting(
+      url_info, InstalledBundle(bundle_path), u"app name", base::Version("1.0"),
+      IconBitmaps());
+  model.SetSignedWebBundleMetadata(metadata);
+  model.SetStep(IsolatedWebAppInstallerModel::Step::kConfirmInstall);
+  model.SetDialogContent(CreateDummyDialog());
+
+  base::test::TestFuture<void> callback;
+  EXPECT_CALL(view, ShowInstallScreen(metadata));
+  EXPECT_CALL(view, ShowInstallSuccessScreen(metadata))
+      .WillOnce(Invoke(&callback, &base::test::TestFuture<void>::SetValue));
+
+  controller.OnChildDialogAccepted();
+
+  EXPECT_TRUE(callback.Wait());
+  EXPECT_TRUE(
+      fake_provider()->registrar_unsafe().IsInstalled(url_info.app_id()));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 13a70f4b..52c96ae9 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -262,6 +262,20 @@
   helper_.CheckSiteNotHandlesFile(Site::kStandalone, FileExtension::kBar);
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppIntegration, DisableEnableFileHandling) {
+  helper_.InstallMenuOption(InstallableSite::kMinimalUi);
+  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kBar);
+
+  helper_.DisableFileHandling(Site::kMinimalUi);
+  helper_.CheckSiteNotHandlesFile(Site::kMinimalUi, FileExtension::kFoo);
+  helper_.CheckSiteNotHandlesFile(Site::kMinimalUi, FileExtension::kBar);
+
+  helper_.EnableFileHandling(Site::kMinimalUi);
+  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kFoo);
+  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kBar);
+}
+
 // Generated tests:
 
 IN_PROC_BROWSER_TEST_F(
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
index cd8ed40..bd076ee85 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest_mac_win_linux.cc
@@ -98,20 +98,6 @@
   helper_.CheckWindowCreated();
 }
 
-IN_PROC_BROWSER_TEST_F(WebAppIntegration, DisableEnableFileHandling) {
-  helper_.InstallMenuOption(InstallableSite::kMinimalUi);
-  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kBar);
-
-  helper_.DisableFileHandling(Site::kMinimalUi);
-  helper_.CheckSiteNotHandlesFile(Site::kMinimalUi, FileExtension::kFoo);
-  helper_.CheckSiteNotHandlesFile(Site::kMinimalUi, FileExtension::kBar);
-
-  helper_.EnableFileHandling(Site::kMinimalUi);
-  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kFoo);
-  helper_.CheckSiteHandlesFile(Site::kMinimalUi, FileExtension::kBar);
-}
-
 IN_PROC_BROWSER_TEST_F(WebAppIntegration, MultiProfileFileHandling) {
   // Install file handling PWA in two profiles.
   helper_.InstallMenuOption(InstallableSite::kMinimalUi);
diff --git a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
index cdb0547..4964f9b 100644
--- a/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
+++ b/chrome/browser/ui/web_applications/test/web_app_browsertest_util.cc
@@ -175,7 +175,11 @@
           ->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
               app_id, apps::LaunchContainer::kLaunchContainerWindow,
               disposition, apps::LaunchSource::kFromTest));
-  EXPECT_TRUE(web_contents);
+
+  if (!web_contents) {
+    return nullptr;
+  }
+
   Browser* browser = chrome::FindBrowserWithTab(web_contents);
   EXPECT_TRUE(AppBrowserController::IsForWebApp(browser, app_id));
   return browser;
@@ -204,7 +208,10 @@
               app_id, apps::LaunchContainer::kLaunchContainerTab,
               WindowOpenDisposition::NEW_FOREGROUND_TAB,
               apps::LaunchSource::kFromTest));
-  DCHECK(web_contents);
+
+  if (!web_contents) {
+    return nullptr;
+  }
 
   EXPECT_EQ(app_id, *WebAppTabHelper::GetAppId(web_contents));
 
diff --git a/chrome/browser/ui/webui/ash/settings/pages/main/main_section.cc b/chrome/browser/ui/webui/ash/settings/pages/main/main_section.cc
index 9141788c..54e3eac 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/main/main_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/main/main_section.cc
@@ -298,7 +298,7 @@
                                             IDS_OS_SETTINGS_PROFILE_LABEL_V2);
   plural_string_handler->AddLocalizedString(
       "nearbyShareContactVisibilityNumUnreachable",
-      IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE);
+      IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE_PH);
 
   plural_string_handler->AddLocalizedString(
       "lockScreenNumberFingerprints",
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 261768b8..7357667c 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -354,7 +354,6 @@
     "chrome://privacy-sandbox-dialog/?debug",
     "chrome://process-internals",
     "chrome://quota-internals",
-    "chrome-untrusted://read-anything-side-panel.top-chrome",
     "chrome://read-later.top-chrome",
     "chrome://reset-password",
     "chrome://safe-browsing",
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
index b7915b1c..03fe44b 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
@@ -122,7 +122,7 @@
   auto plural_string_handler = std::make_unique<PluralStringHandler>();
   plural_string_handler->AddLocalizedString(
       "nearbyShareContactVisibilityNumUnreachable",
-      IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE);
+      IDS_NEARBY_CONTACT_VISIBILITY_NUM_UNREACHABLE_PH);
   web_ui->AddMessageHandler(std::move(plural_string_handler));
   // Add the metrics handler to write uma stats.
   web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
diff --git a/chrome/browser/ui/webui/nearby_share/shared_resources.cc b/chrome/browser/ui/webui/nearby_share/shared_resources.cc
index 9e25b29..ecd6a9c 100644
--- a/chrome/browser/ui/webui/nearby_share/shared_resources.cc
+++ b/chrome/browser/ui/webui/nearby_share/shared_resources.cc
@@ -16,7 +16,7 @@
 
 void RegisterNearbySharedStrings(content::WebUIDataSource* data_source) {
   static constexpr webui::LocalizedString kLocalizedStrings[] = {
-      {"nearbyShareAccountRowLabel", IDS_NEARBY_ACCOUNT_ROW_LABEL},
+      {"nearbyShareAccountRowLabel", IDS_NEARBY_ACCOUNT_ROW_LABEL_PH},
       {"nearbyShareActionsAccept", IDS_NEARBY_ACTIONS_ACCEPT},
       {"nearbyShareActionsCancel", IDS_NEARBY_ACTIONS_CANCEL},
       {"nearbyShareActionsClose", IDS_NEARBY_ACTIONS_CLOSE},
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
index a7393ae..5d53d35 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/rand_util.h"
+#include "chrome/browser/bad_message.h"
 #include "chrome/browser/cart/cart_handler.h"
 #include "chrome/browser/image_fetcher/image_decoder_impl.h"
 #include "chrome/browser/new_tab_page/modules/new_tab_page_modules.h"
@@ -33,6 +34,7 @@
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/search/ntp_features.h"
 #include "components/strings/grit/components_strings.h"
+#include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
@@ -261,9 +263,16 @@
 }
 
 void CustomizeChromeUI::BindInterface(
+    content::RenderFrameHost* host,
     mojo::PendingReceiver<
         side_panel::customize_chrome::mojom::WallpaperSearchHandler>
         pending_receiver) {
+  if (wallpaper_search_handler_) {
+    // Only allowed to create one Mojo pipe per WebUI.
+    bad_message::ReceivedBadMessage(host->GetProcess(),
+                                    bad_message::CCU_SUPERFLUOUS_BIND);
+    return;
+  }
   wallpaper_search_handler_ = std::make_unique<WallpaperSearchHandler>(
       std::move(pending_receiver), profile_, image_decoder_.get(),
       wallpaper_search_background_manager_.get(), id_);
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
index e7dc562..c00ae17 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
@@ -26,6 +26,7 @@
 #include "ui/webui/resources/cr_components/theme_color_picker/theme_color_picker.mojom.h"
 
 namespace content {
+class RenderFrameHost;
 class WebContents;
 }  // namespace content
 
@@ -98,6 +99,7 @@
                          pending_receiver);
 
   void BindInterface(
+      content::RenderFrameHost* host,
       mojo::PendingReceiver<
           side_panel::customize_chrome::mojom::WallpaperSearchHandler>
           pending_receiver);
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/DEPS b/chrome/browser/ui/webui/side_panel/read_anything/DEPS
index 626639e4..b5821e5 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/DEPS
+++ b/chrome/browser/ui/webui/side_panel/read_anything/DEPS
@@ -4,7 +4,7 @@
 
 specific_include_rules = {
   "read_anything_untrusted_page_handler\.*": [
-  "+chrome/browser/ui/views/side_panel/read_anything",
-  "+chrome/browser/ui/views/frame/browser_view.h",
-],
+    "+chrome/browser/ui/views/side_panel/read_anything",
+    "+chrome/browser/ui/views/frame/browser_view.h",
+  ],
 }
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc
index efad2f24..824f1d9 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/files/file_util.h"
 #include "base/path_service.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/side_panel/side_panel_ui.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -46,18 +47,25 @@
       script = "'use strict';" + script;
     }
 
-    // Run the test.
+    // Run the test. Navigating to the URL will trigger the read anything
+    // navigation throttle and open the side panel instead of loading read
+    // anything in the main content area.
     EXPECT_TRUE(ui_test_utils::NavigateToURL(
         browser(), GURL(chrome::kChromeUIUntrustedReadAnythingSidePanelURL)));
-    content::RenderFrameHost* webui = browser()
-                                          ->tab_strip_model()
-                                          ->GetActiveWebContents()
-                                          ->GetPrimaryMainFrame();
-    if (!webui) {
-      return testing::AssertionFailure() << "Failed to navigate to WebUI";
+    // Get the side panel entry registry.
+    auto* side_panel_ui = SidePanelUI::GetSidePanelUIForBrowser(browser());
+    auto* side_panel_web_contents =
+        side_panel_ui->GetWebContentsForTest(SidePanelEntryId::kReadAnything);
+    if (!side_panel_web_contents) {
+      return testing::AssertionFailure()
+             << "Failed to navigate to get WebContents";
     }
-
-    bool result = content::EvalJs(webui, script).ExtractBool();
+    // Wait for the view to load before trying to run the test. This ensures
+    // that chrome.readingMode is set.
+    content::WaitForLoadStop(side_panel_web_contents);
+    // Eval the JS test.
+    bool result =
+        content::EvalJs(side_panel_web_contents, script).ExtractBool();
     return result ? testing::AssertionSuccess()
                   : (testing::AssertionFailure() << "Check console output");
   }
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc
index 181dcd6..550879cd0 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_read_aloud_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/files/file_util.h"
 #include "base/path_service.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/side_panel/side_panel_ui.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -50,18 +51,25 @@
       script = "'use strict';" + script;
     }
 
-    // Run the test.
+    // Run the test. Navigating to the URL will trigger the read anything
+    // navigation throttle and open the side panel instead of loading read
+    // anything in the main content area.
     EXPECT_TRUE(ui_test_utils::NavigateToURL(
         browser(), GURL(chrome::kChromeUIUntrustedReadAnythingSidePanelURL)));
-    content::RenderFrameHost* webui = browser()
-                                          ->tab_strip_model()
-                                          ->GetActiveWebContents()
-                                          ->GetPrimaryMainFrame();
-    if (!webui) {
+    // Get the side panel entry registry.
+    auto* side_panel_ui = SidePanelUI::GetSidePanelUIForBrowser(browser());
+    auto* side_panel_web_contents =
+        side_panel_ui->GetWebContentsForTest(SidePanelEntryId::kReadAnything);
+
+    if (!side_panel_web_contents) {
       return testing::AssertionFailure() << "Failed to navigate to WebUI";
     }
-
-    bool result = content::EvalJs(webui, script).ExtractBool();
+    // Wait for the view to load before trying to run the test. This ensures
+    // that chrome.readingMode is set.
+    content::WaitForLoadStop(side_panel_web_contents);
+    // Eval the JS test.
+    bool result =
+        content::EvalJs(side_panel_web_contents, script).ExtractBool();
     return result ? testing::AssertionSuccess()
                   : (testing::AssertionFailure() << "Check console output");
   }
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
index 84de8f6..4de0e45 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_app_toolbar_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/files/file_util.h"
 #include "base/path_service.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/side_panel/side_panel_ui.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -47,19 +48,25 @@
       base::ReadFileToString(path, &script);
       script = "'use strict';" + script;
     }
-
-    // Run the test.
+    // Run the test. Navigating to the URL will trigger the read anything
+    // navigation throttle and open the side panel instead of loading read
+    // anything in the main content area.
     EXPECT_TRUE(ui_test_utils::NavigateToURL(
         browser(), GURL(chrome::kChromeUIUntrustedReadAnythingSidePanelURL)));
-    content::RenderFrameHost* webui = browser()
-                                          ->tab_strip_model()
-                                          ->GetActiveWebContents()
-                                          ->GetPrimaryMainFrame();
-    if (!webui) {
+    // Get the side panel entry registry.
+    auto* side_panel_ui = SidePanelUI::GetSidePanelUIForBrowser(browser());
+    auto* side_panel_web_contents =
+        side_panel_ui->GetWebContentsForTest(SidePanelEntryId::kReadAnything);
+
+    if (!side_panel_web_contents) {
       return testing::AssertionFailure() << "Failed to navigate to WebUI";
     }
-
-    bool result = content::EvalJs(webui, script).ExtractBool();
+    // Wait for the view to load before trying to run the test. This ensures
+    // that chrome.readingMode is set.
+    content::WaitForLoadStop(side_panel_web_contents);
+    // Eval the JS test.
+    bool result =
+        content::EvalJs(side_panel_web_contents, script).ExtractBool();
     return result ? testing::AssertionSuccess()
                   : (testing::AssertionFailure() << "Check console output");
   }
diff --git a/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc b/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc
index c7cf18e1..530d3ae1 100644
--- a/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/launch_web_app_command_browsertest.cc
@@ -1,4 +1,4 @@
-// Copyright 2022 The Chromium Authors
+// 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.
 
@@ -15,27 +15,48 @@
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
+#include "chrome/browser/apps/app_service/app_registry_cache_waiter.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
-#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
+#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h"
+#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "content/public/test/browser_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
 
-namespace web_app {
-namespace {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/startup/first_run_service.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArg;
+
+namespace web_app {
+
+namespace {
 #if BUILDFLAG(IS_WIN)
 const base::FilePath::CharType kCurrentDirectory[] =
     FILE_PATH_LITERAL("\\path");
@@ -43,6 +64,208 @@
 const base::FilePath::CharType kCurrentDirectory[] = FILE_PATH_LITERAL("/path");
 #endif  // BUILDFLAG(IS_WIN)
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+class FirstRunServiceMock : public FirstRunService {
+ public:
+  FirstRunServiceMock(Profile& profile,
+                      signin::IdentityManager& identity_manager)
+      : FirstRunService(profile, identity_manager) {}
+
+  MOCK_METHOD(bool, ShouldOpenFirstRun, (), (const, override));
+  MOCK_METHOD(void,
+              OpenFirstRunIfNeeded,
+              (EntryPoint entry_point, ResumeTaskCallback callback),
+              (override));
+};
+
+std::unique_ptr<KeyedService> BuildTestFirstRunService(
+    bool create_first_run_service,
+    content::BrowserContext* context) {
+  if (!create_first_run_service) {
+    return nullptr;
+  }
+
+  Profile* profile = Profile::FromBrowserContext(context);
+  CHECK(profile);
+  return std::make_unique<FirstRunServiceMock>(
+      *profile, *IdentityManagerFactory::GetForProfile(profile));
+}
+
+class FirstRunServiceOverrideHelper {
+ public:
+  explicit FirstRunServiceOverrideHelper(bool create_first_run_service)
+      : create_first_run_service_(create_first_run_service) {
+    CHECK(BrowserContextDependencyManager::GetInstance());
+    create_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterCreateServicesCallbackForTesting(
+                base::BindRepeating(&FirstRunServiceOverrideHelper::
+                                        OnWillCreateBrowserContextServices,
+                                    base::Unretained(this)));
+  }
+
+ private:
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    FirstRunServiceFactory::GetInstance()->SetTestingFactory(
+        context, base::BindRepeating(BuildTestFirstRunService,
+                                     create_first_run_service_));
+  }
+
+  bool create_first_run_service_ = false;
+
+  base::CallbackListSubscription create_services_subscription_;
+};
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+class LaunchWebAppWithFirstRunServiceBrowserTest
+    : public WebAppControllerBrowserTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  LaunchWebAppWithFirstRunServiceBrowserTest() = default;
+  ~LaunchWebAppWithFirstRunServiceBrowserTest() override = default;
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  void SetUpInProcessBrowserTestFixture() override {
+    first_run_service_override_helper_ =
+        std::make_unique<FirstRunServiceOverrideHelper>(GetParam());
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+ protected:
+  WebAppProvider& GetProvider() {
+    return *WebAppProvider::GetForTest(browser()->profile());
+  }
+
+  webapps::AppId InstallWebApp(const GURL& app_url) {
+    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), app_url));
+
+    webapps::AppId app_id;
+    base::RunLoop run_loop;
+    GetProvider().scheduler().FetchManifestAndInstall(
+        webapps::WebappInstallSource::OMNIBOX_INSTALL_ICON,
+        browser()->tab_strip_model()->GetActiveWebContents()->GetWeakPtr(),
+        base::BindOnce(test::TestAcceptDialogCallback),
+        base::BindLambdaForTesting([&](const webapps::AppId& new_app_id,
+                                       webapps::InstallResultCode code) {
+          EXPECT_EQ(code, webapps::InstallResultCode::kSuccessNewInstall);
+          app_id = new_app_id;
+          run_loop.Quit();
+        }),
+        /*use_fallback=*/true);
+
+    run_loop.Run();
+    return app_id;
+  }
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+ private:
+  std::unique_ptr<FirstRunServiceOverrideHelper>
+      first_run_service_override_helper_;
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+};
+
+IN_PROC_BROWSER_TEST_P(
+    LaunchWebAppWithFirstRunServiceBrowserTest,
+    LaunchInWindowWithFirstRunServiceRequiredSetupSuccessful) {
+  webapps::AppId app_id =
+      InstallWebApp(https_server()->GetURL("/banners/manifest_test_page.html"));
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  FirstRunServiceMock* first_run_service = static_cast<FirstRunServiceMock*>(
+      FirstRunServiceFactory::GetForBrowserContextIfExists(profile()));
+
+  if (GetParam()) {
+    EXPECT_CALL(*first_run_service, OpenFirstRunIfNeeded(_, _))
+        .WillOnce(WithArg<1>(Invoke([](ResumeTaskCallback callback) {
+          std::move(callback).Run(/*proceed=*/true);
+        })));
+  } else {
+    ASSERT_FALSE(first_run_service);
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+  ASSERT_TRUE(GetProvider().registrar_unsafe().IsLocallyInstalled(app_id));
+
+  Browser* browser = LaunchWebAppBrowser(app_id);
+  ASSERT_TRUE(browser);
+}
+
+IN_PROC_BROWSER_TEST_P(LaunchWebAppWithFirstRunServiceBrowserTest,
+                       LaunchInTabWithFirstRunServiceRequiredSetupSuccessful) {
+  webapps::AppId app_id =
+      InstallWebApp(https_server()->GetURL("/banners/manifest_test_page.html"));
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  FirstRunServiceMock* first_run_service = static_cast<FirstRunServiceMock*>(
+      FirstRunServiceFactory::GetForBrowserContextIfExists(profile()));
+
+  if (GetParam()) {
+    EXPECT_CALL(*first_run_service, OpenFirstRunIfNeeded(_, _))
+        .WillOnce(WithArg<1>(Invoke([](ResumeTaskCallback callback) {
+          std::move(callback).Run(/*proceed=*/true);
+        })));
+  } else {
+    ASSERT_FALSE(first_run_service);
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+  ASSERT_TRUE(GetProvider().registrar_unsafe().IsLocallyInstalled(app_id));
+
+  Browser* browser = LaunchBrowserForWebAppInTab(app_id);
+  ASSERT_TRUE(browser);
+}
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+IN_PROC_BROWSER_TEST_P(LaunchWebAppWithFirstRunServiceBrowserTest,
+                       LaunchInWindowWithFirstRunServiceRequiredSetupSkipped) {
+  webapps::AppId app_id =
+      InstallWebApp(https_server()->GetURL("/banners/manifest_test_page.html"));
+
+  FirstRunServiceMock* first_run_service = static_cast<FirstRunServiceMock*>(
+      FirstRunServiceFactory::GetForBrowserContextIfExists(profile()));
+  if (GetParam()) {
+    EXPECT_CALL(*first_run_service, OpenFirstRunIfNeeded(_, _))
+        .WillOnce(WithArg<1>(Invoke([](ResumeTaskCallback callback) {
+          std::move(callback).Run(/*proceed=*/false);
+        })));
+  } else {
+    ASSERT_FALSE(first_run_service);
+  }
+
+  ASSERT_TRUE(GetProvider().registrar_unsafe().IsLocallyInstalled(app_id));
+
+  Browser* browser = LaunchWebAppBrowser(app_id);
+  ASSERT_EQ(browser == nullptr, GetParam());
+}
+
+IN_PROC_BROWSER_TEST_P(LaunchWebAppWithFirstRunServiceBrowserTest,
+                       LaunchInTabWithFirstRunServiceRequiredSetupSkipped) {
+  webapps::AppId app_id =
+      InstallWebApp(https_server()->GetURL("/banners/manifest_test_page.html"));
+
+  FirstRunServiceMock* first_run_service = static_cast<FirstRunServiceMock*>(
+      FirstRunServiceFactory::GetForBrowserContextIfExists(profile()));
+
+  if (GetParam()) {
+    EXPECT_CALL(*first_run_service, OpenFirstRunIfNeeded(_, _))
+        .WillOnce(WithArg<1>(Invoke([](ResumeTaskCallback callback) {
+          std::move(callback).Run(/*proceed=*/true);
+        })));
+  } else {
+    ASSERT_FALSE(first_run_service);
+  }
+
+  ASSERT_TRUE(GetProvider().registrar_unsafe().IsLocallyInstalled(app_id));
+
+  Browser* browser = LaunchBrowserForWebAppInTab(app_id);
+  ASSERT_TRUE(browser);
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         LaunchWebAppWithFirstRunServiceBrowserTest,
+                         ::testing::Values(true, false));
+
 class LaunchWebAppCommandTest : public WebAppControllerBrowserTest {
  public:
   const std::string kAppName = "TestApp";
@@ -260,6 +483,8 @@
     EXPECT_EQ(launch_container, apps::LaunchContainer::kLaunchContainerWindow);
   }
 }
-#endif
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 }  // namespace
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.cc
index fa3e1149..273515e 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.cc
@@ -160,6 +160,12 @@
       browser_context, partition_domain(), partition_name, in_memory);
 }
 
+bool IsolatedWebAppUrlInfo::operator==(
+    const IsolatedWebAppUrlInfo& other) const {
+  return origin_ == other.origin_ && app_id_ == other.app_id_ &&
+         web_bundle_id_ == other.web_bundle_id_;
+}
+
 std::string IsolatedWebAppUrlInfo::partition_domain() const {
   constexpr char kIsolatedWebAppPartitionPrefix[] = "iwa-";
   // We add a prefix to `partition_domain` to avoid potential name conflicts
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h
index 1135359..a0d90aae 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h
@@ -71,6 +71,8 @@
       const std::string& partition_name,
       bool in_memory) const;
 
+  bool operator==(const IsolatedWebAppUrlInfo& other) const;
+
  private:
   explicit IsolatedWebAppUrlInfo(
       const web_package::SignedWebBundleId& web_bundle_id);
diff --git a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.cc b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.cc
index 141d76a..d98a7a4 100644
--- a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.cc
@@ -197,18 +197,19 @@
 
   fetcher_ref.FetchAndReply(base::BindOnce(
       [](const IsolatedWebAppUrlInfo& url_info,
+         const IsolatedWebAppLocation& location,
          SignedWebBundleMetadataCallback callback,
          base::expected<WebAppInstallInfo, std::string> install_info) {
         std::move(callback).Run(install_info.transform(
-            [&url_info](const WebAppInstallInfo& install_info)
+            [&url_info, &location](const WebAppInstallInfo& install_info)
                 -> SignedWebBundleMetadata {
               return SignedWebBundleMetadata(
-                  url_info, install_info.title,
+                  url_info, location, install_info.title,
                   install_info.isolated_web_app_version,
                   install_info.icon_bitmaps);
             }));
       },
-      url_info,
+      url_info, location,
       std::move(callback).Then(base::OnceClosure(
           base::DoNothingWithBoundArgs(std::move(fetcher))))));
 }
@@ -216,18 +217,21 @@
 // static
 SignedWebBundleMetadata SignedWebBundleMetadata::CreateForTesting(
     const IsolatedWebAppUrlInfo& url_info,
+    const IsolatedWebAppLocation& location,
     const std::u16string& app_name,
     const base::Version& version,
     const IconBitmaps& icons) {
-  return SignedWebBundleMetadata(url_info, app_name, version, icons);
+  return SignedWebBundleMetadata(url_info, location, app_name, version, icons);
 }
 
 SignedWebBundleMetadata::SignedWebBundleMetadata(
     const IsolatedWebAppUrlInfo& url_info,
+    const IsolatedWebAppLocation& location,
     const std::u16string& app_name,
     const base::Version& version,
     const IconBitmaps& icons)
     : url_info_(url_info),
+      location_(location),
       app_name_(app_name),
       version_(version),
       icons_(icons) {}
@@ -240,4 +244,10 @@
 SignedWebBundleMetadata& SignedWebBundleMetadata::operator=(
     const SignedWebBundleMetadata&) = default;
 
+bool SignedWebBundleMetadata::operator==(
+    const SignedWebBundleMetadata& other) const {
+  return url_info_ == other.url_info_ && app_name_ == other.app_name_ &&
+         version_ == other.version_ && icons_ == other.icons_;
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.h b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.h
index 9b047ee..5431c88 100644
--- a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.h
+++ b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_metadata.h
@@ -40,6 +40,7 @@
 
   static SignedWebBundleMetadata CreateForTesting(
       const IsolatedWebAppUrlInfo& url_info,
+      const IsolatedWebAppLocation& location,
       const std::u16string& app_name,
       const base::Version& version,
       const IconBitmaps& icons);
@@ -48,6 +49,10 @@
   SignedWebBundleMetadata(const SignedWebBundleMetadata&);
   SignedWebBundleMetadata& operator=(const SignedWebBundleMetadata&);
 
+  const IsolatedWebAppUrlInfo& url_info() const { return url_info_; }
+
+  const IsolatedWebAppLocation& location() const { return location_; }
+
   const webapps::AppId& app_id() const { return url_info_.app_id(); }
 
   const std::u16string& app_name() const { return app_name_; }
@@ -56,13 +61,17 @@
 
   const IconBitmaps& icons() const { return icons_; }
 
+  bool operator==(const SignedWebBundleMetadata& other) const;
+
  private:
   SignedWebBundleMetadata(const IsolatedWebAppUrlInfo& url_info,
+                          const IsolatedWebAppLocation& location,
                           const std::u16string& app_name,
                           const base::Version& version,
                           const IconBitmaps& icons);
 
   IsolatedWebAppUrlInfo url_info_;
+  IsolatedWebAppLocation location_;
   std::u16string app_name_;
   base::Version version_;
   IconBitmaps icons_;
diff --git a/chrome/browser/win/jumplist.cc b/chrome/browser/win/jumplist.cc
index 16ce739..ed858fe 100644
--- a/chrome/browser/win/jumplist.cc
+++ b/chrome/browser/win/jumplist.cc
@@ -13,7 +13,6 @@
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
-#include "base/metrics/histogram_macros.h"
 #include "base/path_service.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_piece.h"
@@ -29,7 +28,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/top_sites_factory.h"
-#include "chrome/browser/metrics/jumplist_metrics_win.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -97,6 +95,11 @@
 // The maximum allowed time for JumpListUpdater::CommitUpdate.
 constexpr base::TimeDelta kTimeOutForCommitUpdate = base::Milliseconds(1000);
 
+// The category types that can be logged with the `--win-jumplist-action`
+// switch.
+constexpr char kMostVisitedCategory[] = "most-visited";
+constexpr char kRecentlyClosedCategory[] = "recently-closed";
+
 // Appends the common switches to each shell link.
 void AppendCommonSwitches(const base::FilePath& cmd_line_profile_dir,
                           ShellLinkItem* shell_link) {
@@ -464,7 +467,7 @@
     std::wstring url_string_wide = base::UTF8ToWide(url_string);
     link->GetCommandLine()->AppendArgNative(url_string_wide);
     link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction,
-                                              jumplist::kMostVisitedCategory);
+                                              kMostVisitedCategory);
     link->set_title(!url.title.empty() ? url.title
                                        : base::AsString16(url_string_wide));
     link->set_url(url_string);
@@ -495,7 +498,7 @@
   std::string url = current_navigation.virtual_url().spec();
   link->GetCommandLine()->AppendArgNative(base::UTF8ToWide(url));
   link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction,
-                                            jumplist::kRecentlyClosedCategory);
+                                            kRecentlyClosedCategory);
   link->set_title(current_navigation.title());
   link->set_url(url);
   recently_closed_pages_.push_back(link);
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index c07428bd..a113529e 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1699617335-e2d7022eedc6095f77022e47c9f22fc2c0c9b23f.profdata
+chrome-android32-main-1699639168-228fa64532c04b3b2979733d1b4e10c75e8c8097.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 72584e4..ed8523d 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1699617335-dde605504ecc10ee8c1c4fbea0fe2e0da32e390b.profdata
+chrome-android64-main-1699639168-4e63d87d5fbfe1c4e6f72be3ea27b8748fa238ee.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 825a17d..c31010b 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1699617335-4c8cb818a0e64cf3f6be28ae9818a5ef4c753a2e.profdata
+chrome-linux-main-1699639168-7f4d6bf899ed7ddfedd9d650e743f18e1b7ba79c.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index fbf862f..1457169 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1699595874-cd7345b035a0f64886a5c5f226e29fae64041a37.profdata
+chrome-mac-arm-main-1699646371-659155785369548eff409ecc3d67afb0918b8625.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 8501e1b..aa4f956 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1699595874-eaedade8b7ab206ab1b904418c73bc11f4c2968e.profdata
+chrome-mac-main-1699639168-21ceb146921636480965c10d1a5878fa8ff11367.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 69853a8..92b646d7 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1699617335-1d57351203f0afb552eee305330637584ee2084a.profdata
+chrome-win-arm64-main-1699639168-b56c51c34e672274fec9b8b6db7ca427d8881d4b.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 5c6c924..6de2a815 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1699617335-0fa79041b68c431efe0fefe14e2df1e3ad1ec4d2.profdata
+chrome-win32-main-1699639168-f0026dd3e52fff9146ea4625bddcab80b5b1b70e.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 9c7885a..2319f3a 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1699617335-5f24a637de8699773c3e15281857d225782b9b45.profdata
+chrome-win64-main-1699639168-97fd3b9cd75c0d145df6b44aada6f57fe632ab1f.profdata
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index f581e41c..74504d6d 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -22,6 +22,7 @@
 #include "base/command_line.h"
 #include "base/dcheck_is_on.h"
 #include "base/debug/alias.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/debug/handle_hooks_win.h"
 #include "base/file_version_info.h"
 #include "base/files/file_path.h"
@@ -273,7 +274,21 @@
 
     // Notify the parent process that this one is ready to go.
     if (startup_event.IsValid()) {
-      ::SetEvent(startup_event.Get());
+      if (!::SetEvent(startup_event.Get())) {
+        // Failure to signal the event likely means that the handle is invalid.
+        // Clear the ScopedHandle to prevent a crash upon close and proceed with
+        // the operation. The parent process will wait for 30s in this case (see
+        // DelayedOverwriteDisplayVersions) and will then continue on its merry
+        // way.
+        if (auto error = ::GetLastError(); error != ERROR_INVALID_HANDLE) {
+          // It is highly unexpected that this would fail for any other reason.
+          // Send diagnostics for analysis just in case.
+          // TODO(grt): Check for data and remove this in March 2024.
+          base::debug::Alias(&error);
+          base::debug::DumpWithoutCrashing();
+        }
+        (void)startup_event.release();
+      }
       startup_event.Close();
     }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 6525a2e5..b5ccafe1 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4607,6 +4607,9 @@
         "../browser/ash/policy/reporting/metrics_reporting/audio/audio_events_observer_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/cros_healthd_info_metric_sampler_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/device_activity/device_activity_sampler_browsertest.cc",
+        "../browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_browsertest.cc",
+        "../browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.cc",
+        "../browser/ash/policy/reporting/metrics_reporting/fatal_crash/fatal_crash_events_observer_test_util.h",
         "../browser/ash/policy/reporting/metrics_reporting/network/network_events_observer_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/network/network_info_sampler_browsertest.cc",
         "../browser/ash/policy/reporting/metrics_reporting/network/network_telemetry_sampler_browsertest.cc",
diff --git a/chrome/test/data/webui/chromeos/BUILD.gn b/chrome/test/data/webui/chromeos/BUILD.gn
index 562db48..a7d1450 100644
--- a/chrome/test/data/webui/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/BUILD.gn
@@ -202,7 +202,6 @@
     "os_feedback_ui/file_attachment_test.js",
     "os_feedback_ui/help_content_test.js",
     "os_feedback_ui/mojo_interface_provider_test.js",
-    "os_feedback_ui/os_feedback_unified_test.js",
     "os_feedback_ui/search_page_test.js",
     "os_feedback_ui/share_data_page_test.js",
     "shimless_rma/all_inputs_disabled_test.js",
diff --git a/chrome/test/data/webui/chromeos/ash_common/BUILD.gn b/chrome/test/data/webui/chromeos/ash_common/BUILD.gn
index da745bae..5fd313e 100644
--- a/chrome/test/data/webui/chromeos/ash_common/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/ash_common/BUILD.gn
@@ -14,7 +14,6 @@
                     rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
                                 target_gen_dir) ]
   in_files = [
-    "ash_common_unified_test.js",
     "cr_container_shadow_behavior_test.ts",
     "cr_policy_indicator_behavior_test.ts",
     "cr_policy_strings.ts",
@@ -75,7 +74,6 @@
                     "js_module_root=./gen/chrome/test/data/webui/",
                   ]
   deps = [
-    ":ash_common_unified_test",
     ":fake_method_resolver_test",
     ":fake_observables_test",
     ":navigation_selector_test",
@@ -84,16 +82,12 @@
   ]
 }
 
-js_library("ash_common_unified_test") {
-  deps = []
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("fake_observables_test") {
   deps = [
     "..:chai_assert",
     "//ash/webui/common/resources:fake_observables",
   ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
 js_library("fake_method_resolver_test") {
@@ -101,6 +95,7 @@
     "..:chai_assert",
     "//ash/webui/common/resources:fake_method_resolver",
   ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
 js_library("keyboard_diagram_test") {
@@ -108,6 +103,7 @@
     "..:chai_assert",
     "//ash/webui/common/resources:keyboard_diagram",
   ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
 js_library("navigation_selector_test") {
@@ -116,6 +112,7 @@
     "//ash/webui/common/resources:navigation_selector",
     "//third_party/polymer/v3_0/components-chromium/iron-collapse:iron-collapse",
   ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
 js_library("navigation_view_panel_test") {
@@ -124,8 +121,10 @@
     "//ash/webui/common/resources:navigation_selector",
     "//ash/webui/common/resources:navigation_view_panel",
   ]
-  externs_list =
-      [ "//ui/webui/resources/cr_elements/cr_drawer/cr_drawer_externs.js" ]
+  externs_list = [
+    "//ui/webui/resources/cr_elements/cr_drawer/cr_drawer_externs.js",
+    "$externs_path/mocha-2.5.js",
+  ]
 }
 
 js_library("page_toolbar_test") {
@@ -134,4 +133,5 @@
     "..:test_util",
     "//ash/webui/common/resources:page_toolbar",
   ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
diff --git a/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js b/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js
index b2ab471e..7eb9828 100644
--- a/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js
+++ b/chrome/test/data/webui/chromeos/ash_common/ash_common_browsertest.js
@@ -5,14 +5,8 @@
 /**
  * @fileoverview Test fixture for utility components in //ash/webui/common.
  *
- * To run all tests in a single instance (default, faster):
- * `browser_tests --gtest_filter=AshCommon*``
- *
- * To run each test in a new instance:
- * `browser_tests --run-manual --gtest_filter=AshCommon.MANUAL_*``
- *
- * To run a single test suite, such as 'FakeObservables':
- * `browser_tests --run-manual --gtest_filter=AshCommon.MANUAL_FakeObservables`
+ * To run all tests:
+ * `browser_tests --gtest_filter=AshCommonBrowserTest*``
  *
  */
 
@@ -21,43 +15,34 @@
 GEN('#include "ash/constants/ash_features.h"');
 GEN('#include "content/public/test/browser_test.h"');
 
-const testSuites = 'chromeos/ash_common/ash_common_unified_test.js';
-
 this['AshCommon'] = class extends PolymerTest {
   /** @override */
-  get browsePreload() {
-    return `chrome://webui-test/test_loader.html?module=${testSuites}`;
-  }
-
-  /** @override */
   get webuiHost() {
     return 'dummyurl';
   }
 };
 
-// List of names of suites in unified test to register for individual debugging.
-// You must register all suites in unified test here as well for consistency,
-// although technically is not necessary.
-const debug_suites_list = [
-  'FakeObservables',
-  'FakeMethodResolver',
-  'KeyboardDiagram',
-  'NavigationSelector',
-  'NavigationViewPanel',
-  'PageToolbar',
+// List of names of suites as well as their corresponding module. To disable a
+// test suite add 'DISABLED_All' after the module path.
+// Ex. ['FakeObservables', 'fake_observables_test.js', 'DISABLED_All'],
+const tests = [
+  ['FakeObservables', 'fake_observables_test.js'],
+  ['FakeMethodResolver', 'fake_method_resolver_test.js'],
+  ['KeyboardDiagram', 'keyboard_diagram_test.js'],
+  ['NavigationSelector', 'navigation_selector_test.js'],
+  ['NavigationViewPanel', 'navigation_view_panel_test.js'],
+  ['PageToolbar', 'page_toolbar_test.js'],
 ];
 
-TEST_F('AshCommon', 'BrowserTest', function() {
-  assertDeepEquals(
-      debug_suites_list, test_suites_list,
-      'List of registered tests suites and debug suites do not match.\n' +
-          'Did you forget to add your test in debug_suites_list?');
-  mocha.run();
-});
+for (const [suiteName, module, caseName] of tests) {
+  const className = `AshCommonBrowserTest_${suiteName}`;
+  this[className] = class extends AshCommon {
+    /** @override */
+    get browsePreload() {
+      return `chrome://webui-test/test_loader.html?module=chromeos/ash_common/${
+          module}`;
+    }
+  };
 
-// Register each suite listed as individual tests for debugging purposes.
-for (const suiteName of debug_suites_list) {
-  TEST_F('AshCommon', `MANUAL_${suiteName}`, function() {
-    runMochaSuite(suiteName);
-  });
+  TEST_F(className, caseName || 'All', () => mocha.run());
 }
diff --git a/chrome/test/data/webui/chromeos/ash_common/ash_common_unified_test.js b/chrome/test/data/webui/chromeos/ash_common/ash_common_unified_test.js
deleted file mode 100644
index e22cf6eb..0000000
--- a/chrome/test/data/webui/chromeos/ash_common/ash_common_unified_test.js
+++ /dev/null
@@ -1,24 +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.
-
-import {fakeMethodResolverTestSuite} from './fake_method_resolver_test.js';
-import {fakeObservablesTestSuite} from './fake_observables_test.js';
-import {keyboardDiagramTestSuite} from './keyboard_diagram_test.js';
-import {navigationSelectorTestSuite} from './navigation_selector_test.js';
-import {navigationViewPanelTestSuite} from './navigation_view_panel_test.js';
-import {pageToolbarTestSuite} from './page_toolbar_test.js';
-
-window.test_suites_list = [];
-
-function runSuite(suiteName, testFn) {
-  window.test_suites_list.push(suiteName);
-  suite(suiteName, testFn);
-}
-
-runSuite('FakeObservables', fakeObservablesTestSuite);
-runSuite('FakeMethodResolver', fakeMethodResolverTestSuite);
-runSuite('KeyboardDiagram', keyboardDiagramTestSuite);
-runSuite('NavigationSelector', navigationSelectorTestSuite);
-runSuite('NavigationViewPanel', navigationViewPanelTestSuite);
-runSuite('PageToolbar', pageToolbarTestSuite);
diff --git a/chrome/test/data/webui/chromeos/ash_common/fake_method_resolver_test.js b/chrome/test/data/webui/chromeos/ash_common/fake_method_resolver_test.js
index d0a6b20..85dcb31 100644
--- a/chrome/test/data/webui/chromeos/ash_common/fake_method_resolver_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/fake_method_resolver_test.js
@@ -6,7 +6,7 @@
 
 import {assertEquals} from 'chrome://webui-test/chromeos/chai_assert.js';
 
-export function fakeMethodResolverTestSuite() {
+suite('fakeMethodResolverTestSuite', () => {
   let resolver = null;
 
   setup(() => {
@@ -65,4 +65,4 @@
       assertEquals(expected, result);
     });
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/ash_common/fake_observables_test.js b/chrome/test/data/webui/chromeos/ash_common/fake_observables_test.js
index 0ae96a2..b0d4952 100644
--- a/chrome/test/data/webui/chromeos/ash_common/fake_observables_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/fake_observables_test.js
@@ -7,7 +7,7 @@
 
 import {assertEquals} from 'chrome://webui-test/chromeos/chai_assert.js';
 
-export function fakeObservablesTestSuite() {
+suite('fakeObservablesTestSuite', () => {
   let observables = null;
 
   setup(() => {
@@ -215,4 +215,4 @@
         'ObserveFoo_OnFooUpdated', 'foo', 0);
     return resolver.promise;
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.js b/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.js
index f5c9bb5..74dab5f 100644
--- a/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/keyboard_diagram_test.js
@@ -9,7 +9,7 @@
 
 import {assertEquals, assertNotEquals, assertThrows, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
 
-export function keyboardDiagramTestSuite() {
+suite('keyboardDiagramTestSuite', () => {
   /** @type {?KeyboardDiagramElement} */
   let diagramElement = null;
 
@@ -358,4 +358,4 @@
         diagramElement.root.querySelector('[data-code="41"]');
     assertEquals('Kana/alphanumeric switch', letterSwitchKey.ariaName);
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/ash_common/navigation_selector_test.js b/chrome/test/data/webui/chromeos/ash_common/navigation_selector_test.js
index ef065a9..c528114 100644
--- a/chrome/test/data/webui/chromeos/ash_common/navigation_selector_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/navigation_selector_test.js
@@ -8,7 +8,7 @@
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
-export function navigationSelectorTestSuite() {
+suite('navigationSelectorTestSuite', () => {
   /** @type {?NavigationSelectorElement} */
   let navigationElement = null;
 
@@ -80,4 +80,4 @@
     const iconElement = selectorElement.querySelector('iron-icon');
     assertFalse(isVisible(iconElement));
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
index 085660c..b2ac0b8 100644
--- a/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/navigation_view_panel_test.js
@@ -10,7 +10,7 @@
 import {assertEquals, assertFalse, assertThrows, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
-export function navigationViewPanelTestSuite() {
+suite('navigationViewPanelTestSuite', () => {
   /** @type {?NavigationViewPanelElement} */
   let viewElement = null;
 
@@ -363,4 +363,4 @@
     viewElement.selectPageById('does-not-exist');
     assertEquals('dummy3', viewElement.selectedItem.id);
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/ash_common/page_toolbar_test.js b/chrome/test/data/webui/chromeos/ash_common/page_toolbar_test.js
index c7d5877..ddcb0fd 100644
--- a/chrome/test/data/webui/chromeos/ash_common/page_toolbar_test.js
+++ b/chrome/test/data/webui/chromeos/ash_common/page_toolbar_test.js
@@ -7,7 +7,7 @@
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {isVisible} from 'chrome://webui-test/test_util.js';
 
-export function pageToolbarTestSuite() {
+suite('pageToolbarTestSuite', () => {
   /** @type {?PageToolbarElement} */
   let pageToolbarElement = null;
 
@@ -48,4 +48,4 @@
       assertEquals(expectedTitle, actualTitle);
     });
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js b/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js
index 1f140a6a..13db72a 100644
--- a/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js
+++ b/chrome/test/data/webui/chromeos/internet_detail_dialog_test.js
@@ -89,7 +89,8 @@
   }
 
   async function setupCellularNetwork(
-      isPrimary, isInhibited, connectedApn, customApnList, errorState) {
+      isPrimary, isInhibited, connectedApn, customApnList, errorState,
+      portalState) {
     await mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
 
     const cellularNetwork =
@@ -104,6 +105,7 @@
     cellularNetwork.typeProperties.cellular.connectedApn = connectedApn;
     cellularNetwork.typeProperties.cellular.customApnList = customApnList;
     cellularNetwork.errorState = errorState;
+    cellularNetwork.portalState = portalState;
 
     mojoApi_.setManagedPropertiesForTest(cellularNetwork);
     mojoApi_.setDeviceStateForTest({
@@ -309,7 +311,7 @@
       await setupCellularNetwork(
           /* isPrimary= */ true, /* isInhibited= */ false,
           /* connectedApn= */ undefined, /* customApnList= */ undefined,
-          errorState);
+          errorState, PortalState.kNoInternet);
 
       await init();
       const legacyApnElement =
@@ -333,6 +335,7 @@
         assertTrue(!!getApnList());
         assertTrue(getApnList().shouldOmitLinks);
         assertEquals(errorState, getApnList().errorState);
+        assertEquals(PortalState.kNoInternet, getApnList().portalState);
         const isApnListShowing = () =>
             internetDetailDialog.shadowRoot.querySelector('iron-collapse')
                 .opened;
@@ -348,18 +351,27 @@
         internetDetailDialog.onDeviceStateListChanged();
         await flushAsync();
         assertEquals(accessPointName, getApnSectionSublabel());
+        assertFalse(
+            internetDetailDialog.shadowRoot.querySelector('#apnRowSublabel')
+                .hasAttribute('warning'));
         assertFalse(isApnListShowing());
 
-        // Update the APN's name property.
+        // Update the APN's name property and add a restricted connectivity
+        // state.
         const name = 'name';
         await setupCellularNetwork(
             /* isPrimary= */ true, /* isInhibited= */ false,
-            {accessPointName: accessPointName, name: name});
+            {accessPointName: accessPointName, name: name},
+            /* customApnList= */ undefined, /* errorState= */ undefined,
+            PortalState.kNoInternet);
 
         // Force a refresh.
         internetDetailDialog.onDeviceStateListChanged();
         await flushAsync();
         assertEquals(name, getApnSectionSublabel());
+        assertTrue(
+            internetDetailDialog.shadowRoot.querySelector('#apnRowSublabel')
+                .hasAttribute('warning'));
         assertFalse(isApnListShowing());
 
         // Expand the section, the sublabel should no longer show.
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
index d6496a7..5fbf3ebb 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
@@ -25,7 +25,6 @@
     ":file_attachment_test",
     ":help_content_test",
     ":mojo_interface_provider_test",
-    ":os_feedback_unified_test",
     ":search_page_test",
     ":share_data_page_test",
   ]
@@ -59,6 +58,7 @@
     "//ash/webui/os_feedback_ui/resources:search_page",
     "//ash/webui/os_feedback_ui/resources:share_data_page",
   ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
 js_library("file_attachment_test") {
@@ -90,11 +90,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("os_feedback_unified_test") {
-  deps = []
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("search_page_test") {
   deps = [
     "..:chai_assert",
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
index 5a011d0a..c06b418 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/confirmation_page_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://webui-test/mojo_webui_test_support.js';
+
 import {ConfirmationPageElement} from 'chrome://os-feedback/confirmation_page.js';
 import {FakeFeedbackServiceProvider} from 'chrome://os-feedback/fake_feedback_service_provider.js';
 import {FeedbackFlowState} from 'chrome://os-feedback/feedback_flow.js';
@@ -30,7 +32,7 @@
     'experience and will be reviewed by our team. Because of the large ' +
     'number of reports, we won’t be able to send a reply.';
 
-export function confirmationPageTest() {
+suite('confirmationPageTest', () => {
   /** @type {?ConfirmationPageElement} */
   let page = null;
 
@@ -389,4 +391,4 @@
     verifyRecordPostSubmitActionCalled(
         false, FeedbackAppPostSubmitAction.kOpenDiagnosticsApp);
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/fake_help_content_provider_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/fake_help_content_provider_test.js
index 5a011a8..1150055 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/fake_help_content_provider_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/fake_help_content_provider_test.js
@@ -2,13 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://webui-test/mojo_webui_test_support.js';
+
 import {fakeHelpContentList, fakeSearchRequest, fakeSearchResponse} from 'chrome://os-feedback/fake_data.js';
 import {FakeHelpContentProvider} from 'chrome://os-feedback/fake_help_content_provider.js';
 import {HelpContentList, SearchResponse} from 'chrome://os-feedback/feedback_types.js';
 import {mojoString16ToString} from 'chrome://resources/js/mojo_type_util.js';
 import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chromeos/chai_assert.js';
 
-export function fakeHelpContentProviderTestSuite() {
+suite('fakeHelpContentProviderTestSuite', () => {
   /** @type {?FakeHelpContentProvider} */
   let provider = null;
 
@@ -57,4 +59,4 @@
     assertEquals(
         mojoString16ToString(fakeSearchRequest.query), provider.lastQuery);
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
index 88898a2..91a85a7 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/feedback_flow_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://webui-test/mojo_webui_test_support.js';
+
 import {fakeFeedbackContext, fakeFeedbackContextWithoutLinkedCrossDevicePhone, fakeInternalUserFeedbackContext, fakePngData, fakeSearchResponse} from 'chrome://os-feedback/fake_data.js';
 import {FakeFeedbackServiceProvider} from 'chrome://os-feedback/fake_feedback_service_provider.js';
 import {FakeHelpContentProvider} from 'chrome://os-feedback/fake_help_content_provider.js';
@@ -18,7 +20,7 @@
 
 import {eventToPromise, isVisible} from '../test_util.js';
 
-export function FeedbackFlowTestSuite() {
+suite('FeedbackFlowTestSuite', () => {
   /** @type {?FeedbackFlowElement} */
   let page = null;
 
@@ -1368,4 +1370,4 @@
         // Verify that the getFeedbackContext is not called.
         assertEquals(0, feedbackServiceProvider.getFeedbackContextCallCount());
       });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
index a24be69..49762c3f 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
@@ -2,16 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
+import 'chrome://os-feedback/strings.m.js';
 
 import {FakeFeedbackServiceProvider} from 'chrome://os-feedback/fake_feedback_service_provider.js';
 import {FeedbackAppPreSubmitAction} from 'chrome://os-feedback/feedback_types.js';
 import {FileAttachmentElement} from 'chrome://os-feedback/file_attachment.js';
 import {setFeedbackServiceProviderForTesting} from 'chrome://os-feedback/mojo_interface_provider.js';
 import {getDeepActiveElement} from 'chrome://resources/ash/common/util.js';
+import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
-import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
 import {eventToPromise, isVisible} from '../test_util.js';
 
 /** @type {string} */
@@ -19,7 +20,7 @@
 
 const MAX_ATTACH_FILE_SIZE = 10 * 1024 * 1024;
 
-export function fileAttachmentTestSuite() {
+suite('fileAttachmentTestSuite', () => {
   /** @type {?FileAttachmentElement} */
   let page = null;
 
@@ -333,8 +334,7 @@
     assertTrue(isVisible(selectedImageButton));
   });
 
-  // Test that clicking the image will open preview dialog and set the
-  // focus on the close dialog icon button.
+  // Test that clicking the image will open preview dialog.
   test('selectedImagePreviewDialog', async () => {
     await initializePage();
     verifyRecordPreSubmitActionCallCount(
@@ -374,15 +374,5 @@
 
     // The preview dialog's close icon button is visible now.
     assertTrue(isVisible(closeDialogButton));
-    // The preview dialog's close icon button is focused.
-    assertEquals(closeDialogButton, getDeepActiveElement());
-
-    // Press enter should close the preview dialog.
-    closeDialogButton.dispatchEvent(
-        new KeyboardEvent('keydown', {key: 'Enter'}));
-    await flushTasks();
-
-    // The preview dialog's close icon button is not visible now.
-    assertFalse(isVisible(closeDialogButton));
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
index bec70f5..a285ac9 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/help_content_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://webui-test/mojo_webui_test_support.js';
+
 import {fakeHelpContentList, fakePopularHelpContentList} from 'chrome://os-feedback/fake_data.js';
 import {HelpContentList, HelpContentType, SearchResult} from 'chrome://os-feedback/feedback_types.js';
 import {HelpContentElement} from 'chrome://os-feedback/help_content.js';
@@ -11,419 +13,430 @@
 
 import {isVisible} from '../test_util.js';
 
-/**
- * @suppress {missingProperties} for test.skip is not defined in mocha-2.5.js
- */
-export function helpContentTestSuite() {
-  /** @type {?HelpContentElement} */
-  let helpContentElement = null;
+suite(
+    'helpContentTestSuite',
+    /**
+     * @suppress {missingProperties} for test.skip is not defined in
+     * mocha-2.5.js
+     */
+    () => {
+      /** @type {?HelpContentElement} */
+      let helpContentElement = null;
 
-  const noContentImgSelector = 'img[alt="Help content isn\'t available"]';
-  const noContentSvgSelector = '#noContentSvg';
-  const offlineImgSelector = 'img[alt="Device is offline"]';
-  const offlineSvgSelector = '#offlineSvg';
+      const noContentImgSelector = 'img[alt="Help content isn\'t available"]';
+      const noContentSvgSelector = '#noContentSvg';
+      const offlineImgSelector = 'img[alt="Device is offline"]';
+      const offlineSvgSelector = '#offlineSvg';
 
-  setup(() => {
-    document.body.innerHTML = trustedTypes.emptyHTML;
-  });
+      setup(() => {
+        document.body.innerHTML = trustedTypes.emptyHTML;
+      });
 
-  teardown(() => {
-    helpContentElement.remove();
-    helpContentElement = null;
-  });
+      teardown(() => {
+        helpContentElement.remove();
+        helpContentElement = null;
+      });
 
-  /**
-   * @param {!HelpContentList} contentList
-   * @param {boolean} isQueryEmpty
-   * @param {boolean} isPopularContent
-   *
-   */
-  function initializeHelpContentElement(
-      contentList, isQueryEmpty, isPopularContent) {
-    assertFalse(!!helpContentElement);
-    helpContentElement =
-        /** @type {!HelpContentElement} */ (
-            document.createElement('help-content'));
-    assertTrue(!!helpContentElement);
+      /**
+       * @param {!HelpContentList} contentList
+       * @param {boolean} isQueryEmpty
+       * @param {boolean} isPopularContent
+       *
+       */
+      function initializeHelpContentElement(
+          contentList, isQueryEmpty, isPopularContent) {
+        assertFalse(!!helpContentElement);
+        helpContentElement =
+            /** @type {!HelpContentElement} */ (
+                document.createElement('help-content'));
+        assertTrue(!!helpContentElement);
 
-    helpContentElement.searchResult = {
-      contentList: contentList,
-      isQueryEmpty: isQueryEmpty,
-      isPopularContent: isPopularContent,
-    };
+        helpContentElement.searchResult = {
+          contentList: contentList,
+          isQueryEmpty: isQueryEmpty,
+          isPopularContent: isPopularContent,
+        };
 
-    document.body.appendChild(helpContentElement);
+        document.body.appendChild(helpContentElement);
 
-    return flushTasks();
-  }
+        return flushTasks();
+      }
 
-  /**
-   * Helper function that returns the first Element within the element that
-   * matches the specified selector.
-   * @param {string} selector
-   * */
-  function getElement(selector) {
-    return helpContentElement.shadowRoot.querySelector(selector);
-  }
+      /**
+       * Helper function that returns the first Element within the element that
+       * matches the specified selector.
+       * @param {string} selector
+       * */
+      function getElement(selector) {
+        return helpContentElement.shadowRoot.querySelector(selector);
+      }
 
-  /**
-   * @param {!Element} linkElement
-   * @param {!HelpContentType} expectedContentType
-   * */
-  function verifyIconName(linkElement, expectedContentType) {
-    assertEquals(1, linkElement.children.length);
-    // The first child is an iron-icon.
-    const iconName = linkElement.children[0].icon;
+      /**
+       * @param {!Element} linkElement
+       * @param {!HelpContentType} expectedContentType
+       * */
+      function verifyIconName(linkElement, expectedContentType) {
+        assertEquals(1, linkElement.children.length);
+        // The first child is an iron-icon.
+        const iconName = linkElement.children[0].icon;
 
-    if (expectedContentType === HelpContentType.kForum) {
-      assertEquals(iconName, 'content-type:forum');
-    } else {
-      // Both kArticle or kUnknown have the same icon.
-      assertEquals(iconName, 'content-type:article');
-    }
-  }
+        if (expectedContentType === HelpContentType.kForum) {
+          assertEquals(iconName, 'content-type:forum');
+        } else {
+          // Both kArticle or kUnknown have the same icon.
+          assertEquals(iconName, 'content-type:article');
+        }
+      }
 
-  // Verify that all popular help content are displayed.
-  function verifyPopularHelpContent() {
-    assertEquals(2, getElement('dom-repeat').items.length);
-    const helpLinks =
-        helpContentElement.shadowRoot.querySelectorAll('.help-item a');
-    assertEquals(2, helpLinks.length);
+      // Verify that all popular help content are displayed.
+      function verifyPopularHelpContent() {
+        assertEquals(2, getElement('dom-repeat').items.length);
+        const helpLinks =
+            helpContentElement.shadowRoot.querySelectorAll('.help-item a');
+        assertEquals(2, helpLinks.length);
 
-    // Verify the help links are displayed in order with correct title, url and
-    // icon.
-    assertEquals('fake article', helpLinks[0].innerText.trim());
-    assertEquals(
-        'https://support.google.com/chromebook/?q=article', helpLinks[0].href);
-    verifyIconName(helpLinks[0], fakePopularHelpContentList[0].contentType);
+        // Verify the help links are displayed in order with correct title, url
+        // and icon.
+        assertEquals('fake article', helpLinks[0].innerText.trim());
+        assertEquals(
+            'https://support.google.com/chromebook/?q=article',
+            helpLinks[0].href);
+        verifyIconName(helpLinks[0], fakePopularHelpContentList[0].contentType);
 
-    assertEquals('fake forum', helpLinks[1].innerText.trim());
-    assertEquals(
-        'https://support.google.com/chromebook/?q=forum', helpLinks[1].href);
-    verifyIconName(helpLinks[1], fakePopularHelpContentList[1].contentType);
-  }
+        assertEquals('fake forum', helpLinks[1].innerText.trim());
+        assertEquals(
+            'https://support.google.com/chromebook/?q=forum',
+            helpLinks[1].href);
+        verifyIconName(helpLinks[1], fakePopularHelpContentList[1].contentType);
+      }
 
-  function goOffline() {
-    // Simulate going offline.
-    window.dispatchEvent(new CustomEvent('offline'));
-    return flushTasks();
-  }
+      function goOffline() {
+        // Simulate going offline.
+        window.dispatchEvent(new CustomEvent('offline'));
+        return flushTasks();
+      }
 
-  function goOnline() {
-    // Simulate going online.
-    window.dispatchEvent(new CustomEvent('online'));
-    return flushTasks();
-  }
+      function goOnline() {
+        // Simulate going online.
+        window.dispatchEvent(new CustomEvent('online'));
+        return flushTasks();
+      }
 
-  /**
-   * Test that expected HTML elements are in the element when query is empty.
-   */
-  test('ColdStart', async () => {
-    await initializeHelpContentElement(
-        fakePopularHelpContentList, /* isQueryEmpty= */ true,
-        /* isPopularContent= */ true);
+      /**
+       * Test that expected HTML elements are in the element when query is
+       * empty.
+       */
+      test('ColdStart', async () => {
+        await initializeHelpContentElement(
+            fakePopularHelpContentList, /* isQueryEmpty= */ true,
+            /* isPopularContent= */ true);
 
-    // Verify the title is in the helpContentElement.
-    const title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals('Top help content', title.textContent);
+        // Verify the title is in the helpContentElement.
+        const title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals('Top help content', title.textContent);
 
-    // Verify the help content Icon is in the page.
-    const helpContentIcon = getElement('#helpContentIcon');
-    assertTrue(!!helpContentIcon);
-    // The help content icon is not visible.
-    assertFalse(isVisible(helpContentIcon));
+        // Verify the help content Icon is in the page.
+        const helpContentIcon = getElement('#helpContentIcon');
+        assertTrue(!!helpContentIcon);
+        // The help content icon is not visible.
+        assertFalse(isVisible(helpContentIcon));
 
-    verifyPopularHelpContent();
-  });
+        verifyPopularHelpContent();
+      });
 
-  /**
-   * Test that expected HTML elements are in the element when the query is not
-   * empty and there are matches.
-   */
-  test('SuggestedHelpContentLoaded', async () => {
-    await initializeHelpContentElement(
-        fakeHelpContentList, /* isQueryEmpty =*/ false,
-        /* isPopularContent =*/ false);
+      /**
+       * Test that expected HTML elements are in the element when the query is
+       * not empty and there are matches.
+       */
+      test('SuggestedHelpContentLoaded', async () => {
+        await initializeHelpContentElement(
+            fakeHelpContentList, /* isQueryEmpty =*/ false,
+            /* isPopularContent =*/ false);
 
-    // Verify the title is in the helpContentElement.
-    const title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals('Suggested help content', title.textContent);
+        // Verify the title is in the helpContentElement.
+        const title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals('Suggested help content', title.textContent);
 
-    // The help content icon is visible.
-    const helpContentIcon = getElement('#helpContentIcon');
-    assertTrue(isVisible(helpContentIcon));
+        // The help content icon is visible.
+        const helpContentIcon = getElement('#helpContentIcon');
+        assertTrue(isVisible(helpContentIcon));
 
-    // Verify the help content is populated with correct number of items.
-    assertEquals(5, getElement('dom-repeat').items.length);
-    const helpLinks =
-        helpContentElement.shadowRoot.querySelectorAll('.help-item a');
-    assertEquals(5, helpLinks.length);
+        // Verify the help content is populated with correct number of items.
+        assertEquals(5, getElement('dom-repeat').items.length);
+        const helpLinks =
+            helpContentElement.shadowRoot.querySelectorAll('.help-item a');
+        assertEquals(5, helpLinks.length);
 
-    // Verify the help links are displayed in order with correct title, url and
-    // icon.
-    assertEquals('Fix connection problems', helpLinks[0].innerText.trim());
-    assertEquals(
-        'https://support.google.com/chromebook/?q=6318213', helpLinks[0].href);
-    verifyIconName(helpLinks[0], fakeHelpContentList[0].contentType);
+        // Verify the help links are displayed in order with correct title, url
+        // and icon.
+        assertEquals('Fix connection problems', helpLinks[0].innerText.trim());
+        assertEquals(
+            'https://support.google.com/chromebook/?q=6318213',
+            helpLinks[0].href);
+        verifyIconName(helpLinks[0], fakeHelpContentList[0].contentType);
 
-    assertEquals(
-        'Why won\'t my wireless mouse with a USB piece wor...?',
-        helpLinks[1].innerText.trim());
-    assertEquals(
-        'https://support.google.com/chromebook/?q=123920509',
-        helpLinks[1].href);
-    verifyIconName(helpLinks[1], fakeHelpContentList[1].contentType);
+        assertEquals(
+            'Why won\'t my wireless mouse with a USB piece wor...?',
+            helpLinks[1].innerText.trim());
+        assertEquals(
+            'https://support.google.com/chromebook/?q=123920509',
+            helpLinks[1].href);
+        verifyIconName(helpLinks[1], fakeHelpContentList[1].contentType);
 
-    assertEquals(
-        'Wifi Issues - only on Chromebooks', helpLinks[2].innerText.trim());
-    assertEquals(
-        'https://support.google.com/chromebook/?q=114174470',
-        helpLinks[2].href);
-    verifyIconName(helpLinks[2], fakeHelpContentList[2].contentType);
+        assertEquals(
+            'Wifi Issues - only on Chromebooks', helpLinks[2].innerText.trim());
+        assertEquals(
+            'https://support.google.com/chromebook/?q=114174470',
+            helpLinks[2].href);
+        verifyIconName(helpLinks[2], fakeHelpContentList[2].contentType);
 
-    assertEquals('Network Connectivity Fault', helpLinks[3].innerText.trim());
-    assertEquals(
-        'https://support.google.com/chromebook/?q=131459420',
-        helpLinks[3].href);
-    verifyIconName(helpLinks[3], fakeHelpContentList[3].contentType);
+        assertEquals(
+            'Network Connectivity Fault', helpLinks[3].innerText.trim());
+        assertEquals(
+            'https://support.google.com/chromebook/?q=131459420',
+            helpLinks[3].href);
+        verifyIconName(helpLinks[3], fakeHelpContentList[3].contentType);
 
-    assertEquals(
-        'Connected to WiFi but can\'t connect to the internet',
-        helpLinks[4].innerText.trim());
-    assertEquals(
-        'https://support.google.com/chromebook/?q=22864239', helpLinks[4].href);
-    verifyIconName(helpLinks[4], fakeHelpContentList[4].contentType);
+        assertEquals(
+            'Connected to WiFi but can\'t connect to the internet',
+            helpLinks[4].innerText.trim());
+        assertEquals(
+            'https://support.google.com/chromebook/?q=22864239',
+            helpLinks[4].href);
+        verifyIconName(helpLinks[4], fakeHelpContentList[4].contentType);
 
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-  });
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+      });
 
 
-  /**
-   * Test that expected HTML elements are in the element when query is not empty
-   * and there are no matches.
-   */
-  test('NoMatches', async () => {
-    await initializeHelpContentElement(
-        fakePopularHelpContentList, /* isQueryEmpty= */ false,
-        /* isPopularContent= */ true);
+      /**
+       * Test that expected HTML elements are in the element when query is not
+       * empty and there are no matches.
+       */
+      test('NoMatches', async () => {
+        await initializeHelpContentElement(
+            fakePopularHelpContentList, /* isQueryEmpty= */ false,
+            /* isPopularContent= */ true);
 
-    // Verify the title is in the helpContentElement.
-    const title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals(
-        'No suggested content. See top help content.', title.textContent);
+        // Verify the title is in the helpContentElement.
+        const title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals(
+            'No suggested content. See top help content.', title.textContent);
 
-    // The help content icon is not visible.
-    assertFalse(isVisible(getElement('#helpContentIcon')));
+        // The help content icon is not visible.
+        assertFalse(isVisible(getElement('#helpContentIcon')));
 
-    verifyPopularHelpContent();
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-  });
+        verifyPopularHelpContent();
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+      });
 
-  /**
-   * Test that the offline-only elements render when offline, and that the
-   * online-only elements render when online.
-   */
-  // TODO(crbug.com/1401615): Re-enable flaky test.
-  test.skip('OfflineMessage', async () => {
-    loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': false});
-    await initializeHelpContentElement(
-        fakePopularHelpContentList, /* isQueryEmpty= */ true,
-        /* isPopularContent= */ true);
+      /**
+       * Test that the offline-only elements render when offline, and that the
+       * online-only elements render when online.
+       */
+      // TODO(crbug.com/1401615): Re-enable flaky test.
+      test.skip('OfflineMessage', async () => {
+        loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': false});
+        await initializeHelpContentElement(
+            fakePopularHelpContentList, /* isQueryEmpty= */ true,
+            /* isPopularContent= */ true);
 
-    await goOffline();
+        await goOffline();
 
-    // Offline-only content should exist in the DOM when offline.
-    assertTrue(isVisible(getElement(offlineImgSelector)));
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
+        // Offline-only content should exist in the DOM when offline.
+        assertTrue(isVisible(getElement(offlineImgSelector)));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
 
-    // Online-only content should *not* exist in the DOM when offline.
-    assertFalse(isVisible(getElement('.help-item-icon')));
+        // Online-only content should *not* exist in the DOM when offline.
+        assertFalse(isVisible(getElement('.help-item-icon')));
 
-    await goOnline();
+        await goOnline();
 
-    // Offline-only content should *not* exist in the DOM when online.
-    assertFalse(isVisible(getElement('offlineImgSelector')));
+        // Offline-only content should *not* exist in the DOM when online.
+        assertFalse(isVisible(getElement('offlineImgSelector')));
 
-    // Online-only content should exist in the DOM when online.
-    assertTrue(isVisible(getElement('.help-item-icon')));
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-  });
+        // Online-only content should exist in the DOM when online.
+        assertTrue(isVisible(getElement('.help-item-icon')));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+      });
 
-  // Test that the correct SVG appears when jelly colors enabled.
-  test('OfflineMessage_JellyEnabled', async () => {
-    loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': true});
-    await initializeHelpContentElement(
-        fakePopularHelpContentList, /* isQueryEmpty= */ true,
-        /* isPopularContent= */ true);
+      // Test that the correct SVG appears when jelly colors enabled.
+      test('OfflineMessage_JellyEnabled', async () => {
+        loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': true});
+        await initializeHelpContentElement(
+            fakePopularHelpContentList, /* isQueryEmpty= */ true,
+            /* isPopularContent= */ true);
 
-    await goOffline();
+        await goOffline();
 
-    // Offline-only content should exist in the DOM when offline.
-    assertTrue(isVisible(getElement(offlineSvgSelector)));
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentSvgSelector)));
+        // Offline-only content should exist in the DOM when offline.
+        assertTrue(isVisible(getElement(offlineSvgSelector)));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentSvgSelector)));
 
-    // Online-only content should *not* exist in the DOM when offline.
-    assertFalse(isVisible(getElement('.help-item-icon')));
+        // Online-only content should *not* exist in the DOM when offline.
+        assertFalse(isVisible(getElement('.help-item-icon')));
 
-    await goOnline();
+        await goOnline();
 
-    // Offline-only content should *not* exist in the DOM when online.
-    assertFalse(isVisible(getElement('offlineImgSelector')));
+        // Offline-only content should *not* exist in the DOM when online.
+        assertFalse(isVisible(getElement('offlineImgSelector')));
 
-    // Online-only content should exist in the DOM when online.
-    assertTrue(isVisible(getElement('.help-item-icon')));
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentSvgSelector)));
-  });
+        // Online-only content should exist in the DOM when online.
+        assertTrue(isVisible(getElement('.help-item-icon')));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentSvgSelector)));
+      });
 
-  /**
-   * Test that the help content title shows the correct text when the query
-   * doesn't match and the device goes offline.
-   */
-  test('OfflineTitleWhenNoMatches', async () => {
-    // Initialize element with no query matches.
-    await initializeHelpContentElement(
-        fakePopularHelpContentList, /* isQueryEmpty= */ false,
-        /* isPopularContent= */ true);
+      /**
+       * Test that the help content title shows the correct text when the query
+       * doesn't match and the device goes offline.
+       */
+      test('OfflineTitleWhenNoMatches', async () => {
+        // Initialize element with no query matches.
+        await initializeHelpContentElement(
+            fakePopularHelpContentList, /* isQueryEmpty= */ false,
+            /* isPopularContent= */ true);
 
-    // Verify the title is what we expect when there are no matches.
-    let title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals(
-        'No suggested content. See top help content.', title.textContent);
+        // Verify the title is what we expect when there are no matches.
+        let title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals(
+            'No suggested content. See top help content.', title.textContent);
 
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-    await goOffline();
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+        await goOffline();
 
-    title = getElement('.help-content-label');
-    // When offline, we expect the title to always be "Top help content".
-    assertTrue(!!title);
-    assertEquals('Top help content', title.textContent);
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-  });
+        title = getElement('.help-content-label');
+        // When offline, we expect the title to always be "Top help content".
+        assertTrue(!!title);
+        assertEquals('Top help content', title.textContent);
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+      });
 
-  /**
-   * Test that the help content title shows the correct text when we previously
-   * were displaying suggested help content and the device goes offline.
-   */
-  test('OfflineTitleWhenSuggestedContentExists', async () => {
-    // Initialize element with no query matches.
-    await initializeHelpContentElement(
-        fakePopularHelpContentList, /* isQueryEmpty= */ false,
-        /* isPopularContent= */ false);
+      /**
+       * Test that the help content title shows the correct text when we
+       * previously were displaying suggested help content and the device goes
+       * offline.
+       */
+      test('OfflineTitleWhenSuggestedContentExists', async () => {
+        // Initialize element with no query matches.
+        await initializeHelpContentElement(
+            fakePopularHelpContentList, /* isQueryEmpty= */ false,
+            /* isPopularContent= */ false);
 
-    // Verify the title is what we expect when there are suggested matches.
-    let title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals('Suggested help content', title.textContent);
+        // Verify the title is what we expect when there are suggested matches.
+        let title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals('Suggested help content', title.textContent);
 
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
 
-    await goOffline();
+        await goOffline();
 
-    title = getElement('.help-content-label');
-    // When offline, we expect the title to always be "Top help content".
-    assertTrue(!!title);
-    assertEquals('Top help content', title.textContent);
+        title = getElement('.help-content-label');
+        // When offline, we expect the title to always be "Top help content".
+        assertTrue(!!title);
+        assertEquals('Top help content', title.textContent);
 
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-  });
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+      });
 
-  /**
-   * Test that when help content isn't available, the correct image is
-   * displayed.
-   *
-   * Case 1: the query is empty.
-   */
-  // TODO(crbug.com/1401615): Flaky.
-  test.skip('TopHelpContentNotAvailable', async () => {
-    loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': false});
-    // Initialize element with no content and empty query.
-    await initializeHelpContentElement(
-        /* contentList= */[], /* isQueryEmpty= */ true,
-        /* isPopularContent= */ true);
+      /**
+       * Test that when help content isn't available, the correct image is
+       * displayed.
+       *
+       * Case 1: the query is empty.
+       */
+      // TODO(crbug.com/1401615): Flaky.
+      test.skip('TopHelpContentNotAvailable', async () => {
+        loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': false});
+        // Initialize element with no content and empty query.
+        await initializeHelpContentElement(
+            /* contentList= */[], /* isQueryEmpty= */ true,
+            /* isPopularContent= */ true);
 
-    // Verify the title is what we expect when showing top content.
-    const title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals('Top help content', title.textContent);
+        // Verify the title is what we expect when showing top content.
+        const title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals('Top help content', title.textContent);
 
-    // Content not available image should be visible.
-    assertTrue(isVisible(getElement(noContentImgSelector)));
-    assertFalse(isVisible(getElement(offlineImgSelector)));
+        // Content not available image should be visible.
+        assertTrue(isVisible(getElement(noContentImgSelector)));
+        assertFalse(isVisible(getElement(offlineImgSelector)));
 
-    await goOffline();
+        await goOffline();
 
-    // When offline, should show offline message.
-    assertTrue(isVisible(getElement(offlineImgSelector)));
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-  });
+        // When offline, should show offline message.
+        assertTrue(isVisible(getElement(offlineImgSelector)));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+      });
 
-  // Test that the correct SVG appears when Jelly colors enabled.
-  test('TopHelpContentNotAvailable_JellyEnabled', async () => {
-    loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': true});
-    // Initialize element with no content and empty query.
-    await initializeHelpContentElement(
-        /* contentList= */[], /* isQueryEmpty= */ true,
-        /* isPopularContent= */ true);
+      // Test that the correct SVG appears when Jelly colors enabled.
+      test('TopHelpContentNotAvailable_JellyEnabled', async () => {
+        loadTimeData.overrideValues({'isJellyEnabledForOsFeedback': true});
+        // Initialize element with no content and empty query.
+        await initializeHelpContentElement(
+            /* contentList= */[], /* isQueryEmpty= */ true,
+            /* isPopularContent= */ true);
 
-    // Verify the title is what we expect when showing top content.
-    const title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals('Top help content', title.textContent);
+        // Verify the title is what we expect when showing top content.
+        const title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals('Top help content', title.textContent);
 
-    // Content not available image should be visible.
-    assertTrue(isVisible(getElement(noContentSvgSelector)));
-    assertFalse(isVisible(getElement(offlineSvgSelector)));
+        // Content not available image should be visible.
+        assertTrue(isVisible(getElement(noContentSvgSelector)));
+        assertFalse(isVisible(getElement(offlineSvgSelector)));
 
-    await goOffline();
+        await goOffline();
 
-    // When offline, should show offline message.
-    assertTrue(isVisible(getElement(offlineSvgSelector)));
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentSvgSelector)));
-  });
+        // When offline, should show offline message.
+        assertTrue(isVisible(getElement(offlineSvgSelector)));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentSvgSelector)));
+      });
 
-  /**
-   * Test that when help content isn't available, the correct image is
-   * displayed.
-   *
-   * Case 2: the query is NOT empty.
-   */
-  // TODO(crbug.com/1401615): Flaky.
-  test.skip('SuggestedHelpContentNotAvailable', async () => {
-    // Initialize element with no content and empty query.
-    await initializeHelpContentElement(
-        /* contentList= */[], /* isQueryEmpty= */ false,
-        /* isPopularContent= */ false);
+      /**
+       * Test that when help content isn't available, the correct image is
+       * displayed.
+       *
+       * Case 2: the query is NOT empty.
+       */
+      // TODO(crbug.com/1401615): Flaky.
+      test.skip('SuggestedHelpContentNotAvailable', async () => {
+        // Initialize element with no content and empty query.
+        await initializeHelpContentElement(
+            /* contentList= */[], /* isQueryEmpty= */ false,
+            /* isPopularContent= */ false);
 
-    // Verify the title is what we expect when there may be suggested matches.
-    const title = getElement('.help-content-label');
-    assertTrue(!!title);
-    assertEquals('Suggested help content', title.textContent);
+        // Verify the title is what we expect when there may be suggested
+        // matches.
+        const title = getElement('.help-content-label');
+        assertTrue(!!title);
+        assertEquals('Suggested help content', title.textContent);
 
-    // Content not available image should be visible.
-    assertTrue(isVisible(getElement(noContentImgSelector)));
-    assertFalse(isVisible(getElement(offlineImgSelector)));
+        // Content not available image should be visible.
+        assertTrue(isVisible(getElement(noContentImgSelector)));
+        assertFalse(isVisible(getElement(offlineImgSelector)));
 
-    await goOffline();
+        await goOffline();
 
-    // When offline, should show offline message.
-    assertTrue(isVisible(getElement(offlineImgSelector)));
-    // Content not available image should be invisible.
-    assertFalse(isVisible(getElement(noContentImgSelector)));
-  });
-}
+        // When offline, should show offline message.
+        assertTrue(isVisible(getElement(offlineImgSelector)));
+        // Content not available image should be invisible.
+        assertFalse(isVisible(getElement(noContentImgSelector)));
+      });
+    });
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/mojo_interface_provider_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/mojo_interface_provider_test.js
index b2690524..7760bad 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/mojo_interface_provider_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/mojo_interface_provider_test.js
@@ -2,14 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://webui-test/mojo_webui_test_support.js';
+
 import {FakeFeedbackServiceProvider} from 'chrome://os-feedback/fake_feedback_service_provider.js';
 import {FakeHelpContentProvider} from 'chrome://os-feedback/fake_help_content_provider.js';
 import {FeedbackServiceProviderInterface, HelpContentProviderInterface} from 'chrome://os-feedback/feedback_types.js';
 import {getFeedbackServiceProvider, getHelpContentProvider, setFeedbackServiceProviderForTesting, setHelpContentProviderForTesting} from 'chrome://os-feedback/mojo_interface_provider.js';
-
 import {assertEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js';
 
-export function fakeMojoProviderTestSuite() {
+suite('fakeMojoProviderTestSuite', () => {
   test('SettingGettingTestHelpContentProvider', () => {
     const fake_provider =
         /** @type {HelpContentProviderInterface} */ (
@@ -35,4 +36,4 @@
     const provider = getFeedbackServiceProvider();
     assertTrue(!!provider);
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
index eafcbe6..5ec34a9 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_browsertest.js
@@ -4,18 +4,11 @@
 
 /**
  * @fileoverview Test fixture for chrome://os-feedback.
- * Unifieid polymer testing suite for feedback tool.
+ * Testing suite for feedback tool.
  *
- * To run all tests in a single instance (default, faster):
+ * To run all tests:
  * `browser_tests --gtest_filter=OSFeedbackBrowserTest*``
  *
- * To run each test in a new instance:
- * `browser_tests --run-manual --gtest_filter=OSFeedbackBrowserTest.MANUAL_*``
- *
- * To run a single test suite, such as 'ConfirmationPageTest':
- * `browser_tests --run-manual \
- *  --gtest_filter=OSFeedbackBrowserTest.MANUAL_ConfirmationPageTest`
- *
  */
 
 GEN_INCLUDE(['//chrome/test/data/webui/chromeos/polymer_browser_test_base.js']);
@@ -26,12 +19,6 @@
 
 this.OSFeedbackBrowserTest = class extends PolymerTest {
   /** @override */
-  get browsePreload() {
-    return 'chrome://os-feedback/test_loader.html?module=chromeos/' +
-        'os_feedback_ui/os_feedback_unified_test.js';
-  }
-
-  /** @override */
   get featureList() {
     return {
       enabled: ['ash::features::kOsFeedbackJelly', 'chromeos::features::kJelly']
@@ -39,33 +26,33 @@
   }
 };
 
-// List of names of suites in unified test to register for individual debugging.
-// You must register all suites in unified test here as well for consistency,
-// although technically is not necessary.
-const debug_suites_list = [
-  'confirmationPageTest',
-  'fakeHelpContentProviderTest',
-  'fakeMojoProviderTest',
-  'feedbackFlowTest',
-  'fileAttachmentTest',
-  'helpContentTest',
-  'searchPageTest',
-  'shareDataPageTest',
+// Test suites for OS Feedback. To disable a test suite add 'DISABLED_All' as
+// the case name.
+// Ex. ['ConfirmationPage', 'confirmation_page_test.js', 'DISABLED_All']
+// TODO(crbug.com/1401615): Flaky.
+const tests = [
+  ['ConfirmationPage', 'confirmation_page_test.js', 'DISABLED_All'],
+  [
+    'FakeHelpContentProvider', 'fake_help_content_provider_test.js',
+    'DISABLED_All'
+  ],
+  ['MojoInterfaceProvider', 'mojo_interface_provider_test.js', 'DISABLED_All'],
+  ['FeedbackFlow', 'feedback_flow_test.js', 'DISABLED_All'],
+  ['FileAttachment', 'file_attachment_test.js', 'DISABLED_All'],
+  ['HelpContent', 'help_content_test.js', 'DISABLED_All'],
+  ['SearchPage', 'search_page_test.js', 'DISABLED_All'],
+  ['ShareDataPage', 'share_data_page_test.js', 'DISABLED_All'],
 ];
 
-// TODO(crbug.com/1401615): Flaky.
-TEST_F(
-    'OSFeedbackBrowserTest', 'DISABLED_All', function() {
-      assertDeepEquals(
-          debug_suites_list, test_suites_list,
-          'List of registered tests suites and debug suites do not match.\n' +
-              'Did you forget to add your test in debug_suites_list?');
-      mocha.run();
-    });
+for (const [suiteName, module, caseName] of tests) {
+  const className = `OSFeedbackBrowserTest_${suiteName}`;
+  this[className] = class extends OSFeedbackBrowserTest {
+    /** @override */
+    get browsePreload() {
+      return 'chrome://os-feedback/test_loader.html?module=' +
+          `chromeos/os_feedback_ui/${module}`;
+    }
+  }
 
-// Register each suite listed as individual tests for debugging purposes.
-for (const suiteName of debug_suites_list) {
-  TEST_F('OSFeedbackBrowserTest', `MANUAL_${suiteName}`, function() {
-    runMochaSuite(suiteName);
-  });
+  TEST_F(className, caseName || 'All', () => mocha.run());
 }
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
deleted file mode 100644
index 5b837db..0000000
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/os_feedback_unified_test.js
+++ /dev/null
@@ -1,30 +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.
-
-import 'chrome://webui-test/mojo_webui_test_support.js';
-
-import {confirmationPageTest} from './confirmation_page_test.js';
-import {fakeHelpContentProviderTestSuite} from './fake_help_content_provider_test.js';
-import {FeedbackFlowTestSuite} from './feedback_flow_test.js';
-import {fileAttachmentTestSuite} from './file_attachment_test.js';
-import {helpContentTestSuite} from './help_content_test.js';
-import {fakeMojoProviderTestSuite} from './mojo_interface_provider_test.js';
-import {searchPageTestSuite} from './search_page_test.js';
-import {shareDataPageTestSuite} from './share_data_page_test.js';
-
-window.test_suites_list = [];
-
-function runSuite(suiteName, testFn) {
-  window.test_suites_list.push(suiteName);
-  suite(suiteName, testFn);
-}
-
-runSuite('confirmationPageTest', confirmationPageTest);
-runSuite('fakeHelpContentProviderTest', fakeHelpContentProviderTestSuite);
-runSuite('fakeMojoProviderTest', fakeMojoProviderTestSuite);
-runSuite('feedbackFlowTest', FeedbackFlowTestSuite);
-runSuite('fileAttachmentTest', fileAttachmentTestSuite);
-runSuite('helpContentTest', helpContentTestSuite);
-runSuite('searchPageTest', searchPageTestSuite);
-runSuite('shareDataPageTest', shareDataPageTestSuite);
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
index d4cc5f5..9b432e45 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/search_page_test.js
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://webui-test/mojo_webui_test_support.js';
+
 import {fakeEmptySearchResponse, fakeFeedbackContext, fakeInternalUserFeedbackContext, fakeLoginFlowFeedbackContext, fakeSearchResponse} from 'chrome://os-feedback/fake_data.js';
 import {FakeHelpContentProvider} from 'chrome://os-feedback/fake_help_content_provider.js';
 import {FeedbackFlowState} from 'chrome://os-feedback/feedback_flow.js';
@@ -16,7 +18,7 @@
 
 import {eventToPromise, isVisible} from '../test_util.js';
 
-export function searchPageTestSuite() {
+suite('searchPageTestSuite', () => {
   /** @type {?SearchPageElement} */
   let page = null;
 
@@ -804,7 +806,7 @@
     });
   });
 
-    test('typingAudioWithExternalAccountWillNotShowsQuestionnaire', async () => {
+  test('typingAudioWithExternalAccountWillNotShowsQuestionnaire', async () => {
     let textAreaElement = null;
     await initializePage();
     // The questionnaire will be only shown if the account belongs to an
@@ -824,4 +826,4 @@
       assertFalse(textAreaElement.value.indexOf(question) >= 0);
     });
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
index 33541196..2666c6bf 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
-import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {fakeEmptyFeedbackContext, fakeFeedbackContext, fakeInternalUserFeedbackContext, fakeLoginFlowFeedbackContext} from 'chrome://os-feedback/fake_data.js';
 import {FakeFeedbackServiceProvider} from 'chrome://os-feedback/fake_feedback_service_provider.js';
@@ -25,7 +24,7 @@
 /**
  * @suppress {missingProperties} for test.skip is not defined in mocha-2.5.js
  */
-export function shareDataPageTestSuite() {
+suite('shareDataPageTestSuite', () => {
   /** @type {?ShareDataPageElement} */
   let page = null;
 
@@ -1279,4 +1278,4 @@
     assertFalse(requestWithoutBluetoothFlag.sendBluetoothLogs);
     assertFalse(!!requestWithoutBluetoothFlag.feedbackContext.categoryTag);
   });
-}
+});
diff --git a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
index 76c66a7..690a4c7 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/test_wallpaper_interface_provider.ts
@@ -38,6 +38,7 @@
       'isInTabletMode',
       'confirmPreviewWallpaper',
       'cancelPreviewWallpaper',
+      'shouldShowTimeOfDayWallpaperDialog',
     ]);
 
     /**
@@ -207,6 +208,7 @@
   selectLocalImageResponse = true;
   updateDailyRefreshWallpaperResponse = true;
   isInTabletModeResponse = true;
+  shouldShowTimeOfDayWallpaperDialogResponse = true;
   wallpaperObserverUpdateTimeout = 0;
   wallpaperObserverRemote: WallpaperObserverInterface|null = null;
 
@@ -365,6 +367,12 @@
     this.methodCalled('cancelPreviewWallpaper');
   }
 
+  shouldShowTimeOfDayWallpaperDialog() {
+    this.methodCalled('shouldShowTimeOfDayWallpaperDialog');
+    return Promise.resolve(
+        {shouldShowDialog: this.shouldShowTimeOfDayWallpaperDialogResponse});
+  }
+
   setCollections(collections: WallpaperCollection[]) {
     this.collections_ = collections;
   }
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
index e322d91..75a23a28a 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_images_element_test.ts
@@ -4,8 +4,9 @@
 
 import 'chrome://personalization/strings.m.js';
 
-import {OnlineImageType, PersonalizationRouterElement, WallpaperGridItemElement, WallpaperImagesElement} from 'chrome://personalization/js/personalization_app.js';
-import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {OnlineImageType, PersonalizationRouterElement, TimeOfDayWallpaperDialogElement, WallpaperGridItemElement, WallpaperImagesElement} from 'chrome://personalization/js/personalization_app.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {baseSetup, initElement, teardownElement} from './personalization_app_test_utils.js';
@@ -48,6 +49,29 @@
     return element;
   }
 
+  async function selectTimeOfDayWallpaper() {
+    // Click the first image that is not currently selected.
+    wallpaperImagesElement!.shadowRoot!
+        .querySelector<WallpaperGridItemElement>(`${
+            WallpaperGridItemElement
+                .is}[aria-selected='false'][data-is-time-of-day-wallpaper]`)!
+        .click();
+    await waitAfterNextRender(wallpaperImagesElement!);
+    return wallpaperImagesElement;
+  }
+
+  async function clickTimeOfDayWallpaperDialogButton(id: string) {
+    const dialog = wallpaperImagesElement!.shadowRoot!
+                       .querySelector<TimeOfDayWallpaperDialogElement>(
+                           TimeOfDayWallpaperDialogElement.is);
+    assertNotEquals(null, dialog, 'dialog element must exist to click button');
+    const button = dialog!.shadowRoot!.getElementById(id);
+    assertNotEquals(null, button, `button with id ${id} must exist`);
+    button!.click();
+    await waitAfterNextRender(wallpaperImagesElement!);
+    return wallpaperImagesElement;
+  }
+
   test('sets aria-selected for current wallpaper asset id', async () => {
     wallpaperImagesElement = await createWithDefaultData();
     const selectedElements: WallpaperGridItemElement[] =
@@ -310,6 +334,105 @@
     assertEquals(
         wallpaperProvider.isInTabletModeResponse, previewMode,
         'preview mode is same as tablet mode');
+    assertEquals(
+        null,
+        wallpaperImagesElement.shadowRoot!.querySelector(
+            TimeOfDayWallpaperDialogElement.is),
+        'no time of day dialog when selecting a regular image');
+  });
+
+  test('shows dialog when clicking on a time of day wallpaper', async () => {
+    loadTimeData.overrideValues({
+      isTimeOfDayWallpaperForcedAutoScheduleEnabled: true,
+    });
+    personalizationStore.setReducersEnabled(true);
+    personalizationStore.data.theme.colorModeAutoScheduleEnabled = false;
+    wallpaperImagesElement =
+        await createWithDefaultData(wallpaperProvider.timeOfDayCollectionId);
+
+    await selectTimeOfDayWallpaper();
+    assertNotEquals(
+        null,
+        wallpaperImagesElement.shadowRoot!.querySelector(
+            TimeOfDayWallpaperDialogElement.is),
+        'dialog element exists');
+  });
+
+  test(
+      'clicking cancel dismisses the time of day wallpaper dialog',
+      async () => {
+        loadTimeData.overrideValues({
+          isTimeOfDayWallpaperForcedAutoScheduleEnabled: true,
+        });
+        personalizationStore.setReducersEnabled(true);
+        personalizationStore.data.theme.colorModeAutoScheduleEnabled = false;
+        wallpaperImagesElement = await createWithDefaultData(
+            wallpaperProvider.timeOfDayCollectionId);
+
+        await selectTimeOfDayWallpaper();
+        await clickTimeOfDayWallpaperDialogButton('close');
+        assertEquals(
+            null,
+            wallpaperImagesElement.shadowRoot!.querySelector(
+                TimeOfDayWallpaperDialogElement.is),
+            'clicking cancel dismisses the dialog');
+        const [assetId, previewMode] =
+            await wallpaperProvider.whenCalled('selectWallpaper');
+        assertEquals(3n, assetId, 'correct asset id is passed');
+        assertEquals(
+            wallpaperProvider.isInTabletModeResponse, previewMode,
+            'preview mode is same as tablet mode');
+        assertFalse(
+            personalizationStore.data.theme.colorModeAutoScheduleEnabled,
+            'auto dark mode is not enabled');
+      });
+
+  test('clicking confirm on the time of day wallpaper dialog', async () => {
+    loadTimeData.overrideValues({
+      isTimeOfDayWallpaperForcedAutoScheduleEnabled: true,
+    });
+    personalizationStore.setReducersEnabled(true);
+    personalizationStore.data.theme.colorModeAutoScheduleEnabled = false;
+    wallpaperImagesElement =
+        await createWithDefaultData(wallpaperProvider.timeOfDayCollectionId);
+
+    await selectTimeOfDayWallpaper();
+    await clickTimeOfDayWallpaperDialogButton('accept');
+    assertEquals(
+        null,
+        wallpaperImagesElement.shadowRoot!.querySelector(
+            TimeOfDayWallpaperDialogElement.is),
+        'clicking accept dismisses the dialog');
+    const [assetId, previewMode] =
+        await wallpaperProvider.whenCalled('selectWallpaper');
+    assertEquals(3n, assetId, 'correct asset id is passed');
+    assertEquals(
+        wallpaperProvider.isInTabletModeResponse, previewMode,
+        'preview mode is same as tablet mode');
+    assertTrue(
+        personalizationStore.data.theme.colorModeAutoScheduleEnabled,
+        'auto dark mode is enabled');
+  });
+
+  test('do not show time of day dialog with proper settings', async () => {
+    loadTimeData.overrideValues({
+      isTimeOfDayWallpaperForcedAutoScheduleEnabled: true,
+    });
+    personalizationStore.setReducersEnabled(true);
+    wallpaperProvider.shouldShowTimeOfDayWallpaperDialogResponse = false;
+    wallpaperImagesElement =
+        await createWithDefaultData(wallpaperProvider.timeOfDayCollectionId);
+    personalizationStore.notifyObservers();
+    await waitAfterNextRender(wallpaperImagesElement);
+
+    await selectTimeOfDayWallpaper();
+    assertEquals(
+        null,
+        wallpaperImagesElement.shadowRoot!.querySelector(
+            TimeOfDayWallpaperDialogElement.is),
+        'dialog element does not exist');
+    const [assetId, _] = await wallpaperProvider.whenCalled('selectWallpaper');
+    assertEquals(3n, assetId, 'correct asset id is passed');
   });
 
   test('dismiss time of day promo banner after showing images', async () => {
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/apn_list_item_test.js b/chrome/test/data/webui/cr_components/chromeos/network/apn_list_item_test.js
index 0c3bb051..fd150a4 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/apn_list_item_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/network/apn_list_item_test.js
@@ -10,7 +10,7 @@
 import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
 import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
 import {ApnState, CrosNetworkConfigRemote} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
-import {NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
+import {NetworkType, PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
@@ -87,8 +87,16 @@
     await flushTasks();
 
     assertFalse(subLabel.hasAttribute('hidden'));
+    assertFalse(subLabel.hasAttribute('warning'));
     assertEquals(
         apnListItem.i18n('NetworkHealthStateConnected'), subLabel.innerText);
+
+    apnListItem.portalState = PortalState.kNoInternet;
+    assertFalse(subLabel.hasAttribute('hidden'));
+    assertTrue(subLabel.hasAttribute('warning'));
+    assertEquals(
+        apnListItem.i18n('networkListItemConnectedNoConnectivity'),
+        subLabel.innerText);
   });
 
   test('Check if APN three dot menu shows', async function() {
diff --git a/chrome/test/data/webui/cr_components/chromeos/network/apn_list_test.js b/chrome/test/data/webui/cr_components/chromeos/network/apn_list_test.js
index e5184b54..e507f39 100644
--- a/chrome/test/data/webui/cr_components/chromeos/network/apn_list_test.js
+++ b/chrome/test/data/webui/cr_components/chromeos/network/apn_list_test.js
@@ -9,6 +9,7 @@
 import {ApnList} from 'chrome://resources/ash/common/network/apn_list.js';
 import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
 import {ApnProperties, ApnState, ApnType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
+import {PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 
@@ -491,4 +492,18 @@
     assertFalse(apns[0].shouldDisallowEnabling);
     assertFalse(apns[1].shouldDisallowEnabling);
   });
+
+  test('Portal state is set', async function() {
+    apnList.managedCellularProperties = {
+      customApnList: [customApn1],
+    };
+    await flushTasks();
+    const apns = apnList.shadowRoot.querySelectorAll('apn-list-item');
+    assertEquals(apns.length, 1);
+    assertTrue(OncMojo.apnMatch(apns[0].apn, customApn1));
+    assertFalse(!!apns[0].portalState);
+
+    apnList.portalState = PortalState.kNoInternet;
+    assertEquals(PortalState.kNoInternet, apns[0].portalState);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js
index 74729f9..02a333a 100644
--- a/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/apn_subpage_tests.js
@@ -8,7 +8,7 @@
 import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
 import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
 import {CrosNetworkConfigRemote} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
-import {DeviceStateType, NetworkType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
+import {DeviceStateType, NetworkType, PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
@@ -227,4 +227,19 @@
     assertEquals(0, counter);
     Router.getInstance().navigateToPreviousRoute = navigateToPreviousRoute;
   });
+
+  test('Portal state is propagated to <apn-list>', async function() {
+    let props = OncMojo.getDefaultManagedProperties(
+        NetworkType.kCellular, 'cellular_guid', 'cellular');
+    mojoApi_.setManagedPropertiesForTest(props);
+    await flushTasks();
+    const getApnList = () => apnSubpage.shadowRoot.querySelector('apn-list');
+    assertFalse(!!getApnList().portalState);
+
+    props = Object.assign({}, props);
+    props.portalState = PortalState.kNoInternet;
+    mojoApi_.setManagedPropertiesForTest(props);
+    await flushTasks();
+    assertEquals(PortalState.kNoInternet, getApnList().portalState);
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts b/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts
index 9616eb2..def4cfc 100644
--- a/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/internet_page/internet_detail_subpage_test.ts
@@ -1874,6 +1874,16 @@
           await flushTasks();
           assertTrue(!!getApn());
           assertEquals(name, getApn()!.textContent!.trim());
+          assertFalse(getCrLink()!.hasAttribute('warning'));
+
+          // Adding a restricted connectivity state should cause the sublabel to
+          // be a warning.
+          cellularNetwork.portalState = PortalState.kNoInternet;
+          cellularNetwork.connectionState = ConnectionStateType.kPortal;
+          mojoApi.setManagedPropertiesForTest(cellularNetwork);
+          internetDetailPage.init('cellular_guid', 'Cellular', 'cellular');
+          await flushTasks();
+          assertTrue(getCrLink()!.hasAttribute('warning'));
         } else {
           assertNull(getApn());
         }
diff --git a/chrome/test/data/webui/settings/privacy_guide_integration_test.ts b/chrome/test/data/webui/settings/privacy_guide_integration_test.ts
index 4f07faa..bbe94bc1 100644
--- a/chrome/test/data/webui/settings/privacy_guide_integration_test.ts
+++ b/chrome/test/data/webui/settings/privacy_guide_integration_test.ts
@@ -5,6 +5,7 @@
 // clang-format off
 import 'chrome://settings/settings.js';
 
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {SettingsPrivacyGuidePageElement, PrivacyGuideStep} from 'chrome://settings/lazy_load.js';
 import {CrSettingsPrefs, MetricsBrowserProxyImpl, PrivacyGuideStepsEligibleAndReached, Router, routes, SettingsPrefsElement, SyncBrowserProxyImpl} from 'chrome://settings/settings.js';
 import {assertTrue, assertNotReached} from 'chrome://webui-test/chai_assert.js';
@@ -128,11 +129,12 @@
   }
 
   test('recordStepsAreEligibleReached', async function() {
-    const optionalSteps: PrivacyGuideStep[] = [
-      PrivacyGuideStep.HISTORY_SYNC,
-      PrivacyGuideStep.COOKIES,
-      PrivacyGuideStep.SAFE_BROWSING,
-    ];
+    const optionalSteps: PrivacyGuideStep[] = [];
+    optionalSteps.push(PrivacyGuideStep.HISTORY_SYNC);
+    if (!loadTimeData.getBoolean('is3pcdCookieSettingsRedesignEnabled')) {
+      optionalSteps.push(PrivacyGuideStep.COOKIES);
+    }
+    optionalSteps.push(PrivacyGuideStep.SAFE_BROWSING);
 
     const masks: number[] = [];
     for (let i = 0; i < optionalSteps.length; i++) {
diff --git a/chrome/test/webapps/data/framework_supported_actions.csv b/chrome/test/webapps/data/framework_supported_actions.csv
index 18f5112..c371836 100644
--- a/chrome/test/webapps/data/framework_supported_actions.csv
+++ b/chrome/test/webapps/data/framework_supported_actions.csv
@@ -96,8 +96,8 @@
 check_browser_navigation,                              🌕, 🌕,  🌕,   🌕,
 check_browser_navigation_is_app_settings,              🌕, 🌕,  🌕,   🌑,
 await_manifest_update,                                 🌕, 🌕,  🌕,   🌕,
-enable_file_handling,                                  🌕, 🌕,  🌕,   🌑,
-disable_file_handling,                                 🌕, 🌕,  🌕,   🌑,
+enable_file_handling,                                  🌕, 🌕,  🌕,   🌕,
+disable_file_handling,                                 🌕, 🌕,  🌕,   🌕,
 install_sub_app,                                       🌑, 🌑,  🌑,   🌕,
 remove_sub_app,                                        🌑, 🌑,  🌑,   🌕,
 check_has_sub_app,                                     🌑, 🌑,  🌑,   🌕,
diff --git a/chrome/updater/app/app_install.cc b/chrome/updater/app/app_install.cc
index 3524860..9885050 100644
--- a/chrome/updater/app/app_install.cc
+++ b/chrome/updater/app/app_install.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/app/app_install.h"
 
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -30,7 +31,6 @@
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/util.h"
 #include "components/prefs/pref_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -59,8 +59,8 @@
     RegistrationRequest request;
     request.app_id = app_id;
     request.version = base::Version(kNullVersion);
-    absl::optional<tagging::AppArgs> app_args = GetAppArgs(app_id);
-    absl::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
+    std::optional<tagging::AppArgs> app_args = GetAppArgs(app_id);
+    std::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
     if (app_args) {
       request.ap = app_args->ap;
     }
diff --git a/chrome/updater/app/app_install_win.cc b/chrome/updater/app/app_install_win.cc
index 13a3fc6..1e714a48 100644
--- a/chrome/updater/app/app_install_win.cc
+++ b/chrome/updater/app/app_install_win.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/app/app_install.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <tuple>
 #include <vector>
@@ -65,7 +66,6 @@
 #pragma clang diagnostic pop
 
 #include "components/update_client/protocol_parser.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -299,7 +299,7 @@
 
 void SetUsageStats(UpdaterScope scope,
                    const std::string& app_id,
-                   absl::optional<bool> usage_stats) {
+                   std::optional<bool> usage_stats) {
   if (!usage_stats) {
     return;
   }
@@ -445,7 +445,7 @@
   // Contains the result of installing the application. This is populated
   // by the `StateChangeCallback` or the completion callback, if the
   // former callback was not posted.
-  absl::optional<ObserverCompletionInfo> observer_completion_info_;
+  std::optional<ObserverCompletionInfo> observer_completion_info_;
 
   // Called when InstallApp is done.
   base::OnceCallback<void(int)> callback_;
@@ -497,8 +497,8 @@
   RegistrationRequest request;
   request.app_id = app_id_;
   request.version = base::Version(kNullVersion);
-  absl::optional<tagging::AppArgs> app_args = GetAppArgs(app_id_);
-  absl::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
+  std::optional<tagging::AppArgs> app_args = GetAppArgs(app_id_);
+  std::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
   if (app_args) {
     request.ap = app_args->ap;
   }
@@ -509,7 +509,7 @@
   base::ThreadPool::PostTaskAndReply(
       FROM_HERE,
       base::BindOnce(&SetUsageStats, GetUpdaterScope(), app_id_,
-                     tag_args ? tag_args->usage_stats_enable : absl::nullopt),
+                     tag_args ? tag_args->usage_stats_enable : std::nullopt),
       base::BindOnce(
           &UpdateService::Install, update_service_, request,
           GetDecodedInstallDataFromAppArgs(app_id_),
@@ -616,12 +616,12 @@
     VLOG(1) << "Failed to serialize install settings.";
   }
 
-  absl::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
+  std::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
   RegistrationRequest request;
   request.app_id = app_id_;
   request.version = base::Version(kNullVersion);
 
-  absl::optional<tagging::AppArgs> app_args = GetAppArgs(app_id_);
+  std::optional<tagging::AppArgs> app_args = GetAppArgs(app_id_);
   if (app_args) {
     request.ap = app_args->ap;
   }
@@ -635,7 +635,7 @@
   base::ThreadPool::PostTaskAndReply(
       FROM_HERE,
       base::BindOnce(&SetUsageStats, GetUpdaterScope(), app_id_,
-                     tag_args ? tag_args->usage_stats_enable : absl::nullopt),
+                     tag_args ? tag_args->usage_stats_enable : std::nullopt),
       base::BindOnce(
           &UpdateService::RegisterApp, update_service_, request,
           base::BindOnce(
diff --git a/chrome/updater/app/app_server.cc b/chrome/updater/app/app_server.cc
index f65c4dc..5ee4df67 100644
--- a/chrome/updater/app/app_server.cc
+++ b/chrome/updater/app/app_server.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/app/app_server.h"
 
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -34,7 +35,6 @@
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/util.h"
 #include "components/prefs/pref_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "base/win/registry.h"
@@ -214,7 +214,7 @@
       updater_scope(), prefs_->GetPrefService());
   if (ShouldUninstall(persisted_data->GetAppIds(), server_starts_,
                       persisted_data->GetHadApps())) {
-    absl::optional<base::FilePath> executable =
+    std::optional<base::FilePath> executable =
         GetUpdaterExecutablePath(updater_scope());
     if (executable) {
       base::CommandLine command_line(*executable);
diff --git a/chrome/updater/app/app_server_unittest.cc b/chrome/updater/app/app_server_unittest.cc
index 97f82c0..ca9f12bf 100644
--- a/chrome/updater/app/app_server_unittest.cc
+++ b/chrome/updater/app/app_server_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/app/app_server.h"
 
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -24,7 +25,6 @@
 #include "components/prefs/pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <shlobj.h>
@@ -74,7 +74,7 @@
 
 void ClearPrefs() {
   const UpdaterScope updater_scope = GetTestScope();
-  for (const absl::optional<base::FilePath>& path :
+  for (const std::optional<base::FilePath>& path :
        {GetInstallDirectory(updater_scope),
         GetVersionedInstallDirectory(updater_scope)}) {
     ASSERT_TRUE(path);
diff --git a/chrome/updater/app/app_server_win.cc b/chrome/updater/app/app_server_win.cc
index fc6556e..75967ed 100644
--- a/chrome/updater/app/app_server_win.cc
+++ b/chrome/updater/app/app_server_win.cc
@@ -8,6 +8,7 @@
 #include <wrl/module.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -46,7 +47,6 @@
 #include "chrome/updater/win/setup/uninstall.h"
 #include "chrome/updater/win/task_scheduler.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -128,7 +128,7 @@
                       WorkItemList* list) {
   CHECK(list);
 
-  const absl::optional<base::FilePath> target_path =
+  const std::optional<base::FilePath> target_path =
       GetGoogleUpdateExePath(scope);
   if (!target_path || !base::CreateDirectory(target_path->DirName())) {
     return false;
@@ -215,7 +215,7 @@
   return DeleteExcept(GetGoogleUpdateExePath(scope));
 }
 
-absl::optional<int> DaynumFromDWORD(DWORD value) {
+std::optional<int> DaynumFromDWORD(DWORD value) {
   const int daynum = static_cast<int>(value);
 
   // When daynum is positive, it is the number of days since January 1, 2007.
@@ -223,8 +223,8 @@
   // and 50000 (maps to Nov 24, 2143).
   // -1 is special value for first install.
   return daynum == -1 || (daynum >= 3000 && daynum <= 50000)
-             ? absl::make_optional(daynum)
-             : absl::nullopt;
+             ? std::make_optional(daynum)
+             : std::nullopt;
 }
 
 }  // namespace
@@ -360,7 +360,7 @@
 bool AppServerWin::SwapInNewVersion() {
   std::unique_ptr<WorkItemList> list(WorkItem::CreateWorkItemList());
 
-  const absl::optional<base::FilePath> versioned_directory =
+  const std::optional<base::FilePath> versioned_directory =
       GetVersionedInstallDirectory(updater_scope());
   if (!versioned_directory) {
     return false;
@@ -373,7 +373,7 @@
     return false;
   }
 
-  absl::optional<base::ScopedTempDir> temp_dir = CreateSecureTempDir();
+  std::optional<base::ScopedTempDir> temp_dir = CreateSecureTempDir();
   if (!temp_dir) {
     return false;
   }
@@ -392,7 +392,7 @@
   const base::ScopedClosureRunner reset_shutdown_event(
       SignalShutdownEvent(updater_scope()));
 
-  absl::optional<base::FilePath> target =
+  std::optional<base::FilePath> target =
       GetGoogleUpdateExePath(updater_scope());
   if (target) {
     StopProcessesUnderPath(target->DirName(), base::Seconds(45));
diff --git a/chrome/updater/app/app_uninstall.cc b/chrome/updater/app/app_uninstall.cc
index 616112d..5e037d8 100644
--- a/chrome/updater/app/app_uninstall.cc
+++ b/chrome/updater/app/app_uninstall.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/app/app_uninstall.h"
 
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -33,7 +34,6 @@
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/util.h"
 #include "components/update_client/update_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "chrome/updater/win/setup/uninstall.h"
@@ -44,7 +44,7 @@
 namespace updater {
 
 std::vector<base::FilePath> GetVersionExecutablePaths(UpdaterScope scope) {
-  const absl::optional<base::FilePath> updater_folder_path =
+  const std::optional<base::FilePath> updater_folder_path =
       GetInstallDirectory(scope);
   if (!updater_folder_path) {
     LOG(ERROR) << __func__ << ": failed to get the updater install directory.";
diff --git a/chrome/updater/app/app_uninstall_unittest.cc b/chrome/updater/app/app_uninstall_unittest.cc
index bcc7562..74896f0 100644
--- a/chrome/updater/app/app_uninstall_unittest.cc
+++ b/chrome/updater/app/app_uninstall_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/app/app_uninstall.h"
 
+#include <optional>
+
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/test/task_environment.h"
@@ -16,7 +18,6 @@
 #include "chrome/updater/util/util.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -47,7 +48,7 @@
 
   ASSERT_EQ(GetVersionExecutablePaths(GetTestScope()).size(), 3u);
 
-  const absl::optional<base::FilePath> path =
+  const std::optional<base::FilePath> path =
       GetInstallDirectory(GetTestScope());
   ASSERT_TRUE(path);
   ASSERT_TRUE(base::DeletePathRecursively(*path));
diff --git a/chrome/updater/app/app_wakeall.cc b/chrome/updater/app/app_wakeall.cc
index 289fa4c..f773218 100644
--- a/chrome/updater/app/app_wakeall.cc
+++ b/chrome/updater/app/app_wakeall.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/app/app_wakeall.h"
 
+#include <optional>
+
 #include "base/command_line.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
@@ -17,7 +19,6 @@
 #include "chrome/updater/app/app.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -39,7 +40,7 @@
       FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
       base::BindOnce(
           [](UpdaterScope scope) {
-            absl::optional<base::FilePath> base = GetInstallDirectory(scope);
+            std::optional<base::FilePath> base = GetInstallDirectory(scope);
             if (!base) {
               return kErrorNoBaseDirectory;
             }
diff --git a/chrome/updater/app/server/win/com_classes.cc b/chrome/updater/app/server/win/com_classes.cc
index 9f87b4b..f4efdb9 100644
--- a/chrome/updater/app/server/win/com_classes.cc
+++ b/chrome/updater/app/server/win/com_classes.cc
@@ -8,6 +8,7 @@
 #include <wrl/client.h>
 #include <wrl/implements.h>
 
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -32,7 +33,6 @@
 #include "chrome/updater/update_service.h"
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/win_util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -266,35 +266,35 @@
   }
 
   // Validates that string parameters are not longer than 16K characters.
-  absl::optional<RegistrationRequest> request =
+  std::optional<RegistrationRequest> request =
       [app_id, brand_code, brand_path, ap, version,
        existence_checker_path]() -> decltype(request) {
     for (const auto* str : {app_id, brand_code, brand_path, ap, version,
                             existence_checker_path}) {
       if (wcsnlen_s(str, kMaxStringLen) == kMaxStringLen) {
-        return absl::nullopt;
+        return std::nullopt;
       }
     }
 
     RegistrationRequest request;
     if (!app_id || !base::WideToUTF8(app_id, wcslen(app_id), &request.app_id)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     if (!brand_code || !base::WideToUTF8(brand_code, wcslen(brand_code),
                                          &request.brand_code)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.brand_path = base::FilePath(brand_path);
     if (!ap || !base::WideToUTF8(ap, wcslen(ap), &request.ap)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     std::string version_str;
     if (!version || !base::WideToUTF8(version, wcslen(version), &version_str)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.version = base::Version(version_str);
     if (!request.version.IsValid()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.existence_checker_path = base::FilePath(existence_checker_path);
 
@@ -401,7 +401,7 @@
   Microsoft::WRL::ComPtr<IUpdaterObserver> observer_;
 
   // Most recent download progress value the client has been notified about.
-  absl::optional<int> progress_seen_;
+  std::optional<int> progress_seen_;
 };
 
 }  // namespace
@@ -572,36 +572,36 @@
   }
 
   // Validates that string parameters are not longer than 16K characters.
-  absl::optional<RegistrationRequest> request =
+  std::optional<RegistrationRequest> request =
       [app_id, brand_code, brand_path, ap, version, existence_checker_path,
        client_install_data, install_data_index]() -> decltype(request) {
     for (const auto* str :
          {app_id, brand_code, brand_path, ap, version, existence_checker_path,
           client_install_data, install_data_index}) {
       if (wcsnlen_s(str, kMaxStringLen) == kMaxStringLen) {
-        return absl::nullopt;
+        return std::nullopt;
       }
     }
 
     RegistrationRequest request;
     if (!app_id || !base::WideToUTF8(app_id, wcslen(app_id), &request.app_id)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     if (!brand_code || !base::WideToUTF8(brand_code, wcslen(brand_code),
                                          &request.brand_code)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.brand_path = base::FilePath(brand_path);
     if (!ap || !base::WideToUTF8(ap, wcslen(ap), &request.ap)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     std::string version_str;
     if (!version || !base::WideToUTF8(version, wcslen(version), &version_str)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.version = base::Version(version_str);
     if (!request.version.IsValid()) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     request.existence_checker_path = base::FilePath(existence_checker_path);
 
diff --git a/chrome/updater/app/server/win/com_classes_legacy.cc b/chrome/updater/app/server/win/com_classes_legacy.cc
index 44de8ec..d641ab6 100644
--- a/chrome/updater/app/server/win/com_classes_legacy.cc
+++ b/chrome/updater/app/server/win/com_classes_legacy.cc
@@ -9,6 +9,7 @@
 #include <windows.h>
 #include <wrl/client.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -48,7 +49,6 @@
 #include "chrome/updater/win/setup/setup_util.h"
 #include "chrome/updater/win/ui/l10n_util.h"
 #include "chrome/updater/win/ui/resources/updater_installer_strings.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
@@ -59,8 +59,8 @@
 }
 
 // Extracts a string from a VARIANT if the VARIANT is VT_BSTR or VT_BSTR |
-// VT_BYREF. Returns absl::nullopt if the VARIANT is not a BSTR.
-absl::optional<std::wstring> StringFromVariant(const VARIANT& source) {
+// VT_BYREF. Returns std::nullopt if the VARIANT is not a BSTR.
+std::optional<std::wstring> StringFromVariant(const VARIANT& source) {
   if (V_VT(&source) == VT_BSTR) {
     return V_BSTR(&source);
   }
@@ -441,7 +441,7 @@
     // Holds the result of the IPC to retrieve the current version.
     struct CurrentVersionResult
         : public base::RefCountedThreadSafe<CurrentVersionResult> {
-      absl::optional<base::Version> current_version;
+      std::optional<base::Version> current_version;
       base::WaitableEvent completion_event;
 
      private:
@@ -635,8 +635,8 @@
   // Access to `state_update_` and `result_` must be serialized by using the
   // lock.
   mutable base::Lock lock_;
-  absl::optional<UpdateService::UpdateState> state_update_;
-  absl::optional<UpdateService::Result> result_;
+  std::optional<UpdateService::UpdateState> state_update_;
+  std::optional<UpdateService::Result> result_;
 };
 
 // This class implements the legacy Omaha3 IAppBundleWeb interface as expected
@@ -908,7 +908,7 @@
        {substitution1, substitution2, substitution3, substitution4,
         substitution5, substitution6, substitution7, substitution8,
         substitution9}) {
-    const absl::optional<std::wstring> substitution_string =
+    const std::optional<std::wstring> substitution_string =
         StringFromVariant(substitution);
     if (!substitution_string) {
       break;
@@ -1090,7 +1090,7 @@
 // Holds the result of the IPC to retrieve `last checked time`.
 struct LastCheckedTimeResult
     : public base::RefCountedThreadSafe<LastCheckedTimeResult> {
-  absl::optional<DATE> last_checked_time;
+  std::optional<DATE> last_checked_time;
   base::WaitableEvent completion_event;
 
  private:
@@ -1129,7 +1129,7 @@
   }
 
   ValueGetter value_getter;
-  absl::optional<PolicyStatus<T>> value;
+  std::optional<PolicyStatus<T>> value;
   base::WaitableEvent completion_event;
 };
 
@@ -1378,7 +1378,7 @@
       value.effective_policy()
           ? GetStringFromValue(value.effective_policy()->policy)
           : "",
-      value.conflict_policy() != absl::nullopt,
+      value.conflict_policy() != std::nullopt,
       value.conflict_policy() ? value.conflict_policy()->source : "",
       value.conflict_policy()
           ? GetStringFromValue(value.conflict_policy()->policy)
diff --git a/chrome/updater/app/server/win/com_classes_legacy.h b/chrome/updater/app/server/win/com_classes_legacy.h
index 77cafbda..90cd340 100644
--- a/chrome/updater/app/server/win/com_classes_legacy.h
+++ b/chrome/updater/app/server/win/com_classes_legacy.h
@@ -8,6 +8,7 @@
 #include <windows.h>
 #include <wrl/implements.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
diff --git a/chrome/updater/auto_run_on_os_upgrade_task.cc b/chrome/updater/auto_run_on_os_upgrade_task.cc
index ed4ac79908..9a25fd7 100644
--- a/chrome/updater/auto_run_on_os_upgrade_task.cc
+++ b/chrome/updater/auto_run_on_os_upgrade_task.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/auto_run_on_os_upgrade_task.h"
 
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -23,7 +24,6 @@
 #include "chrome/updater/constants.h"
 #include "chrome/updater/persisted_data.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
@@ -108,7 +108,7 @@
 bool AutoRunOnOsUpgradeTask::HasOSUpgraded() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const absl::optional<OSVERSIONINFOEX> previous_os_version =
+  const std::optional<OSVERSIONINFOEX> previous_os_version =
       persisted_data_->GetLastOSVersion();
   if (!previous_os_version) {
     // Initialize the OS version.
@@ -119,7 +119,7 @@
   if (!CompareOSVersions(previous_os_version.value(), VER_GREATER))
     return false;
 
-  if (const absl::optional<OSVERSIONINFOEX> current_os_version = GetOSVersion();
+  if (const std::optional<OSVERSIONINFOEX> current_os_version = GetOSVersion();
       current_os_version) {
     os_upgrade_string_ = GetOSUpgradeVersionsString(previous_os_version.value(),
                                                     current_os_version.value());
diff --git a/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc b/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc
index 5433309..5a70284 100644
--- a/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc
+++ b/chrome/updater/auto_run_on_os_upgrade_task_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/auto_run_on_os_upgrade_task.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/base64.h"
@@ -76,8 +77,8 @@
 };
 
 TEST_F(AutoRunOnOsUpgradeTaskTest, RunOnOsUpgradeForApp) {
-  const absl::optional<OSVERSIONINFOEX> current_os_version = GetOSVersion();
-  ASSERT_NE(current_os_version, absl::nullopt);
+  const std::optional<OSVERSIONINFOEX> current_os_version = GetOSVersion();
+  ASSERT_NE(current_os_version, std::nullopt);
   OSVERSIONINFOEX last_os_version = current_os_version.value();
   --last_os_version.dwMajorVersion;
 
diff --git a/chrome/updater/certificate_tag.cc b/chrome/updater/certificate_tag.cc
index 2753b563..327fe43 100644
--- a/chrome/updater/certificate_tag.cc
+++ b/chrome/updater/certificate_tag.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/certificate_tag.h"
 
+#include <optional>
+
 #include "base/notreached.h"
 #include "third_party/boringssl/src/include/openssl/bytestring.h"
 #include "third_party/boringssl/src/include/openssl/crypto.h"
@@ -41,7 +43,7 @@
 static constexpr uint16_t kAttributeCertificateTypePKCS7SignedData = 2;
 
 // static
-absl::optional<Binary> Binary::Parse(base::span<const uint8_t> binary) {
+std::optional<Binary> Binary::Parse(base::span<const uint8_t> binary) {
   // Parse establishes some offsets into |binary| for structures that |GetTag|
   // and |SetTag| will both need.
 
@@ -83,7 +85,7 @@
       !CBS_get_bytes(&bin_for_header, &optional_header,
                      size_of_optional_header) ||
       !CBS_get_u16le(&optional_header, &optional_header_magic)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   size_t address_size = 0, extra_header_bytes = 0;
@@ -97,7 +99,7 @@
       extra_header_bytes = 4;
       break;
     default:
-      return absl::nullopt;
+      return std::nullopt;
   }
 
   // Skip the Windows-specific header section up until the number of data
@@ -119,14 +121,14 @@
       !CBS_get_u32le(&optional_header, &cert_entry_size) ||
       size_t{cert_entry_virtual_addr} + cert_entry_size < cert_entry_size ||
       size_t{cert_entry_virtual_addr} + cert_entry_size != CBS_len(&bin)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   CBS bin_for_certs = bin;
   CBS certs;
   if (!CBS_skip(&bin_for_certs, cert_entry_virtual_addr) ||
       !CBS_get_bytes(&bin_for_certs, &certs, cert_entry_size)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // See the WIN_CERTIFICATE structure from
@@ -141,7 +143,7 @@
       !CBS_get_u16le(&certs, &certs_type) ||
       certs_type != kAttributeCertificateTypePKCS7SignedData ||
       !CBS_get_asn1_element(&certs, &signed_data, CBS_ASN1_SEQUENCE)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   Binary ret;
@@ -157,7 +159,7 @@
       !CBS_get_u32le(&bin_for_check, &cert_entry_size_duplicate) ||
       cert_entry_size_duplicate != cert_entry_size) {
     NOTREACHED();
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   ret.binary_ = binary;
@@ -165,13 +167,13 @@
   ret.attr_cert_offset_ = cert_entry_virtual_addr;
 
   if (!ret.ParseTag()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return ret;
 }
 
-const absl::optional<base::span<const uint8_t>>& Binary::tag() const {
+const std::optional<base::span<const uint8_t>>& Binary::tag() const {
   return tag_;
 }
 
@@ -204,7 +206,7 @@
          CBB_add_bytes(out, CBS_data(&element), CBS_len(&element)) == 1;
 }
 
-absl::optional<std::vector<uint8_t>> Binary::SetTag(
+std::optional<std::vector<uint8_t>> Binary::SetTag(
     base::span<const uint8_t> tag) const {
   bssl::ScopedCBB cbb;
   if (!CBB_init(cbb.get(), binary_.size() + 1024) ||
@@ -216,7 +218,7 @@
       !CBB_add_u32(cbb.get(), 0 /* Length. Filled in later. */) ||
       !CBB_add_u16le(cbb.get(), kAttributeCertificateRevision) ||
       !CBB_add_u16le(cbb.get(), kAttributeCertificateTypePKCS7SignedData)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Walk the PKCS SignedData structure from the input and copy elements to the
@@ -246,7 +248,7 @@
                     0 | CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC) ||
       !CBB_add_asn1(&pkcs7_cbb, &certs_cbb,
                     0 | CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Copy the certificates from the input to the output, potentially omitting
@@ -258,7 +260,7 @@
     if ((have_last_cert && !CBB_add_bytes(&certs_cbb, CBS_data(&last_cert),
                                           CBS_len(&last_cert))) ||
         !CBS_get_asn1_element(&certs, &last_cert, CBS_ASN1_SEQUENCE)) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     have_last_cert = true;
   }
@@ -268,7 +270,7 @@
   // it.
   if (!tag_.has_value() &&
       !CBB_add_bytes(&certs_cbb, CBS_data(&last_cert), CBS_len(&last_cert))) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // These values are DER-encoded OIDs needed in the X.509 certificate that's
@@ -359,7 +361,7 @@
       // Copy signerInfos from the input PKCS#7 structure.
       !CopyASN1(&pkcs7_cbb, &pkcs7) || CBS_len(&pkcs7) != 0 ||
       !CBB_finish(cbb.get(), &cbb_data, &cbb_len)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // Copy the CBB result into a std::vector, padding to 8-byte alignment.
diff --git a/chrome/updater/certificate_tag.h b/chrome/updater/certificate_tag.h
index 6e50763..a2afb094 100644
--- a/chrome/updater/certificate_tag.h
+++ b/chrome/updater/certificate_tag.h
@@ -6,10 +6,10 @@
 #define CHROME_UPDATER_CERTIFICATE_TAG_H_
 
 #include <cstdint>
+#include <optional>
 #include <vector>
 
 #include "base/containers/span.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace tagging {
@@ -24,15 +24,15 @@
 
   // Parse a signed, Windows PE binary. Note that the returned structure
   // contains pointers into the given data.
-  static absl::optional<Binary> Parse(base::span<const uint8_t> binary);
+  static std::optional<Binary> Parse(base::span<const uint8_t> binary);
 
   // tag returns the embedded tag, if any.
-  const absl::optional<base::span<const uint8_t>>& tag() const;
+  const std::optional<base::span<const uint8_t>>& tag() const;
 
   // SetTag returns an updated version of the binary that contains the given
   // tag, or |nullopt| on error. If the binary already contains a tag then it
   // will be replaced.
-  absl::optional<std::vector<uint8_t>> SetTag(
+  std::optional<std::vector<uint8_t>> SetTag(
       base::span<const uint8_t> tag) const;
 
  private:
@@ -49,7 +49,7 @@
   base::span<const uint8_t> content_info_;
 
   // tag_ contains the embedded tag, or |nullopt| if there isn't one.
-  absl::optional<base::span<const uint8_t>> tag_;
+  std::optional<base::span<const uint8_t>> tag_;
 
   // attr_cert_offset_ is the offset in the file where the |WIN_CERTIFICATE|
   // structure appears. (This is the last structure in the file.)
diff --git a/chrome/updater/certificate_tag_unittest.cc b/chrome/updater/certificate_tag_unittest.cc
index f756a8f..268a70c3 100644
--- a/chrome/updater/certificate_tag_unittest.cc
+++ b/chrome/updater/certificate_tag_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <cstdint>
+#include <optional>
 #include <vector>
 
 #include "base/containers/span.h"
@@ -11,7 +12,6 @@
 #include "chrome/updater/certificate_tag.h"
 #include "chrome/updater/util/unit_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/zlib/google/compression_utils.h"
 
 namespace updater::tagging {
@@ -24,32 +24,32 @@
   const base::span<const uint8_t> exe_span(
       reinterpret_cast<const uint8_t*>(exe.data()), exe.size());
 
-  absl::optional<Binary> bin(Binary::Parse(exe_span));
+  std::optional<Binary> bin(Binary::Parse(exe_span));
   ASSERT_TRUE(bin);
 
   // Binary should be untagged on disk.
-  absl::optional<base::span<const uint8_t>> orig_tag(bin->tag());
+  std::optional<base::span<const uint8_t>> orig_tag(bin->tag());
   EXPECT_FALSE(orig_tag);
 
   constexpr uint8_t kTag[] = {1, 2, 3, 4, 5};
-  absl::optional<std::vector<uint8_t>> updated_exe(bin->SetTag(kTag));
+  std::optional<std::vector<uint8_t>> updated_exe(bin->SetTag(kTag));
   ASSERT_TRUE(updated_exe);
 
-  absl::optional<Binary> bin2(Binary::Parse(*updated_exe));
+  std::optional<Binary> bin2(Binary::Parse(*updated_exe));
   ASSERT_TRUE(bin2);
-  absl::optional<base::span<const uint8_t>> parsed_tag(bin2->tag());
+  std::optional<base::span<const uint8_t>> parsed_tag(bin2->tag());
   ASSERT_TRUE(parsed_tag);
   EXPECT_TRUE(parsed_tag->size() == sizeof(kTag) &&
               memcmp(kTag, parsed_tag->data(), sizeof(kTag)) == 0);
 
   // Update an existing tag.
   constexpr uint8_t kTag2[] = {1, 2, 3, 4, 6};
-  absl::optional<std::vector<uint8_t>> updated_again_exe(bin2->SetTag(kTag2));
+  std::optional<std::vector<uint8_t>> updated_again_exe(bin2->SetTag(kTag2));
   ASSERT_TRUE(updated_again_exe);
 
-  absl::optional<Binary> bin3(Binary::Parse(*updated_again_exe));
+  std::optional<Binary> bin3(Binary::Parse(*updated_again_exe));
   ASSERT_TRUE(bin3);
-  absl::optional<base::span<const uint8_t>> parsed_tag2(bin3->tag());
+  std::optional<base::span<const uint8_t>> parsed_tag2(bin3->tag());
   ASSERT_TRUE(parsed_tag2);
   EXPECT_TRUE(parsed_tag2->size() == sizeof(kTag2) &&
               memcmp(kTag2, parsed_tag2->data(), sizeof(kTag2)) == 0);
diff --git a/chrome/updater/cleanup_task.cc b/chrome/updater/cleanup_task.cc
index 62d947b..e4405ab 100644
--- a/chrome/updater/cleanup_task.cc
+++ b/chrome/updater/cleanup_task.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/cleanup_task.h"
 
+#include <optional>
+
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
@@ -18,7 +20,6 @@
 #include "chrome/updater/app/app_uninstall.h"
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "chrome/updater/util/win_util.h"
@@ -34,7 +35,7 @@
 // TODO(crbug/5002644): we should remove this once we believe the cache has
 // been deleted by the majority of clients.
 void TempCleanupCache(UpdaterScope scope) {
-  absl::optional<base::FilePath> cache_dir = GetCrxDiffCacheDirectory(scope);
+  std::optional<base::FilePath> cache_dir = GetCrxDiffCacheDirectory(scope);
   if (cache_dir.has_value()) {
     base::DeletePathRecursively(cache_dir.value());
   }
@@ -53,7 +54,7 @@
   CHECK(base::Version(kCleanupVersionMax).IsValid());
   CHECK(base::Version(kUpdaterVersion)
             .CompareTo(base::Version(kCleanupVersionMax)) > 0);
-  absl::optional<base::FilePath> dir = GetInstallDirectory(scope);
+  std::optional<base::FilePath> dir = GetInstallDirectory(scope);
   if (!dir) {
     return;
   }
diff --git a/chrome/updater/cleanup_task_unittest.cc b/chrome/updater/cleanup_task_unittest.cc
index 88d7fc89..e94a96a 100644
--- a/chrome/updater/cleanup_task_unittest.cc
+++ b/chrome/updater/cleanup_task_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/cleanup_task.h"
 
+#include <optional>
+
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/memory/scoped_refptr.h"
@@ -14,7 +16,6 @@
 #include "chrome/updater/util/unit_test_util.h"
 #include "chrome/updater/util/util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "chrome/updater/util/win_util.h"
@@ -36,13 +37,13 @@
 #if BUILDFLAG(IS_WIN)
   // Set up a mock `GoogleUpdate.exe`, and the following mock directories:
   // `Download`, `Install`, and a versioned `1.2.3.4` directory.
-  const absl::optional<base::FilePath> google_update_exe =
+  const std::optional<base::FilePath> google_update_exe =
       GetGoogleUpdateExePath(GetTestScope());
   ASSERT_TRUE(google_update_exe.has_value());
   test::SetupMockUpdater(google_update_exe.value());
 #endif  // BUILDFLAG(IS_WIN)
 
-  absl::optional<base::FilePath> folder_path =
+  std::optional<base::FilePath> folder_path =
       GetVersionedInstallDirectory(GetTestScope(), base::Version("100"));
   ASSERT_TRUE(folder_path);
   ASSERT_TRUE(base::CreateDirectory(*folder_path));
diff --git a/chrome/updater/configurator.cc b/chrome/updater/configurator.cc
index 4177b41..02ae692 100644
--- a/chrome/updater/configurator.cc
+++ b/chrome/updater/configurator.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -36,7 +37,6 @@
 #include "components/update_client/unzip/in_process_unzipper.h"
 #include "components/update_client/unzipper.h"
 #include "components/version_info/version_info.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -60,7 +60,7 @@
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
         return base::IsManagedOrEnterpriseDevice();
 #else
-        return absl::nullopt;
+        return std::nullopt;
 #endif
       }()) {
 #if BUILDFLAG(IS_LINUX)
@@ -199,8 +199,8 @@
   return std::make_unique<update_client::ProtocolHandlerFactoryJSON>();
 }
 
-absl::optional<bool> Configurator::IsMachineExternallyManaged() const {
-  const absl::optional<bool> is_managed_overridden =
+std::optional<bool> Configurator::IsMachineExternallyManaged() const {
+  const std::optional<bool> is_managed_overridden =
       external_constants_->IsMachineManaged();
   return is_managed_overridden.has_value() ? is_managed_overridden
                                            : is_managed_device_;
@@ -221,7 +221,7 @@
   });
 }
 
-absl::optional<base::FilePath> Configurator::GetCrxCachePath() const {
+std::optional<base::FilePath> Configurator::GetCrxCachePath() const {
   return updater::GetCrxDiffCacheDirectory(GetUpdaterScope());
 }
 
diff --git a/chrome/updater/configurator.h b/chrome/updater/configurator.h
index 4bea420b..d4c4552d 100644
--- a/chrome/updater/configurator.h
+++ b/chrome/updater/configurator.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_CONFIGURATOR_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -13,7 +14,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "components/update_client/configurator.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 class PrefService;
@@ -78,9 +78,9 @@
   bool IsPerUserInstall() const override;
   std::unique_ptr<update_client::ProtocolHandlerFactory>
   GetProtocolHandlerFactory() const override;
-  absl::optional<bool> IsMachineExternallyManaged() const override;
+  std::optional<bool> IsMachineExternallyManaged() const override;
   update_client::UpdaterStateProvider GetUpdaterStateProvider() const override;
-  absl::optional<base::FilePath> GetCrxCachePath() const override;
+  std::optional<base::FilePath> GetCrxCachePath() const override;
 
   virtual GURL CrashUploadURL() const;
   virtual GURL DeviceManagementURL() const;
@@ -101,7 +101,7 @@
   scoped_refptr<update_client::CrxDownloaderFactory> crx_downloader_factory_;
   scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
   scoped_refptr<update_client::PatcherFactory> patch_factory_;
-  const absl::optional<bool> is_managed_device_;
+  const std::optional<bool> is_managed_device_;
 };
 
 }  // namespace updater
diff --git a/chrome/updater/crash_client.cc b/chrome/updater/crash_client.cc
index 373a2a5..d6bf4084 100644
--- a/chrome/updater/crash_client.cc
+++ b/chrome/updater/crash_client.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/crash_client.h"
 
+#include <optional>
 #include <vector>
 
 #include "base/check.h"
@@ -21,7 +22,6 @@
 #include "chrome/updater/updater_branding.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
 #include "third_party/crashpad/crashpad/client/prune_crash_reports.h"
@@ -57,7 +57,7 @@
 bool CrashClient::InitializeDatabaseOnly(UpdaterScope updater_scope) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const absl::optional<base::FilePath> database_path =
+  const std::optional<base::FilePath> database_path =
       EnsureCrashDatabasePath(updater_scope);
   if (!database_path) {
     LOG(ERROR) << "Failed to get the database path.";
@@ -126,7 +126,7 @@
     LOG(ERROR) << "Failed to fetch pending crash reports: " << status_pending;
   }
 
-  absl::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
+  std::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
   std::string env_usage_stats;
   if ((tag_args && tag_args->usage_stats_enable &&
        *tag_args->usage_stats_enable) ||
diff --git a/chrome/updater/crash_reporter.cc b/chrome/updater/crash_reporter.cc
index 413a713b..bb001fa06 100644
--- a/chrome/updater/crash_reporter.cc
+++ b/chrome/updater/crash_reporter.cc
@@ -7,6 +7,7 @@
 #include <iterator>
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -26,7 +27,6 @@
 #include "chrome/updater/updater_branding.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/crashpad/crashpad/client/crashpad_client.h"
 #include "third_party/crashpad/crashpad/client/crashpad_info.h"
 #include "third_party/crashpad/crashpad/handler/handler_main.h"
@@ -77,7 +77,7 @@
   base::FilePath handler_path;
   base::PathService::Get(base::FILE_EXE, &handler_path);
 
-  const absl::optional<base::FilePath> database_path =
+  const std::optional<base::FilePath> database_path =
       EnsureCrashDatabasePath(updater_scope);
   if (!database_path) {
     LOG(ERROR) << "Failed to get the database path.";
@@ -98,7 +98,7 @@
   crashpad::CrashpadClient& client = GetCrashpadClient();
   std::vector<base::FilePath> attachments;
 #if !BUILDFLAG(IS_MAC)  // Crashpad does not support attachments on macOS.
-  absl::optional<base::FilePath> log_file = GetLogFilePath(updater_scope);
+  std::optional<base::FilePath> log_file = GetLogFilePath(updater_scope);
   if (log_file) {
     attachments.push_back(*log_file);
   }
diff --git a/chrome/updater/device_management/dm_client.cc b/chrome/updater/device_management/dm_client.cc
index fd76407b..8e54fdb 100644
--- a/chrome/updater/device_management/dm_client.cc
+++ b/chrome/updater/device_management/dm_client.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <ostream>
 #include <string>
 #include <utility>
@@ -31,7 +32,6 @@
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/util.h"
 #include "components/update_client/network.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -68,7 +68,7 @@
 class DefaultConfigurator : public DMClient::Configurator {
  public:
   DefaultConfigurator(const GURL& server_url,
-                      absl::optional<PolicyServiceProxyConfiguration>
+                      std::optional<PolicyServiceProxyConfiguration>
                           policy_service_proxy_configuration);
   ~DefaultConfigurator() override = default;
 
@@ -92,7 +92,7 @@
 
 DefaultConfigurator::DefaultConfigurator(
     const GURL& server_url,
-    absl::optional<PolicyServiceProxyConfiguration>
+    std::optional<PolicyServiceProxyConfiguration>
         policy_service_proxy_configuration)
     : server_url_(server_url),
       network_fetcher_factory_(base::MakeRefCounted<NetworkFetcherFactory>(
@@ -414,7 +414,7 @@
 
 std::unique_ptr<DMClient::Configurator> DMClient::CreateDefaultConfigurator(
     const GURL& server_url,
-    absl::optional<PolicyServiceProxyConfiguration>
+    std::optional<PolicyServiceProxyConfiguration>
         policy_service_proxy_configuration) {
   return std::make_unique<DefaultConfigurator>(
       server_url, policy_service_proxy_configuration);
diff --git a/chrome/updater/device_management/dm_client.h b/chrome/updater/device_management/dm_client.h
index 33a3a18..1b7f7e5 100644
--- a/chrome/updater/device_management/dm_client.h
+++ b/chrome/updater/device_management/dm_client.h
@@ -6,13 +6,13 @@
 #define CHROME_UPDATER_DEVICE_MANAGEMENT_DM_CLIENT_H_
 
 #include <memory>
+#include <optional>
 #include <ostream>
 #include <string>
 #include <vector>
 
 #include "base/functional/callback.h"
 #include "base/memory/scoped_refptr.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 
@@ -138,7 +138,7 @@
 
   static std::unique_ptr<Configurator> CreateDefaultConfigurator(
       const GURL& server_url,
-      absl::optional<PolicyServiceProxyConfiguration>
+      std::optional<PolicyServiceProxyConfiguration>
           policy_service_proxy_configuration);
 };
 
diff --git a/chrome/updater/device_management/dm_policy_builder_for_testing.h b/chrome/updater/device_management/dm_policy_builder_for_testing.h
index e3bedd1d..27ac2d6 100644
--- a/chrome/updater/device_management/dm_policy_builder_for_testing.h
+++ b/chrome/updater/device_management/dm_policy_builder_for_testing.h
@@ -6,7 +6,9 @@
 #define CHROME_UPDATER_DEVICE_MANAGEMENT_DM_POLICY_BUILDER_FOR_TESTING_H_
 
 #include <stdint.h>
+
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
diff --git a/chrome/updater/device_management/dm_storage_mac.mm b/chrome/updater/device_management/dm_storage_mac.mm
index 3b9dff7..d1e875c 100644
--- a/chrome/updater/device_management/dm_storage_mac.mm
+++ b/chrome/updater/device_management/dm_storage_mac.mm
@@ -6,6 +6,7 @@
 
 #import <Foundation/Foundation.h>
 
+#include <optional>
 #include <string>
 
 #include "base/apple/foundation_util.h"
@@ -189,7 +190,7 @@
                                                dm_token_path)) {}
 
 scoped_refptr<DMStorage> GetDefaultDMStorage() {
-  absl::optional<base::FilePath> keystone_path =
+  std::optional<base::FilePath> keystone_path =
       GetKeystoneFolderPath(UpdaterScope::kSystem);
   return keystone_path ? base::MakeRefCounted<DMStorage>(
                              keystone_path->AppendASCII("DeviceManagement"))
diff --git a/chrome/updater/enum_traits.h b/chrome/updater/enum_traits.h
index 581acf7..9d10cfc1 100644
--- a/chrome/updater/enum_traits.h
+++ b/chrome/updater/enum_traits.h
@@ -5,11 +5,10 @@
 #ifndef CHROME_UPDATER_ENUM_TRAITS_H_
 #define CHROME_UPDATER_ENUM_TRAITS_H_
 
+#include <optional>
 #include <ostream>
 #include <type_traits>
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
 namespace updater {
 
 // Provides a way to safely convert numeric types to enumerated values.
@@ -42,14 +41,14 @@
 // The enum type must be annotated with traits to specify the lower and upper
 // bounds of the enum values.
 template <typename T, typename V>
-absl::optional<T> CheckedCastToEnum(V v) {
+std::optional<T> CheckedCastToEnum(V v) {
   static_assert(std::is_enum<T>::value, "T must be an enum type.");
   static_assert(std::is_integral<V>::value, "V must be an integral type.");
   using Traits = EnumTraits<T>;
   return (static_cast<V>(Traits::first_elem) <= v &&
           v <= static_cast<V>(Traits::last_elem))
-             ? absl::make_optional(static_cast<T>(v))
-             : absl::nullopt;
+             ? std::make_optional(static_cast<T>(v))
+             : std::nullopt;
 }
 
 }  // namespace updater
diff --git a/chrome/updater/enum_traits_unittest.cc b/chrome/updater/enum_traits_unittest.cc
index def0b499..e53f0e5 100644
--- a/chrome/updater/enum_traits_unittest.cc
+++ b/chrome/updater/enum_traits_unittest.cc
@@ -7,7 +7,6 @@
 #include <ostream>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -27,8 +26,8 @@
   EXPECT_EQ(MyEnum::kVal1, *CheckedCastToEnum<MyEnum>(-1));
   EXPECT_EQ(MyEnum::kVal2, *CheckedCastToEnum<MyEnum>(0));
   EXPECT_EQ(MyEnum::kVal3, *CheckedCastToEnum<MyEnum>(1));
-  EXPECT_EQ(CheckedCastToEnum<MyEnum>(-2), absl::nullopt);
-  EXPECT_EQ(CheckedCastToEnum<MyEnum>(2), absl::nullopt);
+  EXPECT_EQ(CheckedCastToEnum<MyEnum>(-2), std::nullopt);
+  EXPECT_EQ(CheckedCastToEnum<MyEnum>(2), std::nullopt);
 }
 
 }  // namespace updater
diff --git a/chrome/updater/external_constants.h b/chrome/updater/external_constants.h
index 841b9f6..0b27953 100644
--- a/chrome/updater/external_constants.h
+++ b/chrome/updater/external_constants.h
@@ -5,13 +5,13 @@
 #ifndef CHROME_UPDATER_EXTERNAL_CONSTANTS_H_
 #define CHROME_UPDATER_EXTERNAL_CONSTANTS_H_
 
+#include <optional>
 #include <vector>
 
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "base/values.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 
@@ -65,7 +65,7 @@
   virtual base::TimeDelta IdleCheckPeriod() const = 0;
 
   // Overrides machine management state.
-  virtual absl::optional<bool> IsMachineManaged() const = 0;
+  virtual std::optional<bool> IsMachineManaged() const = 0;
 
   // True if the updater should request and apply diff updates.
   virtual bool EnableDiffUpdates() const = 0;
diff --git a/chrome/updater/external_constants_builder.cc b/chrome/updater/external_constants_builder.cc
index aec530e..ab5ad29 100644
--- a/chrome/updater/external_constants_builder.cc
+++ b/chrome/updater/external_constants_builder.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/external_constants_builder.h"
 
 #include <iterator>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -21,7 +22,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
 #include "components/crx_file/crx_verifier.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -168,7 +168,7 @@
 }
 
 ExternalConstantsBuilder& ExternalConstantsBuilder::SetMachineManaged(
-    const absl::optional<bool>& is_managed_device) {
+    const std::optional<bool>& is_managed_device) {
   if (is_managed_device.has_value()) {
     overrides_.Set(kDevOverrideKeyManagedDevice, is_managed_device.value());
   }
@@ -193,7 +193,7 @@
 }
 
 bool ExternalConstantsBuilder::Overwrite() {
-  const absl::optional<base::FilePath> override_path =
+  const std::optional<base::FilePath> override_path =
       GetOverrideFilePath(GetUpdaterScope());
   if (!override_path) {
     LOG(ERROR) << "Can't find base directory; can't save constant overrides.";
diff --git a/chrome/updater/external_constants_builder.h b/chrome/updater/external_constants_builder.h
index e9b713cc..cd95d2d5 100644
--- a/chrome/updater/external_constants_builder.h
+++ b/chrome/updater/external_constants_builder.h
@@ -5,12 +5,12 @@
 #ifndef CHROME_UPDATER_EXTERNAL_CONSTANTS_BUILDER_H_
 #define CHROME_UPDATER_EXTERNAL_CONSTANTS_BUILDER_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "base/files/file_path.h"
 #include "base/values.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class TimeDelta;
@@ -75,7 +75,7 @@
   ExternalConstantsBuilder& ClearIdleCheckPeriod();
 
   ExternalConstantsBuilder& SetMachineManaged(
-      const absl::optional<bool>& is_managed_device);
+      const std::optional<bool>& is_managed_device);
   ExternalConstantsBuilder& ClearMachineManaged();
 
   ExternalConstantsBuilder& SetEnableDiffUpdates(bool enable_diffs);
diff --git a/chrome/updater/external_constants_builder_unittest.cc b/chrome/updater/external_constants_builder_unittest.cc
index 65177ce..bd58542 100644
--- a/chrome/updater/external_constants_builder_unittest.cc
+++ b/chrome/updater/external_constants_builder_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -20,7 +21,6 @@
 #include "chrome/updater/updater_branding.h"
 #include "chrome/updater/util/unit_test_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -38,7 +38,7 @@
 
  private:
   // This test runs non-elevated.
-  const absl::optional<base::FilePath> overrides_file_path_ =
+  const std::optional<base::FilePath> overrides_file_path_ =
       GetOverrideFilePath(UpdaterScope::kUser);
 };
 
@@ -75,7 +75,7 @@
       .SetGroupPolicies(group_policies)
       .SetOverinstallTimeout(base::Seconds(3))
       .SetIdleCheckPeriod(base::Seconds(4))
-      .SetMachineManaged(absl::make_optional(true))
+      .SetMachineManaged(std::make_optional(true))
       .SetEnableDiffUpdates(true);
   EXPECT_TRUE(builder.Overwrite());
 
@@ -181,7 +181,7 @@
           .SetUseCUP(true)
           .SetInitialDelay(base::Seconds(123.4))
           .SetServerKeepAliveTime(base::Seconds(2))
-          .SetMachineManaged(absl::make_optional(true))
+          .SetMachineManaged(std::make_optional(true))
           .SetGroupPolicies(group_policies)
           .SetEnableDiffUpdates(false)
           .SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
@@ -190,7 +190,7 @@
           .SetUseCUP(false)
           .SetInitialDelay(base::Seconds(937.6))
           .SetServerKeepAliveTime(base::Seconds(3))
-          .SetMachineManaged(absl::make_optional(false))
+          .SetMachineManaged(std::make_optional(false))
           .SetEnableDiffUpdates(true)
           .Overwrite());
 
@@ -230,7 +230,7 @@
           .SetServerKeepAliveTime(base::Seconds(3))
           .SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
           .SetGroupPolicies(group_policies)
-          .SetMachineManaged(absl::make_optional(true))
+          .SetMachineManaged(std::make_optional(true))
           .SetEnableDiffUpdates(true)
           .Overwrite());
 
@@ -307,7 +307,7 @@
           .SetCrashUploadURL("https://crash.example.com")
           .SetDeviceManagementURL("https://dm.example.com")
           .SetGroupPolicies(group_policies)
-          .SetMachineManaged(absl::make_optional(false))
+          .SetMachineManaged(std::make_optional(false))
           .SetEnableDiffUpdates(true)
           .Overwrite());
 
diff --git a/chrome/updater/external_constants_default.cc b/chrome/updater/external_constants_default.cc
index f883c00..c0c2770c 100644
--- a/chrome/updater/external_constants_default.cc
+++ b/chrome/updater/external_constants_default.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/updater/external_constants_default.h"
 
+#include <optional>
+#include <vector>
+
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -11,7 +14,6 @@
 #include "chrome/updater/external_constants.h"
 #include "chrome/updater/updater_branding.h"
 #include "components/crx_file/crx_verifier.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -54,9 +56,7 @@
 
   base::TimeDelta IdleCheckPeriod() const override { return base::Minutes(5); }
 
-  absl::optional<bool> IsMachineManaged() const override {
-    return absl::nullopt;
-  }
+  std::optional<bool> IsMachineManaged() const override { return std::nullopt; }
 
   bool EnableDiffUpdates() const override { return false; }
 
diff --git a/chrome/updater/external_constants_override.cc b/chrome/updater/external_constants_override.cc
index f847920..945cf700 100644
--- a/chrome/updater/external_constants_override.cc
+++ b/chrome/updater/external_constants_override.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -27,7 +28,6 @@
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/util.h"
 #include "components/crx_file/crx_verifier.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_MAC)
@@ -56,10 +56,10 @@
 
 namespace updater {
 
-absl::optional<base::FilePath> GetOverrideFilePath(UpdaterScope scope) {
-  absl::optional<base::FilePath> base = GetInstallDirectory(scope);
+std::optional<base::FilePath> GetOverrideFilePath(UpdaterScope scope) {
+  std::optional<base::FilePath> base = GetInstallDirectory(scope);
   if (!base) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return base->DirName().AppendASCII(kDevOverrideFileName);
 }
@@ -209,7 +209,7 @@
   return base::Seconds(value->GetInt());
 }
 
-absl::optional<bool> ExternalConstantsOverrider::IsMachineManaged() const {
+std::optional<bool> ExternalConstantsOverrider::IsMachineManaged() const {
   if (!override_values_.contains(kDevOverrideKeyManagedDevice)) {
     return next_provider_->IsMachineManaged();
   }
@@ -219,7 +219,7 @@
       << "Unexpected type of override[" << kDevOverrideKeyManagedDevice
       << "]: " << base::Value::GetTypeName(is_managed->type());
 
-  return absl::make_optional(is_managed->GetBool());
+  return std::make_optional(is_managed->GetBool());
 }
 
 bool ExternalConstantsOverrider::EnableDiffUpdates() const {
@@ -238,7 +238,7 @@
 scoped_refptr<ExternalConstantsOverrider>
 ExternalConstantsOverrider::FromDefaultJSONFile(
     scoped_refptr<ExternalConstants> next_provider) {
-  const absl::optional<base::FilePath> override_file_path =
+  const std::optional<base::FilePath> override_file_path =
       GetOverrideFilePath(GetUpdaterScope());
   if (!override_file_path) {
     LOG(ERROR) << "Cannot find override file path.";
diff --git a/chrome/updater/external_constants_override.h b/chrome/updater/external_constants_override.h
index fd45e60..f628749 100644
--- a/chrome/updater/external_constants_override.h
+++ b/chrome/updater/external_constants_override.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_EXTERNAL_CONSTANTS_OVERRIDE_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -15,7 +16,6 @@
 #include "base/values.h"
 #include "chrome/updater/external_constants.h"
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 
@@ -31,7 +31,7 @@
 
 namespace updater {
 
-absl::optional<base::FilePath> GetOverrideFilePath(UpdaterScope scope);
+std::optional<base::FilePath> GetOverrideFilePath(UpdaterScope scope);
 
 class ExternalConstantsOverrider : public ExternalConstants {
  public:
@@ -57,7 +57,7 @@
   base::Value::Dict GroupPolicies() const override;
   base::TimeDelta OverinstallTimeout() const override;
   base::TimeDelta IdleCheckPeriod() const override;
-  absl::optional<bool> IsMachineManaged() const override;
+  std::optional<bool> IsMachineManaged() const override;
   bool EnableDiffUpdates() const override;
 
  private:
diff --git a/chrome/updater/installer.cc b/chrome/updater/installer.cc
index 1cdc585..e926f95 100644
--- a/chrome/updater/installer.cc
+++ b/chrome/updater/installer.cc
@@ -27,7 +27,6 @@
 #include "components/crx_file/crx_verifier.h"
 #include "components/update_client/update_client_errors.h"
 #include "components/update_client/utils.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
diff --git a/chrome/updater/installer.h b/chrome/updater/installer.h
index cf21a89..6a86ab01 100644
--- a/chrome/updater/installer.h
+++ b/chrome/updater/installer.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_INSTALLER_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -19,7 +20,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "components/crx_file/crx_verifier.h"
 #include "components/update_client/update_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class TimeDelta;
@@ -57,7 +57,7 @@
     const AppInfo& app_info,
     const base::FilePath& installer_path,
     const std::string& install_args,
-    const absl::optional<base::FilePath>& server_install_data,
+    const std::optional<base::FilePath>& server_install_data,
     bool usage_stats_enabled,
     const base::TimeDelta& timeout,
     InstallProgressCallback progress_callback);
diff --git a/chrome/updater/installer_linux.cc b/chrome/updater/installer_linux.cc
index 72dd98b..7122c0651a 100644
--- a/chrome/updater/installer_linux.cc
+++ b/chrome/updater/installer_linux.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <optional>
+
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/process/launch.h"
@@ -16,7 +18,7 @@
     const AppInfo& app_info,
     const base::FilePath& installer_path,
     const std::string& arguments,
-    const absl::optional<base::FilePath>& install_data_file,
+    const std::optional<base::FilePath>& install_data_file,
     bool usage_stats_enabled,
     const base::TimeDelta& timeout,
     InstallProgressCallback /*progress_callback*/) {
diff --git a/chrome/updater/installer_mac.cc b/chrome/updater/installer_mac.cc
index 7da3ffc..72839c6 100644
--- a/chrome/updater/installer_mac.cc
+++ b/chrome/updater/installer_mac.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/installer.h"
 
+#include <optional>
+
 #include "base/files/file_path.h"
 #include "base/functional/callback.h"
 #include "base/logging.h"
@@ -12,7 +14,6 @@
 #include "chrome/updater/constants.h"
 #include "chrome/updater/mac/install_from_archive.h"
 #include "chrome/updater/util/mac_util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -20,7 +21,7 @@
     const AppInfo& app_info,
     const base::FilePath& app_installer,
     const std::string& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
+    const std::optional<base::FilePath>& installer_data_file,
     bool usage_stats_enabled,
     const base::TimeDelta& timeout,
     InstallProgressCallback /*progress_callback*/) {
@@ -43,14 +44,14 @@
 std::string LookupString(const base::FilePath& path,
                          const std::string& keyname,
                          const std::string& default_value) {
-  absl::optional<std::string> value = ReadValueFromPlist(path, keyname);
+  std::optional<std::string> value = ReadValueFromPlist(path, keyname);
   return value ? *value : default_value;
 }
 
 base::Version LookupVersion(const base::FilePath& path,
                             const std::string& keyname,
                             const base::Version& default_value) {
-  absl::optional<std::string> value = ReadValueFromPlist(path, keyname);
+  std::optional<std::string> value = ReadValueFromPlist(path, keyname);
   if (value) {
     base::Version value_version(*value);
     return value_version.IsValid() ? value_version : default_value;
diff --git a/chrome/updater/ipc/ipc_names_linux.cc b/chrome/updater/ipc/ipc_names_linux.cc
index 28da8d5..79106b19 100644
--- a/chrome/updater/ipc/ipc_names_linux.cc
+++ b/chrome/updater/ipc/ipc_names_linux.cc
@@ -4,10 +4,11 @@
 
 #include "chrome/updater/ipc/ipc_names.h"
 
+#include <optional>
+
 #include "chrome/updater/linux/ipc_constants.h"
 #include "chrome/updater/updater_scope.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -18,7 +19,7 @@
 
 mojo::NamedPlatformChannel::ServerName GetUpdateServiceServerName(
     UpdaterScope scope) {
-  absl::optional<base::FilePath> socket = GetActiveDutySocketPath(scope);
+  std::optional<base::FilePath> socket = GetActiveDutySocketPath(scope);
   CHECK(socket);
   return socket->MaybeAsASCII();
 }
diff --git a/chrome/updater/ipc/update_service_dialer_linux.cc b/chrome/updater/ipc/update_service_dialer_linux.cc
index fb92283d..8dd0f977 100644
--- a/chrome/updater/ipc/update_service_dialer_linux.cc
+++ b/chrome/updater/ipc/update_service_dialer_linux.cc
@@ -6,7 +6,9 @@
 
 #include <sys/socket.h>
 #include <sys/un.h>
+
 #include <cstdio>
+#include <optional>
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -16,7 +18,6 @@
 #include "chrome/updater/linux/ipc_constants.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -49,7 +50,7 @@
 }
 
 bool DialUpdateInternalService(UpdaterScope scope) {
-  absl::optional<base::FilePath> updater = GetUpdaterExecutablePath(scope);
+  std::optional<base::FilePath> updater = GetUpdaterExecutablePath(scope);
   if (updater) {
     base::CommandLine command(*updater);
     command.AppendSwitch(kServerSwitch);
diff --git a/chrome/updater/ipc/update_service_dialer_mac.cc b/chrome/updater/ipc/update_service_dialer_mac.cc
index 64609dc..3144afb 100644
--- a/chrome/updater/ipc/update_service_dialer_mac.cc
+++ b/chrome/updater/ipc/update_service_dialer_mac.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/ipc/update_service_dialer.h"
 
+#include <optional>
+
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -12,7 +14,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/posix_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -36,7 +37,7 @@
 }  // namespace
 
 bool DialUpdateService(UpdaterScope scope) {
-  absl::optional<base::FilePath> launcher = GetUpdateServiceLauncherPath(scope);
+  std::optional<base::FilePath> launcher = GetUpdateServiceLauncherPath(scope);
   if (!launcher) {
     return true;
   }
@@ -44,7 +45,7 @@
 }
 
 bool DialUpdateInternalService(UpdaterScope scope) {
-  absl::optional<base::FilePath> bundle = GetUpdaterAppBundlePath(scope);
+  std::optional<base::FilePath> bundle = GetUpdaterAppBundlePath(scope);
   if (!bundle) {
     return true;
   }
diff --git a/chrome/updater/ipc/update_service_internal_proxy.cc b/chrome/updater/ipc/update_service_internal_proxy.cc
index 3c64a73b..97f9ad422 100644
--- a/chrome/updater/ipc/update_service_internal_proxy.cc
+++ b/chrome/updater/ipc/update_service_internal_proxy.cc
@@ -4,11 +4,11 @@
 
 #include "chrome/updater/ipc/update_service_internal_proxy.h"
 
+#include <optional>
 #include <utility>
 
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_POSIX)
 #include "chrome/updater/ipc/update_service_internal_proxy_posix.h"
@@ -47,7 +47,7 @@
 
 void UpdateServiceInternalProxy::RunDone(base::OnceClosure callback,
                                          int try_count,
-                                         absl::optional<RpcError> error) {
+                                         std::optional<RpcError> error) {
   if (error && CanRetry(try_count)) {
     proxy_->Run(base::BindOnce(&UpdateServiceInternalProxy::RunDone, this,
                                std::move(callback), try_count + 1));
@@ -58,7 +58,7 @@
 
 void UpdateServiceInternalProxy::HelloDone(base::OnceClosure callback,
                                            int try_count,
-                                           absl::optional<RpcError> error) {
+                                           std::optional<RpcError> error) {
   if (error && CanRetry(try_count)) {
     proxy_->Hello(base::BindOnce(&UpdateServiceInternalProxy::HelloDone, this,
                                  std::move(callback), try_count + 1));
diff --git a/chrome/updater/ipc/update_service_internal_proxy.h b/chrome/updater/ipc/update_service_internal_proxy.h
index 2aeb1dc5..04cda7d8 100644
--- a/chrome/updater/ipc/update_service_internal_proxy.h
+++ b/chrome/updater/ipc/update_service_internal_proxy.h
@@ -5,12 +5,13 @@
 #ifndef CHROME_UPDATER_IPC_UPDATE_SERVICE_INTERNAL_PROXY_H_
 #define CHROME_UPDATER_IPC_UPDATE_SERVICE_INTERNAL_PROXY_H_
 
+#include <optional>
+
 #include "base/functional/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "build/build_config.h"
 #include "chrome/updater/update_service_internal.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_POSIX)
 #include "chrome/updater/ipc/update_service_internal_proxy_posix.h"
@@ -36,10 +37,10 @@
 
   void RunDone(base::OnceClosure callback,
                int try_count,
-               absl::optional<RpcError> error);
+               std::optional<RpcError> error);
   void HelloDone(base::OnceClosure callback,
                  int try_count,
-                 absl::optional<RpcError> result);
+                 std::optional<RpcError> result);
 
   SEQUENCE_CHECKER(sequence_checker_);
   scoped_refptr<UpdateServiceInternalProxyImpl> proxy_;
diff --git a/chrome/updater/ipc/update_service_internal_proxy_posix.cc b/chrome/updater/ipc/update_service_internal_proxy_posix.cc
index d4fa2a7..00bb8e23f 100644
--- a/chrome/updater/ipc/update_service_internal_proxy_posix.cc
+++ b/chrome/updater/ipc/update_service_internal_proxy_posix.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/ipc/update_service_internal_proxy_posix.h"
 
 #include <memory>
+#include <optional>
 #include <utility>
 
 #include "base/command_line.h"
@@ -33,7 +34,6 @@
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -45,10 +45,10 @@
 
 // Connect to the server.
 // `retries` is 0 for the first try, 1 for the first retry, etc.
-absl::optional<mojo::PlatformChannelEndpoint> ConnectMojo(UpdaterScope scope,
-                                                          int retries) {
+std::optional<mojo::PlatformChannelEndpoint> ConnectMojo(UpdaterScope scope,
+                                                         int retries) {
   if (retries == 1 && !DialUpdateInternalService(scope)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return named_mojo_ipc_server::ConnectToServer(
       GetUpdateServiceInternalServerName(scope));
@@ -58,22 +58,22 @@
     UpdaterScope scope,
     int tries,
     base::Time deadline,
-    base::OnceCallback<void(absl::optional<mojo::PlatformChannelEndpoint>)>
+    base::OnceCallback<void(std::optional<mojo::PlatformChannelEndpoint>)>
         connected_callback) {
   if (base::Time::Now() > deadline) {
     LOG(ERROR) << "Failed to connect to UpdateServiceInternal remote. "
                   "Connection timed out.";
-    std::move(connected_callback).Run(absl::nullopt);
+    std::move(connected_callback).Run(std::nullopt);
     return;
   }
 
-  absl::optional<mojo::PlatformChannelEndpoint> endpoint =
+  std::optional<mojo::PlatformChannelEndpoint> endpoint =
       ConnectMojo(scope, tries);
 
   if (!endpoint) {
     VLOG(1) << "Failed to connect to UpdateService remote. "
                "No updater exists.";
-    std::move(connected_callback).Run(absl::nullopt);
+    std::move(connected_callback).Run(std::nullopt);
     return;
   }
 
@@ -96,7 +96,7 @@
     : scope_(scope) {}
 
 void UpdateServiceInternalProxyImpl::Run(
-    base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+    base::OnceCallback<void(std::optional<RpcError>)> callback) {
   VLOG(1) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureConnecting();
@@ -104,11 +104,11 @@
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
           base::BindPostTaskToCurrentDefault(std::move(callback)),
           kErrorIpcDisconnect),
-      absl::nullopt));
+      std::nullopt));
 }
 
 void UpdateServiceInternalProxyImpl::Hello(
-    base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+    base::OnceCallback<void(std::optional<RpcError>)> callback) {
   VLOG(1) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   EnsureConnecting();
@@ -116,7 +116,7 @@
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
           base::BindPostTaskToCurrentDefault(std::move(callback)),
           kErrorIpcDisconnect),
-      absl::nullopt));
+      std::nullopt));
 }
 
 UpdateServiceInternalProxyImpl::~UpdateServiceInternalProxyImpl() = default;
@@ -144,7 +144,7 @@
 
 void UpdateServiceInternalProxyImpl::OnConnected(
     mojo::PendingReceiver<mojom::UpdateServiceInternal> pending_receiver,
-    absl::optional<mojo::PlatformChannelEndpoint> endpoint) {
+    std::optional<mojo::PlatformChannelEndpoint> endpoint) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!endpoint) {
     VLOG(2) << "No endpoint received.";
diff --git a/chrome/updater/ipc/update_service_internal_proxy_posix.h b/chrome/updater/ipc/update_service_internal_proxy_posix.h
index 289759e..c226c2e 100644
--- a/chrome/updater/ipc/update_service_internal_proxy_posix.h
+++ b/chrome/updater/ipc/update_service_internal_proxy_posix.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_IPC_UPDATE_SERVICE_INTERNAL_PROXY_POSIX_H_
 
 #include <memory>
+#include <optional>
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
@@ -15,7 +16,6 @@
 #include "chrome/updater/update_service_internal.h"
 #include "chrome/updater/updater_scope.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace mojo {
 class PlatformChannelEndpoint;
@@ -33,8 +33,8 @@
   // It establishes a connection lazily and can be used immediately.
   explicit UpdateServiceInternalProxyImpl(UpdaterScope scope);
 
-  void Run(base::OnceCallback<void(absl::optional<RpcError>)> callback);
-  void Hello(base::OnceCallback<void(absl::optional<RpcError>)> callback);
+  void Run(base::OnceCallback<void(std::optional<RpcError>)> callback);
+  void Hello(base::OnceCallback<void(std::optional<RpcError>)> callback);
 
  private:
   friend class base::RefCountedThreadSafe<UpdateServiceInternalProxyImpl>;
@@ -44,7 +44,7 @@
   void OnDisconnected();
   void OnConnected(
       mojo::PendingReceiver<mojom::UpdateServiceInternal> pending_receiver,
-      absl::optional<mojo::PlatformChannelEndpoint> endpoint);
+      std::optional<mojo::PlatformChannelEndpoint> endpoint);
 
   SEQUENCE_CHECKER(sequence_checker_);
   const UpdaterScope scope_;
diff --git a/chrome/updater/ipc/update_service_internal_proxy_win.cc b/chrome/updater/ipc/update_service_internal_proxy_win.cc
index 160a206..4c0d3b2 100644
--- a/chrome/updater/ipc/update_service_internal_proxy_win.cc
+++ b/chrome/updater/ipc/update_service_internal_proxy_win.cc
@@ -9,6 +9,7 @@
 #include <wrl/implements.h>
 
 #include <ios>
+#include <optional>
 #include <utility>
 
 #include "base/check_op.h"
@@ -24,7 +25,6 @@
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/setup/setup_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -33,7 +33,7 @@
     : public DYNAMICIIDSIMPL(IUpdaterInternalCallback) {
  public:
   explicit UpdaterInternalCallback(
-      base::OnceCallback<void(absl::optional<RpcError>)> callback)
+      base::OnceCallback<void(std::optional<RpcError>)> callback)
       : callback_(std::move(callback)) {}
   UpdaterInternalCallback(const UpdaterInternalCallback&) = delete;
   UpdaterInternalCallback& operator=(const UpdaterInternalCallback&) = delete;
@@ -45,16 +45,16 @@
   // Disconnects this callback from its subject and ensures the callbacks are
   // not posted after this function is called. Returns the completion callback
   // so that the owner of this object can take back the callback ownership.
-  base::OnceCallback<void(absl::optional<RpcError>)> Disconnect();
+  base::OnceCallback<void(std::optional<RpcError>)> Disconnect();
 
  private:
   ~UpdaterInternalCallback() override {
     if (callback_)
-      std::move(callback_).Run(absl::nullopt);
+      std::move(callback_).Run(std::nullopt);
   }
 
   // Called by IUpdaterInternalCallback::Run when the COM RPC call is done.
-  base::OnceCallback<void(absl::optional<RpcError>)> callback_;
+  base::OnceCallback<void(std::optional<RpcError>)> callback_;
 };
 
 IFACEMETHODIMP UpdaterInternalCallback::Run(LONG result) {
@@ -62,7 +62,7 @@
   return S_OK;
 }
 
-base::OnceCallback<void(absl::optional<RpcError>)>
+base::OnceCallback<void(std::optional<RpcError>)>
 UpdaterInternalCallback::Disconnect() {
   VLOG(2) << __func__;
   return std::move(callback_);
@@ -85,13 +85,13 @@
                                   : __uuidof(UpdaterInternalUserClass);
   }
 
-  void Run(base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+  void Run(base::OnceCallback<void(std::optional<RpcError>)> callback) {
     PostRPCTask(
         base::BindOnce(&UpdateServiceInternalProxyImplImpl::RunOnTaskRunner,
                        this, std::move(callback)));
   }
 
-  void Hello(base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+  void Hello(base::OnceCallback<void(std::optional<RpcError>)> callback) {
     PostRPCTask(
         base::BindOnce(&UpdateServiceInternalProxyImplImpl::HelloOnTaskRunner,
                        this, std::move(callback)));
@@ -102,7 +102,7 @@
   ~UpdateServiceInternalProxyImplImpl() = default;
 
   void RunOnTaskRunner(
-      base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+      base::OnceCallback<void(std::optional<RpcError>)> callback) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     if (HRESULT connection = ConnectToServer(); FAILED(connection)) {
       std::move(callback).Run(connection);
@@ -119,7 +119,7 @@
   }
 
   void HelloOnTaskRunner(
-      base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+      base::OnceCallback<void(std::optional<RpcError>)> callback) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     if (HRESULT connection = ConnectToServer(); FAILED(connection)) {
       std::move(callback).Run(connection);
@@ -147,14 +147,14 @@
 }
 
 void UpdateServiceInternalProxyImpl::Run(
-    base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+    base::OnceCallback<void(std::optional<RpcError>)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   VLOG(1) << __func__;
   impl_->Run(base::BindPostTaskToCurrentDefault(std::move(callback)));
 }
 
 void UpdateServiceInternalProxyImpl::Hello(
-    base::OnceCallback<void(absl::optional<RpcError>)> callback) {
+    base::OnceCallback<void(std::optional<RpcError>)> callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   VLOG(1) << __func__;
   impl_->Hello(base::BindPostTaskToCurrentDefault(std::move(callback)));
diff --git a/chrome/updater/ipc/update_service_internal_proxy_win.h b/chrome/updater/ipc/update_service_internal_proxy_win.h
index 3129c73..cf7113d7 100644
--- a/chrome/updater/ipc/update_service_internal_proxy_win.h
+++ b/chrome/updater/ipc/update_service_internal_proxy_win.h
@@ -7,11 +7,12 @@
 
 #include <windows.h>
 
+#include <optional>
+
 #include "base/functional/callback_forward.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "chrome/updater/update_service_internal.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -26,8 +27,8 @@
  public:
   explicit UpdateServiceInternalProxyImpl(UpdaterScope scope);
 
-  void Run(base::OnceCallback<void(absl::optional<RpcError>)> callback);
-  void Hello(base::OnceCallback<void(absl::optional<RpcError>)> callback);
+  void Run(base::OnceCallback<void(std::optional<RpcError>)> callback);
+  void Hello(base::OnceCallback<void(std::optional<RpcError>)> callback);
 
  private:
   friend class base::RefCountedThreadSafe<UpdateServiceInternalProxyImpl>;
diff --git a/chrome/updater/ipc/update_service_proxy.cc b/chrome/updater/ipc/update_service_proxy.cc
index 88b8471..37a77a8 100644
--- a/chrome/updater/ipc/update_service_proxy.cc
+++ b/chrome/updater/ipc/update_service_proxy.cc
@@ -19,7 +19,6 @@
 #include "chrome/updater/constants.h"
 #include "chrome/updater/registration_data.h"
 #include "chrome/updater/update_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
diff --git a/chrome/updater/ipc/update_service_proxy_posix.cc b/chrome/updater/ipc/update_service_proxy_posix.cc
index 39a6205..9707b76a 100644
--- a/chrome/updater/ipc/update_service_proxy_posix.cc
+++ b/chrome/updater/ipc/update_service_proxy_posix.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/ipc/update_service_proxy_posix.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -45,7 +46,6 @@
 #include "mojo/public/cpp/platform/platform_channel_endpoint.h"
 #include "mojo/public/cpp/system/isolated_connection.h"
 #include "mojo/public/cpp/system/message_pipe.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -171,10 +171,10 @@
           ToMojoCallback(std::move(complete_callback))));
 }
 
-absl::optional<mojo::PlatformChannelEndpoint> ConnectMojo(UpdaterScope scope,
-                                                          int tries) {
+std::optional<mojo::PlatformChannelEndpoint> ConnectMojo(UpdaterScope scope,
+                                                         int tries) {
   if (tries == 1 && !DialUpdateService(scope)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return named_mojo_ipc_server::ConnectToServer(
       GetUpdateServiceServerName(scope));
@@ -184,21 +184,21 @@
     UpdaterScope scope,
     int tries,
     base::Time deadline,
-    base::OnceCallback<void(absl::optional<mojo::PlatformChannelEndpoint>)>
+    base::OnceCallback<void(std::optional<mojo::PlatformChannelEndpoint>)>
         connected_callback) {
   if (base::Time::Now() > deadline) {
     VLOG(1) << "Failed to connect to UpdateService remote. "
                "Connection timed out.";
-    std::move(connected_callback).Run(absl::nullopt);
+    std::move(connected_callback).Run(std::nullopt);
     return;
   }
-  absl::optional<mojo::PlatformChannelEndpoint> endpoint =
+  std::optional<mojo::PlatformChannelEndpoint> endpoint =
       ConnectMojo(scope, tries);
 
   if (!endpoint) {
     VLOG(1) << "Failed to connect to UpdateService remote. "
                "No updater exists.";
-    std::move(connected_callback).Run(absl::nullopt);
+    std::move(connected_callback).Run(std::nullopt);
     return;
   }
 
@@ -388,7 +388,7 @@
 
 void UpdateServiceProxyImpl::OnConnected(
     mojo::PendingReceiver<mojom::UpdateService> pending_receiver,
-    absl::optional<mojo::PlatformChannelEndpoint> endpoint) {
+    std::optional<mojo::PlatformChannelEndpoint> endpoint) {
   VLOG(1) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!endpoint) {
diff --git a/chrome/updater/ipc/update_service_proxy_posix.h b/chrome/updater/ipc/update_service_proxy_posix.h
index 20e14e50..34e1733 100644
--- a/chrome/updater/ipc/update_service_proxy_posix.h
+++ b/chrome/updater/ipc/update_service_proxy_posix.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_IPC_UPDATE_SERVICE_PROXY_POSIX_H_
 
 #include <memory>
+#include <optional>
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/ref_counted.h"
@@ -16,7 +17,6 @@
 #include "chrome/updater/update_service.h"
 #include "chrome/updater/updater_scope.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class FilePath;
@@ -111,7 +111,7 @@
   friend class base::RefCountedThreadSafe<UpdateServiceProxyImpl>;
   ~UpdateServiceProxyImpl();
   void OnConnected(mojo::PendingReceiver<mojom::UpdateService> pending_receiver,
-                   absl::optional<mojo::PlatformChannelEndpoint> endpoint);
+                   std::optional<mojo::PlatformChannelEndpoint> endpoint);
   void OnDisconnected();
   void EnsureConnecting();
 
diff --git a/chrome/updater/ipc/update_service_proxy_win.cc b/chrome/updater/ipc/update_service_proxy_win.cc
index 1cab1d7..484e2af 100644
--- a/chrome/updater/ipc/update_service_proxy_win.cc
+++ b/chrome/updater/ipc/update_service_proxy_win.cc
@@ -10,6 +10,7 @@
 
 #include <ios>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -34,7 +35,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -93,7 +93,7 @@
       HRESULT hr = update_state->get_state(&val_state);
       if (SUCCEEDED(hr)) {
         using State = UpdateService::UpdateState::State;
-        absl::optional<State> state = CheckedCastToEnum<State>(val_state);
+        std::optional<State> state = CheckedCastToEnum<State>(val_state);
         if (state)
           update_service_state.state = *state;
       }
@@ -135,7 +135,7 @@
       HRESULT hr = update_state->get_errorCategory(&val_error_category);
       if (SUCCEEDED(hr)) {
         using ErrorCategory = UpdateService::ErrorCategory;
-        absl::optional<ErrorCategory> error_category =
+        std::optional<ErrorCategory> error_category =
             CheckedCastToEnum<ErrorCategory>(val_error_category);
         if (error_category)
           update_service_state.error_category = *error_category;
@@ -262,7 +262,7 @@
     // The safearray is owned by the caller of `Run`, so ownership is released
     // here after acquiring the `LockScope`.
     base::win::ScopedSafearray safearray(V_ARRAY(&updater_app_states));
-    absl::optional<base::win::ScopedSafearray::LockScope<VT_DISPATCH>>
+    std::optional<base::win::ScopedSafearray::LockScope<VT_DISPATCH>>
         lock_scope = safearray.CreateLockScope<VT_DISPATCH>();
     safearray.Release();
 
diff --git a/chrome/updater/linux/setup/setup.cc b/chrome/updater/linux/setup/setup.cc
index 6edcb86..8331e49 100644
--- a/chrome/updater/linux/setup/setup.cc
+++ b/chrome/updater/linux/setup/setup.cc
@@ -7,6 +7,8 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include <optional>
+
 #include "base/base_paths.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -17,15 +19,13 @@
 #include "chrome/updater/linux/systemd_util.h"
 #include "chrome/updater/util/posix_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
 int Setup(UpdaterScope scope) {
   VLOG(1) << base::CommandLine::ForCurrentProcess()->GetCommandLineString()
           << " : " << __func__;
-  absl::optional<base::FilePath> dest_path =
-      GetVersionedInstallDirectory(scope);
+  std::optional<base::FilePath> dest_path = GetVersionedInstallDirectory(scope);
 
   if (!dest_path) {
     return kErrorFailedToGetVersionedInstallDirectory;
@@ -74,7 +74,7 @@
     error = kErrorFailedToDeleteFolder;
   }
 
-  absl::optional<base::FilePath> versioned_socket =
+  std::optional<base::FilePath> versioned_socket =
       GetActiveDutyInternalSocketPath(scope);
   if (!versioned_socket || !base::DeleteFile(versioned_socket.value())) {
     error = kErrorFailedToDeleteSocket;
@@ -85,7 +85,7 @@
 
 int PromoteCandidate(UpdaterScope scope) {
   // Create a hard link in the base install directory to this updater.
-  absl::optional<base::FilePath> launcher_path =
+  std::optional<base::FilePath> launcher_path =
       GetUpdateServiceLauncherPath(scope);
   base::FilePath updater_executable;
 
diff --git a/chrome/updater/linux/systemd_util.cc b/chrome/updater/linux/systemd_util.cc
index 75e80e45..bba0cba 100644
--- a/chrome/updater/linux/systemd_util.cc
+++ b/chrome/updater/linux/systemd_util.cc
@@ -8,6 +8,8 @@
 #include <sys/un.h>
 #include <systemd/sd-daemon.h>
 #include <unistd.h>
+
+#include <optional>
 #include <utility>
 
 #include "base/base_paths.h"
@@ -58,17 +60,17 @@
     "WantedBy=sockets.target";
 
 // Returns the path to the systemd unit directory for the given scope.
-absl::optional<base::FilePath> GetUnitDirectory(UpdaterScope scope) {
+std::optional<base::FilePath> GetUnitDirectory(UpdaterScope scope) {
   base::FilePath unit_dir;
   switch (scope) {
     case UpdaterScope::kUser:
       if (!base::PathService::Get(base::DIR_HOME, &unit_dir)) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       unit_dir = unit_dir.Append(kUserUnitRelativeDirectory);
 
       if (!base::CreateDirectory(unit_dir)) {
-        return absl::nullopt;
+        return std::nullopt;
       }
       break;
     case UpdaterScope::kSystem:
@@ -182,9 +184,9 @@
 }
 
 bool InstallSystemdUnits(UpdaterScope scope) {
-  absl::optional<base::FilePath> launcher_path =
+  std::optional<base::FilePath> launcher_path =
       GetUpdateServiceLauncherPath(scope);
-  absl::optional<base::FilePath> unit_dir = GetUnitDirectory(scope);
+  std::optional<base::FilePath> unit_dir = GetUnitDirectory(scope);
   if (!launcher_path || !unit_dir) {
     return false;
   }
@@ -215,7 +217,7 @@
 }
 
 bool UninstallSystemdUnits(UpdaterScope scope) {
-  absl::optional<base::FilePath> unit_dir = GetUnitDirectory(scope);
+  std::optional<base::FilePath> unit_dir = GetUnitDirectory(scope);
   if (!unit_dir) {
     return false;
   }
@@ -231,7 +233,7 @@
 }
 
 bool SystemdUnitsInstalled(UpdaterScope scope) {
-  absl::optional<base::FilePath> unit_dir = GetUnitDirectory(scope);
+  std::optional<base::FilePath> unit_dir = GetUnitDirectory(scope);
   if (!unit_dir) {
     return false;
   }
diff --git a/chrome/updater/mac/install_from_archive.h b/chrome/updater/mac/install_from_archive.h
index 475f55d..60a0fe8f 100644
--- a/chrome/updater/mac/install_from_archive.h
+++ b/chrome/updater/mac/install_from_archive.h
@@ -5,10 +5,9 @@
 #ifndef CHROME_UPDATER_MAC_INSTALL_FROM_ARCHIVE_H_
 #define CHROME_UPDATER_MAC_INSTALL_FROM_ARCHIVE_H_
 
+#include <optional>
 #include <string>
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
 namespace base {
 class FilePath;
 class TimeDelta;
@@ -55,16 +54,15 @@
 // Choose which type of archive to install from. Possible types of archives are
 // DMG, Zip and just the App. From there, it calls the archive specific
 // installation method.
-int InstallFromArchive(
-    const base::FilePath& file_path,
-    const base::FilePath& existence_checker_path,
-    const std::string& ap,
-    const UpdaterScope& scope,
-    const base::Version& pv,
-    const std::string& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
-    bool usage_stats_enabled,
-    const base::TimeDelta& timeout);
+int InstallFromArchive(const base::FilePath& file_path,
+                       const base::FilePath& existence_checker_path,
+                       const std::string& ap,
+                       const UpdaterScope& scope,
+                       const base::Version& pv,
+                       const std::string& arguments,
+                       const std::optional<base::FilePath>& installer_data_file,
+                       bool usage_stats_enabled,
+                       const base::TimeDelta& timeout);
 
 }  // namespace updater
 
diff --git a/chrome/updater/mac/install_from_archive.mm b/chrome/updater/mac/install_from_archive.mm
index f1b2133..9bb1ad0 100644
--- a/chrome/updater/mac/install_from_archive.mm
+++ b/chrome/updater/mac/install_from_archive.mm
@@ -7,6 +7,7 @@
 #import <Cocoa/Cocoa.h>
 
 #include <map>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -37,7 +38,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/mac_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -136,7 +136,7 @@
 int RunExecutable(const base::FilePath& existence_checker_path,
                   const std::string& ap,
                   const std::string& arguments,
-                  const absl::optional<base::FilePath>& installer_data_file,
+                  const std::optional<base::FilePath>& installer_data_file,
                   const UpdaterScope& scope,
                   const base::Version& pv,
                   bool usage_stats_enabled,
@@ -171,7 +171,7 @@
     command.AppendArg(pv.GetString());
 
     std::string env_path = "/bin:/usr/bin";
-    absl::optional<base::FilePath> ksadmin_path =
+    std::optional<base::FilePath> ksadmin_path =
         GetKSAdminPath(GetUpdaterScope());
     if (ksadmin_path) {
       env_path = base::StrCat({env_path, ":", ksadmin_path->DirName().value()});
@@ -379,16 +379,15 @@
 }
 }  // namespace
 
-int InstallFromArchive(
-    const base::FilePath& file_path,
-    const base::FilePath& existence_checker_path,
-    const std::string& ap,
-    const UpdaterScope& scope,
-    const base::Version& pv,
-    const std::string& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
-    const bool usage_stats_enabled,
-    const base::TimeDelta& timeout) {
+int InstallFromArchive(const base::FilePath& file_path,
+                       const base::FilePath& existence_checker_path,
+                       const std::string& ap,
+                       const UpdaterScope& scope,
+                       const base::Version& pv,
+                       const std::string& arguments,
+                       const std::optional<base::FilePath>& installer_data_file,
+                       const bool usage_stats_enabled,
+                       const base::TimeDelta& timeout) {
   const std::map<std::string,
                  int (*)(const base::FilePath&,
                          base::OnceCallback<int(const base::FilePath&)>)>
diff --git a/chrome/updater/mac/keystone/agent_main.cc b/chrome/updater/mac/keystone/agent_main.cc
index 7bdb7a61..5362f113 100644
--- a/chrome/updater/mac/keystone/agent_main.cc
+++ b/chrome/updater/mac/keystone/agent_main.cc
@@ -6,6 +6,7 @@
 
 #include <iostream>
 #include <map>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -31,7 +32,6 @@
 #include "chrome/updater/update_service.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -188,7 +188,7 @@
 
 void KSAgentApp::Wake() {
   for (UpdaterScope scope : {UpdaterScope::kSystem, UpdaterScope::kUser}) {
-    absl::optional<base::FilePath> path = GetUpdaterExecutablePath(scope);
+    std::optional<base::FilePath> path = GetUpdaterExecutablePath(scope);
     if (!path) {
       continue;
     }
diff --git a/chrome/updater/mac/keystone/ksadmin.mm b/chrome/updater/mac/keystone/ksadmin.mm
index bcd8a08a..a938419 100644
--- a/chrome/updater/mac/keystone/ksadmin.mm
+++ b/chrome/updater/mac/keystone/ksadmin.mm
@@ -7,6 +7,7 @@
 #include <stdio.h>
 
 #include <map>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -43,7 +44,6 @@
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/mac_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -185,7 +185,7 @@
 }
 
 void MaybeInstallUpdater(UpdaterScope scope) {
-  const absl::optional<base::FilePath> path = GetUpdaterExecutablePath(scope);
+  const std::optional<base::FilePath> path = GetUpdaterExecutablePath(scope);
 
   if (path &&
       [NSFileManager.defaultManager
@@ -198,7 +198,7 @@
     return;
   }
 
-  const absl::optional<base::FilePath> setup_path = GetUpdaterExecutablePath(
+  const std::optional<base::FilePath> setup_path = GetUpdaterExecutablePath(
       IsSystemShim() ? UpdaterScope::kSystem : UpdaterScope::kUser);
   if (!setup_path || ![NSFileManager.defaultManager
                          fileExistsAtPath:base::apple::FilePathToNSString(
@@ -317,23 +317,23 @@
   //   3. Choose system updater if user is root.
   //   4. Prefer system updater if app ID is given and is a system app.
   //   5. Otherwise choose user updater.
-  absl::optional<UpdaterScope> scope = absl::nullopt;
+  std::optional<UpdaterScope> scope = std::nullopt;
   if (HasSwitch(kCommandSystemStore)) {
-    scope = absl::make_optional(UpdaterScope::kSystem);
+    scope = std::make_optional(UpdaterScope::kSystem);
   } else if (HasSwitch(kCommandUserStore) || !IsSystemShim()) {
-    scope = absl::make_optional(UpdaterScope::kUser);
+    scope = std::make_optional(UpdaterScope::kUser);
   } else if (HasSwitch(kCommandStorePath)) {
-    scope = absl::make_optional(
+    scope = std::make_optional(
         SwitchValue(kCommandStorePath) ==
                 KeystoneTicketStorePath(UpdaterScope::kSystem)
             ? UpdaterScope::kSystem
             : UpdaterScope::kUser);
   } else if (geteuid() == 0) {
-    scope = absl::make_optional(UpdaterScope::kSystem);
+    scope = std::make_optional(UpdaterScope::kSystem);
   } else {
     const std::string app_id = SwitchValue(kCommandProductId);
     if (app_id.empty())
-      scope = absl::make_optional(UpdaterScope::kSystem);
+      scope = std::make_optional(UpdaterScope::kSystem);
   }
 
   if (scope) {
diff --git a/chrome/updater/mac/keystone/ksinstall.mm b/chrome/updater/mac/keystone/ksinstall.mm
index af25b705..b452205 100644
--- a/chrome/updater/mac/keystone/ksinstall.mm
+++ b/chrome/updater/mac/keystone/ksinstall.mm
@@ -4,12 +4,11 @@
 
 #include "chrome/updater/mac/keystone/ksinstall.h"
 
-#include "base/memory/raw_ptr.h"
-
 #import <Foundation/Foundation.h>
 #import <getopt.h>
-
 #import <stdio.h>
+
+#include <optional>
 #include <string>
 
 #include "base/at_exit.h"
@@ -20,6 +19,7 @@
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/message_loop/message_pump_type.h"
 #include "base/task/single_thread_task_executor.h"
@@ -54,7 +54,7 @@
 void KSInstallApp::Uninstall(base::OnceCallback<void(int)> callback) {
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE, {base::MayBlock()}, base::BindOnce([] {
-        const absl::optional<base::FilePath>& keystone_path =
+        const std::optional<base::FilePath>& keystone_path =
             GetKeystoneFolderPath((geteuid() == 0) ? UpdaterScope::kSystem
                                                    : UpdaterScope::kUser);
         if (!keystone_path ||
diff --git a/chrome/updater/mac/privileged_helper/service.mm b/chrome/updater/mac/privileged_helper/service.mm
index 7aaac899..49eb0f4 100644
--- a/chrome/updater/mac/privileged_helper/service.mm
+++ b/chrome/updater/mac/privileged_helper/service.mm
@@ -148,9 +148,9 @@
 
 int InstallUpdater(const base::FilePath& browser_path) {
   base::FilePath browser_plist = browser_path.Append("Contents/Info.plist");
-  absl::optional<std::string> browser_app_id =
+  std::optional<std::string> browser_app_id =
       ReadValueFromPlist(browser_plist, "KSProductID");
-  absl::optional<std::string> browser_version =
+  std::optional<std::string> browser_version =
       ReadValueFromPlist(browser_plist, "KSVersion");
   if (!browser_app_id || !browser_version) {
     return kFailedToReadBrowserPlist;
diff --git a/chrome/updater/mac/setup/keystone.mm b/chrome/updater/mac/setup/keystone.mm
index afd78e9..b0ea1d8 100644
--- a/chrome/updater/mac/setup/keystone.mm
+++ b/chrome/updater/mac/setup/keystone.mm
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/mac/setup/keystone.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -29,7 +30,6 @@
 #include "chrome/updater/util/mac_util.h"
 #include "chrome/updater/util/posix_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 // Class to read the Keystone apps' client-regulated-counting data.
 @interface CountingMetricsStore : NSObject {
@@ -38,8 +38,8 @@
 
 + (instancetype)storeAtPath:(const base::FilePath&)path;
 
-- (absl::optional<int>)dateLastActiveForApp:(NSString*)appid;
-- (absl::optional<int>)dateLastRollcallForApp:(NSString*)appid;
+- (std::optional<int>)dateLastActiveForApp:(NSString*)appid;
+- (std::optional<int>)dateLastRollcallForApp:(NSString*)appid;
 
 @end
 
@@ -64,21 +64,21 @@
   return self;
 }
 
-- (absl::optional<int>)daynumValueOfKey:(NSString*)key forApp:(NSString*)appid {
+- (std::optional<int>)daynumValueOfKey:(NSString*)key forApp:(NSString*)appid {
   id appObject = [_metrics objectForKey:appid.lowercaseString];
   if (![appObject isKindOfClass:[NSDictionary class]]) {
     LOG(WARNING) << "Malformed input client-regulated-counting data.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   id daynumObject = appObject[key];
   if (!daynumObject) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   if (![daynumObject isKindOfClass:[NSNumber class]]) {
     LOG(WARNING) << "daynum is not a number.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   // daynum the number of days since January 1, 2007. The accepted range is
@@ -86,17 +86,17 @@
   int daynum = [daynumObject intValue];
   if (daynum < 3000 || daynum > 50000) {
     LOG(WARNING) << "Ignored out-of-range daynum: " << daynum;
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return daynum;
 }
 
-- (absl::optional<int>)dateLastActiveForApp:(NSString*)appid {
+- (std::optional<int>)dateLastActiveForApp:(NSString*)appid {
   return [self daynumValueOfKey:@"DayOfLastActive" forApp:appid];
 }
 
-- (absl::optional<int>)dateLastRollcallForApp:(NSString*)appid {
+- (std::optional<int>)dateLastRollcallForApp:(NSString*)appid {
   return [self daynumValueOfKey:@"DayOfLastRollcall" forApp:appid];
 }
 
@@ -121,7 +121,7 @@
     return false;
   }
 
-  const absl::optional<base::FilePath> dest_folder_path =
+  const std::optional<base::FilePath> dest_folder_path =
       GetKeystoneFolderPath(scope);
   if (!dest_folder_path)
     return false;
@@ -246,7 +246,7 @@
 }
 
 void UninstallKeystone(UpdaterScope scope) {
-  const absl::optional<base::FilePath> keystone_folder_path =
+  const std::optional<base::FilePath> keystone_folder_path =
       GetKeystoneFolderPath(scope);
   if (!keystone_folder_path) {
     LOG(ERROR) << "Can't find Keystone path.";
diff --git a/chrome/updater/mac/setup/setup.mm b/chrome/updater/mac/setup/setup.mm
index 64e6edf..503738e 100644
--- a/chrome/updater/mac/setup/setup.mm
+++ b/chrome/updater/mac/setup/setup.mm
@@ -9,6 +9,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <optional>
+
 #include "base/apple/bundle_locations.h"
 #include "base/apple/foundation_util.h"
 #include "base/at_exit.h"
@@ -42,14 +44,13 @@
 #include "chrome/updater/util/posix_util.h"
 #include "chrome/updater/util/util.h"
 #include "components/crash/core/common/crash_key.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
 
 bool CopyBundle(UpdaterScope scope) {
-  absl::optional<base::FilePath> base_install_dir = GetInstallDirectory(scope);
-  absl::optional<base::FilePath> versioned_install_dir =
+  std::optional<base::FilePath> base_install_dir = GetInstallDirectory(scope);
+  std::optional<base::FilePath> versioned_install_dir =
       GetVersionedInstallDirectory(scope);
   if (!base_install_dir || !versioned_install_dir) {
     LOG(ERROR) << "Failed to get install directory.";
@@ -118,7 +119,7 @@
 // plist, with the specified contents. If not, the plist will be overwritten and
 // the item reloaded. May block.
 bool EnsureWakeLaunchItemPresence(UpdaterScope scope, NSDictionary* contents) {
-  const absl::optional<base::FilePath> path = GetWakeTaskPlistPath(scope);
+  const std::optional<base::FilePath> path = GetWakeTaskPlistPath(scope);
   if (!path) {
     VLOG(1) << "Failed to find wake plist path.";
     return false;
@@ -168,7 +169,7 @@
     }
 
     // Update app registration with LaunchServices.
-    const absl::optional<base::FilePath> install_path =
+    const std::optional<base::FilePath> install_path =
         GetInstallDirectory(scope);
     if (install_path) {
       OSStatus ls_result = LSRegisterURL(
@@ -227,7 +228,7 @@
   // Quarantine attribute needs to be removed here as the copied bundle might be
   // given com.apple.quarantine attribute, and the server is attempted to be
   // launched below, Gatekeeper could prompt the user.
-  const absl::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
+  const std::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
   if (!install_dir) {
     return kErrorFailedToGetInstallDir;
   }
@@ -249,7 +250,7 @@
   }
 
   if (scope == UpdaterScope::kSystem) {
-    const absl::optional<base::FilePath> bundle_path =
+    const std::optional<base::FilePath> bundle_path =
         GetUpdaterAppBundlePath(scope);
     if (bundle_path) {
       base::FilePath path =
@@ -279,10 +280,10 @@
 }
 
 int PromoteCandidate(UpdaterScope scope) {
-  const absl::optional<base::FilePath> updater_executable_path =
+  const std::optional<base::FilePath> updater_executable_path =
       GetUpdaterExecutablePath(scope);
-  const absl::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
-  const absl::optional<base::FilePath> bundle_path =
+  const std::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
+  const std::optional<base::FilePath> bundle_path =
       GetUpdaterAppBundlePath(scope);
   if (!updater_executable_path || !install_dir || !bundle_path) {
     return kErrorFailedToGetVersionedInstallDirectory;
diff --git a/chrome/updater/mac/setup/wake_task.h b/chrome/updater/mac/setup/wake_task.h
index d1cfe723..b632d89 100644
--- a/chrome/updater/mac/setup/wake_task.h
+++ b/chrome/updater/mac/setup/wake_task.h
@@ -8,7 +8,6 @@
 #include <Foundation/Foundation.h>
 
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
diff --git a/chrome/updater/mac/setup/wake_task.mm b/chrome/updater/mac/setup/wake_task.mm
index 051caee..3cbdcf3 100644
--- a/chrome/updater/mac/setup/wake_task.mm
+++ b/chrome/updater/mac/setup/wake_task.mm
@@ -6,6 +6,8 @@
 
 #include <Foundation/Foundation.h>
 
+#include <optional>
+
 #include "base/apple/foundation_util.h"
 #include "base/files/file_path.h"
 #include "base/strings/strcat.h"
@@ -15,7 +17,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/mac_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -39,10 +40,10 @@
   return base::SysUTF8ToNSString(base::StrCat({"--", argument, "=", value}));
 }
 
-absl::optional<base::FilePath> GetWakeTaskTarget(UpdaterScope scope) {
-  absl::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
+std::optional<base::FilePath> GetWakeTaskTarget(UpdaterScope scope) {
+  std::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
   if (!install_dir) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return install_dir->Append("Current").Append(GetExecutableRelativePath());
 }
@@ -50,7 +51,7 @@
 }  // namespace
 
 NSDictionary* CreateWakeLaunchdPlist(UpdaterScope scope) {
-  absl::optional<base::FilePath> target = GetWakeTaskTarget(scope);
+  std::optional<base::FilePath> target = GetWakeTaskTarget(scope);
   if (!target) {
     return nil;
   }
diff --git a/chrome/updater/net/network.h b/chrome/updater/net/network.h
index 6564ad6d..82b43ab 100644
--- a/chrome/updater/net/network.h
+++ b/chrome/updater/net/network.h
@@ -6,12 +6,12 @@
 #define CHROME_UPDATER_NET_NETWORK_H_
 
 #include <memory>
+#include <optional>
 
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "build/build_config.h"
 #include "components/update_client/network.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -22,7 +22,7 @@
 // outlive the lives of the network fetchers it creates.
 class NetworkFetcherFactory : public update_client::NetworkFetcherFactory {
  public:
-  explicit NetworkFetcherFactory(absl::optional<PolicyServiceProxyConfiguration>
+  explicit NetworkFetcherFactory(std::optional<PolicyServiceProxyConfiguration>
                                      policy_service_proxy_configuration);
   NetworkFetcherFactory(const NetworkFetcherFactory&) = delete;
   NetworkFetcherFactory& operator=(const NetworkFetcherFactory&) = delete;
diff --git a/chrome/updater/net/network_fetcher_linux.cc b/chrome/updater/net/network_fetcher_linux.cc
index e8b17a8..8cf53ba 100644
--- a/chrome/updater/net/network_fetcher_linux.cc
+++ b/chrome/updater/net/network_fetcher_linux.cc
@@ -10,6 +10,7 @@
 
 #include <array>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -35,7 +36,6 @@
 #include "chrome/updater/policy/service.h"
 #include "chrome/updater/util/util.h"
 #include "components/update_client/network.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -461,7 +461,7 @@
 class NetworkFetcherFactory::Impl {};
 
 NetworkFetcherFactory::NetworkFetcherFactory(
-    absl::optional<PolicyServiceProxyConfiguration>) {}
+    std::optional<PolicyServiceProxyConfiguration>) {}
 NetworkFetcherFactory::~NetworkFetcherFactory() = default;
 
 std::unique_ptr<update_client::NetworkFetcher> NetworkFetcherFactory::Create()
diff --git a/chrome/updater/net/network_fetcher_mac.mm b/chrome/updater/net/network_fetcher_mac.mm
index f6db8dd0..afbef84 100644
--- a/chrome/updater/net/network_fetcher_mac.mm
+++ b/chrome/updater/net/network_fetcher_mac.mm
@@ -8,6 +8,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -29,7 +30,6 @@
 #include "chrome/updater/util/util.h"
 #include "components/update_client/network.h"
 #import "net/base/mac/url_conversions.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 using ResponseStartedCallback =
@@ -399,7 +399,7 @@
 class NetworkFetcherFactory::Impl {};
 
 NetworkFetcherFactory::NetworkFetcherFactory(
-    absl::optional<PolicyServiceProxyConfiguration>) {}
+    std::optional<PolicyServiceProxyConfiguration>) {}
 NetworkFetcherFactory::~NetworkFetcherFactory() = default;
 
 std::unique_ptr<update_client::NetworkFetcher> NetworkFetcherFactory::Create()
diff --git a/chrome/updater/net/network_fetcher_win.cc b/chrome/updater/net/network_fetcher_win.cc
index bc2127fe..e303a15 100644
--- a/chrome/updater/net/network_fetcher_win.cc
+++ b/chrome/updater/net/network_fetcher_win.cc
@@ -8,6 +8,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -28,7 +29,6 @@
 #include "components/winhttp/network_fetcher.h"
 #include "components/winhttp/proxy_configuration.h"
 #include "components/winhttp/scoped_hinternet.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -36,7 +36,7 @@
 
 // Factory method for the proxy configuration strategy.
 scoped_refptr<winhttp::ProxyConfiguration> GetProxyConfiguration(
-    absl::optional<PolicyServiceProxyConfiguration>
+    std::optional<PolicyServiceProxyConfiguration>
         policy_service_proxy_configuration) {
   if (policy_service_proxy_configuration) {
     return base::MakeRefCounted<winhttp::ProxyConfiguration>(winhttp::ProxyInfo{
@@ -182,7 +182,7 @@
 
 class NetworkFetcherFactory::Impl {
  public:
-  explicit Impl(absl::optional<PolicyServiceProxyConfiguration>
+  explicit Impl(std::optional<PolicyServiceProxyConfiguration>
                     policy_service_proxy_configuration)
       : proxy_configuration_(
             GetProxyConfiguration(policy_service_proxy_configuration)),
@@ -203,7 +203,7 @@
 };
 
 NetworkFetcherFactory::NetworkFetcherFactory(
-    absl::optional<PolicyServiceProxyConfiguration>
+    std::optional<PolicyServiceProxyConfiguration>
         policy_service_proxy_configuration)
     : impl_(std::make_unique<Impl>(policy_service_proxy_configuration)) {}
 NetworkFetcherFactory::~NetworkFetcherFactory() = default;
diff --git a/chrome/updater/persisted_data.cc b/chrome/updater/persisted_data.cc
index 8be6e0be..16504af0 100644
--- a/chrome/updater/persisted_data.cc
+++ b/chrome/updater/persisted_data.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/persisted_data.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -25,7 +26,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/update_client/persisted_data.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
@@ -211,7 +211,7 @@
   SetString(id, kAPKey, key);
 }
 
-absl::optional<int> PersistedData::GetDateLastActive(
+std::optional<int> PersistedData::GetDateLastActive(
     const std::string& id) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return GetInteger(id, kDLA);
@@ -222,7 +222,7 @@
   SetInteger(id, kDLA, dla);
 }
 
-absl::optional<int> PersistedData::GetDateLastRollcall(
+std::optional<int> PersistedData::GetDateLastRollcall(
     const std::string& id) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return GetInteger(id, kDLRC);
@@ -394,21 +394,21 @@
   return app;
 }
 
-absl::optional<int> PersistedData::GetInteger(const std::string& id,
-                                              const std::string& key) const {
+std::optional<int> PersistedData::GetInteger(const std::string& id,
+                                             const std::string& key) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!pref_service_) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   ScopedDictPrefUpdate update(pref_service_,
                               update_client::kPersistedDataPreference);
   base::Value::Dict* apps = update->FindDict("apps");
   if (!apps) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::Value::Dict* app = apps->FindDict(base::ToLowerASCII(id));
   if (!app) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return app->FindInt(key);
 }
@@ -482,7 +482,7 @@
 }
 
 #if BUILDFLAG(IS_WIN)
-absl::optional<OSVERSIONINFOEX> PersistedData::GetLastOSVersion() const {
+std::optional<OSVERSIONINFOEX> PersistedData::GetLastOSVersion() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   // Unpacks the os version from a base-64-encoded string internally.
@@ -490,13 +490,13 @@
       pref_service_->GetString(kLastOSVersion);
 
   if (encoded_os_version.empty())
-    return absl::nullopt;
+    return std::nullopt;
 
-  const absl::optional<std::vector<uint8_t>> decoded_os_version =
+  const std::optional<std::vector<uint8_t>> decoded_os_version =
       base::Base64Decode(encoded_os_version);
   if (!decoded_os_version ||
       decoded_os_version->size() != sizeof(OSVERSIONINFOEX)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return *reinterpret_cast<const OSVERSIONINFOEX*>(decoded_os_version->data());
@@ -509,7 +509,7 @@
     return;
 
   // Get and set the current OS version.
-  absl::optional<OSVERSIONINFOEX> os_version = GetOSVersion();
+  std::optional<OSVERSIONINFOEX> os_version = GetOSVersion();
   if (!os_version)
     return;
 
diff --git a/chrome/updater/persisted_data.h b/chrome/updater/persisted_data.h
index 29e63e10..b21ce1ed 100644
--- a/chrome/updater/persisted_data.h
+++ b/chrome/updater/persisted_data.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_PERSISTED_DATA_H_
 #define CHROME_UPDATER_PERSISTED_DATA_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -13,7 +14,6 @@
 #include "base/sequence_checker.h"
 #include "base/values.h"
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
@@ -90,9 +90,9 @@
   // The getters return nullopt when the persisted data does not have the
   // corresponding value, or any node subtype is not expected along the
   // path to the target value.
-  absl::optional<int> GetDateLastActive(const std::string& id) const;
+  std::optional<int> GetDateLastActive(const std::string& id) const;
   void SetDateLastActive(const std::string& id, int dla);
-  absl::optional<int> GetDateLastRollcall(const std::string& id) const;
+  std::optional<int> GetDateLastRollcall(const std::string& id) const;
   void SetDateLastRollcall(const std::string& id, int dlrc);
 
   // These functions access the cohort values for the specified id.
@@ -141,7 +141,7 @@
 
 #if BUILDFLAG(IS_WIN)
   // Retrieves the previously stored OS version.
-  absl::optional<OSVERSIONINFOEX> GetLastOSVersion() const;
+  std::optional<OSVERSIONINFOEX> GetLastOSVersion() const;
 
   // Stores the current os version.
   void SetLastOSVersion();
@@ -158,8 +158,8 @@
   base::Value::Dict* GetOrCreateAppKey(const std::string& id,
                                        base::Value::Dict& root);
 
-  absl::optional<int> GetInteger(const std::string& id,
-                                 const std::string& key) const;
+  std::optional<int> GetInteger(const std::string& id,
+                                const std::string& key) const;
   void SetInteger(const std::string& id, const std::string& key, int value);
   std::string GetString(const std::string& id, const std::string& key) const;
   void SetString(const std::string& id,
diff --git a/chrome/updater/persisted_data_unittest.cc b/chrome/updater/persisted_data_unittest.cc
index e277262c..48e8e49 100644
--- a/chrome/updater/persisted_data_unittest.cc
+++ b/chrome/updater/persisted_data_unittest.cc
@@ -229,11 +229,11 @@
   auto metadata =
       base::MakeRefCounted<PersistedData>(GetTestScope(), pref.get());
 
-  EXPECT_EQ(metadata->GetLastOSVersion(), absl::nullopt);
+  EXPECT_EQ(metadata->GetLastOSVersion(), std::nullopt);
 
   // This will persist the current OS version into the persisted data.
   metadata->SetLastOSVersion();
-  EXPECT_NE(metadata->GetLastOSVersion(), absl::nullopt);
+  EXPECT_NE(metadata->GetLastOSVersion(), std::nullopt);
 
   // Compare the persisted data OS version to the version from `::GetVersionEx`.
   const OSVERSIONINFOEX metadata_os = metadata->GetLastOSVersion().value();
diff --git a/chrome/updater/policy/dm_policy_manager.cc b/chrome/updater/policy/dm_policy_manager.cc
index 55fd891..01e5fc41 100644
--- a/chrome/updater/policy/dm_policy_manager.cc
+++ b/chrome/updater/policy/dm_policy_manager.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/policy/dm_policy_manager.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -17,7 +18,6 @@
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/policy/manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -80,7 +80,7 @@
 DMPolicyManager::DMPolicyManager(
     const ::wireless_android_enterprise_devicemanagement::
         OmahaSettingsClientProto& omaha_settings,
-    const absl::optional<bool>& override_is_managed_device)
+    const std::optional<bool>& override_is_managed_device)
     : is_managed_device_(override_is_managed_device.value_or(true)),
       omaha_settings_(omaha_settings) {}
 
@@ -94,23 +94,23 @@
   return kSourceDMPolicyManager;
 }
 
-absl::optional<base::TimeDelta> DMPolicyManager::GetLastCheckPeriod() const {
+std::optional<base::TimeDelta> DMPolicyManager::GetLastCheckPeriod() const {
   if (!omaha_settings_.has_auto_update_check_period_minutes())
-    return absl::nullopt;
+    return std::nullopt;
 
   return base::Minutes(omaha_settings_.auto_update_check_period_minutes());
 }
 
-absl::optional<UpdatesSuppressedTimes>
+std::optional<UpdatesSuppressedTimes>
 DMPolicyManager::GetUpdatesSuppressedTimes() const {
   if (!omaha_settings_.has_updates_suppressed())
-    return absl::nullopt;
+    return std::nullopt;
 
   const auto& updates_suppressed = omaha_settings_.updates_suppressed();
   if (!updates_suppressed.has_start_hour() ||
       !updates_suppressed.has_start_minute() ||
       !updates_suppressed.has_duration_min())
-    return absl::nullopt;
+    return std::nullopt;
 
   UpdatesSuppressedTimes suppressed_times;
   suppressed_times.start_hour_ = updates_suppressed.start_hour();
@@ -119,38 +119,38 @@
   return suppressed_times;
 }
 
-absl::optional<std::string> DMPolicyManager::GetDownloadPreference() const {
+std::optional<std::string> DMPolicyManager::GetDownloadPreference() const {
   if (!omaha_settings_.has_download_preference())
-    return absl::nullopt;
+    return std::nullopt;
 
   return omaha_settings_.download_preference();
 }
 
-absl::optional<int> DMPolicyManager::GetPackageCacheSizeLimitMBytes() const {
-  return absl::nullopt;
+std::optional<int> DMPolicyManager::GetPackageCacheSizeLimitMBytes() const {
+  return std::nullopt;
 }
 
-absl::optional<int> DMPolicyManager::GetPackageCacheExpirationTimeDays() const {
-  return absl::nullopt;
+std::optional<int> DMPolicyManager::GetPackageCacheExpirationTimeDays() const {
+  return std::nullopt;
 }
 
-absl::optional<std::string> DMPolicyManager::GetProxyMode() const {
+std::optional<std::string> DMPolicyManager::GetProxyMode() const {
   if (!omaha_settings_.has_proxy_mode())
-    return absl::nullopt;
+    return std::nullopt;
 
   return omaha_settings_.proxy_mode();
 }
 
-absl::optional<std::string> DMPolicyManager::GetProxyPacUrl() const {
+std::optional<std::string> DMPolicyManager::GetProxyPacUrl() const {
   if (!omaha_settings_.has_proxy_pac_url())
-    return absl::nullopt;
+    return std::nullopt;
 
   return omaha_settings_.proxy_pac_url();
 }
 
-absl::optional<std::string> DMPolicyManager::GetProxyServer() const {
+std::optional<std::string> DMPolicyManager::GetProxyServer() const {
   if (!omaha_settings_.has_proxy_server())
-    return absl::nullopt;
+    return std::nullopt;
 
   return omaha_settings_.proxy_server();
 }
@@ -177,7 +177,7 @@
   return nullptr;
 }
 
-absl::optional<int> DMPolicyManager::GetEffectivePolicyForAppInstalls(
+std::optional<int> DMPolicyManager::GetEffectivePolicyForAppInstalls(
     const std::string& app_id) const {
   const auto* app_settings = GetAppSettings(app_id);
   if (app_settings && app_settings->has_install()) {
@@ -190,10 +190,10 @@
         omaha_settings_.install_default());
   }
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<int> DMPolicyManager::GetEffectivePolicyForAppUpdates(
+std::optional<int> DMPolicyManager::GetEffectivePolicyForAppUpdates(
     const std::string& app_id) const {
   const auto* app_settings = GetAppSettings(app_id);
   if (app_settings && app_settings->has_update()) {
@@ -205,39 +205,39 @@
     return PolicyValueFromProtoUpdateValue(omaha_settings_.update_default());
   }
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::string> DMPolicyManager::GetTargetVersionPrefix(
+std::optional<std::string> DMPolicyManager::GetTargetVersionPrefix(
     const std::string& app_id) const {
   const auto* app_settings = GetAppSettings(app_id);
   if (!app_settings || !app_settings->has_target_version_prefix())
-    return absl::nullopt;
+    return std::nullopt;
 
   return app_settings->target_version_prefix();
 }
 
-absl::optional<std::string> DMPolicyManager::GetTargetChannel(
+std::optional<std::string> DMPolicyManager::GetTargetChannel(
     const std::string& app_id) const {
   const auto* app_settings = GetAppSettings(app_id);
   if (!app_settings || !app_settings->has_target_channel())
-    return absl::nullopt;
+    return std::nullopt;
 
   return app_settings->target_channel();
 }
 
-absl::optional<bool> DMPolicyManager::IsRollbackToTargetVersionAllowed(
+std::optional<bool> DMPolicyManager::IsRollbackToTargetVersionAllowed(
     const std::string& app_id) const {
   const auto* app_settings = GetAppSettings(app_id);
   if (!app_settings || !app_settings->has_rollback_to_target_version())
-    return absl::nullopt;
+    return std::nullopt;
 
   return (app_settings->rollback_to_target_version() ==
           ::wireless_android_enterprise_devicemanagement::
               ROLLBACK_TO_TARGET_VERSION_ENABLED);
 }
 
-absl::optional<std::vector<std::string>> DMPolicyManager::GetForceInstallApps()
+std::optional<std::vector<std::string>> DMPolicyManager::GetForceInstallApps()
     const {
   std::vector<std::string> force_install_apps;
   for (const auto& app_settings_proto :
@@ -259,11 +259,11 @@
     }
   }
   return force_install_apps.empty()
-             ? absl::nullopt
-             : absl::optional<std::vector<std::string>>(force_install_apps);
+             ? std::nullopt
+             : std::optional<std::vector<std::string>>(force_install_apps);
 }
 
-absl::optional<std::vector<std::string>> DMPolicyManager::GetAppsWithPolicy()
+std::optional<std::vector<std::string>> DMPolicyManager::GetAppsWithPolicy()
     const {
   std::vector<std::string> apps_with_policy;
 
@@ -286,7 +286,7 @@
 }
 
 scoped_refptr<PolicyManagerInterface> CreateDMPolicyManager(
-    const absl::optional<bool>& override_is_managed_device) {
+    const std::optional<bool>& override_is_managed_device) {
   scoped_refptr<DMStorage> default_dm_storage = GetDefaultDMStorage();
   if (!default_dm_storage) {
     return nullptr;
diff --git a/chrome/updater/policy/dm_policy_manager.h b/chrome/updater/policy/dm_policy_manager.h
index 5c95ff0..b5dc8d3cb 100644
--- a/chrome/updater/policy/dm_policy_manager.h
+++ b/chrome/updater/policy/dm_policy_manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_POLICY_DM_POLICY_MANAGER_H_
 #define CHROME_UPDATER_POLICY_DM_POLICY_MANAGER_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -12,7 +13,6 @@
 #include "chrome/updater/device_management/dm_storage.h"
 #include "chrome/updater/policy/manager.h"
 #include "chrome/updater/protos/omaha_settings.pb.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -22,7 +22,7 @@
   DMPolicyManager(
       const ::wireless_android_enterprise_devicemanagement::
           OmahaSettingsClientProto& omaha_settings,
-      const absl::optional<bool>& override_is_managed_device = absl::nullopt);
+      const std::optional<bool>& override_is_managed_device = std::nullopt);
   DMPolicyManager(const DMPolicyManager&) = delete;
   DMPolicyManager& operator=(const DMPolicyManager&) = delete;
 
@@ -31,27 +31,27 @@
 
   bool HasActiveDevicePolicies() const override;
 
-  absl::optional<base::TimeDelta> GetLastCheckPeriod() const override;
-  absl::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
+  std::optional<base::TimeDelta> GetLastCheckPeriod() const override;
+  std::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
       const override;
-  absl::optional<std::string> GetDownloadPreference() const override;
-  absl::optional<int> GetPackageCacheSizeLimitMBytes() const override;
-  absl::optional<int> GetPackageCacheExpirationTimeDays() const override;
-  absl::optional<int> GetEffectivePolicyForAppInstalls(
+  std::optional<std::string> GetDownloadPreference() const override;
+  std::optional<int> GetPackageCacheSizeLimitMBytes() const override;
+  std::optional<int> GetPackageCacheExpirationTimeDays() const override;
+  std::optional<int> GetEffectivePolicyForAppInstalls(
       const std::string& app_id) const override;
-  absl::optional<int> GetEffectivePolicyForAppUpdates(
+  std::optional<int> GetEffectivePolicyForAppUpdates(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetTargetVersionPrefix(
+  std::optional<std::string> GetTargetVersionPrefix(
       const std::string& app_id) const override;
-  absl::optional<bool> IsRollbackToTargetVersionAllowed(
+  std::optional<bool> IsRollbackToTargetVersionAllowed(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetProxyMode() const override;
-  absl::optional<std::string> GetProxyPacUrl() const override;
-  absl::optional<std::string> GetProxyServer() const override;
-  absl::optional<std::string> GetTargetChannel(
+  std::optional<std::string> GetProxyMode() const override;
+  std::optional<std::string> GetProxyPacUrl() const override;
+  std::optional<std::string> GetProxyServer() const override;
+  std::optional<std::string> GetTargetChannel(
       const std::string& app_id) const override;
-  absl::optional<std::vector<std::string>> GetForceInstallApps() const override;
-  absl::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
+  std::optional<std::vector<std::string>> GetForceInstallApps() const override;
+  std::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
 
  private:
   ~DMPolicyManager() override;
@@ -65,7 +65,7 @@
 
 // A factory method to create a DM policy manager.
 scoped_refptr<PolicyManagerInterface> CreateDMPolicyManager(
-    const absl::optional<bool>& override_is_managed_device);
+    const std::optional<bool>& override_is_managed_device);
 
 }  // namespace updater
 
diff --git a/chrome/updater/policy/dm_policy_manager_unittest.cc b/chrome/updater/policy/dm_policy_manager_unittest.cc
index 7dd774a..4821816 100644
--- a/chrome/updater/policy/dm_policy_manager_unittest.cc
+++ b/chrome/updater/policy/dm_policy_manager_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/policy/dm_policy_manager.h"
 
+#include <optional>
+
 #include "base/enterprise_util.h"
 #include "base/memory/ref_counted.h"
 #include "build/build_config.h"
@@ -11,7 +13,6 @@
 #include "chrome/updater/util/unit_test_util.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -123,14 +124,14 @@
   EXPECT_TRUE(policy_manager->HasActiveDevicePolicies());
   EXPECT_EQ(policy_manager->source(), "Device Management");
 
-  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetDownloadPreference(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyMode(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyPacUrl(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyServer(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetDownloadPreference(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyMode(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyPacUrl(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyServer(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), std::nullopt);
   EXPECT_FALSE(
       policy_manager->GetEffectivePolicyForAppInstalls(test::kChromeAppId));
   EXPECT_FALSE(
@@ -138,7 +139,7 @@
   EXPECT_FALSE(
       policy_manager->IsRollbackToTargetVersionAllowed(test::kChromeAppId));
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix(test::kChromeAppId),
-            absl::nullopt);
+            std::nullopt);
 }
 
 TEST(DMPolicyManager, PolicyManagerFromProto) {
@@ -204,7 +205,7 @@
   // Verify global policies
   EXPECT_EQ(policy_manager->GetLastCheckPeriod(), base::Minutes(111));
 
-  absl::optional<UpdatesSuppressedTimes> suppressed_times =
+  std::optional<UpdatesSuppressedTimes> suppressed_times =
       policy_manager->GetUpdatesSuppressedTimes();
   ASSERT_TRUE(suppressed_times);
   EXPECT_EQ(suppressed_times->start_hour_, 9);
@@ -218,8 +219,8 @@
   EXPECT_EQ(policy_manager->GetProxyMode(), "test_proxy_mode");
   EXPECT_EQ(policy_manager->GetProxyPacUrl(), "foo.c/proxy.pa");
 
-  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), std::nullopt);
   EXPECT_EQ(policy_manager->GetForceInstallApps(),
             std::vector<std::string>({kApp2}));
   EXPECT_EQ(policy_manager->GetAppsWithPolicy(),
@@ -234,8 +235,7 @@
   EXPECT_TRUE(
       policy_manager->IsRollbackToTargetVersionAllowed(test::kChromeAppId));
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix(test::kChromeAppId), "81.");
-  EXPECT_EQ(policy_manager->GetTargetChannel(test::kChromeAppId),
-            absl::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel(test::kChromeAppId), std::nullopt);
 
   // Verify app1 policies.
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(kApp1),
@@ -243,8 +243,8 @@
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(kApp1),
             kPolicyDisabled);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(kApp1),
-            absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kApp1), absl::nullopt);
+            std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kApp1), std::nullopt);
   EXPECT_EQ(policy_manager->GetTargetChannel(kApp1), "canary");
 
   // Verify app2 policies.
@@ -253,8 +253,8 @@
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(kApp2),
             kPolicyEnabled);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(kApp2),
-            absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kApp2), absl::nullopt);
+            std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kApp2), std::nullopt);
   EXPECT_EQ(policy_manager->GetTargetChannel(kApp2), "dev");
 
   // Verify that if no app-specific polices, fallback to global-level policies
@@ -265,8 +265,8 @@
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(app_guid),
             kPolicyManualUpdatesOnly);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(app_guid),
-            absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(app_guid), absl::nullopt);
+            std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(app_guid), std::nullopt);
 }
 
 #if BUILDFLAG(IS_MAC)
@@ -289,20 +289,20 @@
   EXPECT_TRUE(policy_manager->HasActiveDevicePolicies());
   EXPECT_EQ(policy_manager->source(), "Device Management");
 
-  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetDownloadPreference(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyMode(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyPacUrl(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyServer(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetDownloadPreference(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyMode(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyPacUrl(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyServer(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), std::nullopt);
 
   const std::string chrome_guid = "com.google.Chrome";
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(chrome_guid),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(chrome_guid),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(chrome_guid),
             true);
 
diff --git a/chrome/updater/policy/mac/managed_preference_policy_manager.h b/chrome/updater/policy/mac/managed_preference_policy_manager.h
index 05a09ca..730d2909 100644
--- a/chrome/updater/policy/mac/managed_preference_policy_manager.h
+++ b/chrome/updater/policy/mac/managed_preference_policy_manager.h
@@ -5,15 +5,16 @@
 #ifndef CHROME_UPDATER_POLICY_MAC_MANAGED_PREFERENCE_POLICY_MANAGER_H_
 #define CHROME_UPDATER_POLICY_MAC_MANAGED_PREFERENCE_POLICY_MANAGER_H_
 
+#include <optional>
+
 #include "base/memory/scoped_refptr.h"
 #include "chrome/updater/policy/manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
 // A factory method to create a managed preference policy manager.
 scoped_refptr<PolicyManagerInterface> CreateManagedPreferencePolicyManager(
-    const absl::optional<bool>& override_is_managed_device = absl::nullopt);
+    const std::optional<bool>& override_is_managed_device = std::nullopt);
 
 }  // namespace updater
 
diff --git a/chrome/updater/policy/mac/managed_preference_policy_manager.mm b/chrome/updater/policy/mac/managed_preference_policy_manager.mm
index 7922c48..17d011c8 100644
--- a/chrome/updater/policy/mac/managed_preference_policy_manager.mm
+++ b/chrome/updater/policy/mac/managed_preference_policy_manager.mm
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/mac/managed_preference_policy_manager.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -17,7 +18,6 @@
 #include "chrome/updater/constants.h"
 #include "chrome/updater/policy/mac/managed_preference_policy_manager_impl.h"
 #include "chrome/updater/policy/manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -28,7 +28,7 @@
  public:
   ManagedPreferencePolicyManager(
       CRUUpdatePolicyDictionary* policy,
-      const absl::optional<bool>& override_is_managed_device);
+      const std::optional<bool>& override_is_managed_device);
   ManagedPreferencePolicyManager(const ManagedPreferencePolicyManager&) =
       delete;
   ManagedPreferencePolicyManager& operator=(
@@ -39,27 +39,27 @@
 
   bool HasActiveDevicePolicies() const override;
 
-  absl::optional<base::TimeDelta> GetLastCheckPeriod() const override;
-  absl::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
+  std::optional<base::TimeDelta> GetLastCheckPeriod() const override;
+  std::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
       const override;
-  absl::optional<std::string> GetDownloadPreference() const override;
-  absl::optional<int> GetPackageCacheSizeLimitMBytes() const override;
-  absl::optional<int> GetPackageCacheExpirationTimeDays() const override;
-  absl::optional<int> GetEffectivePolicyForAppInstalls(
+  std::optional<std::string> GetDownloadPreference() const override;
+  std::optional<int> GetPackageCacheSizeLimitMBytes() const override;
+  std::optional<int> GetPackageCacheExpirationTimeDays() const override;
+  std::optional<int> GetEffectivePolicyForAppInstalls(
       const std::string& app_id) const override;
-  absl::optional<int> GetEffectivePolicyForAppUpdates(
+  std::optional<int> GetEffectivePolicyForAppUpdates(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetTargetVersionPrefix(
+  std::optional<std::string> GetTargetVersionPrefix(
       const std::string& app_id) const override;
-  absl::optional<bool> IsRollbackToTargetVersionAllowed(
+  std::optional<bool> IsRollbackToTargetVersionAllowed(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetProxyMode() const override;
-  absl::optional<std::string> GetProxyPacUrl() const override;
-  absl::optional<std::string> GetProxyServer() const override;
-  absl::optional<std::string> GetTargetChannel(
+  std::optional<std::string> GetProxyMode() const override;
+  std::optional<std::string> GetProxyPacUrl() const override;
+  std::optional<std::string> GetProxyServer() const override;
+  std::optional<std::string> GetTargetChannel(
       const std::string& app_id) const override;
-  absl::optional<std::vector<std::string>> GetForceInstallApps() const override;
-  absl::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
+  std::optional<std::vector<std::string>> GetForceInstallApps() const override;
+  std::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
 
  private:
   ~ManagedPreferencePolicyManager() override;
@@ -70,7 +70,7 @@
 
 ManagedPreferencePolicyManager::ManagedPreferencePolicyManager(
     CRUUpdatePolicyDictionary* policyDict,
-    const absl::optional<bool>& override_is_managed_device)
+    const std::optional<bool>& override_is_managed_device)
     : impl_([[CRUManagedPreferencePolicyManager alloc]
           initWithDictionary:policyDict]),
       is_managed_device_(override_is_managed_device.value_or(
@@ -86,46 +86,46 @@
   return base::SysNSStringToUTF8(impl_.source);
 }
 
-absl::optional<base::TimeDelta>
+std::optional<base::TimeDelta>
 ManagedPreferencePolicyManager::GetLastCheckPeriod() const {
   int minutes = [impl_ lastCheckPeriodMinutes];
   return minutes != kPolicyNotSet
-             ? absl::optional<base::TimeDelta>(base::Minutes(minutes))
-             : absl::nullopt;
+             ? std::optional<base::TimeDelta>(base::Minutes(minutes))
+             : std::nullopt;
 }
 
-absl::optional<UpdatesSuppressedTimes>
+std::optional<UpdatesSuppressedTimes>
 ManagedPreferencePolicyManager::GetUpdatesSuppressedTimes() const {
   UpdatesSuppressedTimes suppressed_times = [impl_ updatesSuppressed];
   return suppressed_times.valid()
-             ? absl::optional<UpdatesSuppressedTimes>(suppressed_times)
-             : absl::nullopt;
+             ? std::optional<UpdatesSuppressedTimes>(suppressed_times)
+             : std::nullopt;
 }
 
-absl::optional<std::string>
+std::optional<std::string>
 ManagedPreferencePolicyManager::GetDownloadPreference() const {
   NSString* value = [impl_ downloadPreference];
-  return value ? absl::optional<std::string>(base::SysNSStringToUTF8(value))
-               : absl::nullopt;
+  return value ? std::optional<std::string>(base::SysNSStringToUTF8(value))
+               : std::nullopt;
 }
 
-absl::optional<int>
+std::optional<int>
 ManagedPreferencePolicyManager::GetPackageCacheSizeLimitMBytes() const {
-  return absl::nullopt;  // Not supported on Mac.
+  return std::nullopt;  // Not supported on Mac.
 }
 
-absl::optional<int>
+std::optional<int>
 ManagedPreferencePolicyManager::GetPackageCacheExpirationTimeDays() const {
-  return absl::nullopt;  // Not supported on Mac.
+  return std::nullopt;  // Not supported on Mac.
 }
 
-absl::optional<int>
+std::optional<int>
 ManagedPreferencePolicyManager::GetEffectivePolicyForAppInstalls(
     const std::string& app_id) const {
-  return absl::nullopt;  // Not supported on Mac.
+  return std::nullopt;  // Not supported on Mac.
 }
 
-absl::optional<int>
+std::optional<int>
 ManagedPreferencePolicyManager::GetEffectivePolicyForAppUpdates(
     const std::string& app_id) const {
   // Check app-specific settings first.
@@ -135,66 +135,66 @@
 
   // Then fallback to global-level policy if needed.
   update_policy = [impl_ defaultUpdatePolicy];
-  return update_policy != kPolicyNotSet ? absl::optional<int>(update_policy)
-                                        : absl::nullopt;
+  return update_policy != kPolicyNotSet ? std::optional<int>(update_policy)
+                                        : std::nullopt;
 }
 
-absl::optional<std::string>
+std::optional<std::string>
 ManagedPreferencePolicyManager::GetTargetVersionPrefix(
     const std::string& app_id) const {
   NSString* value = [impl_ targetVersionPrefix:base::SysUTF8ToNSString(app_id)];
-  return value ? absl::optional<std::string>(base::SysNSStringToUTF8(value))
-               : absl::nullopt;
+  return value ? std::optional<std::string>(base::SysNSStringToUTF8(value))
+               : std::nullopt;
 }
 
-absl::optional<bool>
+std::optional<bool>
 ManagedPreferencePolicyManager::IsRollbackToTargetVersionAllowed(
     const std::string& app_id) const {
   int rollback_policy =
       [impl_ rollbackToTargetVersion:base::SysUTF8ToNSString(app_id)];
   return rollback_policy != kPolicyNotSet
-             ? absl::optional<bool>(rollback_policy != 0)
-             : absl::nullopt;
+             ? std::optional<bool>(rollback_policy != 0)
+             : std::nullopt;
 }
 
-absl::optional<std::string> ManagedPreferencePolicyManager::GetProxyMode()
+std::optional<std::string> ManagedPreferencePolicyManager::GetProxyMode()
     const {
   NSString* value = [impl_ proxyMode];
-  return value ? absl::optional<std::string>(base::SysNSStringToUTF8(value))
-               : absl::nullopt;
+  return value ? std::optional<std::string>(base::SysNSStringToUTF8(value))
+               : std::nullopt;
 }
 
-absl::optional<std::string> ManagedPreferencePolicyManager::GetProxyPacUrl()
+std::optional<std::string> ManagedPreferencePolicyManager::GetProxyPacUrl()
     const {
   NSString* value = [impl_ proxyPacURL];
-  return value ? absl::optional<std::string>(base::SysNSStringToUTF8(value))
-               : absl::nullopt;
+  return value ? std::optional<std::string>(base::SysNSStringToUTF8(value))
+               : std::nullopt;
 }
 
-absl::optional<std::string> ManagedPreferencePolicyManager::GetProxyServer()
+std::optional<std::string> ManagedPreferencePolicyManager::GetProxyServer()
     const {
   NSString* value = [impl_ proxyServer];
-  return value ? absl::optional<std::string>(base::SysNSStringToUTF8(value))
-               : absl::nullopt;
+  return value ? std::optional<std::string>(base::SysNSStringToUTF8(value))
+               : std::nullopt;
 }
 
-absl::optional<std::string> ManagedPreferencePolicyManager::GetTargetChannel(
+std::optional<std::string> ManagedPreferencePolicyManager::GetTargetChannel(
     const std::string& app_id) const {
   NSString* value = [impl_ targetChannel:base::SysUTF8ToNSString(app_id)];
-  return value ? absl::optional<std::string>(base::SysNSStringToUTF8(value))
-               : absl::nullopt;
+  return value ? std::optional<std::string>(base::SysNSStringToUTF8(value))
+               : std::nullopt;
 }
 
-absl::optional<std::vector<std::string>>
+std::optional<std::vector<std::string>>
 ManagedPreferencePolicyManager::GetForceInstallApps() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::vector<std::string>>
+std::optional<std::vector<std::string>>
 ManagedPreferencePolicyManager::GetAppsWithPolicy() const {
   NSArray<NSString*>* apps_with_policy = [impl_ appsWithPolicy];
   if (!apps_with_policy) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   std::vector<std::string> app_ids;
@@ -227,7 +227,7 @@
 }
 
 scoped_refptr<PolicyManagerInterface> CreateManagedPreferencePolicyManager(
-    const absl::optional<bool>& override_is_managed_device) {
+    const std::optional<bool>& override_is_managed_device) {
   NSDictionary* policyDict = ReadManagedPreferencePolicyDictionary();
   return base::MakeRefCounted<ManagedPreferencePolicyManager>(
       policyDict, override_is_managed_device);
diff --git a/chrome/updater/policy/mac/managed_preference_policy_manager_unittest.cc b/chrome/updater/policy/mac/managed_preference_policy_manager_unittest.cc
index 9932d58..0046514 100644
--- a/chrome/updater/policy/mac/managed_preference_policy_manager_unittest.cc
+++ b/chrome/updater/policy/mac/managed_preference_policy_manager_unittest.cc
@@ -7,7 +7,6 @@
 #include <memory>
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
diff --git a/chrome/updater/policy/manager.cc b/chrome/updater/policy/manager.cc
index eb10dfb..73ca22f5 100644
--- a/chrome/updater/policy/manager.cc
+++ b/chrome/updater/policy/manager.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/manager.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -11,7 +12,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "chrome/updater/constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -60,27 +60,27 @@
 
   bool HasActiveDevicePolicies() const override;
 
-  absl::optional<base::TimeDelta> GetLastCheckPeriod() const override;
-  absl::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
+  std::optional<base::TimeDelta> GetLastCheckPeriod() const override;
+  std::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
       const override;
-  absl::optional<std::string> GetDownloadPreference() const override;
-  absl::optional<int> GetPackageCacheSizeLimitMBytes() const override;
-  absl::optional<int> GetPackageCacheExpirationTimeDays() const override;
-  absl::optional<int> GetEffectivePolicyForAppInstalls(
+  std::optional<std::string> GetDownloadPreference() const override;
+  std::optional<int> GetPackageCacheSizeLimitMBytes() const override;
+  std::optional<int> GetPackageCacheExpirationTimeDays() const override;
+  std::optional<int> GetEffectivePolicyForAppInstalls(
       const std::string& app_id) const override;
-  absl::optional<int> GetEffectivePolicyForAppUpdates(
+  std::optional<int> GetEffectivePolicyForAppUpdates(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetTargetVersionPrefix(
+  std::optional<std::string> GetTargetVersionPrefix(
       const std::string& app_id) const override;
-  absl::optional<bool> IsRollbackToTargetVersionAllowed(
+  std::optional<bool> IsRollbackToTargetVersionAllowed(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetProxyMode() const override;
-  absl::optional<std::string> GetProxyPacUrl() const override;
-  absl::optional<std::string> GetProxyServer() const override;
-  absl::optional<std::string> GetTargetChannel(
+  std::optional<std::string> GetProxyMode() const override;
+  std::optional<std::string> GetProxyPacUrl() const override;
+  std::optional<std::string> GetProxyServer() const override;
+  std::optional<std::string> GetTargetChannel(
       const std::string& app_id) const override;
-  absl::optional<std::vector<std::string>> GetForceInstallApps() const override;
-  absl::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
+  std::optional<std::vector<std::string>> GetForceInstallApps() const override;
+  std::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
 
  private:
   ~DefaultValuesPolicyManager() override;
@@ -98,78 +98,77 @@
   return kSourceDefaultValuesPolicyManager;
 }
 
-absl::optional<base::TimeDelta> DefaultValuesPolicyManager::GetLastCheckPeriod()
+std::optional<base::TimeDelta> DefaultValuesPolicyManager::GetLastCheckPeriod()
     const {
   return kDefaultLastCheckPeriod;
 }
 
-absl::optional<UpdatesSuppressedTimes>
+std::optional<UpdatesSuppressedTimes>
 DefaultValuesPolicyManager::GetUpdatesSuppressedTimes() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::string> DefaultValuesPolicyManager::GetDownloadPreference()
+std::optional<std::string> DefaultValuesPolicyManager::GetDownloadPreference()
     const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<int> DefaultValuesPolicyManager::GetPackageCacheSizeLimitMBytes()
+std::optional<int> DefaultValuesPolicyManager::GetPackageCacheSizeLimitMBytes()
     const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<int>
+std::optional<int>
 DefaultValuesPolicyManager::GetPackageCacheExpirationTimeDays() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<int>
-DefaultValuesPolicyManager::GetEffectivePolicyForAppInstalls(
+std::optional<int> DefaultValuesPolicyManager::GetEffectivePolicyForAppInstalls(
     const std::string& app_id) const {
   return kInstallPolicyDefault;
 }
 
-absl::optional<int> DefaultValuesPolicyManager::GetEffectivePolicyForAppUpdates(
+std::optional<int> DefaultValuesPolicyManager::GetEffectivePolicyForAppUpdates(
     const std::string& app_id) const {
   return kUpdatePolicyDefault;
 }
 
-absl::optional<std::string> DefaultValuesPolicyManager::GetTargetVersionPrefix(
+std::optional<std::string> DefaultValuesPolicyManager::GetTargetVersionPrefix(
     const std::string& app_id) const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<bool>
+std::optional<bool>
 DefaultValuesPolicyManager::IsRollbackToTargetVersionAllowed(
     const std::string& app_id) const {
   return false;
 }
 
-absl::optional<std::string> DefaultValuesPolicyManager::GetProxyMode() const {
-  return absl::nullopt;
+std::optional<std::string> DefaultValuesPolicyManager::GetProxyMode() const {
+  return std::nullopt;
 }
 
-absl::optional<std::string> DefaultValuesPolicyManager::GetProxyPacUrl() const {
-  return absl::nullopt;
+std::optional<std::string> DefaultValuesPolicyManager::GetProxyPacUrl() const {
+  return std::nullopt;
 }
 
-absl::optional<std::string> DefaultValuesPolicyManager::GetProxyServer() const {
-  return absl::nullopt;
+std::optional<std::string> DefaultValuesPolicyManager::GetProxyServer() const {
+  return std::nullopt;
 }
 
-absl::optional<std::string> DefaultValuesPolicyManager::GetTargetChannel(
+std::optional<std::string> DefaultValuesPolicyManager::GetTargetChannel(
     const std::string& app_id) const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::vector<std::string>>
+std::optional<std::vector<std::string>>
 DefaultValuesPolicyManager::GetForceInstallApps() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<std::vector<std::string>>
+std::optional<std::vector<std::string>>
 DefaultValuesPolicyManager::GetAppsWithPolicy() const {
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 scoped_refptr<PolicyManagerInterface> GetDefaultValuesPolicyManager() {
diff --git a/chrome/updater/policy/manager.h b/chrome/updater/policy/manager.h
index 87c48b4..f4ff6646 100644
--- a/chrome/updater/policy/manager.h
+++ b/chrome/updater/policy/manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_POLICY_MANAGER_H_
 #define CHROME_UPDATER_POLICY_MANAGER_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -12,7 +13,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/time/time.h"
 #include "chrome/updater/constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -55,30 +55,30 @@
   // Returns the policy for how often the Updater should check for updates.
   // Returns the time interval between update checks.
   // 0 indicates updates are disabled.
-  virtual absl::optional<base::TimeDelta> GetLastCheckPeriod() const = 0;
+  virtual std::optional<base::TimeDelta> GetLastCheckPeriod() const = 0;
 
   // For domain-joined machines, checks the current time against the times that
   // updates are suppressed.
-  virtual absl::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
+  virtual std::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
       const = 0;
 
   // Returns the policy for the download preference.
-  virtual absl::optional<std::string> GetDownloadPreference() const = 0;
+  virtual std::optional<std::string> GetDownloadPreference() const = 0;
 
   // Returns the policy for the package cache size limit in megabytes.
-  virtual absl::optional<int> GetPackageCacheSizeLimitMBytes() const = 0;
+  virtual std::optional<int> GetPackageCacheSizeLimitMBytes() const = 0;
 
   // Returns the policy for the package cache expiration in days.
-  virtual absl::optional<int> GetPackageCacheExpirationTimeDays() const = 0;
+  virtual std::optional<int> GetPackageCacheExpirationTimeDays() const = 0;
 
   // Returns kPolicyEnabled if installation of the specified app is allowed.
   // Otherwise, returns kPolicyDisabled.
-  virtual absl::optional<int> GetEffectivePolicyForAppInstalls(
+  virtual std::optional<int> GetEffectivePolicyForAppInstalls(
       const std::string& app_id) const = 0;
   // Returns kPolicyEnabled if updates of the specified app is allowed.
   // Otherwise, returns one of kPolicyDisabled, kPolicyManualUpdatesOnly, or
   // kPolicyAutomaticUpdatesOnly.
-  virtual absl::optional<int> GetEffectivePolicyForAppUpdates(
+  virtual std::optional<int> GetEffectivePolicyForAppUpdates(
       const std::string& app_id) const = 0;
   // Returns the target version prefix for the app.
   // Examples:
@@ -86,34 +86,33 @@
   // * "55.": update to any minor version of 55 (e.g. 55.24.34 or 55.60.2).
   // * "55.2.": update to any minor version of 55.2 (e.g. 55.2.34 or 55.2.2).
   // * "55.24.34": update to this specific version only.
-  virtual absl::optional<std::string> GetTargetVersionPrefix(
+  virtual std::optional<std::string> GetTargetVersionPrefix(
       const std::string& app_id) const = 0;
   // Returns whether the RollbackToTargetVersion policy has been set for the
   // app. If RollbackToTargetVersion is set, the TargetVersionPrefix policy
   // governs the version to rollback clients with higher versions to.
-  virtual absl::optional<bool> IsRollbackToTargetVersionAllowed(
+  virtual std::optional<bool> IsRollbackToTargetVersionAllowed(
       const std::string& app_id) const = 0;
   // Returns a proxy mode such as |auto_detect|.
-  virtual absl::optional<std::string> GetProxyMode() const = 0;
+  virtual std::optional<std::string> GetProxyMode() const = 0;
 
   // Returns a proxy PAC URL.
-  virtual absl::optional<std::string> GetProxyPacUrl() const = 0;
+  virtual std::optional<std::string> GetProxyPacUrl() const = 0;
 
   // Returns a proxy server.
-  virtual absl::optional<std::string> GetProxyServer() const = 0;
+  virtual std::optional<std::string> GetProxyServer() const = 0;
 
   // Returns a channel, for example {stable|beta|dev}.
-  virtual absl::optional<std::string> GetTargetChannel(
+  virtual std::optional<std::string> GetTargetChannel(
       const std::string& app_id) const = 0;
 
   // Returns a list of apps that need to be downloaded and installed by the
   // updater.
-  virtual absl::optional<std::vector<std::string>> GetForceInstallApps()
+  virtual std::optional<std::vector<std::string>> GetForceInstallApps()
       const = 0;
 
   // Returns all apps that have some policy set.
-  virtual absl::optional<std::vector<std::string>> GetAppsWithPolicy()
-      const = 0;
+  virtual std::optional<std::vector<std::string>> GetAppsWithPolicy() const = 0;
 
  protected:
   friend class base::RefCountedThreadSafe<PolicyManagerInterface>;
diff --git a/chrome/updater/policy/policy_fetcher.cc b/chrome/updater/policy/policy_fetcher.cc
index 344e3904..1d0186b 100644
--- a/chrome/updater/policy/policy_fetcher.cc
+++ b/chrome/updater/policy/policy_fetcher.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/policy_fetcher.h"
 
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -24,15 +25,14 @@
 #include "chrome/updater/policy/dm_policy_manager.h"
 #include "chrome/updater/policy/service.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
 
 PolicyFetcher::PolicyFetcher(
     const GURL& server_url,
-    const absl::optional<PolicyServiceProxyConfiguration>& proxy_configuration,
-    const absl::optional<bool>& override_is_managed_device)
+    const std::optional<PolicyServiceProxyConfiguration>& proxy_configuration,
+    const std::optional<bool>& override_is_managed_device)
     : server_url_(server_url),
       policy_service_proxy_configuration_(proxy_configuration),
       override_is_managed_device_(override_is_managed_device),
diff --git a/chrome/updater/policy/policy_fetcher.h b/chrome/updater/policy/policy_fetcher.h
index 8681ce9..29dacb6 100644
--- a/chrome/updater/policy/policy_fetcher.h
+++ b/chrome/updater/policy/policy_fetcher.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_POLICY_POLICY_FETCHER_H_
 #define CHROME_UPDATER_POLICY_POLICY_FETCHER_H_
 
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback_forward.h"
@@ -17,7 +18,6 @@
 #include "chrome/updater/device_management/dm_response_validator.h"
 #include "chrome/updater/policy/manager.h"
 #include "chrome/updater/policy/service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -25,10 +25,10 @@
 // The PolicyFetcher handles registration and DM policy refreshes.
 class PolicyFetcher : public base::RefCountedThreadSafe<PolicyFetcher> {
  public:
-  PolicyFetcher(const GURL& server_url,
-                const absl::optional<PolicyServiceProxyConfiguration>&
-                    proxy_configuration,
-                const absl::optional<bool>& override_is_managed_device);
+  PolicyFetcher(
+      const GURL& server_url,
+      const std::optional<PolicyServiceProxyConfiguration>& proxy_configuration,
+      const std::optional<bool>& override_is_managed_device);
   void FetchPolicies(
       base::OnceCallback<void(int, scoped_refptr<PolicyManagerInterface>)>
           callback);
@@ -54,9 +54,9 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
   const GURL server_url_;
-  const absl::optional<PolicyServiceProxyConfiguration>
+  const std::optional<PolicyServiceProxyConfiguration>
       policy_service_proxy_configuration_;
-  const absl::optional<bool> override_is_managed_device_;
+  const std::optional<bool> override_is_managed_device_;
   const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
 };
 
diff --git a/chrome/updater/policy/policy_manager.cc b/chrome/updater/policy/policy_manager.cc
index 2087767..fdf143e 100644
--- a/chrome/updater/policy/policy_manager.cc
+++ b/chrome/updater/policy/policy_manager.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/policy_manager.h"
 
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -14,7 +15,6 @@
 #include "base/values.h"
 #include "chrome/updater/policy/manager.h"
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -96,25 +96,24 @@
   return kSourceDictValuesPolicyManager;
 }
 
-absl::optional<base::TimeDelta> PolicyManager::GetLastCheckPeriod() const {
-  absl::optional<int> minutes =
+std::optional<base::TimeDelta> PolicyManager::GetLastCheckPeriod() const {
+  std::optional<int> minutes =
       GetIntegerPolicy(kAutoUpdateCheckPeriodOverrideMinutes);
   if (!minutes) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return base::Minutes(*minutes);
 }
 
-absl::optional<UpdatesSuppressedTimes>
-PolicyManager::GetUpdatesSuppressedTimes() const {
-  absl::optional<int> start_hour =
-      GetIntegerPolicy(kUpdatesSuppressedStartHour);
-  absl::optional<int> start_min = GetIntegerPolicy(kUpdatesSuppressedStartMin);
-  absl::optional<int> duration_min =
+std::optional<UpdatesSuppressedTimes> PolicyManager::GetUpdatesSuppressedTimes()
+    const {
+  std::optional<int> start_hour = GetIntegerPolicy(kUpdatesSuppressedStartHour);
+  std::optional<int> start_min = GetIntegerPolicy(kUpdatesSuppressedStartMin);
+  std::optional<int> duration_min =
       GetIntegerPolicy(kUpdatesSuppressedDurationMin);
 
   if (!start_hour || !start_min || !duration_min)
-    return absl::nullopt;
+    return std::nullopt;
 
   UpdatesSuppressedTimes supressed_times;
   supressed_times.start_hour_ = start_hour.value();
@@ -123,76 +122,75 @@
   return supressed_times;
 }
 
-absl::optional<std::string> PolicyManager::GetDownloadPreference() const {
+std::optional<std::string> PolicyManager::GetDownloadPreference() const {
   return GetStringPolicy(kDownloadPreference);
 }
 
-absl::optional<int> PolicyManager::GetPackageCacheSizeLimitMBytes() const {
+std::optional<int> PolicyManager::GetPackageCacheSizeLimitMBytes() const {
   return GetIntegerPolicy(kCacheSizeLimitMBytes);
 }
 
-absl::optional<int> PolicyManager::GetPackageCacheExpirationTimeDays() const {
+std::optional<int> PolicyManager::GetPackageCacheExpirationTimeDays() const {
   return GetIntegerPolicy(kCacheLifeLimitDays);
 }
 
-absl::optional<int> PolicyManager::GetEffectivePolicyForAppInstalls(
+std::optional<int> PolicyManager::GetEffectivePolicyForAppInstalls(
     const std::string& app_id) const {
   std::string app_value_name(kInstallAppPrefix);
   app_value_name.append(app_id);
-  absl::optional<int> policy = GetIntegerPolicy(app_value_name);
+  std::optional<int> policy = GetIntegerPolicy(app_value_name);
   return policy ? policy : GetIntegerPolicy(kInstallAppsDefault);
 }
 
-absl::optional<int> PolicyManager::GetEffectivePolicyForAppUpdates(
+std::optional<int> PolicyManager::GetEffectivePolicyForAppUpdates(
     const std::string& app_id) const {
   std::string app_value_name(kUpdateAppPrefix);
   app_value_name.append(app_id);
-  absl::optional<int> policy = GetIntegerPolicy(app_value_name);
+  std::optional<int> policy = GetIntegerPolicy(app_value_name);
   return policy ? policy : GetIntegerPolicy(kUpdateAppsDefault);
 }
 
-absl::optional<std::string> PolicyManager::GetTargetChannel(
+std::optional<std::string> PolicyManager::GetTargetChannel(
     const std::string& app_id) const {
   std::string app_value_name(kTargetChannel);
   app_value_name.append(app_id);
   return GetStringPolicy(app_value_name.c_str());
 }
 
-absl::optional<std::string> PolicyManager::GetTargetVersionPrefix(
+std::optional<std::string> PolicyManager::GetTargetVersionPrefix(
     const std::string& app_id) const {
   std::string app_value_name(kTargetVersionPrefix);
   app_value_name.append(app_id);
   return GetStringPolicy(app_value_name.c_str());
 }
 
-absl::optional<bool> PolicyManager::IsRollbackToTargetVersionAllowed(
+std::optional<bool> PolicyManager::IsRollbackToTargetVersionAllowed(
     const std::string& app_id) const {
   std::string app_value_name(kRollbackToTargetVersion);
   app_value_name.append(app_id);
-  absl::optional<int> policy = GetIntegerPolicy(app_value_name);
-  return policy ? absl::optional<bool>(policy.value()) : absl::nullopt;
+  std::optional<int> policy = GetIntegerPolicy(app_value_name);
+  return policy ? std::optional<bool>(policy.value()) : std::nullopt;
 }
 
-absl::optional<std::string> PolicyManager::GetProxyMode() const {
+std::optional<std::string> PolicyManager::GetProxyMode() const {
   return GetStringPolicy(kProxyMode);
 }
 
-absl::optional<std::string> PolicyManager::GetProxyPacUrl() const {
+std::optional<std::string> PolicyManager::GetProxyPacUrl() const {
   return GetStringPolicy(kProxyPacUrl);
 }
 
-absl::optional<std::string> PolicyManager::GetProxyServer() const {
+std::optional<std::string> PolicyManager::GetProxyServer() const {
   return GetStringPolicy(kProxyServer);
 }
 
-absl::optional<std::vector<std::string>> PolicyManager::GetForceInstallApps()
+std::optional<std::vector<std::string>> PolicyManager::GetForceInstallApps()
     const {
-  return force_install_apps_.empty()
-             ? absl::optional<std::vector<std::string>>()
-             : force_install_apps_;
+  return force_install_apps_.empty() ? std::optional<std::vector<std::string>>()
+                                     : force_install_apps_;
 }
 
-absl::optional<std::vector<std::string>> PolicyManager::GetAppsWithPolicy()
+std::optional<std::vector<std::string>> PolicyManager::GetAppsWithPolicy()
     const {
   const std::set<std::string> kPrefixedPolicyNames = {
       // prefixed by kUpdateAppPrefix:
@@ -219,14 +217,14 @@
   return apps_with_policy;
 }
 
-absl::optional<int> PolicyManager::GetIntegerPolicy(
+std::optional<int> PolicyManager::GetIntegerPolicy(
     const std::string& key) const {
   return policies_.FindInt(base::ToLowerASCII(key));
 }
 
-absl::optional<std::string> PolicyManager::GetStringPolicy(
+std::optional<std::string> PolicyManager::GetStringPolicy(
     const std::string& key) const {
   const std::string* policy = policies_.FindString(base::ToLowerASCII(key));
-  return policy ? absl::make_optional(*policy) : absl::nullopt;
+  return policy ? std::make_optional(*policy) : std::nullopt;
 }
 }  // namespace updater
diff --git a/chrome/updater/policy/policy_manager.h b/chrome/updater/policy/policy_manager.h
index 9ef010f1..cdbf475 100644
--- a/chrome/updater/policy/policy_manager.h
+++ b/chrome/updater/policy/policy_manager.h
@@ -5,12 +5,12 @@
 #ifndef CHROME_UPDATER_POLICY_POLICY_MANAGER_H_
 #define CHROME_UPDATER_POLICY_POLICY_MANAGER_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "base/values.h"
 #include "chrome/updater/policy/manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -25,35 +25,35 @@
   PolicyManager(const PolicyManager&) = delete;
   PolicyManager& operator=(const PolicyManager&) = delete;
 
-  absl::optional<int> GetIntegerPolicy(const std::string& key) const;
-  absl::optional<std::string> GetStringPolicy(const std::string& key) const;
+  std::optional<int> GetIntegerPolicy(const std::string& key) const;
+  std::optional<std::string> GetStringPolicy(const std::string& key) const;
 
   // Overrides for PolicyManagerInterface.
   std::string source() const override;
 
   bool HasActiveDevicePolicies() const override;
 
-  absl::optional<base::TimeDelta> GetLastCheckPeriod() const override;
-  absl::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
+  std::optional<base::TimeDelta> GetLastCheckPeriod() const override;
+  std::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
       const override;
-  absl::optional<std::string> GetDownloadPreference() const override;
-  absl::optional<int> GetPackageCacheSizeLimitMBytes() const override;
-  absl::optional<int> GetPackageCacheExpirationTimeDays() const override;
-  absl::optional<int> GetEffectivePolicyForAppInstalls(
+  std::optional<std::string> GetDownloadPreference() const override;
+  std::optional<int> GetPackageCacheSizeLimitMBytes() const override;
+  std::optional<int> GetPackageCacheExpirationTimeDays() const override;
+  std::optional<int> GetEffectivePolicyForAppInstalls(
       const std::string& app_id) const override;
-  absl::optional<int> GetEffectivePolicyForAppUpdates(
+  std::optional<int> GetEffectivePolicyForAppUpdates(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetTargetVersionPrefix(
+  std::optional<std::string> GetTargetVersionPrefix(
       const std::string& app_id) const override;
-  absl::optional<bool> IsRollbackToTargetVersionAllowed(
+  std::optional<bool> IsRollbackToTargetVersionAllowed(
       const std::string& app_id) const override;
-  absl::optional<std::string> GetProxyMode() const override;
-  absl::optional<std::string> GetProxyPacUrl() const override;
-  absl::optional<std::string> GetProxyServer() const override;
-  absl::optional<std::string> GetTargetChannel(
+  std::optional<std::string> GetProxyMode() const override;
+  std::optional<std::string> GetProxyPacUrl() const override;
+  std::optional<std::string> GetProxyServer() const override;
+  std::optional<std::string> GetTargetChannel(
       const std::string& app_id) const override;
-  absl::optional<std::vector<std::string>> GetForceInstallApps() const override;
-  absl::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
+  std::optional<std::vector<std::string>> GetForceInstallApps() const override;
+  std::optional<std::vector<std::string>> GetAppsWithPolicy() const override;
 
  protected:
   ~PolicyManager() override;
diff --git a/chrome/updater/policy/policy_manager_unittest.cc b/chrome/updater/policy/policy_manager_unittest.cc
index 5bf91545..0a566ff7 100644
--- a/chrome/updater/policy/policy_manager_unittest.cc
+++ b/chrome/updater/policy/policy_manager_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/policy/policy_manager.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -29,32 +30,32 @@
 
   EXPECT_EQ(policy_manager->source(), "DictValuePolicy");
 
-  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetDownloadPreference(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyMode(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyServer(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyPacUrl(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetDownloadPreference(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyMode(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyServer(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyPacUrl(), std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(kTestAppID),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_FALSE(policy_manager->GetEffectivePolicyForAppInstalls(
       "non-exist-app-fallback-to-global"));
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(kTestAppID),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_FALSE(policy_manager->GetEffectivePolicyForAppUpdates(
       "non-exist-app-fallback-to-global"));
-  EXPECT_EQ(policy_manager->GetTargetChannel(kTestAppID), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kTestAppID), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel(kTestAppID), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kTestAppID), std::nullopt);
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(kTestAppID),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_FALSE(
       policy_manager->IsRollbackToTargetVersionAllowed("non-exist-app"));
-  EXPECT_EQ(policy_manager->GetForceInstallApps(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetForceInstallApps(), std::nullopt);
 }
 
 TEST_F(PolicyManagerTests, PolicyRead) {
@@ -90,7 +91,7 @@
 
   EXPECT_EQ(policy_manager->GetLastCheckPeriod(), base::Minutes(480));
 
-  absl::optional<UpdatesSuppressedTimes> suppressed_times =
+  std::optional<UpdatesSuppressedTimes> suppressed_times =
       policy_manager->GetUpdatesSuppressedTimes();
   ASSERT_TRUE(suppressed_times);
   EXPECT_EQ(suppressed_times->start_hour_, 2);
@@ -117,17 +118,17 @@
             1);
 
   EXPECT_EQ(policy_manager->GetTargetChannel(kTestAppID), "beta");
-  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), std::nullopt);
 
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kTestAppID), "55.55.");
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
 
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(kTestAppID), true);
   EXPECT_FALSE(
       policy_manager->IsRollbackToTargetVersionAllowed("non-exist-app"));
 
-  absl::optional<std::vector<std::string>> force_install_apps =
+  std::optional<std::vector<std::string>> force_install_apps =
       policy_manager->GetForceInstallApps();
   ASSERT_EQ(force_install_apps.has_value(), !IsSystemInstall());
 
@@ -167,29 +168,29 @@
 
   EXPECT_TRUE(policy_manager->HasActiveDevicePolicies());
 
-  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetDownloadPreference(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyMode(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyServer(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyPacUrl(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetDownloadPreference(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyMode(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyServer(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyPacUrl(), std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(kTestAppID),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_FALSE(policy_manager->GetEffectivePolicyForAppInstalls(
       "non-exist-app-fallback-to-global"));
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(kTestAppID),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_FALSE(policy_manager->GetEffectivePolicyForAppUpdates(
       "non-exist-app-fallback-to-global"));
-  EXPECT_EQ(policy_manager->GetTargetChannel(kTestAppID), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kTestAppID), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel(kTestAppID), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(kTestAppID), std::nullopt);
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(kTestAppID),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_FALSE(
       policy_manager->IsRollbackToTargetVersionAllowed("non-exist-app"));
 }
diff --git a/chrome/updater/policy/service.cc b/chrome/updater/policy/service.cc
index 796a317..73d0b7ab 100644
--- a/chrome/updater/policy/service.cc
+++ b/chrome/updater/policy/service.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/service.h"
 
+#include <optional>
 #include <set>
 #include <string>
 #include <utility>
@@ -33,7 +34,6 @@
 #elif BUILDFLAG(IS_MAC)
 #include "chrome/updater/policy/mac/managed_preference_policy_manager.h"
 #endif
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -303,9 +303,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return QueryPolicy(
       base::BindRepeating(&PolicyManagerInterface::GetLastCheckPeriod)
-          .Then(base::BindRepeating([](absl::optional<base::TimeDelta> period) {
-            return period ? absl::optional<int>(period->InMinutes())
-                          : absl::nullopt;
+          .Then(base::BindRepeating([](std::optional<base::TimeDelta> period) {
+            return period ? std::optional<int>(period->InMinutes())
+                          : std::nullopt;
           })));
 }
 
@@ -458,11 +458,11 @@
 
 template <typename T>
 PolicyStatus<T> PolicyService::QueryPolicy(
-    const base::RepeatingCallback<absl::optional<T>(
-        const PolicyManagerInterface*)>& policy_query_callback,
+    const base::RepeatingCallback<
+        std::optional<T>(const PolicyManagerInterface*)>& policy_query_callback,
     const base::RepeatingCallback<bool(const T&)>& validator) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  absl::optional<T> query_result;
+  std::optional<T> query_result;
   PolicyStatus<T> status;
   for (const scoped_refptr<PolicyManagerInterface>& policy_manager :
        policy_managers_.vector) {
@@ -481,11 +481,11 @@
 template <typename T>
 PolicyStatus<T> PolicyService::QueryAppPolicy(
     const base::RepeatingCallback<
-        absl::optional<T>(const PolicyManagerInterface*, const std::string&)>&
+        std::optional<T>(const PolicyManagerInterface*, const std::string&)>&
         policy_query_callback,
     const std::string& app_id) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  absl::optional<T> query_result;
+  std::optional<T> query_result;
   PolicyStatus<T> status;
   for (const scoped_refptr<PolicyManagerInterface>& policy_manager :
        policy_managers_.vector) {
@@ -506,12 +506,12 @@
 PolicyServiceProxyConfiguration& PolicyServiceProxyConfiguration::operator=(
     const PolicyServiceProxyConfiguration&) = default;
 
-absl::optional<PolicyServiceProxyConfiguration>
+std::optional<PolicyServiceProxyConfiguration>
 PolicyServiceProxyConfiguration::Get(
     scoped_refptr<PolicyService> policy_service) {
   PolicyStatus<std::string> proxy_mode = policy_service->GetProxyMode();
   if (!proxy_mode || proxy_mode.policy().compare(kProxyModeSystem) == 0) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   VLOG(2) << "Using policy proxy " << proxy_mode.policy();
 
@@ -540,7 +540,7 @@
 
   if (!is_policy_config_valid) {
     VLOG(1) << "Configuration set by policy was invalid.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return policy_service_proxy_configuration;
diff --git a/chrome/updater/policy/service.h b/chrome/updater/policy/service.h
index 48f780f..71315bb6 100644
--- a/chrome/updater/policy/service.h
+++ b/chrome/updater/policy/service.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_POLICY_SERVICE_H_
 #define CHROME_UPDATER_POLICY_SERVICE_H_
 
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -17,7 +18,6 @@
 #include "base/time/time.h"
 #include "chrome/updater/external_constants.h"
 #include "chrome/updater/policy/manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -46,17 +46,17 @@
       return;  // We already have enough policies.
 
     if (!effective_policy_ && is_managed) {
-      effective_policy_ = absl::make_optional<Entry>(source, policy);
+      effective_policy_ = std::make_optional<Entry>(source, policy);
     } else if (effective_policy_ &&
                policy != effective_policy_.value().policy) {
-      conflict_policy_ = absl::make_optional<Entry>(source, policy);
+      conflict_policy_ = std::make_optional<Entry>(source, policy);
     }
   }
 
-  const absl::optional<Entry>& effective_policy() const {
+  const std::optional<Entry>& effective_policy() const {
     return effective_policy_;
   }
-  const absl::optional<Entry>& conflict_policy() const {
+  const std::optional<Entry>& conflict_policy() const {
     return conflict_policy_;
   }
 
@@ -71,8 +71,8 @@
   }
 
  private:
-  absl::optional<Entry> effective_policy_;
-  absl::optional<Entry> conflict_policy_;
+  std::optional<Entry> effective_policy_;
+  std::optional<Entry> conflict_policy_;
 };
 
 // The PolicyService returns policies for enterprise managed machines from the
@@ -161,7 +161,7 @@
   // determines the policy status.
   template <typename T>
   PolicyStatus<T> QueryPolicy(
-      const base::RepeatingCallback<absl::optional<T>(
+      const base::RepeatingCallback<std::optional<T>(
           const PolicyManagerInterface*)>& policy_query_callback,
       const base::RepeatingCallback<bool(const T&)>& validator =
           base::NullCallback()) const;
@@ -171,8 +171,8 @@
   template <typename T>
   PolicyStatus<T> QueryAppPolicy(
       const base::RepeatingCallback<
-          absl::optional<T>(const PolicyManagerInterface*,
-                            const std::string& app_id)>& policy_query_callback,
+          std::optional<T>(const PolicyManagerInterface*,
+                           const std::string& app_id)>& policy_query_callback,
       const std::string& app_id) const;
 
   std::set<std::string> GetAppsWithPolicy() const;
@@ -186,12 +186,12 @@
   PolicyServiceProxyConfiguration& operator=(
       const PolicyServiceProxyConfiguration&);
 
-  static absl::optional<PolicyServiceProxyConfiguration> Get(
+  static std::optional<PolicyServiceProxyConfiguration> Get(
       scoped_refptr<PolicyService> policy_service);
 
-  absl::optional<bool> proxy_auto_detect;
-  absl::optional<std::string> proxy_pac_url;
-  absl::optional<std::string> proxy_url;
+  std::optional<bool> proxy_auto_detect;
+  std::optional<std::string> proxy_pac_url;
+  std::optional<std::string> proxy_url;
 };
 
 PolicyService::PolicyManagerVector CreatePolicyManagerVector(
diff --git a/chrome/updater/policy/service_unittest.cc b/chrome/updater/policy/service_unittest.cc
index cc2c732..1ad98a9 100644
--- a/chrome/updater/policy/service_unittest.cc
+++ b/chrome/updater/policy/service_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <utility>
@@ -16,7 +17,6 @@
 #include "chrome/updater/policy/service.h"
 #include "chrome/updater/protos/omaha_settings.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "base/test/test_reg_util_win.h"
@@ -42,13 +42,13 @@
   bool HasActiveDevicePolicies() const override {
     return has_active_device_policies_;
   }
-  absl::optional<base::TimeDelta> GetLastCheckPeriod() const override {
-    return absl::nullopt;
+  std::optional<base::TimeDelta> GetLastCheckPeriod() const override {
+    return std::nullopt;
   }
-  absl::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
+  std::optional<UpdatesSuppressedTimes> GetUpdatesSuppressedTimes()
       const override {
     if (!suppressed_times_.valid())
-      return absl::nullopt;
+      return std::nullopt;
 
     return suppressed_times_;
   }
@@ -56,68 +56,66 @@
       const UpdatesSuppressedTimes& suppressed_times) {
     suppressed_times_ = suppressed_times;
   }
-  absl::optional<std::string> GetDownloadPreference() const override {
+  std::optional<std::string> GetDownloadPreference() const override {
     return download_preference_.empty()
-               ? absl::nullopt
-               : absl::make_optional(download_preference_);
+               ? std::nullopt
+               : std::make_optional(download_preference_);
   }
   void SetDownloadPreference(const std::string& preference) {
     download_preference_ = preference;
   }
-  absl::optional<int> GetPackageCacheSizeLimitMBytes() const override {
-    return absl::nullopt;
+  std::optional<int> GetPackageCacheSizeLimitMBytes() const override {
+    return std::nullopt;
   }
-  absl::optional<int> GetPackageCacheExpirationTimeDays() const override {
-    return absl::nullopt;
+  std::optional<int> GetPackageCacheExpirationTimeDays() const override {
+    return std::nullopt;
   }
-  absl::optional<int> GetEffectivePolicyForAppInstalls(
+  std::optional<int> GetEffectivePolicyForAppInstalls(
       const std::string& app_id) const override {
-    return absl::nullopt;
+    return std::nullopt;
   }
-  absl::optional<int> GetEffectivePolicyForAppUpdates(
+  std::optional<int> GetEffectivePolicyForAppUpdates(
       const std::string& app_id) const override {
     auto value = update_policies_.find(app_id);
     if (value == update_policies_.end())
-      return absl::nullopt;
+      return std::nullopt;
     return value->second;
   }
   void SetUpdatePolicy(const std::string& app_id, int update_policy) {
     update_policies_[app_id] = update_policy;
   }
-  absl::optional<std::string> GetTargetVersionPrefix(
+  std::optional<std::string> GetTargetVersionPrefix(
       const std::string& app_id) const override {
-    return absl::nullopt;
+    return std::nullopt;
   }
-  absl::optional<bool> IsRollbackToTargetVersionAllowed(
+  std::optional<bool> IsRollbackToTargetVersionAllowed(
       const std::string& app_id) const override {
-    return absl::nullopt;
+    return std::nullopt;
   }
-  absl::optional<std::string> GetProxyMode() const override {
-    return proxy_mode_.empty() ? absl::nullopt
-                               : absl::make_optional(proxy_mode_);
+  std::optional<std::string> GetProxyMode() const override {
+    return proxy_mode_.empty() ? std::nullopt : std::make_optional(proxy_mode_);
   }
   void SetProxyMode(const std::string& proxy_mode) { proxy_mode_ = proxy_mode; }
-  absl::optional<std::string> GetProxyPacUrl() const override {
-    return absl::nullopt;
+  std::optional<std::string> GetProxyPacUrl() const override {
+    return std::nullopt;
   }
-  absl::optional<std::string> GetProxyServer() const override {
-    return absl::nullopt;
+  std::optional<std::string> GetProxyServer() const override {
+    return std::nullopt;
   }
-  absl::optional<std::string> GetTargetChannel(
+  std::optional<std::string> GetTargetChannel(
       const std::string& app_id) const override {
     auto value = channels_.find(app_id);
     if (value == channels_.end())
-      return absl::nullopt;
+      return std::nullopt;
     return value->second;
   }
   void SetChannel(const std::string& app_id, std::string channel) {
     channels_[app_id] = std::move(channel);
   }
-  absl::optional<std::vector<std::string>> GetForceInstallApps()
-      const override {
-    return absl::nullopt;
+  std::optional<std::vector<std::string>> GetForceInstallApps() const override {
+    return std::nullopt;
   }
-  absl::optional<std::vector<std::string>> GetAppsWithPolicy() const override {
+  std::optional<std::vector<std::string>> GetAppsWithPolicy() const override {
     std::set<std::string> apps_with_policy;
     for (const auto& policy_entry : update_policies_) {
       apps_with_policy.insert(policy_entry.first);
@@ -219,23 +217,23 @@
       policy_service->GetTargetChannel("app1");
   ASSERT_TRUE(app1_channel);
   EXPECT_EQ(app1_channel.policy(), "test_channel");
-  EXPECT_EQ(app1_channel.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(app1_channel.conflict_policy(), std::nullopt);
 
   PolicyStatus<std::string> app2_channel =
       policy_service->GetTargetChannel("app2");
   EXPECT_FALSE(app2_channel);
-  EXPECT_EQ(app2_channel.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(app2_channel.conflict_policy(), std::nullopt);
 
   PolicyStatus<int> app1_update_status =
       policy_service->GetPolicyForAppUpdates("app1");
   EXPECT_FALSE(app1_update_status);
-  EXPECT_EQ(app1_update_status.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(app1_update_status.conflict_policy(), std::nullopt);
 
   PolicyStatus<int> app2_update_status =
       policy_service->GetPolicyForAppUpdates("app2");
   EXPECT_TRUE(app2_update_status);
   EXPECT_EQ(app2_update_status.policy(), 3);
-  EXPECT_EQ(app2_update_status.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(app2_update_status.conflict_policy(), std::nullopt);
 }
 
 TEST(PolicyService, MultiplePolicyManagers) {
@@ -322,7 +320,7 @@
   EXPECT_EQ(app2_update_policy.source, "group_policy");
   EXPECT_EQ(app2_update_policy.policy, 1);
   EXPECT_EQ(app2_update_status.policy(), 1);
-  EXPECT_EQ(app2_update_status.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(app2_update_status.conflict_policy(), std::nullopt);
 
   PolicyStatus<std::string> download_preference_status =
       policy_service->GetDownloadPreference();
@@ -332,7 +330,7 @@
   EXPECT_EQ(download_preference_policy.source, "imaginary");
   EXPECT_EQ(download_preference_policy.policy, "cacheable");
   EXPECT_EQ(download_preference_status.policy(), "cacheable");
-  EXPECT_EQ(download_preference_status.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(download_preference_status.conflict_policy(), std::nullopt);
 
   EXPECT_FALSE(policy_service->GetPackageCacheSizeLimitMBytes());
   EXPECT_EQ(policy_service->GetAllPoliciesAsString(),
@@ -434,7 +432,7 @@
   PolicyStatus<int> app2_update_status =
       policy_service->GetPolicyForAppUpdates("app2");
   ASSERT_TRUE(app2_update_status);
-  EXPECT_EQ(app2_update_status.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(app2_update_status.conflict_policy(), std::nullopt);
   const PolicyStatus<int>::Entry& app2_update_status_policy =
       app2_update_status.effective_policy().value();
   EXPECT_EQ(app2_update_status_policy.source, "Default");
@@ -447,7 +445,7 @@
   EXPECT_EQ(download_preference_status.effective_policy().value().source,
             "imaginary");
   EXPECT_EQ(download_preference_status.policy(), "cacheable");
-  EXPECT_EQ(download_preference_status.conflict_policy(), absl::nullopt);
+  EXPECT_EQ(download_preference_status.conflict_policy(), std::nullopt);
 
   EXPECT_FALSE(policy_service->GetPackageCacheSizeLimitMBytes());
 
diff --git a/chrome/updater/policy/win/group_policy_manager.cc b/chrome/updater/policy/win/group_policy_manager.cc
index e0a146bd..edd9690 100644
--- a/chrome/updater/policy/win/group_policy_manager.cc
+++ b/chrome/updater/policy/win/group_policy_manager.cc
@@ -4,11 +4,12 @@
 
 #include "chrome/updater/policy/win/group_policy_manager.h"
 
+#include <userenv.h>
+
+#include <optional>
 #include <ostream>
 #include <string>
 
-#include <userenv.h>
-
 #include "base/check.h"
 #include "base/enterprise_util.h"
 #include "base/functional/bind.h"
@@ -26,7 +27,6 @@
 #include "base/values.h"
 #include "base/win/registry.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -116,7 +116,7 @@
 
 GroupPolicyManager::GroupPolicyManager(
     bool should_take_policy_critical_section,
-    const absl::optional<bool>& override_is_managed_device)
+    const std::optional<bool>& override_is_managed_device)
     : PolicyManager(LoadGroupPolicies(should_take_policy_critical_section)),
       is_managed_device_(override_is_managed_device.value_or(
           base::IsManagedOrEnterpriseDevice())) {}
diff --git a/chrome/updater/policy/win/group_policy_manager.h b/chrome/updater/policy/win/group_policy_manager.h
index d21ecbc23..a08c925f 100644
--- a/chrome/updater/policy/win/group_policy_manager.h
+++ b/chrome/updater/policy/win/group_policy_manager.h
@@ -5,10 +5,10 @@
 #ifndef CHROME_UPDATER_POLICY_WIN_GROUP_POLICY_MANAGER_H_
 #define CHROME_UPDATER_POLICY_WIN_GROUP_POLICY_MANAGER_H_
 
+#include <optional>
 #include <string>
 
 #include "chrome/updater/policy/policy_manager.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -17,7 +17,7 @@
  public:
   GroupPolicyManager(
       bool should_take_policy_critical_section,
-      const absl::optional<bool>& override_is_managed_device = absl::nullopt);
+      const std::optional<bool>& override_is_managed_device = std::nullopt);
   GroupPolicyManager(const GroupPolicyManager&) = delete;
   GroupPolicyManager& operator=(const GroupPolicyManager&) = delete;
 
diff --git a/chrome/updater/policy/win/group_policy_manager_unittest.cc b/chrome/updater/policy/win/group_policy_manager_unittest.cc
index 21163bf..4ae01ecf 100644
--- a/chrome/updater/policy/win/group_policy_manager_unittest.cc
+++ b/chrome/updater/policy/win/group_policy_manager_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/policy/win/group_policy_manager.h"
 
+#include <optional>
 #include <string>
 
 #include "base/memory/ref_counted.h"
@@ -17,7 +18,6 @@
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -59,35 +59,35 @@
   EXPECT_EQ(policy_manager->source(), "Group Policy");
   EXPECT_FALSE(policy_manager->CloudPolicyOverridesPlatformPolicy());
 
-  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetDownloadPreference(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyMode(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyServer(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyPacUrl(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetDownloadPreference(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyMode(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyServer(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyPacUrl(), std::nullopt);
 
   std::string app_id = base::WideToUTF8(TEST_APP_ID);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(app_id),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(
                 "non-exist-app-fallback-to-global"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(app_id),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(
                 "non-exist-app-fallback-to-global"),
-            absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetChannel(app_id), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(app_id), absl::nullopt);
+            std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel(app_id), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(app_id), std::nullopt);
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(app_id),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
 }
 
 TEST_F(GroupPolicyManagerTests, PolicyRead) {
@@ -132,7 +132,7 @@
   EXPECT_TRUE(policy_manager->CloudPolicyOverridesPlatformPolicy());
   EXPECT_EQ(policy_manager->GetLastCheckPeriod(), base::Minutes(480));
 
-  absl::optional<UpdatesSuppressedTimes> suppressed_times =
+  std::optional<UpdatesSuppressedTimes> suppressed_times =
       policy_manager->GetUpdatesSuppressedTimes();
   ASSERT_TRUE(suppressed_times);
   EXPECT_EQ(suppressed_times->start_hour_, 2);
@@ -156,13 +156,13 @@
                 "non-exist-app-fallback-to-global"),
             1);
   EXPECT_EQ(policy_manager->GetTargetChannel(app_id), "beta");
-  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), std::nullopt);
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix(app_id), "55.55.");
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(app_id), true);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
 }
 
 TEST_F(GroupPolicyManagerTests, WrongPolicyValueType) {
@@ -203,35 +203,35 @@
   EXPECT_TRUE(policy_manager->HasActiveDevicePolicies());
 
   EXPECT_FALSE(policy_manager->CloudPolicyOverridesPlatformPolicy());
-  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetDownloadPreference(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyMode(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyServer(), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetProxyPacUrl(), absl::nullopt);
+  EXPECT_EQ(policy_manager->GetLastCheckPeriod(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetUpdatesSuppressedTimes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetDownloadPreference(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheSizeLimitMBytes(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetPackageCacheExpirationTimeDays(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyMode(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyServer(), std::nullopt);
+  EXPECT_EQ(policy_manager->GetProxyPacUrl(), std::nullopt);
 
   std::string app_id = base::WideToUTF8(TEST_APP_ID);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(app_id),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppInstalls(
                 "non-exist-app-fallback-to-global"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(app_id),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->GetEffectivePolicyForAppUpdates(
                 "non-exist-app-fallback-to-global"),
-            absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetChannel(app_id), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), absl::nullopt);
-  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(app_id), absl::nullopt);
+            std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel(app_id), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetChannel("non-exist-app"), std::nullopt);
+  EXPECT_EQ(policy_manager->GetTargetVersionPrefix(app_id), std::nullopt);
   EXPECT_EQ(policy_manager->GetTargetVersionPrefix("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed(app_id),
-            absl::nullopt);
+            std::nullopt);
   EXPECT_EQ(policy_manager->IsRollbackToTargetVersionAllowed("non-exist-app"),
-            absl::nullopt);
+            std::nullopt);
 }
 
 }  // namespace updater
diff --git a/chrome/updater/prefs.cc b/chrome/updater/prefs.cc
index 924814f..9062a4a 100644
--- a/chrome/updater/prefs.cc
+++ b/chrome/updater/prefs.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <functional>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -29,7 +30,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/prefs/pref_service_factory.h"
 #include "components/update_client/update_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -153,7 +153,7 @@
     return nullptr;
   }
 
-  const absl::optional<base::FilePath> global_prefs_dir =
+  const std::optional<base::FilePath> global_prefs_dir =
       GetInstallDirectory(scope);
   if (!global_prefs_dir || !base::CreateDirectory(*global_prefs_dir)) {
     return nullptr;
@@ -180,7 +180,7 @@
 
 scoped_refptr<LocalPrefs> CreateLocalPrefs(UpdaterScope scope) {
   VLOG(2) << __func__;
-  const absl::optional<base::FilePath> local_prefs_dir =
+  const std::optional<base::FilePath> local_prefs_dir =
       GetVersionedInstallDirectory(scope);
   if (!local_prefs_dir || !base::CreateDirectory(*local_prefs_dir)) {
     return nullptr;
diff --git a/chrome/updater/registration_data.h b/chrome/updater/registration_data.h
index 08bf2860e..ed738df 100644
--- a/chrome/updater/registration_data.h
+++ b/chrome/updater/registration_data.h
@@ -5,12 +5,12 @@
 #ifndef CHROME_UPDATER_REGISTRATION_DATA_H_
 #define CHROME_UPDATER_REGISTRATION_DATA_H_
 
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
 #include "base/version.h"
 #include "chrome/updater/constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -63,10 +63,10 @@
   base::FilePath existence_checker_path;
 
   // Date-last-active. The value is the number of days since Jan 1, 2007.
-  absl::optional<int> dla;
+  std::optional<int> dla;
 
   // Date-last-rollcall. The value is the number of days since Jan 1, 2007.
-  absl::optional<int> dlrc;
+  std::optional<int> dlrc;
 
   // Opaque cohort string meaningful to the server.
   std::string cohort;
diff --git a/chrome/updater/remove_uninstalled_apps_task.cc b/chrome/updater/remove_uninstalled_apps_task.cc
index ec6f59b..8cbe6311 100644
--- a/chrome/updater/remove_uninstalled_apps_task.cc
+++ b/chrome/updater/remove_uninstalled_apps_task.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/remove_uninstalled_apps_task.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -26,7 +27,6 @@
 #include "chrome/updater/util/util.h"
 #include "components/prefs/pref_service.h"
 #include "components/update_client/update_client.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -66,12 +66,12 @@
 
 std::vector<PingInfo> GetAppIDsToRemove(
     const std::vector<AppInfo>& apps,
-    base::RepeatingCallback<absl::optional<int>(const std::string&,
-                                                const base::FilePath&)>
+    base::RepeatingCallback<std::optional<int>(const std::string&,
+                                               const base::FilePath&)>
         predicate) {
   std::vector<PingInfo> app_ids_to_remove;
   for (const auto& app : apps) {
-    absl::optional<int> remove_reason = predicate.Run(app.app_id_, app.ecp_);
+    std::optional<int> remove_reason = predicate.Run(app.app_id_, app.ecp_);
     if (remove_reason) {
       app_ids_to_remove.emplace_back(app.app_id_, app.app_version_,
                                      *remove_reason);
diff --git a/chrome/updater/remove_uninstalled_apps_task.h b/chrome/updater/remove_uninstalled_apps_task.h
index 5c0a212..fdf1fe6 100644
--- a/chrome/updater/remove_uninstalled_apps_task.h
+++ b/chrome/updater/remove_uninstalled_apps_task.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_REMOVE_UNINSTALLED_APPS_TASK_H_
 #define CHROME_UPDATER_REMOVE_UNINSTALLED_APPS_TASK_H_
 
+#include <optional>
 #include <string>
 
 #include "base/functional/callback_forward.h"
@@ -12,7 +13,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/sequence_checker.h"
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class FilePath;
@@ -38,8 +38,8 @@
   friend class base::RefCountedThreadSafe<RemoveUninstalledAppsTask>;
   virtual ~RemoveUninstalledAppsTask();
 
-  absl::optional<int> GetUnregisterReason(const std::string& app_id,
-                                          const base::FilePath& ecp) const;
+  std::optional<int> GetUnregisterReason(const std::string& app_id,
+                                         const base::FilePath& ecp) const;
 
   SEQUENCE_CHECKER(sequence_checker_);
   scoped_refptr<Configurator> config_;
diff --git a/chrome/updater/remove_uninstalled_apps_task_posix.cc b/chrome/updater/remove_uninstalled_apps_task_posix.cc
index ce54fbb..bc3f9a9 100644
--- a/chrome/updater/remove_uninstalled_apps_task_posix.cc
+++ b/chrome/updater/remove_uninstalled_apps_task_posix.cc
@@ -7,6 +7,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -14,7 +15,6 @@
 #include "base/logging.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -31,19 +31,19 @@
 
 }  // namespace
 
-absl::optional<int> RemoveUninstalledAppsTask::GetUnregisterReason(
+std::optional<int> RemoveUninstalledAppsTask::GetUnregisterReason(
     const std::string& /*app_id*/,
     const base::FilePath& ecp) const {
   if (ecp.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   if (!base::PathExists(ecp)) {
-    return absl::make_optional(kUninstallPingReasonUninstalled);
+    return std::make_optional(kUninstallPingReasonUninstalled);
   }
   if (scope_ == UpdaterScope::kUser && PathOwnedByRoot(ecp)) {
-    return absl::make_optional(kUninstallPingReasonUserNotAnOwner);
+    return std::make_optional(kUninstallPingReasonUserNotAnOwner);
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 }  // namespace updater
diff --git a/chrome/updater/remove_uninstalled_apps_task_win.cc b/chrome/updater/remove_uninstalled_apps_task_win.cc
index 8cbb1f5..5b8481d 100644
--- a/chrome/updater/remove_uninstalled_apps_task_win.cc
+++ b/chrome/updater/remove_uninstalled_apps_task_win.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/remove_uninstalled_apps_task.h"
 
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -16,20 +17,19 @@
 #include "chrome/updater/util/util.h"
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
-absl::optional<int> RemoveUninstalledAppsTask::GetUnregisterReason(
+std::optional<int> RemoveUninstalledAppsTask::GetUnregisterReason(
     const std::string& app_id,
     const base::FilePath& /*ecp*/) const {
   base::win::RegKey key;
   if (key.Open(IsSystemInstall(scope_) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
                GetAppClientsKey(app_id).c_str(),
                Wow6432(KEY_READ)) == ERROR_FILE_NOT_FOUND) {
-    return absl::make_optional(kUninstallPingReasonUninstalled);
+    return std::make_optional(kUninstallPingReasonUninstalled);
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 }  // namespace updater
diff --git a/chrome/updater/tag.cc b/chrome/updater/tag.cc
index 1fd6b94..2b52221 100644
--- a/chrome/updater/tag.cc
+++ b/chrome/updater/tag.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <cstdint>
 #include <map>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -24,7 +25,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/updater/certificate_tag.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace tagging {
@@ -98,7 +98,7 @@
 constexpr uint8_t kTagMagicUtf8[] = {'G', 'a', 'c', 't', '2', '.',
                                      '0', 'O', 'm', 'a', 'h', 'a'};
 
-absl::optional<AppArgs::NeedsAdmin> ParseNeedsAdminEnum(base::StringPiece str) {
+std::optional<AppArgs::NeedsAdmin> ParseNeedsAdminEnum(base::StringPiece str) {
   if (base::EqualsCaseInsensitiveASCII("false", str))
     return AppArgs::NeedsAdmin::kNo;
 
@@ -108,18 +108,18 @@
   if (base::EqualsCaseInsensitiveASCII("prefers", str))
     return AppArgs::NeedsAdmin::kPrefers;
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-// Returns absl::nullopt if parsing failed.
-absl::optional<bool> ParseBool(base::StringPiece str) {
+// Returns std::nullopt if parsing failed.
+std::optional<bool> ParseBool(base::StringPiece str) {
   if (base::EqualsCaseInsensitiveASCII("false", str))
     return false;
 
   if (base::EqualsCaseInsensitiveASCII("true", str))
     return true;
 
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 // Functor used by associative containers of strings as a case-insensitive ASCII
@@ -196,7 +196,7 @@
 }
 
 ErrorCode ParseFlighting(base::StringPiece value, TagArgs* args) {
-  const absl::optional<bool> flighting = ParseBool(value);
+  const std::optional<bool> flighting = ParseBool(value);
   if (!flighting.has_value())
     return ErrorCode::kGlobal_FlightingValueIsNotABoolean;
 
@@ -214,7 +214,7 @@
   } else if (tristate == 1) {
     args->usage_stats_enable = true;
   } else if (tristate == 2) {
-    args->usage_stats_enable = absl::nullopt;
+    args->usage_stats_enable = std::nullopt;
   } else {
     return ErrorCode::kGlobal_UsageStatsValueIsInvalid;
   }
@@ -331,7 +331,7 @@
 // index to |current_app_index|.
 ErrorCode FindAppIdInTagArgs(base::StringPiece value,
                              TagArgs* args,
-                             absl::optional<size_t>* current_app_index) {
+                             std::optional<size_t>* current_app_index) {
   if (!base::IsStringASCII(value))
     return ErrorCode::kApp_AppIdIsNotValid;
 
@@ -350,7 +350,7 @@
 
 ErrorCode ParseInstallerData(base::StringPiece value,
                              TagArgs* args,
-                             absl::optional<size_t>* current_app_index) {
+                             std::optional<size_t>* current_app_index) {
   if (!current_app_index->has_value())
     return ErrorCode::
         kAppInstallerData_InstallerDataCannotBeSpecifiedBeforeAppId;
@@ -369,7 +369,7 @@
 using ParseInstallerDataAttributeFunPtr =
     ErrorCode (*)(base::StringPiece value,
                   TagArgs* args,
-                  absl::optional<size_t>* current_app_index);
+                  std::optional<size_t>* current_app_index);
 
 using InstallerDataParseTable = std::map<base::StringPiece,
                                          ParseInstallerDataAttributeFunPtr,
@@ -481,7 +481,7 @@
 ErrorCode ParseAppInstallerDataArgs(base::StringPiece app_installer_data_args,
                                     TagArgs* args) {
   // The currently tracked app index to apply installer data to.
-  absl::optional<size_t> current_app_index;
+  std::optional<size_t> current_app_index;
 
   // Installer data is assumed to be URL-encoded, so we don't unescape it.
   bool unescape_value = false;
@@ -553,7 +553,7 @@
   return buffer;
 }
 
-absl::optional<tagging::TagArgs> ParseTagBuffer(
+std::optional<tagging::TagArgs> ParseTagBuffer(
     const std::vector<uint8_t>& tag_buffer) {
   if (tag_buffer.empty()) {
     return {};
@@ -647,7 +647,7 @@
 TagArgs& TagArgs::operator=(TagArgs&&) = default;
 
 ErrorCode Parse(base::StringPiece tag,
-                absl::optional<base::StringPiece> app_installer_data_args,
+                std::optional<base::StringPiece> app_installer_data_args,
                 TagArgs* args) {
   if (!IsValidArgs(tag))
     return ErrorCode::kTagIsInvalid;
@@ -780,13 +780,13 @@
 
 std::string ExeReadTag(const base::FilePath& file) {
   const std::vector<uint8_t> contents = ReadEntireFile(file);
-  absl::optional<tagging::Binary> bin = Binary::Parse(contents);
+  std::optional<tagging::Binary> bin = Binary::Parse(contents);
   if (!bin) {
     LOG(ERROR) << __func__ << ": Could not parse binary: " << file;
     return {};
   }
 
-  absl::optional<base::span<const uint8_t>> tag = bin->tag();
+  std::optional<base::span<const uint8_t>> tag = bin->tag();
   if (!tag) {
     LOG(ERROR) << __func__ << ": No superfluous certificate in file: " << file;
     return {};
@@ -805,7 +805,7 @@
                  int padded_length,
                  const base::FilePath& out_file) {
   const std::vector<uint8_t> contents = ReadEntireFile(in_file);
-  absl::optional<tagging::Binary> bin = tagging::Binary::Parse(contents);
+  std::optional<tagging::Binary> bin = tagging::Binary::Parse(contents);
   if (!bin) {
     LOG(ERROR) << __func__ << ": Could not parse binary: " << in_file;
     return false;
@@ -849,7 +849,7 @@
   return true;
 }
 
-absl::optional<tagging::TagArgs> MsiReadTag(const base::FilePath& filename) {
+std::optional<tagging::TagArgs> MsiReadTag(const base::FilePath& filename) {
   return ParseTagBuffer(ReadFileTail(filename));
 }
 
diff --git a/chrome/updater/tag.h b/chrome/updater/tag.h
index 6fda06c..8560dd2 100644
--- a/chrome/updater/tag.h
+++ b/chrome/updater/tag.h
@@ -6,13 +6,13 @@
 #define CHROME_UPDATER_TAG_H_
 
 #include <cstdint>
+#include <optional>
 #include <ostream>
 #include <string>
 #include <vector>
 
 #include "base/files/file_path.h"
 #include "base/strings/string_piece.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace tagging {
@@ -69,7 +69,7 @@
   std::string install_data_index;
   std::string experiment_labels;
   std::string untrusted_data;
-  absl::optional<NeedsAdmin> needs_admin;
+  std::optional<NeedsAdmin> needs_admin;
 };
 
 std::ostream& operator<<(std::ostream&, const AppArgs::NeedsAdmin&);
@@ -105,9 +105,9 @@
   std::string experiment_labels;
   std::string referral_id;
   std::string language;
-  absl::optional<BrowserType> browser_type;
-  absl::optional<bool> flighting = false;
-  absl::optional<bool> usage_stats_enable;
+  std::optional<BrowserType> browser_type;
+  std::optional<bool> flighting = false;
+  std::optional<bool> usage_stats_enable;
 
   // List of apps to install.
   std::vector<AppArgs> apps;
@@ -242,7 +242,7 @@
 //
 // Note: This method assumes all attribute names are ASCII.
 ErrorCode Parse(base::StringPiece tag,
-                absl::optional<base::StringPiece> app_installer_data_args,
+                std::optional<base::StringPiece> app_installer_data_args,
                 TagArgs* args);
 
 std::string ReadTag(std::vector<uint8_t>::const_iterator begin,
@@ -284,7 +284,7 @@
 // |  s   t                                                          |
 // +-----------------------------------------------------------------+
 // Extracts a tag from the end of the MSI `filename`.
-absl::optional<tagging::TagArgs> MsiReadTag(const base::FilePath& filename);
+std::optional<tagging::TagArgs> MsiReadTag(const base::FilePath& filename);
 
 // Tags `file` with `tag_string` and writes the result to `file` by default, or
 // to `out_file` if `out_file` is provided.
diff --git a/chrome/updater/tag_unittest.cc b/chrome/updater/tag_unittest.cc
index 18f647a..e3b7c1d 100644
--- a/chrome/updater/tag_unittest.cc
+++ b/chrome/updater/tag_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/tag.h"
 
 #include <cstdint>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -15,7 +16,6 @@
 #include "base/strings/string_piece.h"
 #include "chrome/updater/util/unit_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
@@ -120,7 +120,7 @@
 
 void VerifyTagParseSuccess(
     base::StringPiece tag,
-    absl::optional<base::StringPiece> app_installer_data_args,
+    std::optional<base::StringPiece> app_installer_data_args,
     const TagArgs& expected) {
   TagArgs actual;
   ASSERT_EQ(ErrorCode::kSuccess, Parse(tag, app_installer_data_args, &actual));
@@ -130,7 +130,7 @@
 
 void VerifyTagParseFail(
     base::StringPiece tag,
-    absl::optional<base::StringPiece> app_installer_data_args,
+    std::optional<base::StringPiece> app_installer_data_args,
     ErrorCode expected) {
   TagArgs args;
   ASSERT_EQ(expected, Parse(tag, app_installer_data_args, &args));
@@ -149,28 +149,28 @@
   VerifyTagParseFail(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname1=Hello",
-      absl::nullopt, ErrorCode::kUnrecognizedName);
+      std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, AppNameSpaceForValue) {
   VerifyTagParseFail(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname= ",
-      absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+      std::nullopt, ErrorCode::kAttributeMustHaveValue);
 }
 
 TEST(TagParserTest, AppNameEncodedSpaceForValue) {
   VerifyTagParseFail(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname=%20",
-      absl::nullopt, ErrorCode::kApp_AppNameCannotBeWhitespace);
+      std::nullopt, ErrorCode::kApp_AppNameCannotBeWhitespace);
 }
 
 TEST(TagParserTest, AppNameValid) {
   VerifyTagParseSuccess(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname=Test",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithBundleName("Test")
           .WithApp(AppArgsBuilder("d0324988-da8a-49e5-bce5-925fcd04eab7")
@@ -184,7 +184,7 @@
   VerifyTagParseSuccess(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname=Test App",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithBundleName("Test App")
           .WithApp(AppArgsBuilder("d0324988-da8a-49e5-bce5-925fcd04eab7")
@@ -197,7 +197,7 @@
   VerifyTagParseSuccess(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname= T Ap p ",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithBundleName("T Ap p")
           .WithApp(AppArgsBuilder("d0324988-da8a-49e5-bce5-925fcd04eab7")
@@ -210,7 +210,7 @@
   VerifyTagParseSuccess(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname=%20T%20Ap%20p%20",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithBundleName("T Ap p")
           .WithApp(AppArgsBuilder("d0324988-da8a-49e5-bce5-925fcd04eab7")
@@ -223,7 +223,7 @@
   VerifyTagParseSuccess(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname= T Ap p",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithBundleName("T Ap p")
           .WithApp(AppArgsBuilder("d0324988-da8a-49e5-bce5-925fcd04eab7")
@@ -237,7 +237,7 @@
   VerifyTagParseSuccess(
       "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&"
       "appname=%E0%A4%B0%E0%A4%B9%E0%A4%BE",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithBundleName(non_ascii_name)
           .WithApp(AppArgsBuilder("d0324988-da8a-49e5-bce5-925fcd04eab7")
@@ -257,7 +257,7 @@
   tag << "appguid=D0324988-DA8A-49e5-BCE5-925FCD04EAB7&";
   tag << "appname=" << escaped;
   VerifyTagParseSuccess(
-      tag.str(), absl::nullopt,
+      tag.str(), std::nullopt,
       TagArgsBuilder()
           .WithBundleName(non_ascii_name)
           .WithApp(AppArgsBuilder("d0324988-da8a-49e5-bce5-925fcd04eab7")
@@ -268,7 +268,7 @@
 
 TEST(TagParserTest, AppIdValid) {
   VerifyTagParseSuccess(
-      "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B", absl::nullopt,
+      "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B", std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -276,7 +276,7 @@
 }
 
 TEST(TagParserTest, AppIdNotASCII) {
-  VerifyTagParseFail("appguid=रहा", absl::nullopt,
+  VerifyTagParseFail("appguid=रहा", std::nullopt,
                      ErrorCode::kApp_AppIdIsNotValid);
 }
 
@@ -284,13 +284,13 @@
 // strings.
 TEST(TagParserTest, AppIdNotAGuid) {
   VerifyTagParseSuccess(
-      "appguid=non-guid-id", absl::nullopt,
+      "appguid=non-guid-id", std::nullopt,
       TagArgsBuilder().WithApp(AppArgsBuilder("non-guid-id").Build()).Build());
 }
 
 TEST(TagParserTest, AppIdCaseInsensitive) {
   VerifyTagParseSuccess(
-      "appguid=ShouldBeCaseInsensitive", absl::nullopt,
+      "appguid=ShouldBeCaseInsensitive", std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("shouldbecaseinsensitive").Build())
           .Build());
@@ -300,21 +300,21 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "needsadmin=Hello",
-      absl::nullopt, ErrorCode::kApp_NeedsAdminValueIsInvalid);
+      std::nullopt, ErrorCode::kApp_NeedsAdminValueIsInvalid);
 }
 
 TEST(TagParserTest, NeedsAdminSpaceForValue) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "needsadmin= ",
-      absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+      std::nullopt, ErrorCode::kAttributeMustHaveValue);
 }
 
 TEST(TagParserTest, NeedsAdminTrueUpperCaseT) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "needsadmin=True",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithNeedsAdmin(AppArgs::NeedsAdmin::kYes)
@@ -326,7 +326,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "needsadmin=true",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithNeedsAdmin(AppArgs::NeedsAdmin::kYes)
@@ -338,7 +338,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "needsadmin=False",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithNeedsAdmin(AppArgs::NeedsAdmin::kNo)
@@ -350,7 +350,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "needsadmin=false",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithNeedsAdmin(AppArgs::NeedsAdmin::kNo)
@@ -363,42 +363,42 @@
 //
 
 TEST(TagParserTest, AssignmentOnly) {
-  VerifyTagParseFail("=", absl::nullopt, ErrorCode::kUnrecognizedName);
+  VerifyTagParseFail("=", std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, ExtraAssignment1) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1=",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, ExtraAssignment2) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "=usagestats=1",
-      absl::nullopt, ErrorCode::kUnrecognizedName);
+      std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, ExtraAssignment3) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1&=",
-      absl::nullopt, ErrorCode::kUnrecognizedName);
+      std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, ExtraAssignment4) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "=&usagestats=1",
-      absl::nullopt, ErrorCode::kUnrecognizedName);
+      std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, ValueWithoutName) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "=hello",
-      absl::nullopt, ErrorCode::kUnrecognizedName);
+      std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 // Also tests ending extra arguments with '='.
@@ -406,28 +406,28 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=",
-      absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+      std::nullopt, ErrorCode::kAttributeMustHaveValue);
 }
 
 TEST(TagParserTest, NameWithoutValueBeforeNextArgument) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=&client=hello",
-      absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+      std::nullopt, ErrorCode::kAttributeMustHaveValue);
 }
 
 TEST(TagParserTest, NameWithoutArgumentSeparatorAfterIntValue) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1client=hello",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, NameWithoutArgumentSeparatorAfterStringValue) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=yesclient=hello",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, TagHasDoubleAmpersand) {
@@ -446,14 +446,14 @@
 }
 
 TEST(TagParserTest, TagAmpersandOnly) {
-  VerifyTagParseSuccess("&", absl::nullopt, TagArgsBuilder().Build());
+  VerifyTagParseSuccess("&", std::nullopt, TagArgsBuilder().Build());
 }
 
 TEST(TagParserTest, TagBeginsInAmpersand) {
   VerifyTagParseSuccess(
       "&appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -465,7 +465,7 @@
   VerifyTagParseSuccess(
       "&appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1&",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -475,7 +475,7 @@
 
 TEST(TagParserTest, WhitespaceOnly) {
   for (const auto* whitespace : {"", " ", "\t", "\r", "\n", "\r\n"}) {
-    VerifyTagParseSuccess(whitespace, absl::nullopt, TagArgsBuilder().Build());
+    VerifyTagParseSuccess(whitespace, std::nullopt, TagArgsBuilder().Build());
   }
 }
 
@@ -487,7 +487,7 @@
   VerifyTagParseSuccess(
       "&appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -499,7 +499,7 @@
   VerifyTagParseSuccess(
       "&appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1&client=hello",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -512,49 +512,49 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1/other_value=9",
-      absl::nullopt, ErrorCode::kTagIsInvalid);
+      std::nullopt, ErrorCode::kTagIsInvalid);
 }
 
 TEST(TagParserTest, TagHasDoubleQuoteInTheMiddle) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1\"/other_value=9",
-      absl::nullopt, ErrorCode::kTagIsInvalid);
+      std::nullopt, ErrorCode::kTagIsInvalid);
 }
 
 TEST(TagParserTest, TagHasDoubleQuoteInTheMiddleAndNoForwardSlash) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1\"other_value=9",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, TagHasSpaceAndForwardSlashBeforeQuote) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1 /other_value=9",
-      absl::nullopt, ErrorCode::kTagIsInvalid);
+      std::nullopt, ErrorCode::kTagIsInvalid);
 }
 
 TEST(TagParserTest, TagHasForwardSlashBeforeQuote) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1/other_value=9",
-      absl::nullopt, ErrorCode::kTagIsInvalid);
+      std::nullopt, ErrorCode::kTagIsInvalid);
 }
 
 TEST(TagParserTest, AttributeSpecifiedTwice) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1\" \"client=10",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, WhiteSpaceBeforeArgs1) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       " usagestats=1",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617EE50-F91C-4DC1-B937-0969EEF59B0B").Build())
@@ -566,7 +566,7 @@
   VerifyTagParseSuccess(
       "\tappguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617EE50-F91C-4DC1-B937-0969EEF59B0B").Build())
@@ -578,7 +578,7 @@
   VerifyTagParseSuccess(
       "\rappguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617EE50-F91C-4DC1-B937-0969EEF59B0B").Build())
@@ -590,7 +590,7 @@
   VerifyTagParseSuccess(
       "\nappguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B"
       "&usagestats=1",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617EE50-F91C-4DC1-B937-0969EEF59B0B").Build())
@@ -599,31 +599,31 @@
 }
 
 TEST(TagParserTest, WhiteSpaceBeforeArgs5) {
-  VerifyTagParseSuccess("\r\nusagestats=1", absl::nullopt,
+  VerifyTagParseSuccess("\r\nusagestats=1", std::nullopt,
                         TagArgsBuilder().WithUsageStatsEnable(true).Build());
 }
 
 TEST(TagParserTest, ForwardSlash1) {
-  VerifyTagParseFail("/", absl::nullopt, ErrorCode::kTagIsInvalid);
+  VerifyTagParseFail("/", std::nullopt, ErrorCode::kTagIsInvalid);
 }
 
 TEST(TagParserTest, ForwardSlash2) {
   VerifyTagParseFail("/ appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B",
-                     absl::nullopt, ErrorCode::kTagIsInvalid);
+                     std::nullopt, ErrorCode::kTagIsInvalid);
 }
 
 TEST(TagParserTest, BackwardSlash1) {
-  VerifyTagParseFail("\\", absl::nullopt, ErrorCode::kUnrecognizedName);
+  VerifyTagParseFail("\\", std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, BackwardSlash2) {
   VerifyTagParseFail("\\appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B",
-                     absl::nullopt, ErrorCode::kUnrecognizedName);
+                     std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, BackwardSlash3) {
   VerifyTagParseFail("\\ appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B",
-                     absl::nullopt, ErrorCode::kUnrecognizedName);
+                     std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, AppArgsMustHaveValue) {
@@ -634,7 +634,7 @@
         "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&needsadmin",
         "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&installdataindex",
         "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&untrusteddata"}) {
-    VerifyTagParseFail(tag, absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+    VerifyTagParseFail(tag, std::nullopt, ErrorCode::kAttributeMustHaveValue);
   }
 }
 
@@ -646,14 +646,14 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "/usagestats",
-      absl::nullopt, ErrorCode::kTagIsInvalid);
+      std::nullopt, ErrorCode::kTagIsInvalid);
 }
 
 TEST(TagParserTest, UsageStatsOn) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=1",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -665,7 +665,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=0",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -677,7 +677,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=2",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -688,28 +688,28 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=3",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, UsageStatsInvalidNegativeValue) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=-1",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, UsageStatsValueIsString) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "usagestats=true",
-      absl::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
+      std::nullopt, ErrorCode::kGlobal_UsageStatsValueIsInvalid);
 }
 
 TEST(TagParserTest, BundleNameValid) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "bundlename=Google%20Bundle",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -722,21 +722,21 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "bundlename= ",
-      absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+      std::nullopt, ErrorCode::kAttributeMustHaveValue);
 }
 
 TEST(TagParserTest, BundleNameEncodedSpaceForValue) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "bundlename=%20",
-      absl::nullopt, ErrorCode::kGlobal_BundleNameCannotBeWhitespace);
+      std::nullopt, ErrorCode::kGlobal_BundleNameCannotBeWhitespace);
 }
 
 TEST(TagParserTest, BundleNameNotPresentButAppNameIs) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "appname=Google%20Chrome",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithAppName("Google Chrome")
@@ -746,7 +746,7 @@
 }
 TEST(TagParserTest, BundleNameNorAppNamePresent) {
   VerifyTagParseSuccess(
-      "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&", absl::nullopt,
+      "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&", std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -755,7 +755,7 @@
 
 TEST(TagParserTest, BundleNameNotPresentAndNoApp) {
   VerifyTagParseSuccess(
-      "browser=0", absl::nullopt,
+      "browser=0", std::nullopt,
       TagArgsBuilder().WithBrowserType(TagArgs::BrowserType::kUnknown).Build());
 }
 
@@ -763,7 +763,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "iid=98CEC468-9429-4984-AEDE-4F53C6A14869",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -775,7 +775,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "iid=रहा",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -787,7 +787,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "brand=GOOG",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -799,7 +799,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "client=some_partner",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -811,7 +811,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "omahaexperiments=experiment%3DgroupA%7Cexpir",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -823,21 +823,21 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "omahaexperiments= ",
-      absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+      std::nullopt, ErrorCode::kAttributeMustHaveValue);
 }
 
 TEST(TagParserTest, UpdaterExperimentIdEncodedSpaceForValue) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "omahaexperiments=%20",
-      absl::nullopt, ErrorCode::kGlobal_ExperimentLabelsCannotBeWhitespace);
+      std::nullopt, ErrorCode::kGlobal_ExperimentLabelsCannotBeWhitespace);
 }
 
 TEST(TagParserTest, AppExperimentIdValid) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "experiments=experiment%3DgroupA%7Cexpir",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithExperimentLabels("experiment=groupA|expir")
@@ -849,21 +849,21 @@
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "experiments= ",
-      absl::nullopt, ErrorCode::kAttributeMustHaveValue);
+      std::nullopt, ErrorCode::kAttributeMustHaveValue);
 }
 
 TEST(TagParserTest, AppExperimentIdEncodedSpaceForValue) {
   VerifyTagParseFail(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "experiments=%20",
-      absl::nullopt, ErrorCode::kApp_ExperimentLabelsCannotBeWhitespace);
+      std::nullopt, ErrorCode::kApp_ExperimentLabelsCannotBeWhitespace);
 }
 
 TEST(TagParserTest, ReferralIdValid) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "referral=ABCD123",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -875,7 +875,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "ap=developer",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithAp("developer")
@@ -915,14 +915,14 @@
       "appname=TestApp2&"
       "needsadmin=true&"
       "installerdata=Hello%20World",
-      absl::nullopt, ErrorCode::kUnrecognizedName);
+      std::nullopt, ErrorCode::kUnrecognizedName);
 }
 
 TEST(TagParserTest, InstallDataIndexValid) {
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "installdataindex=foobar",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b")
                        .WithInstallDataIndex("foobar")
@@ -945,7 +945,7 @@
     tag << "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&";
     tag << "browser=" << std::get<0>(pair);
     VerifyTagParseSuccess(
-        tag.str(), absl::nullopt,
+        tag.str(), std::nullopt,
         TagArgsBuilder()
             .WithApp(
                 AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -962,7 +962,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "browser=5",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -972,7 +972,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "browser=9",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -984,7 +984,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "lang=en",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -997,7 +997,7 @@
   VerifyTagParseSuccess(
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "lang=foobar",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(
               AppArgsBuilder("8617ee50-f91c-4dc1-b937-0969eef59b0b").Build())
@@ -1010,7 +1010,7 @@
       "appguid=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "appname=TestApp&"
       "appname=TestApp2&",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617EE50-F91C-4DC1-B937-0969EEF59B0B")
                        .WithAppName("TestApp2")
@@ -1023,7 +1023,7 @@
   VerifyTagParseSuccess(
       "APPguID=8617EE50-F91C-4DC1-B937-0969EEF59B0B&"
       "APPNAME=TestApp&",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("8617EE50-F91C-4DC1-B937-0969EEF59B0B")
                        .WithAppName("TestApp")
@@ -1036,7 +1036,7 @@
   VerifyTagParseSuccess(
       "appguid=%7B8617EE50-F91C-4DC1-B937-0969EEF59B0B%7D&"
       "appname=TestApp&",
-      absl::nullopt,
+      std::nullopt,
       TagArgsBuilder()
           .WithApp(AppArgsBuilder("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}")
                        .WithAppName("TestApp")
@@ -1058,7 +1058,7 @@
       "ap=test_ap&"
       "usagestats=1&"
       "browser=2&",
-      absl::nullopt, ErrorCode::kApp_AppIdNotSpecified);
+      std::nullopt, ErrorCode::kApp_AppIdNotSpecified);
 }
 
 // This also tests that the last occurrence of a global extra arg is the one
@@ -1234,7 +1234,7 @@
 
 struct MsiTagTestMsiReadTagTestCase {
   const std::string msi_file_name;
-  const absl::optional<tagging::TagArgs> expected_tag_args;
+  const std::optional<tagging::TagArgs> expected_tag_args;
 };
 
 class MsiTagTestMsiReadTagTest
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h
index b8e1f01..2553f6b 100644
--- a/chrome/updater/test/integration_test_commands.h
+++ b/chrome/updater/test/integration_test_commands.h
@@ -12,7 +12,6 @@
 #include "build/build_config.h"
 #include "chrome/updater/test/integration_tests_impl.h"
 #include "chrome/updater/update_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc
index d940b10..9c5d1ce 100644
--- a/chrome/updater/test/integration_test_commands_system.cc
+++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -4,6 +4,7 @@
 
 #include <cstdlib>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -30,7 +31,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -65,7 +65,7 @@
   void PrintLog() const override { RunCommand("print_log"); }
 
   void CopyLog() const override {
-    const absl::optional<base::FilePath> path =
+    const std::optional<base::FilePath> path =
         GetInstallDirectory(updater_scope_);
     ASSERT_TRUE(path);
     if (path)
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc
index bb4aaa6..c884639 100644
--- a/chrome/updater/test/integration_test_commands_user.cc
+++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -4,6 +4,7 @@
 
 #include <cstdlib>
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -20,7 +21,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater {
@@ -37,7 +37,7 @@
   void PrintLog() const override { updater::test::PrintLog(updater_scope_); }
 
   void CopyLog() const override {
-    absl::optional<base::FilePath> path = GetInstallDirectory(updater_scope_);
+    std::optional<base::FilePath> path = GetInstallDirectory(updater_scope_);
     EXPECT_TRUE(path);
     if (path)
       updater::test::CopyLog(*path);
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index b93bb15..074a69f 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -4,6 +4,7 @@
 
 #include <cstdlib>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -52,7 +53,6 @@
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_LINUX)
@@ -766,7 +766,7 @@
   // Waking the new version should clean up the old.
   ASSERT_NO_FATAL_FAILURE(RunWake(0));
   ASSERT_TRUE(WaitForUpdaterExit());
-  absl::optional<base::FilePath> path = GetInstallDirectory(GetTestScope());
+  std::optional<base::FilePath> path = GetInstallDirectory(GetTestScope());
   ASSERT_TRUE(path);
   int dirs = 0;
   base::FileEnumerator(*path, false, base::FileEnumerator::DIRECTORIES)
@@ -1695,7 +1695,7 @@
 
   // Delete the dmp files generated by this test, so `ExpectNoCrashes` won't
   // complain at TearDown.
-  absl::optional<base::FilePath> database_path(
+  std::optional<base::FilePath> database_path(
       GetCrashDatabasePath(GetTestScope()));
   if (database_path && base::PathExists(*database_path)) {
     base::FileEnumerator(*database_path, true, base::FileEnumerator::FILES,
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index 8b85fdb2..207b78f 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -4,6 +4,7 @@
 
 #include <iostream>
 #include <map>
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -34,7 +35,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/unit_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -59,7 +59,7 @@
 constexpr int kBadCommand = 102;
 
 base::Value ValueFromString(const std::string& values) {
-  absl::optional<base::Value> results_value = base::JSONReader::Read(values);
+  std::optional<base::Value> results_value = base::JSONReader::Read(values);
   EXPECT_TRUE(results_value);
   return results_value->Clone();
 }
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index 5880df5..96a0cf9 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -7,6 +7,7 @@
 #include <cstdint>
 #include <cstdlib>
 #include <memory>
+#include <optional>
 #include <set>
 #include <string>
 #include <utility>
@@ -68,7 +69,6 @@
 #include "crypto/secure_hash.h"
 #include "crypto/sha2.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/re2/src/re2/re2.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -118,7 +118,7 @@
     const base::FilePath& update_file,
     const std::string& run_action,
     const std::string& arguments,
-    const absl::optional<std::string>& file_hash = absl::nullopt) {
+    const std::optional<std::string>& file_hash = std::nullopt) {
   return base::StringPrintf(
       R"(    {)"
       R"(      "appid":"%s",)"
@@ -193,8 +193,8 @@
 void RunUpdaterWithSwitch(const base::Version& version,
                           UpdaterScope scope,
                           const std::string& command,
-                          absl::optional<int> expected_exit_code) {
-  const absl::optional<base::FilePath> installed_executable_path =
+                          std::optional<int> expected_exit_code) {
+  const std::optional<base::FilePath> installed_executable_path =
       GetVersionedInstallDirectory(scope, version)
           ->Append(GetExecutableRelativePath());
   ASSERT_TRUE(installed_executable_path);
@@ -457,7 +457,7 @@
 
 void PrintLog(UpdaterScope scope) {
   std::string contents;
-  absl::optional<base::FilePath> path = GetInstallDirectory(scope);
+  std::optional<base::FilePath> path = GetInstallDirectory(scope);
   EXPECT_TRUE(path);
   if (path &&
       base::ReadFileToString(path->AppendASCII("updater.log"), &contents)) {
@@ -499,7 +499,7 @@
 }
 
 void ExpectNoCrashes(UpdaterScope scope) {
-  absl::optional<base::FilePath> database_path(GetCrashDatabasePath(scope));
+  std::optional<base::FilePath> database_path(GetCrashDatabasePath(scope));
   if (!database_path || !base::PathExists(*database_path)) {
     return;
   }
@@ -648,11 +648,11 @@
 
 void RunCrashMe(UpdaterScope scope) {
   RunUpdaterWithSwitch(base::Version(kUpdaterVersion), scope, kCrashMeSwitch,
-                       absl::nullopt);
+                       std::nullopt);
 }
 
 void RunServer(UpdaterScope scope, int expected_exit_code, bool internal) {
-  const absl::optional<base::FilePath> installed_executable_path =
+  const std::optional<base::FilePath> installed_executable_path =
       GetVersionedInstallDirectory(scope, base::Version(kUpdaterVersion))
           ->Append(GetExecutableRelativePath());
   ASSERT_TRUE(installed_executable_path);
@@ -734,7 +734,7 @@
     EXPECT_EQ(final_update_state.p, *_state_member); \
   }
 #define CHECK_STATE_MEMBER_INT(p)                                      \
-  if (const absl::optional<int> _state_member =                        \
+  if (const std::optional<int> _state_member =                         \
           expected_update_state->FindInt(#p);                          \
       _state_member) {                                                 \
     EXPECT_EQ(static_cast<int>(final_update_state.p), *_state_member); \
@@ -763,7 +763,7 @@
 #undef CHECK_STATE_MEMBER_STRING
   }
 
-  if (const absl::optional<int> expected_result =
+  if (const std::optional<int> expected_result =
           expected_final_values.FindInt("expected_result");
       expected_result) {
     EXPECT_EQ(static_cast<int>(final_result), *expected_result);
@@ -811,7 +811,7 @@
 }
 
 void DeleteUpdaterDirectory(UpdaterScope scope) {
-  absl::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
+  std::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
   ASSERT_TRUE(install_dir);
   ASSERT_TRUE(base::DeletePathRecursively(*install_dir));
 }
@@ -825,7 +825,7 @@
     ASSERT_TRUE(active_version.IsValid()) << "No active updater.";
   }
 
-  absl::optional<base::FilePath> exe_path =
+  std::optional<base::FilePath> exe_path =
       GetUpdaterExecutablePath(scope, active_version);
   ASSERT_TRUE(exe_path.has_value())
       << "No path for active updater. Version: " << active_version;
@@ -833,7 +833,7 @@
 #if BUILDFLAG(IS_LINUX)
   // On Linux, a qualified service makes a full copy of itself, so we have to
   // delete the copy that systemd uses too.
-  absl::optional<base::FilePath> launcher_path =
+  std::optional<base::FilePath> launcher_path =
       GetUpdateServiceLauncherPath(GetTestScope());
   ASSERT_TRUE(launcher_path.has_value()) << "No launcher path.";
   DeleteFile(*launcher_path);
@@ -878,7 +878,7 @@
 }
 
 void FillLog(UpdaterScope scope) {
-  absl::optional<base::FilePath> log = GetLogFilePath(scope);
+  std::optional<base::FilePath> log = GetLogFilePath(scope);
   ASSERT_TRUE(log);
   std::string data = "This test string is used to fill up log space.\n";
   for (int i = 0; i < 1024 * 1024 * 3; i += data.length()) {
@@ -887,7 +887,7 @@
 }
 
 void ExpectLogRotated(UpdaterScope scope) {
-  absl::optional<base::FilePath> log = GetLogFilePath(scope);
+  std::optional<base::FilePath> log = GetLogFilePath(scope);
   ASSERT_TRUE(log);
   EXPECT_TRUE(base::PathExists(log->AddExtension(FILE_PATH_LITERAL(".old"))));
   int64_t size = 0;
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index e427c75b..2149a1ab 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_TEST_INTEGRATION_TESTS_IMPL_H_
 #define CHROME_UPDATER_TEST_INTEGRATION_TESTS_IMPL_H_
 
+#include <optional>
 #include <set>
 #include <string>
 #include <vector>
@@ -16,7 +17,6 @@
 #include "build/build_config.h"
 #include "chrome/updater/test/server.h"
 #include "chrome/updater/update_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 
@@ -218,7 +218,7 @@
          int* exit_code = nullptr);
 
 // Returns the path of the Updater executable.
-absl::optional<base::FilePath> GetInstalledExecutablePath(UpdaterScope scope);
+std::optional<base::FilePath> GetInstalledExecutablePath(UpdaterScope scope);
 
 // Sets up a fake updater on the system at a version lower than the test.
 void SetupFakeUpdaterLowerVersion(UpdaterScope scope);
diff --git a/chrome/updater/test/integration_tests_linux.cc b/chrome/updater/test/integration_tests_linux.cc
index 193b75b..151a0e3 100644
--- a/chrome/updater/test/integration_tests_linux.cc
+++ b/chrome/updater/test/integration_tests_linux.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 <optional>
 #include <string>
 
 #include "base/base_paths.h"
@@ -37,7 +38,6 @@
 #include "chrome/updater/util/util.h"
 #include "components/crx_file/crx_verifier.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater::test {
@@ -51,7 +51,7 @@
 }
 }  // namespace
 
-absl::optional<base::FilePath> GetFakeUpdaterInstallFolderPath(
+std::optional<base::FilePath> GetFakeUpdaterInstallFolderPath(
     UpdaterScope scope,
     const base::Version& version) {
   return GetVersionedInstallDirectory(scope, version);
@@ -62,10 +62,10 @@
   return GetExecutablePath();
 }
 
-absl::optional<base::FilePath> GetInstalledExecutablePath(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetVersionedInstallDirectory(scope);
+std::optional<base::FilePath> GetInstalledExecutablePath(UpdaterScope scope) {
+  std::optional<base::FilePath> path = GetVersionedInstallDirectory(scope);
   if (!path) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return path->Append(GetExecutableRelativePath());
 }
@@ -81,7 +81,7 @@
 }
 
 void Uninstall(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetExecutablePath();
+  std::optional<base::FilePath> path = GetExecutablePath();
   ASSERT_TRUE(path);
   base::CommandLine command_line(*path);
   command_line.AppendSwitch(kUninstallSwitch);
@@ -91,7 +91,7 @@
 }
 
 void ExpectCandidateUninstalled(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetVersionedInstallDirectory(scope);
+  std::optional<base::FilePath> path = GetVersionedInstallDirectory(scope);
   EXPECT_TRUE(path);
   if (path) {
     EXPECT_FALSE(base::PathExists(*path));
@@ -99,7 +99,7 @@
 }
 
 void ExpectInstalled(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetInstalledExecutablePath(scope);
+  std::optional<base::FilePath> path = GetInstalledExecutablePath(scope);
   EXPECT_TRUE(path);
   if (path) {
     EXPECT_TRUE(base::PathExists(*path));
@@ -107,7 +107,7 @@
 }
 
 void Clean(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetInstallDirectory(scope);
+  std::optional<base::FilePath> path = GetInstallDirectory(scope);
   EXPECT_TRUE(path);
   if (path) {
     EXPECT_TRUE(base::DeletePathRecursively(*path));
@@ -120,7 +120,7 @@
 // itself, because it uses the prefs file and writes the log file while it
 // is operating. If the provided path exists, it must be a directory with
 // only these residual files present to be considered "clean".
-void ExpectMostlyClean(const absl::optional<base::FilePath>& path) {
+void ExpectMostlyClean(const std::optional<base::FilePath>& path) {
   EXPECT_TRUE(path);
   if (!path || !base::PathExists(*path)) {
     return;
@@ -162,7 +162,7 @@
 }
 
 void SetActive(UpdaterScope scope, const std::string& app_id) {
-  const absl::optional<base::FilePath> path =
+  const std::optional<base::FilePath> path =
       GetActiveFile(base::GetHomeDir(), app_id);
   ASSERT_TRUE(path);
   base::File::Error err = base::File::FILE_OK;
@@ -172,7 +172,7 @@
 }
 
 void ExpectActive(UpdaterScope scope, const std::string& app_id) {
-  const absl::optional<base::FilePath> path =
+  const std::optional<base::FilePath> path =
       GetActiveFile(base::GetHomeDir(), app_id);
   ASSERT_TRUE(path);
   EXPECT_TRUE(base::PathExists(*path));
@@ -180,7 +180,7 @@
 }
 
 void ExpectNotActive(UpdaterScope scope, const std::string& app_id) {
-  const absl::optional<base::FilePath> path =
+  const std::optional<base::FilePath> path =
       GetActiveFile(base::GetHomeDir(), app_id);
   ASSERT_TRUE(path);
   EXPECT_FALSE(base::PathExists(*path));
diff --git a/chrome/updater/test/integration_tests_mac.mm b/chrome/updater/test/integration_tests_mac.mm
index 4ddaac4..0607e88 100644
--- a/chrome/updater/test/integration_tests_mac.mm
+++ b/chrome/updater/test/integration_tests_mac.mm
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <stdint.h>
+#include <cstdint>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -43,7 +44,6 @@
 #include "chrome/updater/util/util.h"
 #include "components/crx_file/crx_verifier.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater::test {
@@ -56,13 +56,13 @@
   return out_dir.Append(GetExecutableRelativePath());
 }
 
-absl::optional<base::FilePath> GetActiveFile(UpdaterScope /*scope*/,
-                                             const std::string& id) {
+std::optional<base::FilePath> GetActiveFile(UpdaterScope /*scope*/,
+                                            const std::string& id) {
   // The active user is always managed in the updater scope for the user.
-  const absl::optional<base::FilePath> path =
+  const std::optional<base::FilePath> path =
       GetLibraryFolderPath(UpdaterScope::kUser);
   if (!path)
-    return absl::nullopt;
+    return std::nullopt;
 
   return path->AppendASCII(COMPANY_SHORTNAME_STRING)
       .AppendASCII(COMPANY_SHORTNAME_STRING "SoftwareUpdate")
@@ -97,7 +97,7 @@
 void Clean(UpdaterScope scope) {
   CleanProcesses();
 
-  absl::optional<base::FilePath> path = GetInstallDirectory(scope);
+  std::optional<base::FilePath> path = GetInstallDirectory(scope);
   EXPECT_TRUE(path);
   if (path) {
     EXPECT_TRUE(base::DeletePathRecursively(*path));
@@ -110,13 +110,13 @@
     EXPECT_TRUE(base::DeletePathRecursively(*path));
   }
 
-  absl::optional<base::FilePath> keystone_path = GetKeystoneFolderPath(scope);
+  std::optional<base::FilePath> keystone_path = GetKeystoneFolderPath(scope);
   EXPECT_TRUE(keystone_path);
   if (keystone_path) {
     EXPECT_TRUE(base::DeletePathRecursively(*keystone_path));
   }
 
-  absl::optional<base::FilePath> cache_path = GetCacheBaseDirectory(scope);
+  std::optional<base::FilePath> cache_path = GetCacheBaseDirectory(scope);
   EXPECT_TRUE(cache_path);
   if (cache_path) {
     EXPECT_TRUE(base::DeletePathRecursively(*cache_path));
@@ -149,13 +149,13 @@
 
   // Caches must have been removed. On Mac, this is separate from other
   // updater directories, so we can reliably remove it completely.
-  absl::optional<base::FilePath> cache_path = GetCacheBaseDirectory(scope);
+  std::optional<base::FilePath> cache_path = GetCacheBaseDirectory(scope);
   EXPECT_TRUE(cache_path);
   if (cache_path) {
     EXPECT_FALSE(base::PathExists(*cache_path));
   }
 
-  absl::optional<base::FilePath> path = GetInstallDirectory(scope);
+  std::optional<base::FilePath> path = GetInstallDirectory(scope);
   EXPECT_TRUE(path);
   if (path && base::PathExists(*path)) {
     // If the path exists, then expect only the log and json files to be
@@ -178,7 +178,7 @@
     }
   }
   // Keystone must not exist on the file system.
-  absl::optional<base::FilePath> keystone_path = GetKeystoneFolderPath(scope);
+  std::optional<base::FilePath> keystone_path = GetKeystoneFolderPath(scope);
   EXPECT_TRUE(keystone_path);
   if (keystone_path) {
     EXPECT_FALSE(
@@ -187,7 +187,7 @@
 }
 
 void ExpectInstalled(UpdaterScope scope) {
-  absl::optional<base::FilePath> keystone_path = GetKeystoneFolderPath(scope);
+  std::optional<base::FilePath> keystone_path = GetKeystoneFolderPath(scope);
   ASSERT_TRUE(keystone_path);
 
   // Files must exist on the file system.
@@ -200,19 +200,19 @@
   EXPECT_TRUE(base::PathExists(*GetWakeTaskPlistPath(scope)));
 }
 
-absl::optional<base::FilePath> GetInstalledExecutablePath(UpdaterScope scope) {
+std::optional<base::FilePath> GetInstalledExecutablePath(UpdaterScope scope) {
   return GetUpdaterExecutablePath(scope);
 }
 
 void ExpectCandidateUninstalled(UpdaterScope scope) {
-  absl::optional<base::FilePath> versioned_folder_path =
+  std::optional<base::FilePath> versioned_folder_path =
       GetVersionedInstallDirectory(scope);
   ASSERT_TRUE(versioned_folder_path);
   EXPECT_FALSE(base::PathExists(*versioned_folder_path));
 }
 
 void Uninstall(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetExecutablePath();
+  std::optional<base::FilePath> path = GetExecutablePath();
   ASSERT_TRUE(path);
   base::CommandLine command_line(*path);
   command_line.AppendSwitch(kUninstallSwitch);
@@ -222,7 +222,7 @@
 }
 
 void SetActive(UpdaterScope scope, const std::string& app_id) {
-  const absl::optional<base::FilePath> path = GetActiveFile(scope, app_id);
+  const std::optional<base::FilePath> path = GetActiveFile(scope, app_id);
   ASSERT_TRUE(path);
   VLOG(0) << "Actives file: " << *path;
   base::File::Error err = base::File::FILE_OK;
@@ -232,14 +232,14 @@
 }
 
 void ExpectActive(UpdaterScope scope, const std::string& app_id) {
-  const absl::optional<base::FilePath> path = GetActiveFile(scope, app_id);
+  const std::optional<base::FilePath> path = GetActiveFile(scope, app_id);
   ASSERT_TRUE(path);
   EXPECT_TRUE(base::PathExists(*path));
   EXPECT_TRUE(base::PathIsWritable(*path));
 }
 
 void ExpectNotActive(UpdaterScope scope, const std::string& app_id) {
-  const absl::optional<base::FilePath> path = GetActiveFile(scope, app_id);
+  const std::optional<base::FilePath> path = GetActiveFile(scope, app_id);
   ASSERT_TRUE(path);
   EXPECT_FALSE(base::PathExists(*path));
   EXPECT_FALSE(base::PathIsWritable(*path));
diff --git a/chrome/updater/test/integration_tests_win.cc b/chrome/updater/test/integration_tests_win.cc
index ac3cc28..0fa2157 100644
--- a/chrome/updater/test/integration_tests_win.cc
+++ b/chrome/updater/test/integration_tests_win.cc
@@ -11,6 +11,7 @@
 #include <functional>
 #include <iostream>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -82,7 +83,6 @@
 #include "components/crx_file/crx_verifier.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 namespace updater::test {
@@ -397,7 +397,7 @@
         });
   }
 
-  const absl::optional<base::FilePath> path =
+  const std::optional<base::FilePath> path =
       GetVersionedInstallDirectory(scope, base::Version(kUpdaterVersion));
   ASSERT_TRUE(path);
   EXPECT_TRUE(WaitFor([&]() { return is_installed == base::PathExists(*path); },
@@ -619,7 +619,7 @@
 
   EXPECT_TRUE(DeleteRegKey(root, app_client_state_key));
 
-  const absl::optional<base::FilePath> updater_exe =
+  const std::optional<base::FilePath> updater_exe =
       GetUpdaterExecutablePath(scope);
   ASSERT_TRUE(updater_exe.has_value());
 
@@ -829,12 +829,12 @@
         EXPECT_TRUE(task_scheduler->DeleteTask(task_name));
       });
 
-  const absl::optional<base::FilePath> target_path =
+  const std::optional<base::FilePath> target_path =
       GetGoogleUpdateExePath(scope);
   if (target_path)
     base::DeleteFile(*target_path);
 
-  absl::optional<base::FilePath> path = GetInstallDirectory(scope);
+  std::optional<base::FilePath> path = GetInstallDirectory(scope);
   ASSERT_TRUE(path);
   ASSERT_TRUE(base::DeletePathRecursively(*path)) << *path;
 }
@@ -867,7 +867,7 @@
                     CheckInstallationVersions::kCheckActiveAndSxS);
 
   // Check that the caches have been removed.
-  const absl::optional<base::FilePath> path = GetCacheBaseDirectory(scope);
+  const std::optional<base::FilePath> path = GetCacheBaseDirectory(scope);
   ASSERT_TRUE(path);
   EXPECT_TRUE(WaitFor(
       [&]() { return !base::PathExists(*path); },
@@ -1679,7 +1679,7 @@
 }
 
 void RunHandoff(UpdaterScope scope, const std::string& app_id) {
-  const absl::optional<base::FilePath> installed_executable_path =
+  const std::optional<base::FilePath> installed_executable_path =
       GetUpdaterExecutablePath(scope);
   ASSERT_TRUE(installed_executable_path);
   ASSERT_TRUE(base::PathExists(*installed_executable_path));
@@ -1802,14 +1802,14 @@
 
   // Set up a mock `GoogleUpdate.exe`, and the following mock directories:
   // `Download`, `Install`, and a versioned `1.2.3.4` directory.
-  const absl::optional<base::FilePath> google_update_exe =
+  const std::optional<base::FilePath> google_update_exe =
       GetGoogleUpdateExePath(scope);
   ASSERT_TRUE(google_update_exe.has_value());
   SetupMockUpdater(google_update_exe.value());
 }
 
 void RunFakeLegacyUpdater(UpdaterScope scope) {
-  const absl::optional<base::FilePath> google_update_exe =
+  const std::optional<base::FilePath> google_update_exe =
       GetGoogleUpdateExePath(scope);
   ASSERT_TRUE(base::PathExists(*google_update_exe));
 
@@ -1945,7 +1945,7 @@
 
   // Expect only a single file `GoogleUpdate.exe` and nothing else under
   // `\Google\Update`.
-  const absl::optional<base::FilePath> google_update_exe =
+  const std::optional<base::FilePath> google_update_exe =
       GetGoogleUpdateExePath(scope);
   ASSERT_TRUE(google_update_exe.has_value());
   ExpectOnlyMockUpdater(google_update_exe.value());
diff --git a/chrome/updater/test/request_matcher.cc b/chrome/updater/test/request_matcher.cc
index c189e867..562ff3c 100644
--- a/chrome/updater/test/request_matcher.cc
+++ b/chrome/updater/test/request_matcher.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/test/request_matcher.h"
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -20,7 +21,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/re2/src/re2/re2.h"
 
 namespace updater::test::request {
@@ -99,7 +99,7 @@
 Matcher GetScopeMatcher(UpdaterScope scope) {
   return base::BindLambdaForTesting([scope](const HttpRequest& request) {
     const bool is_match = [&scope, &request]() {
-      const absl::optional<base::Value> doc =
+      const std::optional<base::Value> doc =
           base::JSONReader::Read(request.decoded_content);
       if (!doc || !doc->is_dict()) {
         return false;
@@ -109,7 +109,7 @@
       if (!object_request) {
         return false;
       }
-      absl::optional<bool> ismachine = object_request->FindBool("ismachine");
+      std::optional<bool> ismachine = object_request->FindBool("ismachine");
       if (!ismachine.has_value()) {
         return false;
       }
@@ -133,7 +133,7 @@
   return base::BindLambdaForTesting(
       [app_id, priority](const HttpRequest& request) {
         const bool is_match = [&app_id, priority, &request]() {
-          const absl::optional<base::Value> doc =
+          const std::optional<base::Value> doc =
               base::JSONReader::Read(request.decoded_content);
           if (!doc || !doc->is_dict()) {
             return false;
diff --git a/chrome/updater/test/test_installer/main.cc b/chrome/updater/test/test_installer/main.cc
index d2b20ec..580040e 100644
--- a/chrome/updater/test/test_installer/main.cc
+++ b/chrome/updater/test/test_installer/main.cc
@@ -8,6 +8,8 @@
 // All command lines arguments are forwarded to the child process.
 
 #include <windows.h>
+
+#include <optional>
 #include <string>
 
 #include "base/command_line.h"
@@ -15,7 +17,6 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/process/launch.h"
 #include "chrome/updater/win/installer/pe_resource.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace {
 
@@ -45,7 +46,7 @@
   return {};
 }
 
-absl::optional<int> RunScript(const base::FilePath& script_path) {
+std::optional<int> RunScript(const base::FilePath& script_path) {
   // Copy current process's command line so all arguments are forwarded.
   base::CommandLine command = *base::CommandLine::ForCurrentProcess();
   command.SetProgram(script_path);
@@ -53,11 +54,11 @@
   int exit_code = -1;
   return base::LaunchProcess(command, {})
                  .WaitForExitWithTimeout(base::Minutes(1), &exit_code)
-             ? absl::make_optional(exit_code)
-             : absl::nullopt;
+             ? std::make_optional(exit_code)
+             : std::nullopt;
 }
 
-absl::optional<base::FilePath> CreateScriptFile(
+std::optional<base::FilePath> CreateScriptFile(
     HMODULE module,
     const std::wstring& name,
     const std::wstring& type,
@@ -73,8 +74,8 @@
       working_dir.AppendASCII("TestAppSetup")
           .AddExtension(ExtensionFromResourceName(name));
   return resource.WriteToDisk(script_path.value().c_str())
-             ? absl::make_optional(script_path)
-             : absl::nullopt;
+             ? std::make_optional(script_path)
+             : std::nullopt;
 }
 
 BOOL CALLBACK OnResourceFound(HMODULE module,
diff --git a/chrome/updater/tools/tag_main.cc b/chrome/updater/tools/tag_main.cc
index 290a624..f20e6475 100644
--- a/chrome/updater/tools/tag_main.cc
+++ b/chrome/updater/tools/tag_main.cc
@@ -5,6 +5,7 @@
 #include <cstdint>
 #include <cstdlib>
 #include <iostream>
+#include <optional>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -129,7 +130,7 @@
       if (args.is_exe) {
         return tagging::ExeReadTag(args.in_filename);
       }
-      absl::optional<tagging::TagArgs> tag_args =
+      std::optional<tagging::TagArgs> tag_args =
           tagging::MsiReadTag(args.in_filename);
       return tag_args ? tag_args->tag_string : std::string();
     }();
diff --git a/chrome/updater/update_service_impl.cc b/chrome/updater/update_service_impl.cc
index 7a2b9e18..7e36ee4 100644
--- a/chrome/updater/update_service_impl.cc
+++ b/chrome/updater/update_service_impl.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <map>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -56,7 +57,6 @@
 #include "components/update_client/crx_update_item.h"
 #include "components/update_client/update_client.h"
 #include "components/update_client/update_client_errors.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <winhttp.h>
@@ -108,29 +108,27 @@
     UpdateService::PolicySameVersionUpdate policy_same_version_update,
     const std::vector<std::string>& ids,
     base::OnceCallback<
-        void(const std::vector<absl::optional<update_client::CrxComponent>>&)>
+        void(const std::vector<std::optional<update_client::CrxComponent>>&)>
         callback) {
   VLOG(1) << __func__
           << ". Same version update: " << policy_same_version_update;
   const bool is_foreground = priority == UpdateService::Priority::kForeground;
   auto barrier_callback =
-      base::BarrierCallback<absl::optional<update_client::CrxComponent>>(
+      base::BarrierCallback<std::optional<update_client::CrxComponent>>(
           ids.size(),
           base::BindOnce(
               [](const std::vector<std::string>& ids,
-                 const std::vector<absl::optional<update_client::CrxComponent>>&
+                 const std::vector<std::optional<update_client::CrxComponent>>&
                      unordered) {
                 // Re-order the vector to match the order of `ids`.
-                std::vector<absl::optional<update_client::CrxComponent>>
-                    ordered;
+                std::vector<std::optional<update_client::CrxComponent>> ordered;
                 for (const auto& id : ids) {
                   auto it = std::find_if(
                       unordered.begin(), unordered.end(),
-                      [&id](absl::optional<update_client::CrxComponent> v) {
+                      [&id](std::optional<update_client::CrxComponent> v) {
                         return v && v->app_id == id;
                       });
-                  ordered.push_back(it != unordered.end() ? *it
-                                                          : absl::nullopt);
+                  ordered.push_back(it != unordered.end() ? *it : std::nullopt);
                 }
                 return ordered;
               },
diff --git a/chrome/updater/update_service_impl.h b/chrome/updater/update_service_impl.h
index ec77507..2bbb504 100644
--- a/chrome/updater/update_service_impl.h
+++ b/chrome/updater/update_service_impl.h
@@ -6,6 +6,7 @@
 #define CHROME_UPDATER_UPDATE_SERVICE_IMPL_H_
 
 #include <map>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -16,7 +17,6 @@
 #include "base/sequence_checker.h"
 #include "base/values.h"
 #include "chrome/updater/update_service.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class FilePath;
@@ -153,7 +153,7 @@
     UpdateService::PolicySameVersionUpdate policy_same_version_update,
     const std::vector<std::string>& ids,
     base::OnceCallback<
-        void(const std::vector<absl::optional<update_client::CrxComponent>>&)>
+        void(const std::vector<std::optional<update_client::CrxComponent>>&)>
         callback);
 
 #if BUILDFLAG(IS_WIN)
diff --git a/chrome/updater/update_service_impl_unittest.cc b/chrome/updater/update_service_impl_unittest.cc
index e77870f..ad7d551b 100644
--- a/chrome/updater/update_service_impl_unittest.cc
+++ b/chrome/updater/update_service_impl_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/update_service_impl.h"
 
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -78,7 +79,7 @@
   metadata->SetAPKey("id1", "brand_key");
   metadata->SetBrandCode("id1", "BRND");
 
-  std::vector<absl::optional<update_client::CrxComponent>> crxs;
+  std::vector<std::optional<update_client::CrxComponent>> crxs;
   base::RunLoop loop;
   internal::GetComponents(
       base::MakeRefCounted<Configurator>(nullptr, CreateExternalConstants()),
@@ -86,7 +87,7 @@
       UpdateService::PolicySameVersionUpdate::kNotAllowed,
       {"id1", "id2", "id3", "id4"},
       base::BindLambdaForTesting(
-          [&](const std::vector<absl::optional<update_client::CrxComponent>>&
+          [&](const std::vector<std::optional<update_client::CrxComponent>>&
                   out) {
             crxs = out;
             loop.Quit();
@@ -105,8 +106,8 @@
   const UpdateService::ErrorCategory error_category;
   const int error_code;
   const std::string expected_completion_message;
-  absl::optional<int> extra_code;
-  absl::optional<bool> is_installer_error;
+  std::optional<int> extra_code;
+  std::optional<bool> is_installer_error;
 };
 
 class UpdateServiceImplGetInstallerTextTest
diff --git a/chrome/updater/update_service_internal_impl_qualifying_mac.cc b/chrome/updater/update_service_internal_impl_qualifying_mac.cc
index 062445f..d362046 100644
--- a/chrome/updater/update_service_internal_impl_qualifying_mac.cc
+++ b/chrome/updater/update_service_internal_impl_qualifying_mac.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/update_service_internal_impl_qualifying.h"
 
+#include <optional>
+
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/process/launch.h"
@@ -20,7 +22,7 @@
 // and exit with exit code 0 if so. This function returns true if the launcher
 // exits with 0.
 bool CheckLauncherCanLaunchServer(UpdaterScope scope) {
-  absl::optional<base::FilePath> app_bundle_path =
+  std::optional<base::FilePath> app_bundle_path =
       GetUpdaterAppBundlePath(scope);
   if (!app_bundle_path) {
     VLOG(1) << "No app bundle path.";
diff --git a/chrome/updater/update_usage_stats_task_mac.mm b/chrome/updater/update_usage_stats_task_mac.mm
index c7d48fd..f49b94a1 100644
--- a/chrome/updater/update_usage_stats_task_mac.mm
+++ b/chrome/updater/update_usage_stats_task_mac.mm
@@ -5,6 +5,7 @@
 #include "chrome/updater/update_usage_stats_task.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -15,7 +16,6 @@
 #include "chrome/updater/updater_branding.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/mac_util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
 #include "third_party/crashpad/crashpad/client/settings.h"
 
@@ -55,7 +55,7 @@
 bool OtherAppUsageStatsAllowed(const std::vector<std::string>& app_ids,
                                UpdaterScope scope) {
   if (!IsSystemInstall(scope)) {
-    absl::optional<base::FilePath> application_support_dir =
+    std::optional<base::FilePath> application_support_dir =
         GetApplicationSupportDirectory(UpdaterScope::kUser);
     return application_support_dir &&
            OtherAppUsageStatsAllowedInDir(*application_support_dir);
diff --git a/chrome/updater/update_usage_stats_task_unittest.cc b/chrome/updater/update_usage_stats_task_unittest.cc
index bb079b0d..0605c5f 100644
--- a/chrome/updater/update_usage_stats_task_unittest.cc
+++ b/chrome/updater/update_usage_stats_task_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/updater/update_usage_stats_task.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -28,7 +29,6 @@
 
 #if BUILDFLAG(IS_MAC)
 #include "chrome/updater/util/mac_util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #elif BUILDFLAG(IS_WIN)
 #include "base/strings/sys_string_conversions.h"
 #include "base/win/registry.h"
@@ -43,7 +43,7 @@
 
 #if BUILDFLAG(IS_MAC)
 base::FilePath AppIDToPath(const std::string& app_id) {
-  absl::optional<base::FilePath> application_support_dir =
+  std::optional<base::FilePath> application_support_dir =
       GetApplicationSupportDirectory(UpdaterScope::kUser);
   EXPECT_TRUE(application_support_dir);
   return (*application_support_dir)
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index b71d42ac..db6bc300 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -41,7 +41,6 @@
 #include "chrome/updater/util/util.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/crash/core/common/crash_keys.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_POSIX)
 #include "chrome/updater/ipc/ipc_support.h"
diff --git a/chrome/updater/updater_scope.cc b/chrome/updater/updater_scope.cc
index 22a16470..a34d43b 100644
--- a/chrome/updater/updater_scope.cc
+++ b/chrome/updater/updater_scope.cc
@@ -4,12 +4,13 @@
 
 #include "chrome/updater/updater_scope.h"
 
+#include <optional>
+
 #include "base/command_line.h"
 #include "base/path_service.h"
 #include "build/build_config.h"
 #include "chrome/updater/constants.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "chrome/updater/tag.h"
@@ -27,7 +28,7 @@
 
 bool IsPrefersForCommandLine(const base::CommandLine& command_line) {
 #if BUILDFLAG(IS_WIN)
-  const absl::optional<tagging::TagArgs> tag_args =
+  const std::optional<tagging::TagArgs> tag_args =
       GetTagArgsForCommandLine(command_line).tag_args;
   return tag_args && !tag_args->apps.empty() &&
          tag_args->apps.front().needs_admin &&
@@ -46,7 +47,7 @@
   }
 
   // Assume only one app is present since bundles are not supported.
-  const absl::optional<tagging::TagArgs> tag_args =
+  const std::optional<tagging::TagArgs> tag_args =
       GetTagArgsForCommandLine(command_line).tag_args;
   if (tag_args && !tag_args->apps.empty() &&
       tag_args->apps.front().needs_admin) {
@@ -66,7 +67,7 @@
   // explicitly. This includes command line switches: '/healthcheck', '/regsvc',
   // '/regserver', and '/ping'. In this case, choose system scope if this
   // program is run as a system shim.
-  absl::optional<base::FilePath> system_shim_path =
+  std::optional<base::FilePath> system_shim_path =
       GetGoogleUpdateExePath(UpdaterScope::kSystem);
   base::FilePath exe_path;
   if (system_shim_path && base::PathService::Get(base::FILE_EXE, &exe_path) &&
diff --git a/chrome/updater/util/linux_util.cc b/chrome/updater/util/linux_util.cc
index b165433..5f642143 100644
--- a/chrome/updater/util/linux_util.cc
+++ b/chrome/updater/util/linux_util.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/updater/util/linux_util.h"
 
+#include <optional>
+
 #include "base/base_paths.h"
 #include "base/files/file_path.h"
 #include "base/path_service.h"
@@ -13,7 +15,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/posix_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -31,7 +32,7 @@
   return base::FilePath(base::StrCat({kExecutableName, kExecutableSuffix}));
 }
 
-absl::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope) {
+std::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope) {
   base::FilePath path;
   switch (scope) {
     case UpdaterScope::kUser:
@@ -43,14 +44,13 @@
     case UpdaterScope::kSystem:
       return base::FilePath(kSystemDataPath).Append(GetUpdaterFolderName());
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<base::FilePath> GetUpdateServiceLauncherPath(
-    UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetInstallDirectory(scope);
-  return path ? absl::optional<base::FilePath>(path->AppendASCII(kLauncherName))
-              : absl::nullopt;
+std::optional<base::FilePath> GetUpdateServiceLauncherPath(UpdaterScope scope) {
+  std::optional<base::FilePath> path = GetInstallDirectory(scope);
+  return path ? std::optional<base::FilePath>(path->AppendASCII(kLauncherName))
+              : std::nullopt;
 }
 
 }  // namespace updater
diff --git a/chrome/updater/util/linux_util.h b/chrome/updater/util/linux_util.h
index 6b234282..b0f67809 100644
--- a/chrome/updater/util/linux_util.h
+++ b/chrome/updater/util/linux_util.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_UPDATER_UTIL_LINUX_UTIL_H_
 #define CHROME_UPDATER_UTIL_LINUX_UTIL_H_
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
diff --git a/chrome/updater/util/mac_util.h b/chrome/updater/util/mac_util.h
index 24078f5..e85e6a9 100644
--- a/chrome/updater/util/mac_util.h
+++ b/chrome/updater/util/mac_util.h
@@ -5,10 +5,10 @@
 #ifndef CHROME_UPDATER_UTIL_MAC_UTIL_H_
 #define CHROME_UPDATER_UTIL_MAC_UTIL_H_
 
+#include <optional>
 #include <string>
 
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class FilePath;
@@ -18,23 +18,23 @@
 
 // For user installations returns: the "~/Library" for the logged in user.
 // For system installations returns: "/Library".
-absl::optional<base::FilePath> GetLibraryFolderPath(UpdaterScope scope);
+std::optional<base::FilePath> GetLibraryFolderPath(UpdaterScope scope);
 
 // For user installations returns "~/Library/Application Support" for the
 // logged in user. For system installations returns
 // "/Library/Application Support".
-absl::optional<base::FilePath> GetApplicationSupportDirectory(
+std::optional<base::FilePath> GetApplicationSupportDirectory(
     UpdaterScope scope);
 
 // Returns the path to Keystone's root directory.
-absl::optional<base::FilePath> GetKeystoneFolderPath(UpdaterScope scope);
+std::optional<base::FilePath> GetKeystoneFolderPath(UpdaterScope scope);
 
 // Returns the path to ksadmin, if it is present on the system. Ksadmin may be
 // the shim installed by this updater or a Keystone ksadmin.
-absl::optional<base::FilePath> GetKSAdminPath(UpdaterScope scope);
+std::optional<base::FilePath> GetKSAdminPath(UpdaterScope scope);
 
 // Returns the path to the wake task plist.
-absl::optional<base::FilePath> GetWakeTaskPlistPath(UpdaterScope scope);
+std::optional<base::FilePath> GetWakeTaskPlistPath(UpdaterScope scope);
 
 std::string GetWakeLaunchdName(UpdaterScope scope);
 
@@ -49,8 +49,8 @@
 // Reads the value associated with `key` from the plist at `path`. Returns
 // nullopt if `path` or `key` are empty, if the plist does not contain `key`, or
 // if there are any errors.
-absl::optional<std::string> ReadValueFromPlist(const base::FilePath& path,
-                                               const std::string& key);
+std::optional<std::string> ReadValueFromPlist(const base::FilePath& path,
+                                              const std::string& key);
 
 }  // namespace updater
 
diff --git a/chrome/updater/util/mac_util.mm b/chrome/updater/util/mac_util.mm
index 2c21a6f..df5f845c 100644
--- a/chrome/updater/util/mac_util.mm
+++ b/chrome/updater/util/mac_util.mm
@@ -6,6 +6,8 @@
 
 #import <CoreFoundation/CoreFoundation.h>
 
+#include <optional>
+
 #include "base/apple/bridging.h"
 #include "base/apple/foundation_util.h"
 #include "base/command_line.h"
@@ -27,7 +29,6 @@
 #include "chrome/updater/updater_version.h"
 #include "chrome/updater/util/posix_util.h"
 #include "chrome/updater/util/util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -53,7 +54,7 @@
   }
 }
 
-absl::optional<base::FilePath> GetLibraryFolderPath(UpdaterScope scope) {
+std::optional<base::FilePath> GetLibraryFolderPath(UpdaterScope scope) {
   switch (scope) {
     case UpdaterScope::kUser:
       return base::apple::GetUserLibraryPath();
@@ -62,14 +63,14 @@
       if (!base::apple::GetLocalDirectory(NSLibraryDirectory,
                                           &local_library_path)) {
         VLOG(1) << "Could not get local library path";
-        return absl::nullopt;
+        return std::nullopt;
       }
       return local_library_path;
     }
   }
 }
 
-absl::optional<base::FilePath> GetApplicationSupportDirectory(
+std::optional<base::FilePath> GetApplicationSupportDirectory(
     UpdaterScope scope) {
   base::FilePath path;
   switch (scope) {
@@ -87,22 +88,22 @@
   }
 
   VLOG(1) << "Could not get applications support path";
-  return absl::nullopt;
+  return std::nullopt;
 }
 
-absl::optional<base::FilePath> GetKSAdminPath(UpdaterScope scope) {
-  const absl::optional<base::FilePath> keystone_folder_path =
+std::optional<base::FilePath> GetKSAdminPath(UpdaterScope scope) {
+  const std::optional<base::FilePath> keystone_folder_path =
       GetKeystoneFolderPath(scope);
   if (!keystone_folder_path || !base::PathExists(*keystone_folder_path))
-    return absl::nullopt;
+    return std::nullopt;
   base::FilePath ksadmin_path =
       keystone_folder_path->Append(FILE_PATH_LITERAL(KEYSTONE_NAME ".bundle"))
           .Append(FILE_PATH_LITERAL("Contents"))
           .Append(FILE_PATH_LITERAL("Helpers"))
           .Append(FILE_PATH_LITERAL("ksadmin"));
   if (!base::PathExists(ksadmin_path))
-    return absl::nullopt;
-  return absl::make_optional(ksadmin_path);
+    return std::nullopt;
+  return std::make_optional(ksadmin_path);
 }
 
 std::string GetWakeLaunchdName(UpdaterScope scope) {
@@ -111,7 +112,7 @@
 }
 
 bool RemoveWakeJobFromLaunchd(UpdaterScope scope) {
-  const absl::optional<base::FilePath> path = GetWakeTaskPlistPath(scope);
+  const std::optional<base::FilePath> path = GetWakeTaskPlistPath(scope);
   if (!path) {
     return false;
   }
@@ -158,20 +159,20 @@
   return exit_code <= 1;
 }
 
-absl::optional<base::FilePath> GetExecutableFolderPathForVersion(
+std::optional<base::FilePath> GetExecutableFolderPathForVersion(
     UpdaterScope scope,
     const base::Version& version) {
-  absl::optional<base::FilePath> path =
+  std::optional<base::FilePath> path =
       GetVersionedInstallDirectory(scope, version);
   if (!path)
-    return absl::nullopt;
+    return std::nullopt;
   return path->Append(ExecutableFolderPath());
 }
 
-absl::optional<base::FilePath> GetUpdaterAppBundlePath(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetVersionedInstallDirectory(scope);
+std::optional<base::FilePath> GetUpdaterAppBundlePath(UpdaterScope scope) {
+  std::optional<base::FilePath> path = GetVersionedInstallDirectory(scope);
   if (!path)
-    return absl::nullopt;
+    return std::nullopt;
   return path->Append(
       base::StrCat({PRODUCT_FULLNAME_STRING, kExecutableSuffix, ".app"}));
 }
@@ -181,10 +182,10 @@
       base::StrCat({PRODUCT_FULLNAME_STRING, kExecutableSuffix}));
 }
 
-absl::optional<base::FilePath> GetKeystoneFolderPath(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetLibraryFolderPath(scope);
+std::optional<base::FilePath> GetKeystoneFolderPath(UpdaterScope scope) {
+  std::optional<base::FilePath> path = GetLibraryFolderPath(scope);
   if (!path)
-    return absl::nullopt;
+    return std::nullopt;
   return path->Append(FILE_PATH_LITERAL(COMPANY_SHORTNAME_STRING))
       .Append(FILE_PATH_LITERAL(KEYSTONE_NAME));
 }
@@ -219,36 +220,35 @@
   return true;
 }
 
-absl::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope) {
-  absl::optional<base::FilePath> path = GetLibraryFolderPath(scope);
-  return path ? absl::optional<base::FilePath>(
+std::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope) {
+  std::optional<base::FilePath> path = GetLibraryFolderPath(scope);
+  return path ? std::optional<base::FilePath>(
                     path->Append("Application Support")
                         .Append(GetUpdaterFolderName()))
-              : absl::nullopt;
+              : std::nullopt;
 }
 
-absl::optional<base::FilePath> GetCacheBaseDirectory(UpdaterScope scope) {
+std::optional<base::FilePath> GetCacheBaseDirectory(UpdaterScope scope) {
   base::FilePath caches_path;
   if (!base::apple::GetLocalDirectory(NSCachesDirectory, &caches_path)) {
     VLOG(1) << "Could not get Caches path";
-    return absl::nullopt;
+    return std::nullopt;
   }
-  return absl::optional<base::FilePath>(
+  return std::optional<base::FilePath>(
       caches_path.AppendASCII(MAC_BUNDLE_IDENTIFIER_STRING));
 }
 
-absl::optional<base::FilePath> GetUpdateServiceLauncherPath(
-    UpdaterScope scope) {
-  absl::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
+std::optional<base::FilePath> GetUpdateServiceLauncherPath(UpdaterScope scope) {
+  std::optional<base::FilePath> install_dir = GetInstallDirectory(scope);
   return install_dir
-             ? absl::optional<base::FilePath>(
+             ? std::optional<base::FilePath>(
                    install_dir->Append("Current")
                        .Append(base::StrCat({PRODUCT_FULLNAME_STRING,
                                              kExecutableSuffix, ".app"}))
                        .Append("Contents")
                        .Append("Helpers")
                        .Append("launcher"))
-             : absl::nullopt;
+             : std::nullopt;
 }
 
 bool RemoveQuarantineAttributes(const base::FilePath& updater_bundle_path) {
@@ -264,13 +264,13 @@
   return success;
 }
 
-absl::optional<base::FilePath> GetWakeTaskPlistPath(UpdaterScope scope) {
+std::optional<base::FilePath> GetWakeTaskPlistPath(UpdaterScope scope) {
   @autoreleasepool {
     NSArray* library_paths = NSSearchPathForDirectoriesInDomains(
         NSLibraryDirectory,
         IsSystemInstall(scope) ? NSLocalDomainMask : NSUserDomainMask, YES);
     if ([library_paths count] < 1) {
-      return absl::nullopt;
+      return std::nullopt;
     }
     return base::apple::NSStringToFilePath(library_paths[0])
         .Append(IsSystemInstall(scope) ? "LaunchDaemons" : "LaunchAgents")
@@ -278,10 +278,10 @@
   }
 }
 
-absl::optional<std::string> ReadValueFromPlist(const base::FilePath& path,
-                                               const std::string& key) {
+std::optional<std::string> ReadValueFromPlist(const base::FilePath& path,
+                                              const std::string& key) {
   if (key.empty() || path.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   NSData* data;
   {
@@ -291,7 +291,7 @@
         [NSData dataWithContentsOfFile:base::apple::FilePathToNSString(path)];
   }
   if ([data length] == 0) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   NSDictionary* all_keys = base::apple::ObjCCastStrict<NSDictionary>(
       [NSPropertyListSerialization propertyListWithData:data
@@ -299,13 +299,13 @@
                                                  format:nil
                                                   error:nil]);
   if (all_keys == nil) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   CFStringRef value = base::apple::GetValueFromDictionary<CFStringRef>(
       base::apple::NSToCFPtrCast(all_keys),
       base::SysUTF8ToCFStringRef(key).get());
   if (value == nullptr) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return base::SysCFStringRefToUTF8(value);
 }
diff --git a/chrome/updater/util/posix_util.cc b/chrome/updater/util/posix_util.cc
index 2ce5937..6a52cd80 100644
--- a/chrome/updater/util/posix_util.cc
+++ b/chrome/updater/util/posix_util.cc
@@ -8,6 +8,8 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <optional>
+
 #include "base/files/file.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
@@ -42,7 +44,7 @@
 }  // namespace
 
 // Recursively delete a folder and its contents, returning `true` on success.
-bool DeleteFolder(const absl::optional<base::FilePath>& installed_path) {
+bool DeleteFolder(const std::optional<base::FilePath>& installed_path) {
   if (!installed_path)
     return false;
   if (!base::DeletePathRecursively(*installed_path)) {
diff --git a/chrome/updater/util/posix_util.h b/chrome/updater/util/posix_util.h
index 87c4b0b..7e4f9d266 100644
--- a/chrome/updater/util/posix_util.h
+++ b/chrome/updater/util/posix_util.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_UPDATER_UTIL_POSIX_UTIL_H_
 #define CHROME_UPDATER_UTIL_POSIX_UTIL_H_
 
-#include <third_party/abseil-cpp/absl/types/optional.h>
+#include <optional>
 
 namespace base {
 class FilePath;
@@ -15,14 +15,14 @@
 enum class UpdaterScope;
 
 // Recursively delete a folder and its contents, returning `true` on success.
-bool DeleteFolder(const absl::optional<base::FilePath>& installed_path);
+bool DeleteFolder(const std::optional<base::FilePath>& installed_path);
 
 // Delete this updater's versioned install folder.
 bool DeleteCandidateInstallFolder(UpdaterScope scope);
 
 base::FilePath GetUpdaterFolderName();
 
-absl::optional<base::FilePath> GetUpdateServiceLauncherPath(UpdaterScope scope);
+std::optional<base::FilePath> GetUpdateServiceLauncherPath(UpdaterScope scope);
 
 // Copy a directory, including symlinks.
 bool CopyDir(const base::FilePath& from_path,
diff --git a/chrome/updater/util/unit_test_util.cc b/chrome/updater/util/unit_test_util.cc
index d4b9c152..383e470 100644
--- a/chrome/updater/util/unit_test_util.cc
+++ b/chrome/updater/util/unit_test_util.cc
@@ -6,6 +6,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -44,7 +45,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 #if BUILDFLAG(IS_WIN)
 #include <shlobj.h>
@@ -144,7 +144,7 @@
 void SetupFakeUpdaterInstallFolder(UpdaterScope scope,
                                    const base::Version& version,
                                    bool should_create_updater_executable) {
-  absl::optional<base::FilePath> folder_path =
+  std::optional<base::FilePath> folder_path =
       GetVersionedInstallDirectory(scope, version);
   ASSERT_TRUE(folder_path);
   const base::FilePath updater_executable_path(
@@ -222,7 +222,7 @@
 }
 
 bool DeleteFileAndEmptyParentDirectories(
-    const absl::optional<base::FilePath>& file_path) {
+    const std::optional<base::FilePath>& file_path) {
   struct Local {
     // Deletes recursively `dir` and its parents up, if dir is empty
     // and until one non-empty parent directory is found.
@@ -249,11 +249,11 @@
 }
 
 void InitLoggingForUnitTest(const base::FilePath& log_base_path) {
-  const absl::optional<base::FilePath> log_file_path = [&log_base_path]() {
+  const std::optional<base::FilePath> log_file_path = [&log_base_path]() {
     const base::FilePath dest_dir = GetLogDestinationDir();
     return dest_dir.empty()
-               ? absl::nullopt
-               : absl::make_optional(dest_dir.Append(log_base_path));
+               ? std::nullopt
+               : std::make_optional(dest_dir.Append(log_base_path));
   }();
   if (log_file_path) {
     logging::LoggingSettings settings;
diff --git a/chrome/updater/util/unit_test_util.h b/chrome/updater/util/unit_test_util.h
index 89b56218..5f83353 100644
--- a/chrome/updater/util/unit_test_util.h
+++ b/chrome/updater/util/unit_test_util.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_UTIL_UNIT_TEST_UTIL_H_
 #define CHROME_UPDATER_UTIL_UNIT_TEST_UTIL_H_
 
+#include <optional>
 #include <string>
 
 #include "base/files/file_path.h"
@@ -15,7 +16,6 @@
 #include "base/process/process_iterator.h"
 #include "base/synchronization/waitable_event.h"
 #include "chrome/updater/tag.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class TimeDelta;
@@ -58,7 +58,7 @@
 // - the file does not exist.
 // - the directory is not empty.
 bool DeleteFileAndEmptyParentDirectories(
-    const absl::optional<base::FilePath>& file_path);
+    const std::optional<base::FilePath>& file_path);
 
 // Fetches the path to the ${ISOLATED_OUTDIR} env var.
 // ResultDB reads logs and test artifacts info from there.
diff --git a/chrome/updater/util/unit_test_util_unittest.cc b/chrome/updater/util/unit_test_util_unittest.cc
index cd5c3c54..441c632 100644
--- a/chrome/updater/util/unit_test_util_unittest.cc
+++ b/chrome/updater/util/unit_test_util_unittest.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/updater/util/unit_test_util.h"
 
+#include <optional>
+#include <string>
+#include <vector>
+
 #include "base/base_paths.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
@@ -24,7 +28,6 @@
 #include "chrome/updater/test/integration_tests_impl.h"
 #include "chrome/updater/test_scope.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/re2/src/re2/re2.h"
 
 #if BUILDFLAG(IS_WIN)
@@ -130,7 +133,7 @@
 }
 
 TEST(UnitTestUtil, DeleteFileAndEmptyParentDirectories) {
-  EXPECT_FALSE(DeleteFileAndEmptyParentDirectories(absl::nullopt));
+  EXPECT_FALSE(DeleteFileAndEmptyParentDirectories(std::nullopt));
 
   const base::FilePath path_not_found(FILE_PATH_LITERAL("path-not-found"));
   EXPECT_TRUE(DeleteFileAndEmptyParentDirectories(path_not_found));
diff --git a/chrome/updater/util/util.cc b/chrome/updater/util/util.cc
index 551547d..671acc2 100644
--- a/chrome/updater/util/util.cc
+++ b/chrome/updater/util/util.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/util/util.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -40,7 +41,6 @@
 #include "chrome/updater/updater_branding.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/updater_version.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_LINUX)
@@ -112,65 +112,63 @@
 
 }  // namespace
 
-absl::optional<base::FilePath> GetVersionedInstallDirectory(
+std::optional<base::FilePath> GetVersionedInstallDirectory(
     UpdaterScope scope,
     const base::Version& version) {
-  const absl::optional<base::FilePath> path = GetInstallDirectory(scope);
+  const std::optional<base::FilePath> path = GetInstallDirectory(scope);
   if (!path)
-    return absl::nullopt;
+    return std::nullopt;
   return path->AppendASCII(version.GetString());
 }
 
-absl::optional<base::FilePath> GetVersionedInstallDirectory(
-    UpdaterScope scope) {
+std::optional<base::FilePath> GetVersionedInstallDirectory(UpdaterScope scope) {
   return GetVersionedInstallDirectory(scope, base::Version(kUpdaterVersion));
 }
 
-absl::optional<base::FilePath> GetUpdaterExecutablePath(
+std::optional<base::FilePath> GetUpdaterExecutablePath(
     UpdaterScope scope,
     const base::Version& version) {
-  absl::optional<base::FilePath> path =
+  std::optional<base::FilePath> path =
       GetVersionedInstallDirectory(scope, version);
   if (!path) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return path->Append(GetExecutableRelativePath());
 }
 
 #if !BUILDFLAG(IS_MAC)
-absl::optional<base::FilePath> GetCacheBaseDirectory(UpdaterScope scope) {
+std::optional<base::FilePath> GetCacheBaseDirectory(UpdaterScope scope) {
   return GetInstallDirectory(scope);
 }
 #endif
 
-absl::optional<base::FilePath> GetCrxDiffCacheDirectory(UpdaterScope scope) {
-  const absl::optional<base::FilePath> cache_path(GetCacheBaseDirectory(scope));
+std::optional<base::FilePath> GetCrxDiffCacheDirectory(UpdaterScope scope) {
+  const std::optional<base::FilePath> cache_path(GetCacheBaseDirectory(scope));
   if (!cache_path) {
-    return absl::nullopt;
+    return std::nullopt;
   }
-  return absl::optional<base::FilePath>(cache_path->AppendASCII("crx_cache"));
+  return std::optional<base::FilePath>(cache_path->AppendASCII("crx_cache"));
 }
 
-absl::optional<base::FilePath> GetUpdaterExecutablePath(UpdaterScope scope) {
+std::optional<base::FilePath> GetUpdaterExecutablePath(UpdaterScope scope) {
   return GetUpdaterExecutablePath(scope, base::Version(kUpdaterVersion));
 }
 
-absl::optional<base::FilePath> GetCrashDatabasePath(UpdaterScope scope) {
-  const absl::optional<base::FilePath> path(
-      GetVersionedInstallDirectory(scope));
-  return path ? absl::optional<base::FilePath>(path->AppendASCII("Crashpad"))
-              : absl::nullopt;
+std::optional<base::FilePath> GetCrashDatabasePath(UpdaterScope scope) {
+  const std::optional<base::FilePath> path(GetVersionedInstallDirectory(scope));
+  return path ? std::optional<base::FilePath>(path->AppendASCII("Crashpad"))
+              : std::nullopt;
 }
 
-absl::optional<base::FilePath> EnsureCrashDatabasePath(UpdaterScope scope) {
-  const absl::optional<base::FilePath> database_path(
+std::optional<base::FilePath> EnsureCrashDatabasePath(UpdaterScope scope) {
+  const std::optional<base::FilePath> database_path(
       GetCrashDatabasePath(scope));
   return database_path && base::CreateDirectory(*database_path) ? database_path
-                                                                : absl::nullopt;
+                                                                : std::nullopt;
 }
 
 TagParsingResult::TagParsingResult() = default;
-TagParsingResult::TagParsingResult(absl::optional<tagging::TagArgs> tag_args,
+TagParsingResult::TagParsingResult(std::optional<tagging::TagArgs> tag_args,
                                    tagging::ErrorCode error)
     : tag_args(tag_args), error(error) {}
 TagParsingResult::~TagParsingResult() = default;
@@ -198,10 +196,10 @@
   return GetTagArgsForCommandLine(*base::CommandLine::ForCurrentProcess());
 }
 
-absl::optional<tagging::AppArgs> GetAppArgs(const std::string& app_id) {
-  const absl::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
+std::optional<tagging::AppArgs> GetAppArgs(const std::string& app_id) {
+  const std::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
   if (!tag_args || tag_args->apps.empty()) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   const std::vector<tagging::AppArgs>& apps_args = tag_args->apps;
@@ -209,12 +207,12 @@
       apps_args, [&app_id](const tagging::AppArgs& app_args) {
         return base::EqualsCaseInsensitiveASCII(app_args.app_id, app_id);
       });
-  return it != std::end(apps_args) ? absl::optional<tagging::AppArgs>(*it)
-                                   : absl::nullopt;
+  return it != std::end(apps_args) ? std::optional<tagging::AppArgs>(*it)
+                                   : std::nullopt;
 }
 
 std::string GetDecodedInstallDataFromAppArgs(const std::string& app_id) {
-  const absl::optional<tagging::AppArgs> app_args = GetAppArgs(app_id);
+  const std::optional<tagging::AppArgs> app_args = GetAppArgs(app_id);
   if (!app_args) {
     return std::string();
   }
@@ -232,21 +230,21 @@
 }
 
 std::string GetInstallDataIndexFromAppArgs(const std::string& app_id) {
-  const absl::optional<tagging::AppArgs> app_args = GetAppArgs(app_id);
+  const std::optional<tagging::AppArgs> app_args = GetAppArgs(app_id);
   return app_args ? app_args->install_data_index : std::string();
 }
 
 // The log file is created in DIR_LOCAL_APP_DATA or DIR_ROAMING_APP_DATA.
-absl::optional<base::FilePath> GetLogFilePath(UpdaterScope scope) {
-  const absl::optional<base::FilePath> log_dir = GetInstallDirectory(scope);
+std::optional<base::FilePath> GetLogFilePath(UpdaterScope scope) {
+  const std::optional<base::FilePath> log_dir = GetInstallDirectory(scope);
   if (log_dir) {
     return log_dir->Append(FILE_PATH_LITERAL("updater.log"));
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 void InitLogging(UpdaterScope updater_scope) {
-  absl::optional<base::FilePath> log_file = GetLogFilePath(updater_scope);
+  std::optional<base::FilePath> log_file = GetLogFilePath(updater_scope);
   if (!log_file) {
     LOG(ERROR) << "Error getting base dir.";
     return;
@@ -315,35 +313,35 @@
 }
 
 base::CommandLine GetCommandLineLegacyCompatible() {
-  absl::optional<base::CommandLine> cmd_line =
+  std::optional<base::CommandLine> cmd_line =
       CommandLineForLegacyFormat(::GetCommandLine());
   return cmd_line ? *cmd_line : *base::CommandLine::ForCurrentProcess();
 }
 
 #endif  // BUILDFLAG(IS_WIN)
 
-absl::optional<base::FilePath> WriteInstallerDataToTempFile(
+std::optional<base::FilePath> WriteInstallerDataToTempFile(
     const base::FilePath& directory,
     const std::string& installer_data) {
   VLOG(2) << __func__ << ": " << directory << ": " << installer_data;
 
   if (!base::DirectoryExists(directory))
-    return absl::nullopt;
+    return std::nullopt;
 
   if (installer_data.empty())
-    return absl::nullopt;
+    return std::nullopt;
 
   base::FilePath path;
   base::File file = base::CreateAndOpenTemporaryFileInDir(directory, &path);
   if (!file.IsValid())
-    return absl::nullopt;
+    return std::nullopt;
 
   const std::string installer_data_utf8_bom =
       base::StrCat({kUTF8BOM, installer_data});
   if (file.Write(0, installer_data_utf8_bom.c_str(),
                  installer_data_utf8_bom.length()) == -1) {
     VLOG(2) << __func__ << " file.Write failed";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return path;
@@ -363,7 +361,7 @@
   base::ThreadPoolInstance::Get()->Start(init_params);
 }
 
-bool DeleteExcept(const absl::optional<base::FilePath>& except) {
+bool DeleteExcept(const std::optional<base::FilePath>& except) {
   if (!except) {
     return false;
   }
diff --git a/chrome/updater/util/util.h b/chrome/updater/util/util.h
index 1851795..96635d38 100644
--- a/chrome/updater/util/util.h
+++ b/chrome/updater/util/util.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_UTIL_UTIL_H_
 #define CHROME_UPDATER_UTIL_UTIL_H_
 
+#include <optional>
 #include <ostream>
 #include <string>
 #include <type_traits>
@@ -19,7 +20,6 @@
 #include "build/build_config.h"
 #include "chrome/updater/tag.h"
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
 
@@ -30,11 +30,11 @@
 class Version;
 
 template <class T>
-std::ostream& operator<<(std::ostream& os, const absl::optional<T>& opt) {
+std::ostream& operator<<(std::ostream& os, const std::optional<T>& opt) {
   if (opt.has_value()) {
     return os << opt.value();
   } else {
-    return os << "absl::nullopt";
+    return os << "std::nullopt";
   }
 }
 
@@ -60,33 +60,33 @@
 // executables. For example, on macOS this function may return
 // ~/Library/Google/GoogleUpdater/88.0.4293.0 (/Library for system). Does not
 // create the directory if it does not exist.
-absl::optional<base::FilePath> GetVersionedInstallDirectory(
+std::optional<base::FilePath> GetVersionedInstallDirectory(
     UpdaterScope scope,
     const base::Version& version);
 
 // Simpler form of GetVersionedInstallDirectory for the currently running
 // version of the updater.
-absl::optional<base::FilePath> GetVersionedInstallDirectory(UpdaterScope scope);
+std::optional<base::FilePath> GetVersionedInstallDirectory(UpdaterScope scope);
 
 // Returns the base install directory common to all versions of the updater.
 // Does not create the directory if it does not exist.
-absl::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope);
+std::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope);
 
 // Returns the base path for discardable caches. Deleting a discardable cache
 // between runs of the updater may impair performance, cause a redownload, etc.,
 // but otherwise not interfere with overall updater function. Cache contents
 // should only be stored in subpaths under this path. Does not create the
 // directory if it does not exist.
-absl::optional<base::FilePath> GetCacheBaseDirectory(UpdaterScope scope);
+std::optional<base::FilePath> GetCacheBaseDirectory(UpdaterScope scope);
 
 // Returns the path where CRXes cached for delta updates should be stored,
 // common to all versions of the updater. Does not create the directory if it
 // does not exist.
-absl::optional<base::FilePath> GetCrxDiffCacheDirectory(UpdaterScope scope);
+std::optional<base::FilePath> GetCrxDiffCacheDirectory(UpdaterScope scope);
 
 #if BUILDFLAG(IS_MAC)
 // For example: ~/Library/Google/GoogleUpdater/88.0.4293.0/GoogleUpdater.app
-absl::optional<base::FilePath> GetUpdaterAppBundlePath(UpdaterScope scope);
+std::optional<base::FilePath> GetUpdaterAppBundlePath(UpdaterScope scope);
 #endif  // BUILDFLAG(IS_MAC)
 
 // For user installations:
@@ -95,13 +95,13 @@
 // For system installations:
 // /Library/Google/GoogleUpdater/88.0.4293.0/GoogleUpdater.app/Contents/
 //    MacOS/GoogleUpdater
-absl::optional<base::FilePath> GetUpdaterExecutablePath(
+std::optional<base::FilePath> GetUpdaterExecutablePath(
     UpdaterScope scope,
     const base::Version& version);
 
 // Simpler form of GetUpdaterExecutablePath for the currently running version
 // of the updater.
-absl::optional<base::FilePath> GetUpdaterExecutablePath(UpdaterScope scope);
+std::optional<base::FilePath> GetUpdaterExecutablePath(UpdaterScope scope);
 
 // Returns a relative path to the executable from GetVersionedInstallDirectory.
 // "GoogleUpdater.app/Contents/MacOS/GoogleUpdater" on macOS.
@@ -110,22 +110,22 @@
 
 // Returns the path to the crashpad database directory. The directory is not
 // created if it does not exist.
-absl::optional<base::FilePath> GetCrashDatabasePath(UpdaterScope scope);
+std::optional<base::FilePath> GetCrashDatabasePath(UpdaterScope scope);
 
 // Returns the path to the crashpad database, creating it if it does not exist.
-absl::optional<base::FilePath> EnsureCrashDatabasePath(UpdaterScope scope);
+std::optional<base::FilePath> EnsureCrashDatabasePath(UpdaterScope scope);
 
 // Return the parsed values from --tag command line argument. The functions
 // return {} if there was no tag at all. An error is set if the tag fails to
 // parse.
 struct TagParsingResult {
   TagParsingResult();
-  TagParsingResult(absl::optional<tagging::TagArgs> tag_args,
+  TagParsingResult(std::optional<tagging::TagArgs> tag_args,
                    tagging::ErrorCode error);
   ~TagParsingResult();
   TagParsingResult(const TagParsingResult&);
   TagParsingResult& operator=(const TagParsingResult&);
-  absl::optional<tagging::TagArgs> tag_args;
+  std::optional<tagging::TagArgs> tag_args;
   tagging::ErrorCode error = tagging::ErrorCode::kSuccess;
 };
 
@@ -133,13 +133,13 @@
     const base::CommandLine& command_line);
 TagParsingResult GetTagArgs();
 
-absl::optional<tagging::AppArgs> GetAppArgs(const std::string& app_id);
+std::optional<tagging::AppArgs> GetAppArgs(const std::string& app_id);
 
 std::string GetDecodedInstallDataFromAppArgs(const std::string& app_id);
 
 std::string GetInstallDataIndexFromAppArgs(const std::string& app_id);
 
-absl::optional<base::FilePath> GetLogFilePath(UpdaterScope scope);
+std::optional<base::FilePath> GetLogFilePath(UpdaterScope scope);
 
 // Initializes logging for an executable.
 void InitLogging(UpdaterScope updater_scope);
@@ -170,7 +170,7 @@
 bool UnzipWithExe(const base::FilePath& src_path,
                   const base::FilePath& dest_path);
 
-absl::optional<base::FilePath> GetKeystoneFolderPath(UpdaterScope scope);
+std::optional<base::FilePath> GetKeystoneFolderPath(UpdaterScope scope);
 
 // Read the file at path to confirm that the file at the path has the same
 // permissions as the given permissions mask.
@@ -194,7 +194,7 @@
 // The string must be in format like:
 //   program.exe /switch1 value1 /switch2 /switch3 value3
 // Returns empty if a Chromium style switch is found.
-absl::optional<base::CommandLine> CommandLineForLegacyFormat(
+std::optional<base::CommandLine> CommandLineForLegacyFormat(
     const std::wstring& cmd_string);
 
 // Returns the command line for current process, either in legacy style, or
@@ -205,7 +205,7 @@
 
 // Writes the provided string prefixed with the UTF8 byte order mark to a
 // temporary file. The temporary file is created in the specified `directory`.
-absl::optional<base::FilePath> WriteInstallerDataToTempFile(
+std::optional<base::FilePath> WriteInstallerDataToTempFile(
     const base::FilePath& directory,
     const std::string& installer_data);
 
@@ -219,7 +219,7 @@
 bool WrongUser(UpdaterScope scope);
 
 // Delete everything other than `except` under `except.DirName()`.
-[[nodiscard]] bool DeleteExcept(const absl::optional<base::FilePath>& except);
+[[nodiscard]] bool DeleteExcept(const std::optional<base::FilePath>& except);
 
 }  // namespace updater
 
diff --git a/chrome/updater/util/util_mac_unittest.mm b/chrome/updater/util/util_mac_unittest.mm
index ed21e71..59a8de9 100644
--- a/chrome/updater/util/util_mac_unittest.mm
+++ b/chrome/updater/util/util_mac_unittest.mm
@@ -4,13 +4,14 @@
 
 #include "chrome/updater/util/util.h"
 
+#include <optional>
+
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "chrome/updater/test_scope.h"
 #include "chrome/updater/updater_branding.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -36,7 +37,7 @@
 }
 
 TEST(UtilTest, GetCacheBaseDirectoryTest) {
-  absl::optional<base::FilePath> path(GetCacheBaseDirectory(GetTestScope()));
+  std::optional<base::FilePath> path(GetCacheBaseDirectory(GetTestScope()));
   ASSERT_TRUE(path);
 
   EXPECT_EQ(path->BaseName().value(),
diff --git a/chrome/updater/util/util_unittest.cc b/chrome/updater/util/util_unittest.cc
index b9000ed..e660654 100644
--- a/chrome/updater/util/util_unittest.cc
+++ b/chrome/updater/util/util_unittest.cc
@@ -4,7 +4,9 @@
 
 #include "chrome/updater/util/util.h"
 
+#include <optional>
 #include <sstream>
+#include <string>
 
 #include "base/command_line.h"
 #include "base/files/file_enumerator.h"
@@ -26,7 +28,6 @@
 #include "chrome/updater/test_scope.h"
 #include "chrome/updater/util/unit_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -49,10 +50,10 @@
                                     "a69d9e530f96&appname=TestApp&ap=TestAP");
 
     // Test GetAppArgs.
-    EXPECT_EQ(GetAppArgs("NonExistentAppId"), absl::nullopt);
-    absl::optional<tagging::AppArgs> app_args =
+    EXPECT_EQ(GetAppArgs("NonExistentAppId"), std::nullopt);
+    std::optional<tagging::AppArgs> app_args =
         GetAppArgs("8a69f345-c564-463c-aff1-a69d9e530f96");
-    ASSERT_NE(app_args, absl::nullopt);
+    ASSERT_NE(app_args, std::nullopt);
     EXPECT_STREQ(app_args->app_id.c_str(),
                  "8a69f345-c564-463c-aff1-a69d9e530f96");
     EXPECT_STREQ(app_args->app_name.c_str(), "TestApp");
@@ -71,7 +72,7 @@
       directory.Append(FILE_PATH_LITERAL("NonExistentDirectory")),
       kInstallerData));
 
-  const absl::optional<base::FilePath> installer_data_file =
+  const std::optional<base::FilePath> installer_data_file =
       WriteInstallerDataToTempFile(directory, kInstallerData);
   ASSERT_TRUE(installer_data_file);
 
@@ -102,7 +103,7 @@
 }
 
 TEST(Util, GetCrashDatabasePath) {
-  absl::optional<base::FilePath> crash_database_path(
+  std::optional<base::FilePath> crash_database_path(
       GetCrashDatabasePath(GetTestScope()));
   ASSERT_TRUE(crash_database_path);
   EXPECT_EQ(crash_database_path->BaseName().value(),
@@ -110,7 +111,7 @@
 }
 
 TEST(Util, GetCrxDiffCacheDirectory) {
-  absl::optional<base::FilePath> diff_cache_directory(
+  std::optional<base::FilePath> diff_cache_directory(
       GetCrxDiffCacheDirectory(GetTestScope()));
   ASSERT_TRUE(diff_cache_directory);
   EXPECT_EQ(diff_cache_directory->BaseName().value(),
diff --git a/chrome/updater/util/util_win_unittest.cc b/chrome/updater/util/util_win_unittest.cc
index aa9c5235..940e89f 100644
--- a/chrome/updater/util/util_win_unittest.cc
+++ b/chrome/updater/util/util_win_unittest.cc
@@ -4,17 +4,17 @@
 
 #include "chrome/updater/util/util.h"
 
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "chrome/updater/tag.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
 TEST(UtilTest, CommandLineForLegacyFormat) {
-  absl::optional<base::CommandLine> cmd_line = CommandLineForLegacyFormat(
+  std::optional<base::CommandLine> cmd_line = CommandLineForLegacyFormat(
       L"program.exe /handoff \"appguid={8a69}&appname=Chrome\" /appargs "
       L"\"&appguid={8a69}"
       L"&installerdata=%7B%22homepage%22%3A%22http%3A%2F%2Fwww.google.com%\" "
@@ -35,7 +35,7 @@
 }
 
 TEST(UtilTest, CommandLineForLegacyFormat_Mixed) {
-  absl::optional<base::CommandLine> cmd_line = CommandLineForLegacyFormat(
+  std::optional<base::CommandLine> cmd_line = CommandLineForLegacyFormat(
       L"program.exe --handoff \"appguid={8a69}&appname=Chrome\""
       L"/silent /sessionid {123-456}");
 
@@ -43,7 +43,7 @@
 }
 
 TEST(UtilTest, CommandLineForLegacyFormat_WithArgs) {
-  absl::optional<base::CommandLine> cmd_line = CommandLineForLegacyFormat(
+  std::optional<base::CommandLine> cmd_line = CommandLineForLegacyFormat(
       L"program.exe arg1 /SWITCH1 value1 \"arg2 with space\" /Switch2 /s3");
 
   EXPECT_TRUE(cmd_line);
diff --git a/chrome/updater/util/win_util.cc b/chrome/updater/util/win_util.cc
index b7310d56..ccd92658 100644
--- a/chrome/updater/util/win_util.cc
+++ b/chrome/updater/util/win_util.cc
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <cstdlib>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -67,7 +68,6 @@
 #include "chrome/updater/win/user_info.h"
 #include "chrome/updater/win/win_constants.h"
 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -230,32 +230,32 @@
   }
 }
 
-absl::optional<CSecurityDesc> GetCurrentUserDefaultSecurityDescriptor() {
+std::optional<CSecurityDesc> GetCurrentUserDefaultSecurityDescriptor() {
   CAccessToken token;
   if (!token.GetProcessToken(TOKEN_QUERY))
-    return absl::nullopt;
+    return std::nullopt;
 
   CSecurityDesc security_desc;
   CSid sid_owner;
   if (!token.GetOwner(&sid_owner))
-    return absl::nullopt;
+    return std::nullopt;
 
   security_desc.SetOwner(sid_owner);
   CSid sid_group;
   if (!token.GetPrimaryGroup(&sid_group))
-    return absl::nullopt;
+    return std::nullopt;
 
   security_desc.SetGroup(sid_group);
 
   CDacl dacl;
   if (!token.GetDefaultDacl(&dacl))
-    return absl::nullopt;
+    return std::nullopt;
 
   CSid sid_user;
   if (!token.GetUser(&sid_user))
-    return absl::nullopt;
+    return std::nullopt;
   if (!dacl.AddAllowedAce(sid_user, GENERIC_ALL))
-    return absl::nullopt;
+    return std::nullopt;
 
   security_desc.SetDacl(dacl);
 
@@ -595,14 +595,14 @@
       base::win::ScopedVariant::kEmptyVariant);
 }
 
-absl::optional<base::FilePath> GetGoogleUpdateExePath(UpdaterScope scope) {
+std::optional<base::FilePath> GetGoogleUpdateExePath(UpdaterScope scope) {
   base::FilePath goopdate_base_dir;
   if (!base::PathService::Get(IsSystemInstall(scope)
                                   ? base::DIR_PROGRAM_FILESX86
                                   : base::DIR_LOCAL_APP_DATA,
                               &goopdate_base_dir)) {
     LOG(ERROR) << "Can't retrieve GoogleUpdate base directory.";
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   return goopdate_base_dir.AppendASCII(COMPANY_SHORTNAME_STRING)
@@ -622,7 +622,7 @@
 
 std::wstring BuildMsiCommandLine(
     const std::wstring& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
+    const std::optional<base::FilePath>& installer_data_file,
     const base::FilePath& msi_installer) {
   if (!msi_installer.MatchesExtension(L".msi")) {
     return std::wstring();
@@ -647,7 +647,7 @@
 
 std::wstring BuildExeCommandLine(
     const std::wstring& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
+    const std::optional<base::FilePath>& installer_data_file,
     const base::FilePath& exe_installer) {
   if (!exe_installer.MatchesExtension(L".exe")) {
     return std::wstring();
@@ -699,7 +699,7 @@
   return IsSystemInstall(scope) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
 }
 
-absl::optional<OSVERSIONINFOEX> GetOSVersion() {
+std::optional<OSVERSIONINFOEX> GetOSVersion() {
   // `::RtlGetVersion` is being used here instead of `::GetVersionEx`, because
   // the latter function can return the incorrect version if it is shimmed using
   // an app compat shim.
@@ -707,14 +707,14 @@
   static const RtlGetVersion rtl_get_version = reinterpret_cast<RtlGetVersion>(
       ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "RtlGetVersion"));
   if (!rtl_get_version)
-    return absl::nullopt;
+    return std::nullopt;
 
   OSVERSIONINFOEX os_out = {};
   os_out.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
 
   rtl_get_version(&os_out);
   if (!os_out.dwMajorVersion)
-    return absl::nullopt;
+    return std::nullopt;
 
   return os_out;
 }
@@ -761,7 +761,7 @@
   return true;
 }
 
-absl::optional<base::ScopedTempDir> CreateSecureTempDir() {
+std::optional<base::ScopedTempDir> CreateSecureTempDir() {
   // This function uses `base::CreateNewTempDirectory` and then a
   // `base::ScopedTempDir` as owner, instead of just
   // `base::ScopedTempDir::CreateUniqueTempDir`, because the former allows
@@ -770,14 +770,14 @@
   base::FilePath temp_dir;
   if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL(COMPANY_SHORTNAME_STRING),
                                     &temp_dir)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
 
   base::ScopedTempDir temp_dir_owner;
   if (temp_dir_owner.Set(temp_dir)) {
     return temp_dir_owner;
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 base::ScopedClosureRunner SignalShutdownEvent(UpdaterScope scope) {
@@ -855,13 +855,13 @@
   }
 }
 
-absl::optional<base::CommandLine> CommandLineForLegacyFormat(
+std::optional<base::CommandLine> CommandLineForLegacyFormat(
     const std::wstring& cmd_string) {
   int num_args = 0;
   base::win::ScopedLocalAllocTyped<wchar_t*> args(
       ::CommandLineToArgvW(cmd_string.c_str(), &num_args));
   if (!args)
-    return absl::nullopt;
+    return std::nullopt;
 
   auto is_switch = [](const std::wstring& arg) { return arg[0] == L'-'; };
 
@@ -877,7 +877,7 @@
 
     if (is_switch(args.get()[i]) || is_switch(next_arg)) {
       // Won't parse Chromium-style command line.
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     if (!is_legacy_switch(args.get()[i])) {
@@ -889,7 +889,7 @@
     const std::string switch_name = base::WideToASCII(&args.get()[i][1]);
     if (switch_name.empty()) {
       VLOG(1) << "Empty switch in command line: [" << cmd_string << "]";
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     if (is_legacy_switch(next_arg) || next_arg.empty()) {
@@ -904,13 +904,13 @@
   return command_line;
 }
 
-absl::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope) {
+std::optional<base::FilePath> GetInstallDirectory(UpdaterScope scope) {
   base::FilePath app_data_dir;
   if (!base::PathService::Get(IsSystemInstall(scope) ? base::DIR_PROGRAM_FILES
                                                      : base::DIR_LOCAL_APP_DATA,
                               &app_data_dir)) {
     LOG(ERROR) << "Can't retrieve app data directory.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   return app_data_dir.AppendASCII(COMPANY_SHORTNAME_STRING)
       .AppendASCII(PRODUCT_FULLNAME_STRING);
@@ -1047,20 +1047,20 @@
   }
 }
 
-absl::optional<base::FilePath> GetInstallDirectoryX86(UpdaterScope scope) {
+std::optional<base::FilePath> GetInstallDirectoryX86(UpdaterScope scope) {
   if (!IsSystemInstall(scope)) {
     return GetInstallDirectory(scope);
   }
   base::FilePath install_dir;
   if (!base::PathService::Get(base::DIR_PROGRAM_FILESX86, &install_dir)) {
     LOG(ERROR) << "Can't retrieve directory for DIR_PROGRAM_FILESX86.";
-    return absl::nullopt;
+    return std::nullopt;
   }
   return install_dir.AppendASCII(COMPANY_SHORTNAME_STRING)
       .AppendASCII(PRODUCT_FULLNAME_STRING);
 }
 
-absl::optional<std::wstring> GetRegKeyContents(const std::wstring& reg_key) {
+std::optional<std::wstring> GetRegKeyContents(const std::wstring& reg_key) {
   base::FilePath system_path;
   if (!base::PathService::Get(base::DIR_SYSTEM, &system_path)) {
     return {};
diff --git a/chrome/updater/util/win_util.h b/chrome/updater/util/win_util.h
index ec600531..bfce902b 100644
--- a/chrome/updater/util/win_util.h
+++ b/chrome/updater/util/win_util.h
@@ -10,6 +10,7 @@
 #include <wrl/implements.h>
 
 #include <cstdint>
+#include <optional>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -35,7 +36,6 @@
 #include "base/win/win_util.h"
 #include "base/win/windows_types.h"
 #include "chrome/updater/updater_scope.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class FilePath;
@@ -184,7 +184,7 @@
 // Gets the security descriptor with the default DACL for the current process
 // user. The owner is the current user, the group is the current primary group.
 // Returns security attributes on success, nullopt on failure.
-absl::optional<CSecurityDesc> GetCurrentUserDefaultSecurityDescriptor();
+std::optional<CSecurityDesc> GetCurrentUserDefaultSecurityDescriptor();
 
 // Get security descriptor containing a DACL that grants the ACCESS_MASK access
 // to admins and system.
@@ -294,7 +294,7 @@
 // spawned process.
 HRESULT RunDeElevated(const std::wstring& path, const std::wstring& parameters);
 
-absl::optional<base::FilePath> GetGoogleUpdateExePath(UpdaterScope scope);
+std::optional<base::FilePath> GetGoogleUpdateExePath(UpdaterScope scope);
 
 // Causes the COM runtime not to handle exceptions. Failing to set this
 // up is a critical error, since ignoring exceptions may lead to corrupted
@@ -306,14 +306,14 @@
 // a log file in the same directory as the MSI installer.
 std::wstring BuildMsiCommandLine(
     const std::wstring& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
+    const std::optional<base::FilePath>& installer_data_file,
     const base::FilePath& msi_installer);
 
 // Builds a command line running the provided `exe_installer`, `arguments`, and
 // `installer_data_file`.
 std::wstring BuildExeCommandLine(
     const std::wstring& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
+    const std::optional<base::FilePath>& installer_data_file,
     const base::FilePath& exe_installer);
 
 // Returns `true` if the service specified is currently running or starting.
@@ -325,7 +325,7 @@
 HKEY UpdaterScopeToHKeyRoot(UpdaterScope scope);
 
 // Returns an OSVERSIONINFOEX for the current OS version.
-absl::optional<OSVERSIONINFOEX> GetOSVersion();
+std::optional<OSVERSIONINFOEX> GetOSVersion();
 
 // Compares the current OS to the supplied version.  The value of `oper` should
 // be one of the predicate values from `::VerSetConditionMask()`, for example,
@@ -347,7 +347,7 @@
 
 // Creates a unique temporary directory. The directory is created under a secure
 // location if the caller is admin.
-absl::optional<base::ScopedTempDir> CreateSecureTempDir();
+std::optional<base::ScopedTempDir> CreateSecureTempDir();
 
 // Signals the shutdown event that causes legacy GoogleUpdate processes to exit.
 // Returns a closure that resets the shutdown event when it goes out of scope.
@@ -413,11 +413,11 @@
 
 // Returns the base install directory for the x86 versions of the updater.
 // Does not create the directory if it does not exist.
-[[nodiscard]] absl::optional<base::FilePath> GetInstallDirectoryX86(
+[[nodiscard]] std::optional<base::FilePath> GetInstallDirectoryX86(
     UpdaterScope scope);
 
 // Gets the contents under a given registry key.
-absl::optional<std::wstring> GetRegKeyContents(const std::wstring& reg_key);
+std::optional<std::wstring> GetRegKeyContents(const std::wstring& reg_key);
 
 // Returns the textual description of a system `error` as provided by the
 // operating system. The function assumes that the locale value for the calling
diff --git a/chrome/updater/util/win_util_unittest.cc b/chrome/updater/util/win_util_unittest.cc
index 516ecb99..d3e9c47 100644
--- a/chrome/updater/util/win_util_unittest.cc
+++ b/chrome/updater/util/win_util_unittest.cc
@@ -9,6 +9,7 @@
 #include <shlobj.h>
 #include <windows.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -51,7 +52,6 @@
 #include "chrome/updater/win/test/test_strings.h"
 #include "chrome/updater/win/win_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -200,8 +200,8 @@
 }
 
 TEST(WinUtil, GetOSVersion) {
-  absl::optional<OSVERSIONINFOEX> rtl_os_version = GetOSVersion();
-  ASSERT_NE(rtl_os_version, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> rtl_os_version = GetOSVersion();
+  ASSERT_NE(rtl_os_version, std::nullopt);
 
   // Compare to the version from `::GetVersionEx`.
   OSVERSIONINFOEX os = {};
@@ -224,8 +224,8 @@
 }
 
 TEST(WinUtil, CompareOSVersions_SameAsCurrent) {
-  absl::optional<OSVERSIONINFOEX> this_os = GetOSVersion();
-  ASSERT_NE(this_os, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> this_os = GetOSVersion();
+  ASSERT_NE(this_os, std::nullopt);
 
   EXPECT_TRUE(CompareOSVersions(this_os.value(), VER_EQUAL));
   EXPECT_TRUE(CompareOSVersions(this_os.value(), VER_GREATER_EQUAL));
@@ -235,8 +235,8 @@
 }
 
 TEST(WinUtil, CompareOSVersions_NewBuildNumber) {
-  absl::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
-  ASSERT_NE(prior_os, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
+  ASSERT_NE(prior_os, std::nullopt);
   ASSERT_GT(prior_os->dwBuildNumber, 0UL);
   --prior_os->dwBuildNumber;
 
@@ -248,8 +248,8 @@
 }
 
 TEST(WinUtil, CompareOSVersions_NewMajor) {
-  absl::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
-  ASSERT_NE(prior_os, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
+  ASSERT_NE(prior_os, std::nullopt);
   ASSERT_GT(prior_os->dwMajorVersion, 0UL);
   --prior_os->dwMajorVersion;
 
@@ -261,8 +261,8 @@
 }
 
 TEST(WinUtil, CompareOSVersions_NewMinor) {
-  absl::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
-  ASSERT_NE(prior_os, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
+  ASSERT_NE(prior_os, std::nullopt);
 
   // This test only runs if the current OS has a minor version.
   if (prior_os->dwMinorVersion >= 1) {
@@ -277,8 +277,8 @@
 }
 
 TEST(WinUtil, CompareOSVersions_NewMajorWithLowerMinor) {
-  absl::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
-  ASSERT_NE(prior_os, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
+  ASSERT_NE(prior_os, std::nullopt);
   ASSERT_GT(prior_os->dwMajorVersion, 0UL);
   --prior_os->dwMajorVersion;
   ++prior_os->dwMinorVersion;
@@ -291,8 +291,8 @@
 }
 
 TEST(WinUtil, CompareOSVersions_OldMajor) {
-  absl::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
-  ASSERT_NE(prior_os, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
+  ASSERT_NE(prior_os, std::nullopt);
   ++prior_os->dwMajorVersion;
 
   EXPECT_FALSE(CompareOSVersions(prior_os.value(), VER_EQUAL));
@@ -303,8 +303,8 @@
 }
 
 TEST(WinUtil, CompareOSVersions_OldMajorWithHigherMinor) {
-  absl::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
-  ASSERT_NE(prior_os, absl::nullopt);
+  std::optional<OSVERSIONINFOEX> prior_os = GetOSVersion();
+  ASSERT_NE(prior_os, std::nullopt);
 
   // This test only runs if the current OS has a minor version.
   if (prior_os->dwMinorVersion >= 1) {
@@ -332,7 +332,7 @@
 }
 
 TEST(WinUtil, CreateSecureTempDir) {
-  absl::optional<base::ScopedTempDir> temp_dir = CreateSecureTempDir();
+  std::optional<base::ScopedTempDir> temp_dir = CreateSecureTempDir();
   EXPECT_TRUE(temp_dir);
   EXPECT_TRUE(temp_dir->IsValid());
 }
@@ -552,7 +552,7 @@
     }));
 
 TEST_P(WinUtilGetRegKeyContentsTest, TestCases) {
-  absl::optional<std::wstring> contents = GetRegKeyContents(GetParam().reg_key);
+  std::optional<std::wstring> contents = GetRegKeyContents(GetParam().reg_key);
   ASSERT_TRUE(contents);
   ASSERT_NE(contents->find(GetParam().expected_substring), std::wstring::npos);
 }
diff --git a/chrome/updater/win/app_command_runner.cc b/chrome/updater/win/app_command_runner.cc
index 7a891ef..1c6a1fb 100644
--- a/chrome/updater/win/app_command_runner.cc
+++ b/chrome/updater/win/app_command_runner.cc
@@ -7,6 +7,7 @@
 #include <shellapi.h>
 #include <windows.h>
 
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -28,7 +29,6 @@
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -268,7 +268,7 @@
 }
 
 // static
-absl::optional<std::wstring> AppCommandRunner::FormatParameter(
+std::optional<std::wstring> AppCommandRunner::FormatParameter(
     const std::wstring& parameter,
     const std::vector<std::wstring>& substitutions) {
   return base::internal::DoReplaceStringPlaceholders(
@@ -279,17 +279,17 @@
 }
 
 // static
-absl::optional<std::wstring> AppCommandRunner::FormatAppCommandLine(
+std::optional<std::wstring> AppCommandRunner::FormatAppCommandLine(
     const std::vector<std::wstring>& parameters,
     const std::vector<std::wstring>& substitutions) {
   std::wstring formatted_command_line;
   for (size_t i = 0; i < parameters.size(); ++i) {
-    absl::optional<std::wstring> formatted_parameter =
+    std::optional<std::wstring> formatted_parameter =
         FormatParameter(parameters[i], substitutions);
     if (!formatted_parameter) {
       VLOG(1) << __func__ << " FormatParameter failed: " << parameters[i]
               << ": " << substitutions.size();
-      return absl::nullopt;
+      return std::nullopt;
     }
 
     constexpr wchar_t kQuotableCharacters[] = L" \t\\\"";
@@ -318,7 +318,7 @@
           << base::JoinString(parameters, L",")
           << base::JoinString(substitutions, L",");
 
-  const absl::optional<std::wstring> command_line_parameters =
+  const std::optional<std::wstring> command_line_parameters =
       FormatAppCommandLine(parameters, substitutions);
   if (!command_line_parameters) {
     LOG(ERROR) << __func__ << "!command_line_parameters";
diff --git a/chrome/updater/win/app_command_runner.h b/chrome/updater/win/app_command_runner.h
index 3ddd352..b313774 100644
--- a/chrome/updater/win/app_command_runner.h
+++ b/chrome/updater/win/app_command_runner.h
@@ -7,6 +7,7 @@
 
 #include <windows.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -15,7 +16,6 @@
 #include "base/process/process.h"
 #include "chrome/updater/updater_scope.h"
 #include "chrome/updater/util/win_util.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -68,10 +68,10 @@
   // `parameter` is replaced with substitutions[N - 1]. Any literal `%` needs to
   // be escaped with a `%`.
   //
-  // Returns `absl::nullopt` if:
+  // Returns `std::nullopt` if:
   // * a placeholder %N is encountered where N > substitutions.size().
   // * a literal `%` is not escaped with a `%`.
-  static absl::optional<std::wstring> FormatParameter(
+  static std::optional<std::wstring> FormatParameter(
       const std::wstring& parameter,
       const std::vector<std::wstring>& substitutions);
 
@@ -84,10 +84,10 @@
   // parameter will be interpreted as a single command-line parameter according
   // to the rules for ::CommandLineToArgvW.
   //
-  // Returns `absl::nullopt` if:
+  // Returns `std::nullopt` if:
   // * a placeholder %N is encountered where N > substitutions.size().
   // * a literal `%` is not escaped with a `%`.
-  static absl::optional<std::wstring> FormatAppCommandLine(
+  static std::optional<std::wstring> FormatAppCommandLine(
       const std::vector<std::wstring>& parameters,
       const std::vector<std::wstring>& substitutions);
 
diff --git a/chrome/updater/win/app_command_runner_unittest.cc b/chrome/updater/win/app_command_runner_unittest.cc
index 79d3050..257f5f1 100644
--- a/chrome/updater/win/app_command_runner_unittest.cc
+++ b/chrome/updater/win/app_command_runner_unittest.cc
@@ -9,6 +9,7 @@
 #include <windows.h>
 
 #include <array>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -29,7 +30,6 @@
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -198,12 +198,12 @@
     }));
 
 TEST_P(AppCommandFormatParameterTest, TestCases) {
-  absl::optional<std::wstring> output = AppCommandRunner::FormatParameter(
+  std::optional<std::wstring> output = AppCommandRunner::FormatParameter(
       GetParam().format_string, GetParam().substitutions);
   if (GetParam().expected_output) {
     EXPECT_EQ(output.value(), GetParam().expected_output);
   } else {
-    EXPECT_EQ(output, absl::nullopt);
+    EXPECT_EQ(output, std::nullopt);
   }
 }
 
@@ -316,11 +316,11 @@
             base::CommandLine::FromString(process_command_line).GetProgram());
   EXPECT_EQ(parameters.size(), GetParam().input.size());
 
-  absl::optional<std::wstring> command_line =
+  std::optional<std::wstring> command_line =
       AppCommandRunner::FormatAppCommandLine(parameters,
                                              GetParam().substitutions);
   if (!GetParam().output) {
-    EXPECT_EQ(command_line, absl::nullopt);
+    EXPECT_EQ(command_line, std::nullopt);
     return;
   }
 
diff --git a/chrome/updater/win/installer/installer.cc b/chrome/updater/win/installer/installer.cc
index 649d8af..f84374b 100644
--- a/chrome/updater/win/installer/installer.cc
+++ b/chrome/updater/win/installer/installer.cc
@@ -16,6 +16,7 @@
 #include <shellapi.h>
 #include <shlobj.h>
 
+#include <optional>
 #include <string>
 
 // TODO(crbug.com/1128529): remove the dependencies on //base/ to reduce the
@@ -46,7 +47,6 @@
 #include "chrome/updater/win/installer/configuration.h"
 #include "chrome/updater/win/installer/installer_constants.h"
 #include "chrome/updater/win/installer/pe_resource.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -162,12 +162,12 @@
   return TRUE;
 }
 
-absl::optional<base::FilePath> FindOfflineDir(
+std::optional<base::FilePath> FindOfflineDir(
     const base::FilePath& unpack_path) {
   const base::FilePath base_offline_dir =
       unpack_path.Append(L"bin").Append(L"Offline");
   if (!base::PathExists(base_offline_dir)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::FileEnumerator file_enumerator(base_offline_dir, false,
                                        base::FileEnumerator::DIRECTORIES);
@@ -177,7 +177,7 @@
       return path;
     }
   }
-  return absl::nullopt;
+  return std::nullopt;
 }
 
 // Finds and writes to disk resources of type 'B7' (7zip archive). Returns false
@@ -406,7 +406,7 @@
 
   // First get a path where we can extract the resource payload, which is
   // a compressed LZMA archive of a single file.
-  absl::optional<base::ScopedTempDir> base_path_owner = CreateSecureTempDir();
+  std::optional<base::ScopedTempDir> base_path_owner = CreateSecureTempDir();
   if (!base_path_owner) {
     return ProcessExitResult(TEMP_DIR_FAILED);
   }
@@ -422,7 +422,7 @@
                                     &compressed_archive);
 
   // Create a temp folder where the archives are unpacked.
-  absl::optional<base::ScopedTempDir> temp_path = CreateSecureTempDir();
+  std::optional<base::ScopedTempDir> temp_path = CreateSecureTempDir();
   if (!temp_path) {
     return ProcessExitResult(TEMP_DIR_FAILED);
   }
@@ -455,8 +455,7 @@
   // Determine if an offlinedir is embedded and, if it is, add an
   // --offlinedir={GUID} switch to indicate that an offline install should
   // be performed.
-  const absl::optional<base::FilePath> offline_dir =
-      FindOfflineDir(unpack_path);
+  const std::optional<base::FilePath> offline_dir = FindOfflineDir(unpack_path);
   if (offline_dir.has_value()) {
     if (!cmd_line_args.append(L" --") ||
         !cmd_line_args.append(base::SysUTF8ToWide(kOfflineDirSwitch).c_str()) ||
diff --git a/chrome/updater/win/installer/installer.h b/chrome/updater/win/installer/installer.h
index fdbbfcd..49ed74e 100644
--- a/chrome/updater/win/installer/installer.h
+++ b/chrome/updater/win/installer/installer.h
@@ -7,10 +7,11 @@
 
 #include <windows.h>
 
+#include <optional>
+
 #include "base/command_line.h"
 #include "chrome/updater/win/installer/exit_code.h"
 #include "chrome/updater/win/installer/string.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace base {
 class FilePath;
@@ -35,8 +36,7 @@
 // (which is a path), plus a few extra arguments.
 using CommandString = StackString<MAX_PATH * 4>;
 
-absl::optional<base::FilePath> FindOfflineDir(
-    const base::FilePath& unpack_path);
+std::optional<base::FilePath> FindOfflineDir(const base::FilePath& unpack_path);
 
 // Handles elevating the installer, waiting for the installer process, and
 // returning the resulting process exit code.
diff --git a/chrome/updater/win/installer/installer_win_unittest.cc b/chrome/updater/win/installer/installer_win_unittest.cc
index 68ac37e..97de22a2 100644
--- a/chrome/updater/win/installer/installer_win_unittest.cc
+++ b/chrome/updater/win/installer/installer_win_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <shlobj.h>
 
+#include <optional>
+
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -48,7 +50,7 @@
           .Append(L"{8D5D0563-F2A0-40E3-932D-AFEAE261A9D1}");
   ASSERT_TRUE(base::CreateDirectory(offline_install_dir));
 
-  absl::optional<base::FilePath> offline_dir =
+  std::optional<base::FilePath> offline_dir =
       updater::FindOfflineDir(unpack_path);
   EXPECT_TRUE(offline_dir.has_value());
   EXPECT_EQ(offline_dir->BaseName(),
diff --git a/chrome/updater/win/installer/msi_custom_action.cc b/chrome/updater/win/installer/msi_custom_action.cc
index b9e33e9..7fb4029 100644
--- a/chrome/updater/win/installer/msi_custom_action.cc
+++ b/chrome/updater/win/installer/msi_custom_action.cc
@@ -9,6 +9,7 @@
 #include <msi.h>
 #include <msiquery.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -18,7 +19,6 @@
 #include "chrome/updater/tag.h"
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -73,8 +73,8 @@
 }
 
 // Gets the value of the property `name` from `msi_handle`.
-absl::optional<std::wstring> MsiGetProperty(MsiHandleInterface& msi_handle,
-                                            const std::wstring& name) {
+std::optional<std::wstring> MsiGetProperty(MsiHandleInterface& msi_handle,
+                                           const std::wstring& name) {
   DWORD value_length = 0;
   UINT result = ERROR_SUCCESS;
   std::vector<wchar_t> value;
@@ -83,18 +83,18 @@
     result = msi_handle.GetProperty(name, value, value_length);
   } while (result == ERROR_MORE_DATA && value_length <= 0xFFFF);
   return result == ERROR_SUCCESS && !value.empty()
-             ? absl::make_optional(std::wstring(value.begin(), value.end()))
-             : absl::nullopt;
+             ? std::make_optional(std::wstring(value.begin(), value.end()))
+             : std::nullopt;
 }
 
 // If the app installer failed with a custom error and provided a UI string,
 // returns that string.
-absl::optional<std::wstring> GetLastInstallerResultUIString(
+std::optional<std::wstring> GetLastInstallerResultUIString(
     const std::wstring& app_id) {
   if (app_id.empty()) {
     return {};
   }
-  auto key = [&app_id]() -> absl::optional<base::win::RegKey> {
+  auto key = [&app_id]() -> std::optional<base::win::RegKey> {
     if (base::win::RegKey client_state_key(HKEY_LOCAL_MACHINE,
                                            GetAppClientStateKey(app_id).c_str(),
                                            Wow6432(KEY_READ));
@@ -119,8 +119,8 @@
                  key->ReadValue(kRegValueLastInstallerResultUIString, &val) ==
                      ERROR_SUCCESS &&
                  !val.empty()
-             ? absl::make_optional(val)
-             : absl::nullopt;
+             ? std::make_optional(val)
+             : std::nullopt;
 }
 
 }  // namespace
diff --git a/chrome/updater/win/installer/msi_custom_action.h b/chrome/updater/win/installer/msi_custom_action.h
index 056a219..43c9515 100644
--- a/chrome/updater/win/installer/msi_custom_action.h
+++ b/chrome/updater/win/installer/msi_custom_action.h
@@ -12,7 +12,6 @@
 #include <string>
 #include <vector>
 
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
diff --git a/chrome/updater/win/installer/msi_custom_action_unittest.cc b/chrome/updater/win/installer/msi_custom_action_unittest.cc
index c1365bf9..ac2ddd86e 100644
--- a/chrome/updater/win/installer/msi_custom_action_unittest.cc
+++ b/chrome/updater/win/installer/msi_custom_action_unittest.cc
@@ -17,7 +17,6 @@
 #include "chrome/updater/win/installer_api.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
diff --git a/chrome/updater/win/installer_api.cc b/chrome/updater/win/installer_api.cc
index 45dc3a5..e307b8b 100644
--- a/chrome/updater/win/installer_api.cc
+++ b/chrome/updater/win/installer_api.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <iterator>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -30,7 +31,6 @@
 #include "chrome/updater/util/util.h"
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -38,19 +38,19 @@
 // Creates or opens the registry ClientState subkey for the `app_id`. `regsam`
 // must contain the KEY_WRITE access right for the creation of the subkey to
 // succeed.
-absl::optional<base::win::RegKey> ClientStateAppKeyCreate(
+std::optional<base::win::RegKey> ClientStateAppKeyCreate(
     UpdaterScope updater_scope,
     const std::string& app_id,
     REGSAM regsam) {
   std::wstring subkey;
   if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &subkey)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::win::RegKey key(UpdaterScopeToHKeyRoot(updater_scope), CLIENT_STATE_KEY,
                         Wow6432(regsam));
   if (!key.Valid() ||
       key.CreateKey(subkey.c_str(), Wow6432(regsam)) != ERROR_SUCCESS) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return key;
 }
@@ -92,7 +92,7 @@
 
 void PersistLastInstallerResultValues(UpdaterScope updater_scope,
                                       const std::string& app_id) {
-  absl::optional<base::win::RegKey> key =
+  std::optional<base::win::RegKey> key =
       ClientStateAppKeyOpen(updater_scope, app_id, KEY_READ | KEY_WRITE);
   if (!key) {
     return;
@@ -139,10 +139,10 @@
       .Valid();
 }
 
-absl::optional<InstallerOutcome> GetLastInstallerOutcome(
-    absl::optional<base::win::RegKey> key) {
+std::optional<InstallerOutcome> GetLastInstallerOutcome(
+    std::optional<base::win::RegKey> key) {
   if (!key) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   InstallerOutcome installer_outcome;
   {
@@ -186,19 +186,19 @@
 InstallerOutcome::InstallerOutcome(const InstallerOutcome&) = default;
 InstallerOutcome::~InstallerOutcome() = default;
 
-absl::optional<base::win::RegKey> ClientStateAppKeyOpen(
+std::optional<base::win::RegKey> ClientStateAppKeyOpen(
     UpdaterScope updater_scope,
     const std::string& app_id,
     REGSAM regsam) {
   std::wstring subkey;
   if (!base::UTF8ToWide(app_id.c_str(), app_id.size(), &subkey)) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   base::win::RegKey key(UpdaterScopeToHKeyRoot(updater_scope), CLIENT_STATE_KEY,
                         Wow6432(regsam));
   if (!key.Valid() ||
       key.OpenKey(subkey.c_str(), Wow6432(regsam)) != ERROR_SUCCESS) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   return key;
 }
@@ -218,7 +218,7 @@
 // {HKLM|HKCU}\Software\Google\Update\ClientState\<appid>\InstallerProgress.
 int GetInstallerProgress(UpdaterScope updater_scope,
                          const std::string& app_id) {
-  absl::optional<base::win::RegKey> key =
+  std::optional<base::win::RegKey> key =
       ClientStateAppKeyOpen(updater_scope, app_id, KEY_READ);
   DWORD progress = 0;
   if (!key || key->ReadValueDW(kRegValueInstallerProgress, &progress) !=
@@ -231,7 +231,7 @@
 bool SetInstallerProgressForTesting(UpdaterScope updater_scope,
                                     const std::string& app_id,
                                     int value) {
-  absl::optional<base::win::RegKey> key =
+  std::optional<base::win::RegKey> key =
       ClientStateAppKeyCreate(updater_scope, app_id, KEY_WRITE);
   return key && key->WriteValue(kRegValueInstallerProgress,
                                 static_cast<DWORD>(value)) == ERROR_SUCCESS;
@@ -242,7 +242,7 @@
   if (!ClientStateAppKeyExists(updater_scope, app_id)) {
     return false;
   }
-  absl::optional<base::win::RegKey> key =
+  std::optional<base::win::RegKey> key =
       ClientStateAppKeyOpen(updater_scope, app_id, KEY_SET_VALUE);
   return key && key->DeleteValue(kRegValueInstallerProgress) == ERROR_SUCCESS;
 }
@@ -252,7 +252,7 @@
   if (!ClientStateAppKeyExists(updater_scope, app_id)) {
     return false;
   }
-  absl::optional<base::win::RegKey> key = ClientStateAppKeyOpen(
+  std::optional<base::win::RegKey> key = ClientStateAppKeyOpen(
       updater_scope, app_id, KEY_SET_VALUE | KEY_QUERY_VALUE);
   if (!key) {
     return false;
@@ -271,13 +271,12 @@
   return !base::Contains(results, false);
 }
 
-absl::optional<InstallerOutcome> GetInstallerOutcome(
-    UpdaterScope updater_scope,
-    const std::string& app_id) {
-  absl::optional<base::win::RegKey> key =
+std::optional<InstallerOutcome> GetInstallerOutcome(UpdaterScope updater_scope,
+                                                    const std::string& app_id) {
+  std::optional<base::win::RegKey> key =
       ClientStateAppKeyOpen(updater_scope, app_id, KEY_READ);
   if (!key) {
-    return absl::nullopt;
+    return std::nullopt;
   }
   InstallerOutcome installer_outcome;
   {
@@ -316,17 +315,17 @@
   return installer_outcome;
 }
 
-absl::optional<InstallerOutcome> GetClientStateKeyLastInstallerOutcome(
+std::optional<InstallerOutcome> GetClientStateKeyLastInstallerOutcome(
     UpdaterScope updater_scope,
     const std::string& app_id) {
   return GetLastInstallerOutcome(
       ClientStateAppKeyOpen(updater_scope, app_id, KEY_READ));
 }
 
-absl::optional<InstallerOutcome> GetUpdaterKeyLastInstallerOutcome(
+std::optional<InstallerOutcome> GetUpdaterKeyLastInstallerOutcome(
     UpdaterScope updater_scope) {
   return GetLastInstallerOutcome(
-      [&updater_scope]() -> absl::optional<base::win::RegKey> {
+      [&updater_scope]() -> std::optional<base::win::RegKey> {
         if (base::win::RegKey updater_key(UpdaterScopeToHKeyRoot(updater_scope),
                                           UPDATER_KEY, Wow6432(KEY_READ));
             updater_key.Valid()) {
@@ -339,7 +338,7 @@
 bool SetInstallerOutcomeForTesting(UpdaterScope updater_scope,
                                    const std::string& app_id,
                                    const InstallerOutcome& installer_outcome) {
-  absl::optional<base::win::RegKey> key =
+  std::optional<base::win::RegKey> key =
       ClientStateAppKeyCreate(updater_scope, app_id, KEY_WRITE);
   if (!key) {
     return false;
@@ -388,7 +387,7 @@
 // backward compatible with the implementation of the Installer API in
 // Omaha/Google Update. Some edge cases could be missing.
 Installer::Result MakeInstallerResult(
-    absl::optional<InstallerOutcome> installer_outcome,
+    std::optional<InstallerOutcome> installer_outcome,
     int exit_code) {
   InstallerOutcome outcome;
   if (installer_outcome && installer_outcome->installer_result) {
@@ -468,7 +467,7 @@
     const AppInfo& app_info,
     const base::FilePath& app_installer,
     const std::string& arguments,
-    const absl::optional<base::FilePath>& installer_data_file,
+    const std::optional<base::FilePath>& installer_data_file,
     bool usage_stats_enabled,
     const base::TimeDelta& timeout,
     InstallProgressCallback progress_callback) {
diff --git a/chrome/updater/win/installer_api.h b/chrome/updater/win/installer_api.h
index 1a8b733..f089b14 100644
--- a/chrome/updater/win/installer_api.h
+++ b/chrome/updater/win/installer_api.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_UPDATER_WIN_INSTALLER_API_H_
 #define CHROME_UPDATER_WIN_INSTALLER_API_H_
 
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -12,7 +13,6 @@
 #include "chrome/updater/enum_traits.h"
 #include "chrome/updater/installer.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 
@@ -84,15 +84,15 @@
   InstallerOutcome(const InstallerOutcome&);
   ~InstallerOutcome();
 
-  absl::optional<InstallerResult> installer_result;
-  absl::optional<int> installer_error;
-  absl::optional<int> installer_extracode1;
-  absl::optional<std::string> installer_text;
-  absl::optional<std::string> installer_cmd_line;
+  std::optional<InstallerResult> installer_result;
+  std::optional<int> installer_error;
+  std::optional<int> installer_extracode1;
+  std::optional<std::string> installer_text;
+  std::optional<std::string> installer_cmd_line;
 };
 
 // Opens the registry ClientState subkey for the `app_id`.
-absl::optional<base::win::RegKey> ClientStateAppKeyOpen(
+std::optional<base::win::RegKey> ClientStateAppKeyOpen(
     UpdaterScope updater_scope,
     const std::string& app_id,
     REGSAM regsam);
@@ -116,14 +116,14 @@
 // Returns the Installer API outcome, best-effort, and renames the InstallerXXX
 // values to LastInstallerXXX values. The LastInstallerXXX values remain around
 // until the next update or install.
-absl::optional<InstallerOutcome> GetInstallerOutcome(UpdaterScope updater_scope,
-                                                     const std::string& app_id);
+std::optional<InstallerOutcome> GetInstallerOutcome(UpdaterScope updater_scope,
+                                                    const std::string& app_id);
 
 // Returns the Last Installer API outcome, i.e., the LastInstallerXXX values.
-absl::optional<InstallerOutcome> GetClientStateKeyLastInstallerOutcome(
+std::optional<InstallerOutcome> GetClientStateKeyLastInstallerOutcome(
     UpdaterScope updater_scope,
     const std::string& app_id);
-absl::optional<InstallerOutcome> GetUpdaterKeyLastInstallerOutcome(
+std::optional<InstallerOutcome> GetUpdaterKeyLastInstallerOutcome(
     UpdaterScope updater_scope);
 
 bool SetInstallerOutcomeForTesting(UpdaterScope updater_scope,
@@ -141,7 +141,7 @@
 //   mean `FINGERPRINT_WRITE_FAILED = 2` or the windows error
 //   `ERROR_FILE_NOT_FOUND`.
 Installer::Result MakeInstallerResult(
-    absl::optional<InstallerOutcome> installer_outcome,
+    std::optional<InstallerOutcome> installer_outcome,
     int exit_code);
 
 }  // namespace updater
diff --git a/chrome/updater/win/installer_api_unittest.cc b/chrome/updater/win/installer_api_unittest.cc
index db89f51..143b7bf 100644
--- a/chrome/updater/win/installer_api_unittest.cc
+++ b/chrome/updater/win/installer_api_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/updater/win/installer_api.h"
 
+#include <optional>
 #include <string>
 
 #include "base/strings/utf_string_conversions.h"
@@ -14,7 +15,6 @@
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -73,7 +73,7 @@
                                               installer_outcome));
   }
 
-  absl::optional<InstallerOutcome> installer_outcome =
+  std::optional<InstallerOutcome> installer_outcome =
       GetInstallerOutcome(updater_scope_, kAppId);
   ASSERT_TRUE(installer_outcome);
   EXPECT_EQ(installer_outcome->installer_result, InstallerResult::kSystemError);
@@ -83,7 +83,7 @@
   EXPECT_STREQ(installer_outcome->installer_cmd_line->c_str(), "some cmd line");
 
   // Checks that LastInstallerXXX values match the installer outcome.
-  for (absl::optional<InstallerOutcome> last_installer_outcome :
+  for (std::optional<InstallerOutcome> last_installer_outcome :
        {GetClientStateKeyLastInstallerOutcome(updater_scope_, kAppId),
         GetUpdaterKeyLastInstallerOutcome(updater_scope_)}) {
     ASSERT_TRUE(last_installer_outcome);
@@ -162,7 +162,7 @@
     EXPECT_EQ(installer_result.extended_error, -2);
     EXPECT_STREQ(installer_result.installer_text.c_str(), "some text");
     EXPECT_TRUE(installer_result.installer_cmd_line.empty());
-    installer_outcome.installer_error = absl::nullopt;
+    installer_outcome.installer_error = std::nullopt;
     installer_result = MakeInstallerResult(installer_outcome, 10);
     EXPECT_EQ(installer_result.error, kErrorApplicationInstallerFailed);
     EXPECT_EQ(installer_result.original_error, 10);
@@ -184,7 +184,7 @@
     EXPECT_EQ(installer_result.extended_error, -2);
     EXPECT_FALSE(installer_result.installer_text.empty());
     EXPECT_TRUE(installer_result.installer_cmd_line.empty());
-    installer_outcome.installer_error = absl::nullopt;
+    installer_outcome.installer_error = std::nullopt;
     installer_result = MakeInstallerResult(installer_outcome, 10);
     EXPECT_EQ(installer_result.error, kErrorApplicationInstallerFailed);
     EXPECT_EQ(installer_result.original_error, 10);
@@ -206,7 +206,7 @@
     EXPECT_EQ(installer_result.extended_error, -2);
     EXPECT_FALSE(installer_result.installer_text.empty());
     EXPECT_TRUE(installer_result.installer_cmd_line.empty());
-    installer_outcome.installer_error = absl::nullopt;
+    installer_outcome.installer_error = std::nullopt;
     installer_result = MakeInstallerResult(installer_outcome, 10);
     EXPECT_EQ(installer_result.error, kErrorApplicationInstallerFailed);
     EXPECT_EQ(installer_result.original_error, 10);
diff --git a/chrome/updater/win/manifest_util.cc b/chrome/updater/win/manifest_util.cc
index 05c39ff..da70dfa 100644
--- a/chrome/updater/win/manifest_util.cc
+++ b/chrome/updater/win/manifest_util.cc
@@ -6,6 +6,7 @@
 
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -24,14 +25,13 @@
 #include "chrome/updater/win/protocol_parser_xml.h"
 #include "components/update_client/protocol_parser.h"
 #include "components/update_client/utils.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
 
 constexpr char kArchAmd64Omaha3[] = "x64";
 
-absl::optional<base::FilePath> GetOfflineManifest(
+std::optional<base::FilePath> GetOfflineManifest(
     const base::FilePath& offline_dir,
     const std::string& app_id) {
   // Check manifest with fixed name first.
@@ -44,14 +44,14 @@
   manifest_path =
       offline_dir.AppendASCII(app_id).AddExtension(FILE_PATH_LITERAL(".gup"));
   return base::PathExists(manifest_path)
-             ? absl::optional<base::FilePath>(manifest_path)
-             : absl::nullopt;
+             ? std::optional<base::FilePath>(manifest_path)
+             : std::nullopt;
 }
 
 std::unique_ptr<ProtocolParserXML> ParseOfflineManifest(
     const base::FilePath& offline_dir,
     const std::string& app_id) {
-  absl::optional<base::FilePath> manifest_path =
+  std::optional<base::FilePath> manifest_path =
       GetOfflineManifest(offline_dir, app_id);
   if (!manifest_path) {
     VLOG(2) << "Cannot find manifest file in: " << offline_dir;
diff --git a/chrome/updater/win/setup/setup.cc b/chrome/updater/win/setup/setup.cc
index 79ed694e..a2e96097 100644
--- a/chrome/updater/win/setup/setup.cc
+++ b/chrome/updater/win/setup/setup.cc
@@ -7,6 +7,7 @@
 #include <shlobj.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -29,7 +30,6 @@
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/setup/setup_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -72,7 +72,7 @@
     LOG(ERROR) << "GetTempDir failed.";
     return kErrorCreatingTempDir;
   }
-  const absl::optional<base::FilePath> versioned_dir =
+  const std::optional<base::FilePath> versioned_dir =
       GetVersionedInstallDirectory(scope);
   if (!versioned_dir) {
     LOG(ERROR) << "GetVersionedInstallDirectory failed.";
diff --git a/chrome/updater/win/setup/setup_util.cc b/chrome/updater/win/setup/setup_util.cc
index e282623..0a770d3 100644
--- a/chrome/updater/win/setup/setup_util.cc
+++ b/chrome/updater/win/setup/setup_util.cc
@@ -11,6 +11,7 @@
 #include <wrl/implements.h>
 
 #include <cstring>
+#include <optional>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -230,7 +231,7 @@
 bool InstallComInterfaces(UpdaterScope scope, bool is_internal) {
   VLOG(1) << __func__ << ": scope: " << scope
           << ": is_internal: " << is_internal;
-  const absl::optional<base::FilePath> versioned_directory =
+  const std::optional<base::FilePath> versioned_directory =
       GetVersionedInstallDirectory(scope);
   if (!versioned_directory) {
     return false;
diff --git a/chrome/updater/win/setup/uninstall.cc b/chrome/updater/win/setup/uninstall.cc
index 2b00069e..2cbc16a 100644
--- a/chrome/updater/win/setup/uninstall.cc
+++ b/chrome/updater/win/setup/uninstall.cc
@@ -8,6 +8,7 @@
 #include <windows.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -30,7 +31,6 @@
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/setup/setup_util.h"
 #include "chrome/updater/win/win_constants.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace updater {
 namespace {
@@ -137,7 +137,7 @@
 void DeleteGoogleUpdateFilesAndKeys(UpdaterScope scope) {
   DeleteUpdaterKey(scope);
 
-  const absl::optional<base::FilePath> target_path =
+  const std::optional<base::FilePath> target_path =
       GetGoogleUpdateExePath(scope);
   if (target_path) {
     base::DeletePathRecursively(target_path->DirName());
@@ -145,13 +145,13 @@
 }
 
 int RunUninstallScript(UpdaterScope scope, bool uninstall_all) {
-  const absl::optional<base::FilePath> versioned_dir =
+  const std::optional<base::FilePath> versioned_dir =
       GetVersionedInstallDirectory(scope);
   if (!versioned_dir) {
     LOG(ERROR) << "GetVersionedInstallDirectory failed.";
     return kErrorNoVersionedDirectory;
   }
-  const absl::optional<base::FilePath> base_dir = GetInstallDirectory(scope);
+  const std::optional<base::FilePath> base_dir = GetInstallDirectory(scope);
   if (IsSystemInstall(scope) && !base_dir) {
     LOG(ERROR) << "GetInstallDirectory failed.";
     return kErrorNoBaseDirectory;
diff --git a/chrome/updater/win/task_scheduler_unittest.cc b/chrome/updater/win/task_scheduler_unittest.cc
index eef30ace..c6b01af3 100644
--- a/chrome/updater/win/task_scheduler_unittest.cc
+++ b/chrome/updater/win/task_scheduler_unittest.cc
@@ -11,6 +11,7 @@
 #include <taskschd.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -82,7 +83,7 @@
   }
 
   std::wstring GetRegKeyTaskCacheTasksContents() {
-    absl::optional<std::wstring> contents = GetRegKeyContents(
+    std::optional<std::wstring> contents = GetRegKeyContents(
         L"HKLM\\SOFTWARE\\Microsoft\\Windows "
         L"NT\\CurrentVersion\\Schedule\\TaskCache\\Tasks");
     return contents ? *contents : L"";
diff --git a/chromeos/ash/components/dbus/featured/featured_client.cc b/chromeos/ash/components/dbus/featured/featured_client.cc
index 5d8f130f..3dcdfbb8 100644
--- a/chromeos/ash/components/dbus/featured/featured_client.cc
+++ b/chromeos/ash/components/dbus/featured/featured_client.cc
@@ -4,18 +4,21 @@
 
 #include "chromeos/ash/components/dbus/featured/featured_client.h"
 
+#include <memory>
 #include <string>
 
 #include "base/check_is_test.h"
 #include "base/files/dir_reader_posix.h"
 #include "base/files/file_path.h"
 #include "base/files/file_path_watcher.h"
+#include "base/files/file_util.h"
 #include "base/functional/callback.h"
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/field_trial.h"
 #include "base/strings/escape.h"
 #include "base/strings/string_split.h"
+#include "base/task/thread_pool.h"
 #include "chromeos/ash/components/dbus/featured/fake_featured_client.h"
 #include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "dbus/bus.h"
@@ -30,21 +33,100 @@
 
 FeaturedClient* g_instance = nullptr;
 
+struct FileWatchOptions {
+  FeaturedClient::ListenForTrialCallback listen_callback;
+  const base::FilePath expected_dir;
+};
+
+void RecordEarlyBootTrialInUMA(const std::string& trial_name,
+                               const std::string& group_name) {
+  base::FieldTrial* trial =
+      base::FieldTrialList::CreateFieldTrial(trial_name, group_name);
+  // This records the trial in UMA.
+  trial->Activate();
+}
+
+// Assumes |filename| is valid (that its directory name is correct).
+bool RecordEarlyBootTrialInChrome(
+    FeaturedClient::ListenForTrialCallback listen_callback,
+    const base::FilePath& filename) {
+  base::FieldTrial::ActiveGroup active_group;
+  if (!FeaturedClient::ParseTrialFilename(filename, active_group)) {
+    return false;
+  }
+  listen_callback.Run(active_group.trial_name, active_group.group_name);
+  return true;
+}
+
+void RecordEarlyBootTrialAfterChromeStartup(const FileWatchOptions& opts,
+                                            const base::FilePath& path,
+                                            bool error) {
+  if (error || path.DirName() != opts.expected_dir) {
+    // TODO(b/296394808): Add UMA metric if we enter this code path since it
+    // is not expected.
+    return;
+  }
+  // TODO(b/296394808): Add UMA metric if unable to record trial due to parse
+  // error.
+  RecordEarlyBootTrialInChrome(opts.listen_callback, path);
+}
+
+void ListenForActiveEarlyBootTrials(base::FilePathWatcher* watcher,
+                                    const FileWatchOptions& opts) {
+  base::FilePathWatcher::WatchOptions options = {
+      // Watches for changes in a directory.
+      .type = base::FilePathWatcher::Type::kRecursive,
+      // Reports the path of modified files in the directory.
+      .report_modified_path = true};
+
+  watcher->WatchWithOptions(
+      opts.expected_dir, options,
+      base::BindRepeating(&RecordEarlyBootTrialAfterChromeStartup, opts));
+}
+
+void ReadTrialsActivatedBeforeChromeStartup(const FileWatchOptions& opts) {
+  base::DirReaderPosix reader(opts.expected_dir.value().c_str());
+  if (!reader.IsValid()) {
+    // TODO(b/296394808): Add UMA metric if we are unable to enumerate trials
+    // activated before Chrome startup.
+    return;
+  }
+
+  while (reader.Next()) {
+    if (std::string(reader.name()) == "." ||
+        std::string(reader.name()) == "..") {
+      continue;
+    }
+    // TODO(b/296394808): Add UMA metric if unable to record trial due to
+    // parse error.
+    RecordEarlyBootTrialInChrome(opts.listen_callback,
+                                 base::FilePath(reader.name()));
+  }
+}
+
+// We need to delete the FilePathWatcher instance via a posted task since we
+// call FilePathWatche::Watch() on a posted task. The documentation states the
+// instance must be destroyed on the same sequence it watches from.
+void DeleteWatcher(std::unique_ptr<base::FilePathWatcher> watcher) {
+  watcher.reset();
+}
+
 // Production implementation of FeaturedClient.
 class FeaturedClientImpl : public FeaturedClient {
  public:
-  FeaturedClientImpl() = default;
+  FeaturedClientImpl() : watcher_(std::make_unique<base::FilePathWatcher>()) {}
 
   FeaturedClientImpl(const FeaturedClient&) = delete;
   FeaturedClientImpl operator=(const FeaturedClient&) = delete;
 
-  ~FeaturedClientImpl() override = default;
+  ~FeaturedClientImpl() override {
+    file_listener_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&DeleteWatcher, std::move(watcher_)));
+  }
 
   void Init(dbus::Bus* const bus) {
-    InitWithCallback(
-        bus, base::FilePath(feature::kActiveTrialFileDirectory),
-        base::BindRepeating(&FeaturedClientImpl::RecordEarlyBootTrialInUMA,
-                            weak_ptr_factory_.GetWeakPtr()));
+    InitWithCallback(bus, base::FilePath(feature::kActiveTrialFileDirectory),
+                     base::BindRepeating(&RecordEarlyBootTrialInUMA));
   }
 
   void InitForTesting(dbus::Bus* const bus,  // IN-TEST
@@ -90,84 +172,32 @@
                             dbus::ObjectPath(::featured::kFeaturedServicePath));
     expected_dir_ = expected_dir;
     listen_callback_ = callback;
-    // TODO(b/305042166): Call ListenForActiveEarlyBootTrials() once fixed.
-    ReadTrialsActivatedBeforeChromeStartup();
+    FileWatchOptions opts = {.listen_callback = callback,
+                             .expected_dir = expected_dir};
+    file_listener_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ListenForActiveEarlyBootTrials, watcher_.get(), opts));
+    file_listener_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ReadTrialsActivatedBeforeChromeStartup, opts));
   }
 
-  void RecordEarlyBootTrialInUMA(const std::string& trial_name,
-                                 const std::string& group_name) {
-    base::FieldTrial* trial =
-        base::FieldTrialList::CreateFieldTrial(trial_name, group_name);
-    // This records the trial in UMA.
-    trial->Activate();
-  }
-
-  // Assumes |filename| is valid (that its directory name is correct).
-  bool RecordEarlyBootTrialInChrome(const base::FilePath& filename) {
-    base::FieldTrial::ActiveGroup active_group;
-    if (!ParseTrialFilename(filename, active_group)) {
-      return false;
-    }
-    listen_callback_.Run(active_group.trial_name, active_group.group_name);
-    return true;
-  }
-
-  void RecordEarlyBootTrialAfterChromeStartup(const base::FilePath& path,
-                                              bool error) {
-    if (error || path.DirName() != expected_dir_) {
-      // TODO(b/296394808): Add UMA metric if we enter this code path since it
-      // is not expected.
-      return;
-    }
-    // TODO(b/296394808): Add UMA metric if unable to record trial due to parse
-    // error.
-    RecordEarlyBootTrialInChrome(path);
-  }
-
-  void ListenForActiveEarlyBootTrials() {
-    base::FilePathWatcher::WatchOptions options = {
-        // Watches for changes in a directory.
-        .type = base::FilePathWatcher::Type::kRecursive,
-        // Reports the path of modified files in the directory.
-        .report_modified_path = true};
-
-    watcher_.WatchWithOptions(
-        expected_dir_, options,
-        base::BindRepeating(
-            &FeaturedClientImpl::RecordEarlyBootTrialAfterChromeStartup,
-            weak_ptr_factory_.GetWeakPtr()));
-  }
-
-  void ReadTrialsActivatedBeforeChromeStartup() {
-    base::DirReaderPosix reader(expected_dir_.value().c_str());
-    if (!reader.IsValid()) {
-      // TODO(b/296394808): Add UMA metric if we are unable to enumerate trials
-      // activated before Chrome startup.
-      return;
-    }
-
-    while (reader.Next()) {
-      if (std::string(reader.name()) == "." ||
-          std::string(reader.name()) == "..") {
-        continue;
-      }
-      // TODO(b/296394808): Add UMA metric if unable to record trial due to
-      // parse error.
-      RecordEarlyBootTrialInChrome(base::FilePath(reader.name()));
-    }
-  }
-
-  // Watches for early-boot trial files written to `expected_dir_`.
-  base::FilePathWatcher watcher_;
-
   // Callback used when early-boot trial files are written to `expected_dir_`.
   FeaturedClient::ListenForTrialCallback listen_callback_;
 
   // Directory where active trial files on platform are written to.
   base::FilePath expected_dir_;
 
+  // Watches for early-boot trial files written to `expected_dir_`.
+  std::unique_ptr<base::FilePathWatcher> watcher_;
+
   raw_ptr<dbus::ObjectProxy, ExperimentalAsh> featured_service_proxy_ = nullptr;
 
+  // Sequence runner that an post tasks that may block.
+  scoped_refptr<base::SequencedTaskRunner> file_listener_task_runner_ =
+      base::ThreadPool::CreateSequencedTaskRunner(
+          {base::MayBlock(), base::TaskPriority::BEST_EFFORT});
+
   // Note: This should remain the last member so it'll be destroyed and
   // invalidate its weak pointers before any other members are destroyed.
   base::WeakPtrFactory<FeaturedClientImpl> weak_ptr_factory_{this};
diff --git a/chromeos/ash/components/dbus/featured/featured_client.h b/chromeos/ash/components/dbus/featured/featured_client.h
index d0b742a..033aa160 100644
--- a/chromeos/ash/components/dbus/featured/featured_client.h
+++ b/chromeos/ash/components/dbus/featured/featured_client.h
@@ -59,11 +59,6 @@
       const ::featured::SeedDetails& safe_seed,
       base::OnceCallback<void(bool success)> callback) = 0;
 
- protected:
-  // Initialize/Shutdown should be used instead.
-  FeaturedClient();
-  virtual ~FeaturedClient();
-
   // Returns true if the base component of |path| is of the format
   // `TrialName,GroupName`. |active_group| will contain the trial name and group
   // name specified by the filename. If the return value is false,
@@ -74,6 +69,11 @@
   static bool ParseTrialFilename(const base::FilePath& path,
                                  base::FieldTrial::ActiveGroup& active_group);
 
+ protected:
+  // Initialize/Shutdown should be used instead.
+  FeaturedClient();
+  virtual ~FeaturedClient();
+
  private:
   friend class FeaturedClientTest;
 };
diff --git a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
index 60371b2..6e8fe57 100644
--- a/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
+++ b/chromeos/ash/components/dbus/featured/featured_client_unittest.cc
@@ -7,6 +7,7 @@
 #include <map>
 #include <string>
 
+#include "base/barrier_closure.h"
 #include "base/check_op.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -14,6 +15,8 @@
 #include "base/strings/escape.h"
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
 #include "chromeos/ash/components/dbus/featured/fake_featured_client.h"
 #include "chromeos/ash/components/dbus/featured/featured.pb.h"
 #include "dbus/message.h"
@@ -278,13 +281,19 @@
   expected.insert({"test_trial_1", "test_group_1"});
   expected.insert({"test_trial_2", "test_group_2"});
 
+  base::RunLoop run_loop;
+  base::RepeatingClosure barrier_callback =
+      base::BarrierClosure(2, run_loop.QuitClosure());
   std::map<std::string, std::string> actual;
   FeaturedClient::InitializeForTesting(
       bus_.get(), active_trials_dir_,
-      base::BindLambdaForTesting([&actual](const std::string& trial_name,
-                                           const std::string& group_name) {
-        actual.insert({trial_name, group_name});
-      }));
+      base::BindLambdaForTesting(
+          [&actual, &barrier_callback](const std::string& trial_name,
+                                       const std::string& group_name) {
+            actual.insert({trial_name, group_name});
+            barrier_callback.Run();
+          }));
+  run_loop.Run();
   EXPECT_EQ(actual, expected);
 
   FeaturedClient::Shutdown();
diff --git a/chromeos/ash/components/dbus/session_manager/session_manager_client.cc b/chromeos/ash/components/dbus/session_manager/session_manager_client.cc
index 3a1009d8..0dd6f96 100644
--- a/chromeos/ash/components/dbus/session_manager/session_manager_client.cc
+++ b/chromeos/ash/components/dbus/session_manager/session_manager_client.cc
@@ -367,9 +367,8 @@
   }
 
   void StartDeviceWipe(chromeos::VoidDBusMethodCallback callback) override {
-    dbus::MethodCall method_call(
-        login_manager::kSessionManagerInterface,
-        login_manager::kSessionManagerClearForcedReEnrollmentVpd);
+    dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
+                                 login_manager::kSessionManagerStartDeviceWipe);
     session_manager_proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
diff --git a/chromeos/ash/components/report/device_metrics/actives/one_day_impl_unittest.cc b/chromeos/ash/components/report/device_metrics/actives/one_day_impl_unittest.cc
index 33d45be..a4500a7 100644
--- a/chromeos/ash/components/report/device_metrics/actives/one_day_impl_unittest.cc
+++ b/chromeos/ash/components/report/device_metrics/actives/one_day_impl_unittest.cc
@@ -142,27 +142,30 @@
   void SetUp() override {
     OneDayImplBase::SetUp();
 
-    // |psm_client_delegate_| is owned by |use_case_params_|.
+    // |psm_client_delegate| is owned by |psm_client_manager_|.
     // Stub successful request payloads when created by the PSM client.
-    StubPsmClientManagerDelegate* psm_client_delegate =
-        new StubPsmClientManagerDelegate();
-    SimulateOprfRequest(psm_client_delegate,
+    std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
+        std::make_unique<StubPsmClientManagerDelegate>();
+    SimulateOprfRequest(psm_client_delegate.get(),
                         psm_rlwe::PrivateMembershipRlweOprfRequest());
-    SimulateQueryRequest(psm_client_delegate,
+    SimulateQueryRequest(psm_client_delegate.get(),
                          psm_rlwe::PrivateMembershipRlweQueryRequest());
-    SimulateMembershipResponses(psm_client_delegate, GetMembershipResponses());
+    SimulateMembershipResponses(psm_client_delegate.get(),
+                                GetMembershipResponses());
+    psm_client_manager_ =
+        std::make_unique<PsmClientManager>(std::move(psm_client_delegate));
 
     use_case_params_ = std::make_unique<UseCaseParameters>(
         GetFakeTimeNow(), kFakeChromeParameters, GetUrlLoaderFactory(),
         utils::kFakeHighEntropySeed, GetLocalState(),
-        std::make_unique<PsmClientManager>(
-            base::WrapUnique(psm_client_delegate)));
+        psm_client_manager_.get());
     one_day_impl_ = std::make_unique<OneDayImpl>(use_case_params_.get());
   }
 
   void TearDown() override {
     one_day_impl_.reset();
     use_case_params_.reset();
+    psm_client_manager_.reset();
   }
 
   OneDayImpl* GetOneDayImpl() { return one_day_impl_.get(); }
@@ -189,6 +192,7 @@
   }
 
  private:
+  std::unique_ptr<PsmClientManager> psm_client_manager_;
   std::unique_ptr<UseCaseParameters> use_case_params_;
   std::unique_ptr<OneDayImpl> one_day_impl_;
 };
@@ -276,27 +280,30 @@
   void SetUp() override {
     OneDayImplBase::SetUp();
 
-    // |psm_client_delegate_| is owned by |use_case_params_|.
-    // Stub PSM client method calls.
-    StubPsmClientManagerDelegate* psm_client_delegate =
-        new StubPsmClientManagerDelegate();
-    SimulateOprfRequest(psm_client_delegate,
+    // |psm_client_delegate| is owned by |psm_client_manager_|.
+    // Stub successful request payloads when created by the PSM client.
+    std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
+        std::make_unique<StubPsmClientManagerDelegate>();
+    SimulateOprfRequest(psm_client_delegate.get(),
                         psm_rlwe::PrivateMembershipRlweOprfRequest());
-    SimulateQueryRequest(psm_client_delegate,
+    SimulateQueryRequest(psm_client_delegate.get(),
                          psm_rlwe::PrivateMembershipRlweQueryRequest());
-    SimulateMembershipResponses(psm_client_delegate, GetMembershipResponses());
+    SimulateMembershipResponses(psm_client_delegate.get(),
+                                GetMembershipResponses());
+    psm_client_manager_ =
+        std::make_unique<PsmClientManager>(std::move(psm_client_delegate));
 
     use_case_params_ = std::make_unique<UseCaseParameters>(
         GetFakeTimeNow(), kFakeChromeParameters, GetUrlLoaderFactory(),
         utils::kFakeHighEntropySeed, GetLocalState(),
-        std::make_unique<PsmClientManager>(
-            base::WrapUnique(psm_client_delegate)));
+        psm_client_manager_.get());
     one_day_impl_ = std::make_unique<OneDayImpl>(use_case_params_.get());
   }
 
   void TearDown() override {
     one_day_impl_.reset();
     use_case_params_.reset();
+    psm_client_manager_.reset();
   }
 
   OneDayImpl* GetOneDayImpl() { return one_day_impl_.get(); }
@@ -323,6 +330,7 @@
   }
 
  private:
+  std::unique_ptr<PsmClientManager> psm_client_manager_;
   std::unique_ptr<UseCaseParameters> use_case_params_;
   std::unique_ptr<OneDayImpl> one_day_impl_;
 };
diff --git a/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc b/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc
index 0bc3761..e079c800 100644
--- a/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc
+++ b/chromeos/ash/components/report/device_metrics/actives/twenty_eight_day_impl_unittest.cc
@@ -142,21 +142,17 @@
   void SetUp() override {
     TwentyEightDayImplBase::SetUp();
 
-    // |psm_client_delegate_| is owned by |use_case_params_|.
+    // |psm_client_delegate| is owned by |psm_client_manager_|.
     // Stub successful request payloads when created by the PSM client.
-    StubPsmClientManagerDelegate* psm_client_delegate =
-        new StubPsmClientManagerDelegate();
-    SimulateOprfRequest(psm_client_delegate,
-                        psm_rlwe::PrivateMembershipRlweOprfRequest());
-    SimulateQueryRequest(psm_client_delegate,
-                         psm_rlwe::PrivateMembershipRlweQueryRequest());
-    SimulateMembershipResponses(psm_client_delegate, GetMembershipResponses());
+    std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
+        std::make_unique<StubPsmClientManagerDelegate>();
+    psm_client_manager_ =
+        std::make_unique<PsmClientManager>(std::move(psm_client_delegate));
 
     use_case_params_ = std::make_unique<UseCaseParameters>(
         GetFakeTimeNow(), kFakeChromeParameters, GetUrlLoaderFactory(),
         utils::kFakeHighEntropySeed, GetLocalState(),
-        std::make_unique<PsmClientManager>(
-            base::WrapUnique(psm_client_delegate)));
+        psm_client_manager_.get());
     twenty_eight_day_impl_ =
         std::make_unique<TwentyEightDayImpl>(use_case_params_.get());
   }
@@ -164,6 +160,7 @@
   void TearDown() override {
     twenty_eight_day_impl_.reset();
     use_case_params_.reset();
+    psm_client_manager_.reset();
   }
 
   TwentyEightDayImpl* GetTwentyEightDayImpl() {
@@ -191,6 +188,7 @@
   }
 
  private:
+  std::unique_ptr<PsmClientManager> psm_client_manager_;
   std::unique_ptr<UseCaseParameters> use_case_params_;
   std::unique_ptr<TwentyEightDayImpl> twenty_eight_day_impl_;
 };
diff --git a/chromeos/ash/components/report/device_metrics/churn/cohort_impl_unittest.cc b/chromeos/ash/components/report/device_metrics/churn/cohort_impl_unittest.cc
index 2c927c2..b5fe2e64 100644
--- a/chromeos/ash/components/report/device_metrics/churn/cohort_impl_unittest.cc
+++ b/chromeos/ash/components/report/device_metrics/churn/cohort_impl_unittest.cc
@@ -141,27 +141,30 @@
   void SetUp() override {
     CohortImplTestBase::SetUp();
 
-    // |psm_client_delegate_| is owned by |use_case_params_|.
+    // |psm_client_delegate| is owned by |psm_client_manager_|.
     // Stub successful request payloads when created by the PSM client.
-    StubPsmClientManagerDelegate* psm_client_delegate =
-        new StubPsmClientManagerDelegate();
-    SimulateOprfRequest(psm_client_delegate,
+    std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
+        std::make_unique<StubPsmClientManagerDelegate>();
+    SimulateOprfRequest(psm_client_delegate.get(),
                         psm_rlwe::PrivateMembershipRlweOprfRequest());
-    SimulateQueryRequest(psm_client_delegate,
+    SimulateQueryRequest(psm_client_delegate.get(),
                          psm_rlwe::PrivateMembershipRlweQueryRequest());
-    SimulateMembershipResponses(psm_client_delegate, GetMembershipResponses());
+    SimulateMembershipResponses(psm_client_delegate.get(),
+                                GetMembershipResponses());
+    psm_client_manager_ =
+        std::make_unique<PsmClientManager>(std::move(psm_client_delegate));
 
     use_case_params_ = std::make_unique<UseCaseParameters>(
         GetFakeTimeNow(), kFakeChromeParameters, GetUrlLoaderFactory(),
         utils::kFakeHighEntropySeed, GetLocalState(),
-        std::make_unique<PsmClientManager>(
-            base::WrapUnique(psm_client_delegate)));
+        psm_client_manager_.get());
     cohort_impl_ = std::make_unique<CohortImpl>(use_case_params_.get());
   }
 
   void TearDown() override {
     cohort_impl_.reset();
     use_case_params_.reset();
+    psm_client_manager_.reset();
   }
 
   CohortImpl* GetCohortImpl() { return cohort_impl_.get(); }
@@ -193,6 +196,7 @@
   }
 
  private:
+  std::unique_ptr<PsmClientManager> psm_client_manager_;
   std::unique_ptr<UseCaseParameters> use_case_params_;
   std::unique_ptr<CohortImpl> cohort_impl_;
 };
diff --git a/chromeos/ash/components/report/device_metrics/churn/observation_impl_unittest.cc b/chromeos/ash/components/report/device_metrics/churn/observation_impl_unittest.cc
index d7d73df..f71b968a 100644
--- a/chromeos/ash/components/report/device_metrics/churn/observation_impl_unittest.cc
+++ b/chromeos/ash/components/report/device_metrics/churn/observation_impl_unittest.cc
@@ -142,21 +142,23 @@
   void SetUp() override {
     ObservationImplTestBase::SetUp();
 
-    // |psm_client_delegate_| is owned by |use_case_params_|.
+    // |psm_client_delegate| is owned by |psm_client_manager_|.
     // Stub successful request payloads when created by the PSM client.
-    StubPsmClientManagerDelegate* psm_client_delegate =
-        new StubPsmClientManagerDelegate();
-    SimulateOprfRequest(psm_client_delegate,
+    std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
+        std::make_unique<StubPsmClientManagerDelegate>();
+    SimulateOprfRequest(psm_client_delegate.get(),
                         psm_rlwe::PrivateMembershipRlweOprfRequest());
-    SimulateQueryRequest(psm_client_delegate,
+    SimulateQueryRequest(psm_client_delegate.get(),
                          psm_rlwe::PrivateMembershipRlweQueryRequest());
-    SimulateMembershipResponses(psm_client_delegate, GetMembershipResponses());
+    SimulateMembershipResponses(psm_client_delegate.get(),
+                                GetMembershipResponses());
+    psm_client_manager_ =
+        std::make_unique<PsmClientManager>(std::move(psm_client_delegate));
 
     use_case_params_ = std::make_unique<UseCaseParameters>(
         GetFakeTimeNow(), kFakeChromeParameters, GetUrlLoaderFactory(),
         utils::kFakeHighEntropySeed, GetLocalState(),
-        std::make_unique<PsmClientManager>(
-            base::WrapUnique(psm_client_delegate)));
+        psm_client_manager_.get());
     observation_impl_ =
         std::make_unique<ObservationImpl>(use_case_params_.get());
   }
@@ -164,6 +166,7 @@
   void TearDown() override {
     observation_impl_.reset();
     use_case_params_.reset();
+    psm_client_manager_.reset();
   }
 
   ObservationImpl* GetObservationImpl() { return observation_impl_.get(); }
@@ -190,6 +193,7 @@
   }
 
  private:
+  std::unique_ptr<PsmClientManager> psm_client_manager_;
   std::unique_ptr<UseCaseParameters> use_case_params_;
   std::unique_ptr<ObservationImpl> observation_impl_;
 };
diff --git a/chromeos/ash/components/report/device_metrics/use_case/use_case.cc b/chromeos/ash/components/report/device_metrics/use_case/use_case.cc
index 1101a34..803f4ea 100644
--- a/chromeos/ash/components/report/device_metrics/use_case/use_case.cc
+++ b/chromeos/ash/components/report/device_metrics/use_case/use_case.cc
@@ -18,13 +18,13 @@
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& high_entropy_seed,
     PrefService* local_state,
-    std::unique_ptr<PsmClientManager> psm_client_manager)
+    PsmClientManager* psm_client_manager)
     : active_ts_(active_ts),
       chrome_device_params_(chrome_device_params),
       url_loader_factory_(url_loader_factory),
       high_entropy_seed_(high_entropy_seed),
       local_state_(local_state),
-      psm_client_manager_(std::move(psm_client_manager)) {}
+      psm_client_manager_(psm_client_manager) {}
 
 UseCaseParameters::~UseCaseParameters() = default;
 
@@ -51,7 +51,7 @@
 }
 
 PsmClientManager* UseCaseParameters::GetPsmClientManager() const {
-  return psm_client_manager_.get();
+  return psm_client_manager_;
 }
 
 UseCase::UseCase(UseCaseParameters* params) : params_(params) {}
diff --git a/chromeos/ash/components/report/device_metrics/use_case/use_case.h b/chromeos/ash/components/report/device_metrics/use_case/use_case.h
index 39804a4..e42b0b6 100644
--- a/chromeos/ash/components/report/device_metrics/use_case/use_case.h
+++ b/chromeos/ash/components/report/device_metrics/use_case/use_case.h
@@ -48,7 +48,7 @@
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& high_entropy_seed,
       PrefService* local_state,
-      std::unique_ptr<PsmClientManager> psm_client_manager);
+      PsmClientManager* psm_client_manager);
   ~UseCaseParameters();
 
   const base::Time GetActiveTs() const;
@@ -93,8 +93,9 @@
   // Persists fresnel pref key/value pairs over device restarts.
   const raw_ptr<PrefService, ExperimentalAsh> local_state_;
 
-  // Abstract class used to generate the PSM RLWE client.
-  std::unique_ptr<PsmClientManager> psm_client_manager_;
+  // Pointer to the abstract class used to generate the PSM RLWE client.
+  // Lifetime of pointer is maintained by ReportController class.
+  const raw_ptr<PsmClientManager> psm_client_manager_;
 };
 
 // Base class for each use case that is reporting to Fresnel server.
diff --git a/chromeos/ash/components/report/report_controller.cc b/chromeos/ash/components/report/report_controller.cc
index 4dae63c..a98b8cfa 100644
--- a/chromeos/ash/components/report/report_controller.cc
+++ b/chromeos/ash/components/report/report_controller.cc
@@ -447,7 +447,7 @@
   // Create instances of use cases and parameters.
   use_case_params_ = std::make_unique<device_metrics::UseCaseParameters>(
       active_ts_, chrome_device_params_, url_loader_factory_,
-      high_entropy_seed_, local_state_, std::move(psm_client_manager_));
+      high_entropy_seed_, local_state_, psm_client_manager_.get());
   one_day_impl_ =
       std::make_unique<device_metrics::OneDayImpl>(use_case_params_.get());
   twenty_eight_day_impl_ = std::make_unique<device_metrics::TwentyEightDayImpl>(
diff --git a/chromeos/ash/components/report/report_controller_unittest.cc b/chromeos/ash/components/report/report_controller_unittest.cc
index 21c86c4..529c0b8 100644
--- a/chromeos/ash/components/report/report_controller_unittest.cc
+++ b/chromeos/ash/components/report/report_controller_unittest.cc
@@ -234,21 +234,21 @@
     GetPrivateComputingTestInterface()->SetSaveLastPingDatesStatusResponse(
         private_computing::SaveStatusResponse());
 
-    // |psm_client_delegate_| is owned by |use_case_params_|.
+    // |psm_client_delegate| is owned by |psm_client_manager_|.
     // Stub successful request payloads when created by the PSM client.
-    StubPsmClientManagerDelegate* psm_client_delegate =
-        new StubPsmClientManagerDelegate();
-    SimulateOprfRequest(psm_client_delegate,
+    std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
+        std::make_unique<StubPsmClientManagerDelegate>();
+    SimulateOprfRequest(psm_client_delegate.get(),
                         psm_rlwe::PrivateMembershipRlweOprfRequest());
-    SimulateQueryRequest(psm_client_delegate,
+    SimulateQueryRequest(psm_client_delegate.get(),
                          psm_rlwe::PrivateMembershipRlweQueryRequest());
-    SimulateMembershipResponses(psm_client_delegate, GetMembershipResponses());
+    SimulateMembershipResponses(psm_client_delegate.get(),
+                                GetMembershipResponses());
 
     report_controller_ = std::make_unique<ReportController>(
         kFakeChromeParameters, GetLocalState(), GetUrlLoaderFactory(),
         base::Time(), base::BindRepeating([]() { return base::Minutes(1); }),
-        std::make_unique<PsmClientManager>(
-            base::WrapUnique(psm_client_delegate)));
+        std::make_unique<PsmClientManager>(std::move(psm_client_delegate)));
 
     task_environment_.RunUntilIdle();
   }
@@ -577,21 +577,21 @@
     GetPrivateComputingTestInterface()->SetSaveLastPingDatesStatusResponse(
         test.save_response());
 
-    // |psm_client_delegate_| is owned by |use_case_params_|.
+    // |psm_client_delegate| is owned by |psm_client_manager_|.
     // Stub successful request payloads when created by the PSM client.
-    StubPsmClientManagerDelegate* psm_client_delegate =
-        new StubPsmClientManagerDelegate();
-    SimulateOprfRequest(psm_client_delegate,
+    std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
+        std::make_unique<StubPsmClientManagerDelegate>();
+    SimulateOprfRequest(psm_client_delegate.get(),
                         psm_rlwe::PrivateMembershipRlweOprfRequest());
-    SimulateQueryRequest(psm_client_delegate,
+    SimulateQueryRequest(psm_client_delegate.get(),
                          psm_rlwe::PrivateMembershipRlweQueryRequest());
-    SimulateMembershipResponses(psm_client_delegate, GetMembershipResponses());
+    SimulateMembershipResponses(psm_client_delegate.get(),
+                                GetMembershipResponses());
 
     report_controller_ = std::make_unique<ReportController>(
         kFakeChromeParameters, GetLocalState(), GetUrlLoaderFactory(),
         base::Time(), base::BindRepeating([]() { return base::Minutes(1); }),
-        std::make_unique<PsmClientManager>(
-            base::WrapUnique(psm_client_delegate)));
+        std::make_unique<PsmClientManager>(std::move(psm_client_delegate)));
 
     task_environment_.RunUntilIdle();
   }
diff --git a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc
index 5b35541d..c98efa15 100644
--- a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc
+++ b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.cc
@@ -16,37 +16,8 @@
 using assistant::AssistantTimer;
 using assistant::AssistantTimerState;
 
-AssistantTimerState GetTimerState(assistant_client::Timer::State state) {
-  switch (state) {
-    case assistant_client::Timer::State::UNKNOWN:
-      return AssistantTimerState::kUnknown;
-    case assistant_client::Timer::State::SCHEDULED:
-      return AssistantTimerState::kScheduled;
-    case assistant_client::Timer::State::PAUSED:
-      return AssistantTimerState::kPaused;
-    case assistant_client::Timer::State::FIRED:
-      return AssistantTimerState::kFired;
-  }
-}
-
 }  // namespace
 
-::assistant::api::OnAlarmTimerEventRequest
-CreateOnAlarmTimerEventRequestProtoForV1(
-    const std::vector<AssistantTimer>& all_curr_timers) {
-  ::assistant::api::OnAlarmTimerEventRequest proto;
-  auto* timer_params = proto.mutable_event()
-                           ->mutable_on_timer_state_changed()
-                           ->mutable_timer_params();
-
-  for (const auto& t : all_curr_timers) {
-    auto* timer = timer_params->add_timer();
-    ConvertAssistantTimerToProtoTimer(t, timer);
-  }
-
-  return proto;
-}
-
 std::vector<AssistantTimer> ConstructAssistantTimersFromProto(
     const ::assistant::api::params::TimerParams& timer_params) {
   std::vector<AssistantTimer> assistant_timers;
@@ -105,36 +76,4 @@
   output->label = input.label();
 }
 
-std::vector<AssistantTimer> GetAllCurrentTimersFromEvents(
-    const std::vector<assistant_client::AlarmTimerManager::Event>& events) {
-  std::vector<AssistantTimer> result;
-  for (const auto& event : events) {
-    // Note that we currently only handle timers, alarms are unsupported.
-    if (event.type != assistant_client::AlarmTimerEvent::TIMER)
-      continue;
-
-    AssistantTimer timer;
-    timer.id = event.timer_data.timer_id;
-    timer.label = event.timer_data.label;
-    timer.state = GetTimerState(event.timer_data.state);
-    timer.original_duration =
-        base::Milliseconds(event.timer_data.original_duration_ms);
-
-    // LibAssistant provides |fire_time_ms| as an offset from unix epoch.
-    timer.fire_time = base::Time::UnixEpoch() +
-                      base::Milliseconds(event.timer_data.fire_time_ms);
-
-    // If the |timer| is paused, LibAssistant will specify the amount of time
-    // remaining. Otherwise we calculate it based on |fire_time|.
-    timer.remaining_time =
-        timer.state == AssistantTimerState::kPaused
-            ? base::Milliseconds(event.timer_data.remaining_duration_ms)
-            : timer.fire_time - base::Time::Now();
-
-    result.push_back(std::move(timer));
-  }
-
-  return result;
-}
-
 }  // namespace ash::libassistant
diff --git a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h
index 83ec11e5..8e648ea 100644
--- a/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h
+++ b/chromeos/ash/services/libassistant/grpc/utils/timer_utils.h
@@ -10,7 +10,6 @@
 
 namespace assistant {
 namespace api {
-class OnAlarmTimerEventRequest;
 
 namespace params {
 enum class TimerStatus;
@@ -23,10 +22,6 @@
 
 namespace ash::libassistant {
 
-::assistant::api::OnAlarmTimerEventRequest
-CreateOnAlarmTimerEventRequestProtoForV1(
-    const std::vector<assistant::AssistantTimer>& all_curr_timers);
-
 // `timer_params` contains the information of all the current timers.
 std::vector<assistant::AssistantTimer> ConstructAssistantTimersFromProto(
     const ::assistant::api::params::TimerParams& timer_params);
@@ -38,10 +33,6 @@
     const ::assistant::api::params::Timer& input,
     assistant::AssistantTimer* output);
 
-// Used both in |AssistantClientV1| and |FakeAssistantClient|.
-std::vector<assistant::AssistantTimer> GetAllCurrentTimersFromEvents(
-    const std::vector<assistant_client::AlarmTimerManager::Event>& events);
-
 }  // namespace ash::libassistant
 
 #endif  // CHROMEOS_ASH_SERVICES_LIBASSISTANT_GRPC_UTILS_TIMER_UTILS_H_
diff --git a/chromeos/chromeos_strings.grd b/chromeos/chromeos_strings.grd
index 0f74f88..c24ac84 100644
--- a/chromeos/chromeos_strings.grd
+++ b/chromeos/chromeos_strings.grd
@@ -2782,6 +2782,18 @@
         <message name="IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_COLLECTION_SUBLABEL" desc="Sublabel for the Time of Day wallpaper collection.">
           Exclusive
         </message>
+        <message name="IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_TITLE" desc="Title for the dialog confirming user's selection of auto dark mode for Time of Day wallpaper.">
+          Turn on auto dark mode?
+        </message>
+        <message name="IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONTENT" desc="Content for the dialog confirming user's selection of auto dark mode for Time of Day wallpaper.">
+          For best results with this wallpaper, please turn on auto-dark mode. You can change it back in the app anytime.
+        </message>
+        <message name="IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_BACK_BUTTON" desc="Label for the button to close the dialog confirming user's selection of auto dark mode for Time of Day wallpaper">
+          No thanks
+        </message>
+        <message name="IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONFIRM_BUTTON" desc="Label for the button to accept the dialog confirming user's selection of auto dark mode for Time of Day wallpaper.">
+          Turn on auto dark mode
+        </message>
 
         <!-- Personalization App Search Results -->
         <message name="IDS_PERSONALIZATION_APP_SEARCH_RESULT_TITLE" desc="Text for search result item which, when clicked, navigates the user to personalization app.">
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_BACK_BUTTON.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_BACK_BUTTON.png.sha1
new file mode 100644
index 0000000..d60acef
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_BACK_BUTTON.png.sha1
@@ -0,0 +1 @@
+21d8d9303c37db3f87605bba4ece4c6d1de76e3a
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONFIRM_BUTTON.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONFIRM_BUTTON.png.sha1
new file mode 100644
index 0000000..d60acef
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONFIRM_BUTTON.png.sha1
@@ -0,0 +1 @@
+21d8d9303c37db3f87605bba4ece4c6d1de76e3a
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONTENT.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONTENT.png.sha1
new file mode 100644
index 0000000..d60acef
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_CONTENT.png.sha1
@@ -0,0 +1 @@
+21d8d9303c37db3f87605bba4ece4c6d1de76e3a
\ No newline at end of file
diff --git a/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_TITLE.png.sha1 b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..d60acef
--- /dev/null
+++ b/chromeos/chromeos_strings_grd/IDS_PERSONALIZATION_APP_TIME_OF_DAY_WALLPAPER_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+21d8d9303c37db3f87605bba4ece4c6d1de76e3a
\ No newline at end of file
diff --git a/clank b/clank
index b0a7c7b..a9deb4b 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit b0a7c7b4fab38799037256bba133600deda76ca8
+Subproject commit a9deb4b2dbe57163358275cf16643421f9c56e8e
diff --git a/components/OWNERS b/components/OWNERS
index 61635fd..431c48c 100644
--- a/components/OWNERS
+++ b/components/OWNERS
@@ -38,6 +38,7 @@
 per-file payments_strings.grdp=file://components/payments/OWNERS
 per-file permissions_strings.grdp=file://components/permissions/OWNERS
 per-file pdf_strings.grdp=file://pdf/OWNERS
+per-file plus_addresses_strings.grdp=file://components/plus_addresses/OWNERS
 per-file policy_strings.grdp=file://components/policy/OWNERS
 per-file print_media_strings.grdp=file://components/printing/OWNERS
 per-file printing_component_strings.grdp=file://components/printing/OWNERS
diff --git a/components/arc_strings.grdp b/components/arc_strings.grdp
index ac9c029..7e510ac6 100644
--- a/components/arc_strings.grdp
+++ b/components/arc_strings.grdp
@@ -36,6 +36,11 @@
       =1 {To send this file using Nearby Share, free up space (<ph name="DISK_SPACE_SIZE">$1<ex>20MB</ex></ph>) on your device}
       other {To send these files using Nearby Share, free up space (<ph name="DISK_SPACE_SIZE">$1<ex>20MB</ex></ph>) on your device}}
   </message>
+  <message name="IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_MESSAGE_PH" desc="Feature name placeholder: Dialog body that tells the user Nearby Share failed due to low disk space while sharing a file(s).">
+    {NUM_FILES, plural,
+      =1 {To send this file using <ph name="FEATURE_NAME">{1}<ex>Nearby Share</ex></ph>, free up space (<ph name="DISK_SPACE_SIZE">{2}<ex>20MB</ex></ph>) on your device}
+      other {To send these files using <ph name="FEATURE_NAME">{1}<ex>Nearby Share</ex></ph>, free up space (<ph name="DISK_SPACE_SIZE">{2}<ex>20MB</ex></ph>) on your device}}
+  </message>
   <message name="IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_STORAGE_BUTTON" desc="Label for button to acknowledge Nearby Share failed due to low disk space and open the Storage management settings page.">
     Storage management
   </message>
diff --git a/components/arc_strings_grdp/IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_MESSAGE_PH.png.sha1 b/components/arc_strings_grdp/IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_MESSAGE_PH.png.sha1
new file mode 100644
index 0000000..238d18f
--- /dev/null
+++ b/components/arc_strings_grdp/IDS_ASH_ARC_NEARBY_SHARE_LOW_DISK_SPACE_DIALOG_MESSAGE_PH.png.sha1
@@ -0,0 +1 @@
+9439df2f151569dc3d194c888df4ff0eb8bff30d
\ No newline at end of file
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index 3bc2967..124537c 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -1234,10 +1234,8 @@
       suggestion.payload = Suggestion::ValueToFill(iban->GetStrippedValue());
     } else {
       CHECK(iban->record_type() == Iban::kServerIban);
-      int64_t instrument_id = 0;
-      CHECK(base::StringToInt64(iban->instrument_id(), &instrument_id));
-      suggestion.payload =
-          Suggestion::BackendId(Suggestion::InstrumentId(instrument_id));
+      suggestion.payload = Suggestion::BackendId(
+          Suggestion::InstrumentId(iban->instrument_id()));
     }
     if (!iban->nickname().empty())
       suggestion.labels = {{Suggestion::Text(iban->nickname())}};
diff --git a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
index be085c5..198e7787 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
@@ -2103,30 +2103,26 @@
   // payment methods settings page.
   ASSERT_EQ(iban_suggestions.size(), 5u);
 
-  int64_t instrument_id = 0;
-  CHECK(base::StringToInt64(server_iban1.instrument_id(), &instrument_id));
   EXPECT_THAT(
       iban_suggestions[0],
-      EqualsIbanSuggestion(
-          server_iban1.GetIdentifierStringForAutofillDisplay(),
-          Suggestion::BackendId(Suggestion::InstrumentId(instrument_id)),
-          server_iban1.nickname()));
+      EqualsIbanSuggestion(server_iban1.GetIdentifierStringForAutofillDisplay(),
+                           Suggestion::BackendId(Suggestion::InstrumentId(
+                               server_iban1.instrument_id())),
+                           server_iban1.nickname()));
 
-  CHECK(base::StringToInt64(server_iban2.instrument_id(), &instrument_id));
   EXPECT_THAT(
       iban_suggestions[1],
-      EqualsIbanSuggestion(
-          server_iban2.GetIdentifierStringForAutofillDisplay(),
-          Suggestion::BackendId(Suggestion::InstrumentId(instrument_id)),
-          server_iban2.nickname()));
+      EqualsIbanSuggestion(server_iban2.GetIdentifierStringForAutofillDisplay(),
+                           Suggestion::BackendId(Suggestion::InstrumentId(
+                               server_iban2.instrument_id())),
+                           server_iban2.nickname()));
 
-  CHECK(base::StringToInt64(server_iban3.instrument_id(), &instrument_id));
   EXPECT_THAT(
       iban_suggestions[2],
-      EqualsIbanSuggestion(
-          server_iban3.GetIdentifierStringForAutofillDisplay(),
-          Suggestion::BackendId(Suggestion::InstrumentId(instrument_id)),
-          server_iban3.nickname()));
+      EqualsIbanSuggestion(server_iban3.GetIdentifierStringForAutofillDisplay(),
+                           Suggestion::BackendId(Suggestion::InstrumentId(
+                               server_iban3.instrument_id())),
+                           server_iban3.nickname()));
 
   EXPECT_EQ(iban_suggestions[3].popup_item_id, PopupItemId::kSeparator);
 
@@ -2151,22 +2147,19 @@
   // payment methods settings page.
   ASSERT_EQ(iban_suggestions.size(), 5u);
 
-  int64_t instrument_id = 0;
-  CHECK(base::StringToInt64(server_iban1.instrument_id(), &instrument_id));
   EXPECT_THAT(
       iban_suggestions[0],
-      EqualsIbanSuggestion(
-          server_iban1.GetIdentifierStringForAutofillDisplay(),
-          Suggestion::BackendId(Suggestion::InstrumentId(instrument_id)),
-          server_iban1.nickname()));
+      EqualsIbanSuggestion(server_iban1.GetIdentifierStringForAutofillDisplay(),
+                           Suggestion::BackendId(Suggestion::InstrumentId(
+                               server_iban1.instrument_id())),
+                           server_iban1.nickname()));
 
-  CHECK(base::StringToInt64(server_iban2.instrument_id(), &instrument_id));
   EXPECT_THAT(
       iban_suggestions[1],
-      EqualsIbanSuggestion(
-          server_iban2.GetIdentifierStringForAutofillDisplay(),
-          Suggestion::BackendId(Suggestion::InstrumentId(instrument_id)),
-          server_iban2.nickname()));
+      EqualsIbanSuggestion(server_iban2.GetIdentifierStringForAutofillDisplay(),
+                           Suggestion::BackendId(Suggestion::InstrumentId(
+                               server_iban2.instrument_id())),
+                           server_iban2.nickname()));
 
   EXPECT_THAT(iban_suggestions[2],
               EqualsIbanSuggestion(
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index 918a0ae7..aaca4e31 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -297,7 +297,7 @@
 }
 
 Iban GetServerIban() {
-  Iban iban(Iban::InstrumentId("1234567"));
+  Iban iban(Iban::InstrumentId(1234567));
   iban.set_prefix(u"FR76");
   iban.set_suffix(u"0189");
   iban.set_length(27);
@@ -306,7 +306,7 @@
 }
 
 Iban GetServerIban2() {
-  Iban iban(Iban::InstrumentId("1234568"));
+  Iban iban(Iban::InstrumentId(1234568));
   iban.set_prefix(u"BE71");
   iban.set_suffix(u"8676");
   iban.set_length(16);
@@ -315,7 +315,7 @@
 }
 
 Iban GetServerIban3() {
-  Iban iban(Iban::InstrumentId("1234569"));
+  Iban iban(Iban::InstrumentId(1234569));
   iban.set_prefix(u"DE91");
   iban.set_suffix(u"6789");
   iban.set_length(22);
diff --git a/components/autofill/core/browser/data_model/iban.cc b/components/autofill/core/browser/data_model/iban.cc
index e92aaa5..168b0fa 100644
--- a/components/autofill/core/browser/data_model/iban.cc
+++ b/components/autofill/core/browser/data_model/iban.cc
@@ -202,7 +202,9 @@
 AutofillMetadata Iban::GetMetadata() const {
   CHECK_NE(record_type_, Iban::kUnknown);
   AutofillMetadata metadata = AutofillDataModel::GetMetadata();
-  metadata.id = record_type_ == Iban::kLocalIban ? guid() : instrument_id();
+  metadata.id = record_type_ == Iban::kLocalIban
+                    ? guid()
+                    : base::NumberToString(instrument_id());
   return metadata;
 }
 
@@ -333,7 +335,7 @@
   return absl::get<Guid>(identifier_).value();
 }
 
-const std::string& Iban::instrument_id() const {
+int64_t Iban::instrument_id() const {
   CHECK(absl::holds_alternative<InstrumentId>(identifier_));
   return absl::get<InstrumentId>(identifier_).value();
 }
diff --git a/components/autofill/core/browser/data_model/iban.h b/components/autofill/core/browser/data_model/iban.h
index dcf2871c..917a3825 100644
--- a/components/autofill/core/browser/data_model/iban.h
+++ b/components/autofill/core/browser/data_model/iban.h
@@ -20,7 +20,7 @@
 class Iban : public AutofillDataModel {
  public:
   using Guid = base::StrongAlias<class GuidTag, std::string>;
-  using InstrumentId = base::StrongAlias<class InstrumentIdTag, std::string>;
+  using InstrumentId = base::StrongAlias<class InstrumentIdTag, int64_t>;
 
   enum RecordType {
     // An IBAN extracted from a submitted form, whose record type is currently
@@ -98,7 +98,7 @@
   void set_identifier(const absl::variant<Guid, InstrumentId>& identifier);
 
   const std::string& guid() const;
-  const std::string& instrument_id() const;
+  int64_t instrument_id() const;
 
   // Returns the value (the actual bank account number) of IBAN.
   const std::u16string& value() const { return value_; }
diff --git a/components/autofill/core/browser/data_model/iban_unittest.cc b/components/autofill/core/browser/data_model/iban_unittest.cc
index 64e16c08..b3d7429 100644
--- a/components/autofill/core/browser/data_model/iban_unittest.cc
+++ b/components/autofill/core/browser/data_model/iban_unittest.cc
@@ -58,9 +58,9 @@
 }
 
 TEST(IbanTest, ConstructServerIban) {
-  Iban server_iban(Iban::InstrumentId("1234567"));
+  Iban server_iban(Iban::InstrumentId(1234567));
   EXPECT_EQ(server_iban.record_type(), Iban::RecordType::kServerIban);
-  EXPECT_EQ("1234567", server_iban.instrument_id());
+  EXPECT_EQ(1234567, server_iban.instrument_id());
 }
 
 TEST(IbanTest, GetMetadata) {
@@ -201,7 +201,7 @@
 }
 
 TEST(IbanTest, GetUserFacingValue_ServerIban_UnmaskNotAllowed) {
-  Iban server_iban(Iban::InstrumentId("1234567"));
+  Iban server_iban(Iban::InstrumentId(1234567));
   // Set the prefix, suffix and length of the server IBAN.
   server_iban.set_prefix(u"FR76");
   server_iban.set_suffix(u"0189");
@@ -212,7 +212,7 @@
 }
 
 TEST(IbanTest, GetUserFacingValue_ServerIban_RegularPrefixAndSuffix) {
-  Iban server_iban(Iban::InstrumentId("1234567"));
+  Iban server_iban(Iban::InstrumentId(1234567));
   // Set the prefix, suffix and length of the server IBAN.
   server_iban.set_prefix(u"FR76");
   server_iban.set_suffix(u"0189");
@@ -223,7 +223,7 @@
 
 TEST(IbanTest, GetUserFacingValue_ServerIban_EmptyPrefix) {
   // Set up a `server_iban` with empty prefix.
-  Iban server_iban(Iban::InstrumentId("1234567"));
+  Iban server_iban(Iban::InstrumentId(1234567));
   server_iban.set_prefix(u"");
   server_iban.set_suffix(u"0189");
   server_iban.set_length(27);
@@ -233,7 +233,7 @@
 
 TEST(IbanTest, GetUserFacingValue_ServerIban_EmptySuffix) {
   // Set up a `server_iban` with empty suffix.
-  Iban server_iban(Iban::InstrumentId("1234567"));
+  Iban server_iban(Iban::InstrumentId(1234567));
   server_iban.set_prefix(u"FR76");
   server_iban.set_suffix(u"");
   server_iban.set_length(27);
@@ -243,7 +243,7 @@
 
 TEST(IbanTest, GetUserFacingValue_ServerIban_OtherLengthOfPrefixAndSuffix) {
   // Set the prefix and suffix of the server IBAN with length other than 4.
-  Iban server_iban(Iban::InstrumentId("1234567"));
+  Iban server_iban(Iban::InstrumentId(1234567));
   server_iban.set_prefix(u"FR7");
   server_iban.set_suffix(u"10189");
   server_iban.set_length(27);
@@ -431,7 +431,7 @@
   Iban local_iban(
       Iban::Guid(base::Uuid::GenerateRandomV4().AsLowercaseString()));
   local_iban.set_value(u"CH56 0483 5012 3456 7800 9");
-  Iban server_iban(Iban::InstrumentId("1234567"));
+  Iban server_iban(Iban::InstrumentId(1234567));
   server_iban.set_prefix(u"CH56");
   server_iban.set_suffix(u"8009");
   server_iban.set_length(21);
diff --git a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
index 4264b0fd..abe8c36 100644
--- a/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/iban_save_manager_unittest.cc
@@ -180,7 +180,7 @@
 
 // Test that an existing server IBAN should not be offered save at all.
 TEST_F(IbanSaveManagerTest, ShouldOfferUploadSave_ServerIban) {
-  Iban iban(Iban::InstrumentId("1234567"));
+  Iban iban(Iban::InstrumentId(1234567));
   iban.set_prefix(u"DE91");
   iban.set_suffix(u"6789");
   iban.set_length(22);
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 52518b0..3a2192b 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -1102,7 +1102,7 @@
   local_iban1.set_value(u"FR76 3000 6000 0112 3456 7890 189");
   Iban local_iban2;
   local_iban2.set_value(u"CH56 0483 5012 3456 7800 9");
-  Iban server_iban1(Iban::InstrumentId("1234567"));
+  Iban server_iban1(Iban::InstrumentId(1234567));
   server_iban1.set_prefix(u"FR76");
   server_iban1.set_suffix(u"0189");
   server_iban1.set_length(27);
diff --git a/components/autofill/core/browser/webdata/autofill_change.h b/components/autofill/core/browser/webdata/autofill_change.h
index c44f07d6..f7fd306 100644
--- a/components/autofill/core/browser/webdata/autofill_change.h
+++ b/components/autofill/core/browser/webdata/autofill_change.h
@@ -64,7 +64,8 @@
       return;
     }
     if constexpr (std::is_same_v<DataType, Iban>) {
-      CHECK(data_model_.guid() == key_ || data_model_.instrument_id() == key_);
+      CHECK(data_model_.guid() == key_ ||
+            base::NumberToString(data_model_.instrument_id()) == key_);
     } else if constexpr (std::is_same_v<DataType, ServerCvc>) {
       CHECK(base::NumberToString(data_model_.instrument_id) == key_);
     } else if constexpr (std::is_same_v<DataType, CreditCard>) {
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc
index 4ab232a..c146f970 100644
--- a/components/autofill/core/browser/webdata/autofill_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_table.cc
@@ -2405,7 +2405,7 @@
   CHECK_EQ(Iban::RecordType::kServerIban, iban.record_type());
   // There's no need to verify if removal succeeded, because if it's a new IBAN,
   // the removal call won't do anything.
-  RemoveServerIbanMetadata(iban.instrument_id());
+  RemoveServerIbanMetadata(base::NumberToString(iban.instrument_id()));
 
   sql::Statement s;
   InsertBuilder(db_, s, kMaskedIbansMetadataTable,
@@ -2553,8 +2553,12 @@
   std::vector<std::unique_ptr<Iban>> ibans;
   while (s.Step()) {
     int index = 0;
+    int64_t instrument_id = 0;
+    if (!base::StringToInt64(s.ColumnString(index++), &instrument_id)) {
+      continue;
+    }
     std::unique_ptr<Iban> iban =
-        std::make_unique<Iban>(Iban::InstrumentId(s.ColumnString(index++)));
+        std::make_unique<Iban>(Iban::InstrumentId(instrument_id));
     iban->set_use_count(s.ColumnInt64(index++));
     iban->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
     iban->set_nickname(s.ColumnString16(index++));
@@ -2582,7 +2586,7 @@
   for (const Iban& iban : ibans) {
     CHECK_EQ(Iban::RecordType::kServerIban, iban.record_type());
     int index = 0;
-    s.BindString(index++, iban.instrument_id());
+    s.BindString(index++, base::NumberToString(iban.instrument_id()));
     s.BindString16(index++, iban.nickname());
     s.BindString16(index++, iban.prefix());
     s.BindString16(index++, iban.suffix());
diff --git a/components/autofill/ios/form_util/BUILD.gn b/components/autofill/ios/form_util/BUILD.gn
index 62e17fe..c2b8ad5 100644
--- a/components/autofill/ios/form_util/BUILD.gn
+++ b/components/autofill/ios/form_util/BUILD.gn
@@ -68,7 +68,7 @@
   sources = [
     "resources/create_fill_namespace.ts",
     "resources/fill_constants.ts",
-    "resources/fill_element_inference.js",
+    "resources/fill_element_inference.ts",
     "resources/fill_element_inference_util.ts",
     "resources/fill_util.js",
   ]
diff --git a/components/autofill/ios/form_util/resources/fill_element_inference.js b/components/autofill/ios/form_util/resources/fill_element_inference.ts
similarity index 73%
rename from components/autofill/ios/form_util/resources/fill_element_inference.js
rename to components/autofill/ios/form_util/resources/fill_element_inference.ts
index 332b2d6..2d13ce3 100644
--- a/components/autofill/ios/form_util/resources/fill_element_inference.js
+++ b/components/autofill/ios/form_util/resources/fill_element_inference.ts
@@ -2,8 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {gCrWeb} from '//ios/web/public/js_messaging/resources/gcrweb.js';
 import * as inferenceUtil from '//components/autofill/ios/form_util/resources/fill_element_inference_util.js';
 
+declare type FormControlElement
+    = HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement;
+
 /**
  * Shared function for InferLabelFromPrevious() and InferLabelFromNext().
  *
@@ -12,14 +16,15 @@
  *                                    bool forward)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @param {boolean} forward whether to search for the next or previous element.
- * @return {string} The label of element or an empty string if there is no
+ * @param element An element to examine.
+ * @param forward whether to search for the next or previous element.
+ * @return The label of element or an empty string if there is no
  *                  sibling or no label.
  */
-function inferLabelFromSibling(element, forward) {
+function inferLabelFromSibling(
+    element: FormControlElement | null, forward: boolean): string {
   let inferredLabel = '';
-  let sibling = element;
+  let sibling: Node | null = element;
   if (!sibling) {
     return '';
   }
@@ -50,18 +55,18 @@
     // Coalesce any text contained in multiple consecutive
     //  (a) plain text nodes or
     //  (b) inline HTML elements that are essentially equivalent to text nodes.
-    if (nodeType === Node.TEXT_NODE || __gCrWeb.fill.hasTagName(sibling, 'b') ||
-        __gCrWeb.fill.hasTagName(sibling, 'strong') ||
-        __gCrWeb.fill.hasTagName(sibling, 'span') ||
-        __gCrWeb.fill.hasTagName(sibling, 'font')) {
+    if (nodeType === Node.TEXT_NODE || gCrWeb.fill.hasTagName(sibling, 'b') ||
+        gCrWeb.fill.hasTagName(sibling, 'strong') ||
+        gCrWeb.fill.hasTagName(sibling, 'span') ||
+        gCrWeb.fill.hasTagName(sibling, 'font')) {
       const value = inferenceUtil.findChildText(sibling);
       // A text node's value will be empty if it is for a line break.
       const addSpace = nodeType === Node.TEXT_NODE && value.length === 0;
       if (forward) {
-        inferredLabel = __gCrWeb.fill.combineAndCollapseWhitespace(
+        inferredLabel = gCrWeb.fill.combineAndCollapseWhitespace(
             inferredLabel, value, addSpace);
       } else {
-        inferredLabel = __gCrWeb.fill.combineAndCollapseWhitespace(
+        inferredLabel = gCrWeb.fill.combineAndCollapseWhitespace(
             value, inferredLabel, addSpace);
       }
       continue;
@@ -76,14 +81,14 @@
 
     // <img> and <br> tags often appear between the input element and its
     // label text, so skip over them.
-    if (__gCrWeb.fill.hasTagName(sibling, 'img') ||
-        __gCrWeb.fill.hasTagName(sibling, 'br')) {
+    if (gCrWeb.fill.hasTagName(sibling, 'img') ||
+        gCrWeb.fill.hasTagName(sibling, 'br')) {
       continue;
     }
 
     // We only expect <p> and <label> tags to contain the full label text.
-    if (__gCrWeb.fill.hasTagName(sibling, 'p') ||
-        __gCrWeb.fill.hasTagName(sibling, 'label')) {
+    if (gCrWeb.fill.hasTagName(sibling, 'p') ||
+        gCrWeb.fill.hasTagName(sibling, 'label')) {
       inferredLabel = inferenceUtil.findChildText(sibling);
     }
     break;
@@ -105,10 +110,11 @@
  *     string16 InferLabelFromPrevious(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-__gCrWeb.fill.inferLabelFromPrevious = function(element) {
+gCrWeb.fill.inferLabelFromPrevious = function(
+    element: FormControlElement): string {
   return inferLabelFromSibling(element, false);
 };
 
@@ -120,10 +126,10 @@
  *     string16 InferLabelFromNext(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-function inferLabelFromNext(element) {
+function inferLabelFromNext(element: FormControlElement): string {
   return inferLabelFromSibling(element, true);
 }
 
@@ -135,15 +141,19 @@
  *     string16 InferLabelFromPlaceholder(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-function inferLabelFromPlaceholder(element) {
+function inferLabelFromPlaceholder(element: FormControlElement): string {
   if (!element) {
     return '';
   }
 
-  return element.placeholder || element.getAttribute('placeholder') || '';
+  if ('placeholder' in element) {
+    return element.placeholder;
+  }
+
+  return element.getAttribute('placeholder') || '';
 }
 
 /**
@@ -155,10 +165,10 @@
  *     string16 InferLabelFromValueAttr(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-function inferLabelFromValueAttr(element) {
+function inferLabelFromValueAttr(element: FormControlElement): string {
   if (!element || !element.value || !element.hasAttribute('value') ||
       element.value !== element.getAttribute('value')) {
     return '';
@@ -176,21 +186,22 @@
  *     string16 InferLabelFromListItem(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-__gCrWeb.fill.inferLabelFromListItem = function(element) {
+gCrWeb.fill.inferLabelFromListItem = function(
+    element: FormControlElement): string {
   if (!element) {
     return '';
   }
 
   let parentNode = element.parentNode;
   while (parentNode && parentNode.nodeType === Node.ELEMENT_NODE &&
-         !__gCrWeb.fill.hasTagName(parentNode, 'li')) {
+         !gCrWeb.fill.hasTagName(parentNode, 'li')) {
     parentNode = parentNode.parentNode;
   }
 
-  if (parentNode && __gCrWeb.fill.hasTagName(parentNode, 'li')) {
+  if (parentNode && gCrWeb.fill.hasTagName(parentNode, 'li')) {
     return inferenceUtil.findChildText(parentNode);
   }
 
@@ -209,17 +220,18 @@
  *    string16 InferLabelFromTableColumn(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-__gCrWeb.fill.inferLabelFromTableColumn = function(element) {
+gCrWeb.fill.inferLabelFromTableColumn = function(
+    element: FormControlElement): string {
   if (!element) {
     return '';
   }
 
   let parentNode = element.parentNode;
   while (parentNode && parentNode.nodeType === Node.ELEMENT_NODE &&
-         !__gCrWeb.fill.hasTagName(parentNode, 'td')) {
+         !gCrWeb.fill.hasTagName(parentNode, 'td')) {
     parentNode = parentNode.parentNode;
   }
 
@@ -232,8 +244,8 @@
   let inferredLabel = '';
   let previous = parentNode.previousSibling;
   while (inferredLabel.length === 0 && previous) {
-    if (__gCrWeb.fill.hasTagName(previous, 'td') ||
-        __gCrWeb.fill.hasTagName(previous, 'th')) {
+    if (gCrWeb.fill.hasTagName(previous, 'td') ||
+        gCrWeb.fill.hasTagName(previous, 'th')) {
       inferredLabel = inferenceUtil.findChildText(previous);
     }
     previous = previous.previousSibling;
@@ -256,23 +268,25 @@
  *     string16 InferLabelFromTableRow(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-__gCrWeb.fill.inferLabelFromTableRow = function(element) {
+gCrWeb.fill.inferLabelFromTableRow = function(
+    element: FormControlElement): string {
   if (!element) {
     return '';
   }
 
-  let cell = element.parentNode;
-  while (cell) {
-    if (cell.nodeType === Node.ELEMENT_NODE &&
-        __gCrWeb.fill.hasTagName(cell, 'td')) {
+  let parentCell = element.parentNode;
+  while (parentCell) {
+    if (parentCell.nodeType === Node.ELEMENT_NODE &&
+        gCrWeb.fill.hasTagName(parentCell, 'td')) {
       break;
     }
-    cell = cell.parentNode;
+    parentCell = parentCell.parentNode;
   }
 
+  const cell: HTMLTableCellElement | null = parentCell as HTMLTableCellElement;
   // Not in a cell - bail out.
   if (!cell) {
     return '';
@@ -287,8 +301,8 @@
   let cellIterator = cell.previousSibling;
   while (cellIterator) {
     if (cellIterator.nodeType === Node.ELEMENT_NODE &&
-        __gCrWeb.fill.hasTagName(cellIterator, 'td')) {
-      cellPosition += cellIterator.colSpan;
+        gCrWeb.fill.hasTagName(cellIterator, 'td')) {
+      cellPosition += (cellIterator as HTMLTableCellElement).colSpan;
     }
     cellIterator = cellIterator.previousSibling;
   }
@@ -297,8 +311,8 @@
   cellIterator = cell.nextSibling;
   while (cellIterator) {
     if (cellIterator.nodeType === Node.ELEMENT_NODE &&
-        __gCrWeb.fill.hasTagName(cellIterator, 'td')) {
-      cellCount += cellIterator.colSpan;
+        gCrWeb.fill.hasTagName(cellIterator, 'td')) {
+      cellCount += (cellIterator as HTMLTableCellElement).colSpan;
     }
     cellIterator = cellIterator.nextSibling;
   }
@@ -310,7 +324,7 @@
   // Find the current row.
   let parentNode = element.parentNode;
   while (parentNode && parentNode.nodeType === Node.ELEMENT_NODE &&
-         !__gCrWeb.fill.hasTagName(parentNode, 'tr')) {
+         !gCrWeb.fill.hasTagName(parentNode, 'tr')) {
     parentNode = parentNode.parentNode;
   }
 
@@ -322,7 +336,7 @@
   let rowIt = parentNode.previousSibling;
   while (rowIt) {
     if (rowIt.nodeType === Node.ELEMENT_NODE &&
-        __gCrWeb.fill.hasTagName(parentNode, 'tr')) {
+        gCrWeb.fill.hasTagName(parentNode, 'tr')) {
       break;
     }
     rowIt = rowIt.previousSibling;
@@ -336,9 +350,9 @@
     let prevRowIt = rowIt.firstChild;
     while (prevRowIt) {
       if (prevRowIt.nodeType === Node.ELEMENT_NODE) {
-        if (__gCrWeb.fill.hasTagName(prevRowIt, 'td') ||
-            __gCrWeb.fill.hasTagName(prevRowIt, 'th')) {
-          const span = prevRowIt.colSpan;
+        if (gCrWeb.fill.hasTagName(prevRowIt, 'td') ||
+            gCrWeb.fill.hasTagName(prevRowIt, 'th')) {
+          const span = (prevRowIt as HTMLTableCellElement).colSpan;
           const prevRowCountEnd = prevRowCount + span - 1;
           if (prevRowCount === cellPosition &&
               prevRowCountEnd === cellPositionEnd) {
@@ -363,7 +377,7 @@
   let inferredLabel = '';
   let previous = parentNode.previousSibling;
   while (inferredLabel.length === 0 && previous) {
-    if (__gCrWeb.fill.hasTagName(previous, 'tr')) {
+    if (gCrWeb.fill.hasTagName(previous, 'tr')) {
       inferredLabel = inferenceUtil.findChildText(previous);
     }
     previous = previous.previousSibling;
@@ -381,15 +395,16 @@
  *        const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-__gCrWeb.fill.inferLabelFromEnclosingLabel = function(element) {
+gCrWeb.fill.inferLabelFromEnclosingLabel = function(
+    element: FormControlElement): string {
   if (!element) {
     return '';
   }
   let node = element.parentNode;
-  while (node && !__gCrWeb.fill.hasTagName(node, 'label')) {
+  while (node && !gCrWeb.fill.hasTagName(node, 'label')) {
     node = node.parentNode;
   }
   if (node) {
@@ -417,22 +432,23 @@
  *    string16 InferLabelFromDivTable(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-__gCrWeb.fill.inferLabelFromDivTable = function(element) {
+gCrWeb.fill.inferLabelFromDivTable = function(
+    element: FormControlElement): string {
   if (!element) {
     return '';
   }
 
-  let node = element.parentNode;
+  let node: ParentNode | ChildNode | null = element.parentNode;
   let lookingForParent = true;
-  const divsToSkip = [];
+  const divsToSkip: Node[] = [];
 
   // Search the sibling and parent <div>s until we find a candidate label.
   let inferredLabel = '';
   while (inferredLabel.length === 0 && node) {
-    if (__gCrWeb.fill.hasTagName(node, 'div')) {
+    if (gCrWeb.fill.hasTagName(node, 'div')) {
       if (lookingForParent) {
         inferredLabel =
             inferenceUtil.findChildTextWithIgnoreList(node, divsToSkip);
@@ -440,8 +456,9 @@
         inferredLabel = inferenceUtil.findChildText(node);
       }
       // Avoid sibling DIVs that contain autofillable fields.
-      if (!lookingForParent && inferredLabel.length > 0) {
-        const resultElement = node.querySelector('input, select, textarea');
+      if (!lookingForParent &&inferredLabel.length > 0) {
+        const resultElement = (node as HTMLDivElement)
+            .querySelector('input, select, textarea');
         if (resultElement) {
           inferredLabel = '';
           let addDiv = true;
@@ -460,10 +477,11 @@
       lookingForParent = false;
     } else if (!lookingForParent) {
       // Infer a label from text nodes and unassigned <label> siblings.
-      if (__gCrWeb.fill.hasTagName(node, 'label') && !node.control) {
+      if (gCrWeb.fill.hasTagName(node, 'label') &&
+          !(node as HTMLLabelElement).control) {
         inferredLabel = inferenceUtil.findChildText(node);
       } else if (node.nodeType === Node.TEXT_NODE) {
-        inferredLabel = __gCrWeb.fill.nodeValue(node).trim();
+        inferredLabel = gCrWeb.fill.nodeValue(node).trim();
       }
     } else if (inferenceUtil.isTraversableContainerElement(node)) {
       // If the element is in a non-div container, its label most likely is too.
@@ -496,21 +514,22 @@
  *        const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The label of element.
+ * @param element An element to examine.
+ * @return The label of element.
  */
-__gCrWeb.fill.inferLabelFromDefinitionList = function(element) {
+gCrWeb.fill.inferLabelFromDefinitionList = function(
+    element: FormControlElement): string {
   if (!element) {
     return '';
   }
 
   let parentNode = element.parentNode;
   while (parentNode && parentNode.nodeType === Node.ELEMENT_NODE &&
-         !__gCrWeb.fill.hasTagName(parentNode, 'dd')) {
+         !gCrWeb.fill.hasTagName(parentNode, 'dd')) {
     parentNode = parentNode.parentNode;
   }
 
-  if (!parentNode || !__gCrWeb.fill.hasTagName(parentNode, 'dd')) {
+  if (!parentNode || !gCrWeb.fill.hasTagName(parentNode, 'dd')) {
     return '';
   }
 
@@ -520,7 +539,7 @@
     previous = previous.previousSibling;
   }
 
-  if (!previous || !__gCrWeb.fill.hasTagName(previous, 'dt')) {
+  if (!previous || !gCrWeb.fill.hasTagName(previous, 'dt')) {
     return '';
   }
 
@@ -535,19 +554,20 @@
  *    string16 InferLabelForElement(const WebFormControlElement& element)
  * in chromium/src/components/autofill/content/renderer/form_autofill_util.cc.
  *
- * @param {FormControlElement} element An element to examine.
- * @return {string} The inferred label of element, or '' if none could be found.
+ * @param element An element to examine.
+ * @return The inferred label of element, or '' if none could be found.
  */
-__gCrWeb.fill.inferLabelForElement = function(element) {
+gCrWeb.fill.inferLabelForElement = function(
+    element: FormControlElement): string {
   let inferredLabel;
-  if (__gCrWeb.fill.isCheckableElement(element)) {
+  if (gCrWeb.fill.isCheckableElement(element)) {
     inferredLabel = inferLabelFromNext(element);
     if (inferenceUtil.isLabelValid(inferredLabel)) {
       return inferredLabel;
     }
   }
 
-  inferredLabel = __gCrWeb.fill.inferLabelFromPrevious(element);
+  inferredLabel = gCrWeb.fill.inferLabelFromPrevious(element);
   if (inferenceUtil.isLabelValid(inferredLabel)) {
     return inferredLabel;
   }
@@ -559,7 +579,7 @@
   }
 
   // If we didn't find a placeholder, check for the aria-label case.
-  inferredLabel = __gCrWeb.fill.getAriaLabel(element);
+  inferredLabel = gCrWeb.fill.getAriaLabel(element);
   if (inferenceUtil.isLabelValid(inferredLabel)) {
     return inferredLabel;
   }
@@ -567,27 +587,27 @@
   // For all other searches that involve traversing up the tree, the search
   // order is based on which tag is the closest ancestor to |element|.
   const tagNames = inferenceUtil.ancestorTagNames(element);
-  const seenTagNames = {};
+  const seenTagNames: {[key: string]: boolean} = {};
   for (let index = 0; index < tagNames.length; ++index) {
-    const tagName = tagNames[index];
+    const tagName = tagNames[index]!;
     if (tagName in seenTagNames) {
       continue;
     }
 
     seenTagNames[tagName] = true;
     if (tagName === 'LABEL') {
-      inferredLabel = __gCrWeb.fill.inferLabelFromEnclosingLabel(element);
+      inferredLabel = gCrWeb.fill.inferLabelFromEnclosingLabel(element);
     } else if (tagName === 'DIV') {
-      inferredLabel = __gCrWeb.fill.inferLabelFromDivTable(element);
+      inferredLabel = gCrWeb.fill.inferLabelFromDivTable(element);
     } else if (tagName === 'TD') {
-      inferredLabel = __gCrWeb.fill.inferLabelFromTableColumn(element);
+      inferredLabel = gCrWeb.fill.inferLabelFromTableColumn(element);
       if (!inferenceUtil.isLabelValid(inferredLabel)) {
-        inferredLabel = __gCrWeb.fill.inferLabelFromTableRow(element);
+        inferredLabel = gCrWeb.fill.inferLabelFromTableRow(element);
       }
     } else if (tagName === 'DD') {
-      inferredLabel = __gCrWeb.fill.inferLabelFromDefinitionList(element);
+      inferredLabel = gCrWeb.fill.inferLabelFromDefinitionList(element);
     } else if (tagName === 'LI') {
-      inferredLabel = __gCrWeb.fill.inferLabelFromListItem(element);
+      inferredLabel = gCrWeb.fill.inferLabelFromListItem(element);
     } else if (tagName === 'FIELDSET') {
       break;
     }
diff --git a/components/components_strings.grd b/components/components_strings.grd
index 0ccb816..887f259 100644
--- a/components/components_strings.grd
+++ b/components/components_strings.grd
@@ -316,6 +316,7 @@
       <part file="payments_strings.grdp" />
       <part file="pdf_strings.grdp" />
       <part file="permissions_strings.grdp" />
+      <part file="plus_addresses_strings.grdp" />
       <part file="policy_strings.grdp" />
       <part file="print_media_strings.grdp" />
       <part file="printing_component_strings.grdp" />
diff --git a/components/media_effects/test/fake_video_capture_service.cc b/components/media_effects/test/fake_video_capture_service.cc
index 0784468..1f6ca88e 100644
--- a/components/media_effects/test/fake_video_capture_service.cc
+++ b/components/media_effects/test/fake_video_capture_service.cc
@@ -20,6 +20,13 @@
   fake_provider_.SetOnRepliedWithSourceInfosCallback(std::move(callback));
 }
 
+// `callback` will be triggered when the source provider receives a
+// GetVideoSource call.
+void FakeVideoCaptureService::SetOnGetVideoSourceCallback(
+    FakeVideoSourceProvider::GetVideoSourceCallback callback) {
+  fake_provider_.SetOnGetVideoSourceCallback(std::move(callback));
+}
+
 void FakeVideoCaptureService::ConnectToVideoSourceProvider(
     mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver) {
   fake_provider_.Bind(std::move(receiver));
diff --git a/components/media_effects/test/fake_video_capture_service.h b/components/media_effects/test/fake_video_capture_service.h
index 690b72b..996f453 100644
--- a/components/media_effects/test/fake_video_capture_service.h
+++ b/components/media_effects/test/fake_video_capture_service.h
@@ -8,6 +8,8 @@
 #include "components/media_effects/test/fake_video_source_provider.h"
 #include "services/video_capture/public/mojom/video_capture_service.mojom.h"
 
+#include <string>
+
 namespace media_effects {
 
 class FakeVideoCaptureService
@@ -25,10 +27,15 @@
 
   void RemoveFakeCamera(const std::string& device_id);
 
-  // `callback` will be triggered after this source provider replies back to its
+  // `callback` will be triggered after the source provider replies back to its
   // client in GetSourceInfos(). Useful as a stopping point for a base::RunLoop.
   void SetOnRepliedWithSourceInfosCallback(base::OnceClosure callback);
 
+  // `callback` will be triggered when the source provider receives a
+  // GetVideoSource call.
+  void SetOnGetVideoSourceCallback(
+      FakeVideoSourceProvider::GetVideoSourceCallback callback);
+
   // video_capture::mojom::VideoCaptureService implementation
   void ConnectToVideoSourceProvider(
       mojo::PendingReceiver<video_capture::mojom::VideoSourceProvider> receiver)
diff --git a/components/media_effects/test/fake_video_source_provider.cc b/components/media_effects/test/fake_video_source_provider.cc
index ceb10adb..42d8f20 100644
--- a/components/media_effects/test/fake_video_source_provider.cc
+++ b/components/media_effects/test/fake_video_source_provider.cc
@@ -33,6 +33,13 @@
   on_replied_with_source_infos_ = std::move(callback);
 }
 
+// `callback` will be triggered when the source provider receives a
+// GetVideoSource call.
+void FakeVideoSourceProvider::SetOnGetVideoSourceCallback(
+    GetVideoSourceCallback callback) {
+  on_get_video_source_ = std::move(callback);
+}
+
 void FakeVideoSourceProvider::GetSourceInfos(GetSourceInfosCallback callback) {
   std::vector<media::VideoCaptureDeviceInfo> devices;
   for (const auto& [_, device_info] : device_infos_) {
@@ -54,7 +61,7 @@
 void FakeVideoSourceProvider::GetVideoSource(
     const std::string& source_id,
     mojo::PendingReceiver<video_capture::mojom::VideoSource> stream) {
-  NOTIMPLEMENTED() << source_id;
+  on_get_video_source_.Run(source_id, std::move(stream));
 }
 
 void FakeVideoSourceProvider::Close(CloseCallback callback) {}
diff --git a/components/media_effects/test/fake_video_source_provider.h b/components/media_effects/test/fake_video_source_provider.h
index fa443aa..f136876 100644
--- a/components/media_effects/test/fake_video_source_provider.h
+++ b/components/media_effects/test/fake_video_source_provider.h
@@ -35,6 +35,14 @@
   // client in GetSourceInfos(). Useful as a stopping point for a base::RunLoop.
   void SetOnRepliedWithSourceInfosCallback(base::OnceClosure callback);
 
+  using GetVideoSourceCallback = base::RepeatingCallback<void(
+      const std::string& source_id,
+      mojo::PendingReceiver<video_capture::mojom::VideoSource> stream)>;
+
+  // `callback` will be triggered when this source provider receives a
+  // GetVideoSource call.
+  void SetOnGetVideoSourceCallback(GetVideoSourceCallback callback);
+
   // video_capture::mojom::VideoSourceProvider:
   void GetSourceInfos(GetSourceInfosCallback callback) override;
   void GetVideoSource(
@@ -67,6 +75,7 @@
       device_infos_;
 
   base::OnceClosure on_replied_with_source_infos_;
+  GetVideoSourceCallback on_get_video_source_;
 };
 
 }  // namespace media_effects
diff --git a/components/metrics/generate_expired_histograms_array.gni b/components/metrics/generate_expired_histograms_array.gni
index ca46916..9fd3f35 100644
--- a/components/metrics/generate_expired_histograms_array.gni
+++ b/components/metrics/generate_expired_histograms_array.gni
@@ -73,6 +73,7 @@
       "//tools/metrics/histograms/metadata/content/enums.xml",
       "//tools/metrics/histograms/metadata/content/histograms.xml",
       "//tools/metrics/histograms/metadata/content_creation/histograms.xml",
+      "//tools/metrics/histograms/metadata/cookie/enums.xml",
       "//tools/metrics/histograms/metadata/cookie/histograms.xml",
       "//tools/metrics/histograms/metadata/cras/histograms.xml",
       "//tools/metrics/histograms/metadata/cros/histograms.xml",
@@ -190,6 +191,7 @@
       "//tools/metrics/histograms/metadata/stability/histograms.xml",
       "//tools/metrics/histograms/metadata/start_surface/histograms.xml",
       "//tools/metrics/histograms/metadata/startup/histograms.xml",
+      "//tools/metrics/histograms/metadata/storage/enums.xml",
       "//tools/metrics/histograms/metadata/storage/histograms.xml",
       "//tools/metrics/histograms/metadata/subresource/histograms.xml",
       "//tools/metrics/histograms/metadata/structured_metrics/histograms.xml",
@@ -197,6 +199,7 @@
       "//tools/metrics/histograms/metadata/sync/histograms.xml",
       "//tools/metrics/histograms/metadata/system/histograms.xml",
       "//tools/metrics/histograms/metadata/invalidation/histograms.xml",
+      "//tools/metrics/histograms/metadata/tab/enums.xml",
       "//tools/metrics/histograms/metadata/tab/histograms.xml",
       "//tools/metrics/histograms/metadata/translate/histograms.xml",
       "//tools/metrics/histograms/metadata/trusted_vault/histograms.xml",
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 5cdd437..2e30573 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -11,6 +11,7 @@
 #include <map>
 #include <memory>
 #include <numeric>
+#include <queue>
 #include <set>
 #include <string>
 #include <unordered_set>
@@ -1832,6 +1833,9 @@
   std::priority_queue<int> relevance_heap;
   std::priority_queue<std::pair<float, AutocompleteResult::iterator>>
       prediction_and_match_itr_heap;
+  // Likewise, keep the same number of shortcut boosted suggestions but reassign
+  // them to the highest scoring suggestions.
+  size_t boosted_shortcut_count = 0;
   for (auto& [prediction, stripped_destination_url] : results) {
     if (!prediction.has_value()) {
       continue;
@@ -1847,6 +1851,8 @@
 
     relevance_heap.emplace(match_itr->relevance);
     prediction_and_match_itr_heap.emplace(prediction.value(), match_itr);
+    if (match_itr->shortcut_boosted)
+      boosted_shortcut_count++;
   }
 
   if (!relevance_heap.empty()) {
@@ -1873,6 +1879,13 @@
       match_itr->RecordAdditionalInfo(
           "ml model output", prediction_and_match_itr_heap.top().first);
       match_itr->relevance = relevance_heap.top();
+      if (boosted_shortcut_count) {
+        match_itr->RecordAdditionalInfo("ML shortcut boosted", "true");
+        match_itr->shortcut_boosted = true;
+        boosted_shortcut_count--;
+      } else {
+        match_itr->shortcut_boosted = false;
+      }
     }
     relevance_heap.pop();
     prediction_and_match_itr_heap.pop();
diff --git a/components/omnibox/browser/autocomplete_controller_unittest.cc b/components/omnibox/browser/autocomplete_controller_unittest.cc
index d68987f..8cfa05a 100644
--- a/components/omnibox/browser/autocomplete_controller_unittest.cc
+++ b/components/omnibox/browser/autocomplete_controller_unittest.cc
@@ -9,12 +9,16 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
+#include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_type.h"
+#include "components/omnibox/browser/autocomplete_scoring_model_service.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/test_scheme_classifier.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -45,6 +49,53 @@
 };
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
 
+namespace {
+
+// Used to construct the ML input matches and ML output response.
+struct MlMatchTestData {
+  std::string name;
+  AutocompleteMatchType::Type type;
+  bool allowed_to_be_default_match;
+  bool shortcut_boosted;
+  int traditional_relevance;
+  float ml_output;
+
+  static MlMatchTestData MakeSearch(std::string name,
+                                    bool allowed_to_be_default_match,
+                                    int traditional_relevance) {
+    return {name,
+            AutocompleteMatchType::SEARCH_SUGGEST,
+            allowed_to_be_default_match,
+            false,
+            traditional_relevance,
+            -1};
+  }
+
+  static MlMatchTestData MakeHistory(std::string name,
+                                     bool allowed_to_be_default_match,
+                                     int traditional_relevance,
+                                     float ml_output) {
+    return {name,
+            AutocompleteMatchType::HISTORY_URL,
+            allowed_to_be_default_match,
+            false,
+            traditional_relevance,
+            ml_output};
+  }
+
+  static MlMatchTestData MakeShortcut(std::string name,
+                                      int traditional_relevance,
+                                      float ml_output) {
+    return {name,
+            AutocompleteMatchType::HISTORY_URL,
+            true,
+            true,
+            traditional_relevance,
+            ml_output};
+  }
+};
+}  // namespace
+
 class AutocompleteControllerTest : public testing::Test {
  public:
   AutocompleteControllerTest() = default;
@@ -104,6 +155,45 @@
     return controller_->autocomplete_provider_client();
   }
 
+  std::vector<std::string> MlRank(std::vector<MlMatchTestData> datas) {
+    ACMatches matches;
+    std::vector<AutocompleteScoringModelService::Result> ml_results;
+    for (const auto& data : datas) {
+      AutocompleteMatch match{nullptr, data.traditional_relevance, false,
+                              data.type};
+      match.shortcut_boosted = data.shortcut_boosted;
+      match.allowed_to_be_default_match = data.allowed_to_be_default_match;
+      match.stripped_destination_url = GURL{"https://google.com/" + data.name};
+      match.contents = base::UTF8ToUTF16(data.name);
+      if (data.ml_output >= 0) {
+        match.scoring_signals = {{}};
+        ml_results.push_back(
+            {data.ml_output, match.stripped_destination_url.spec()});
+      }
+      matches.push_back(match);
+    }
+
+    controller_->internal_result_.Reset();
+    controller_->internal_result_.AppendMatches(matches);
+    base::RunLoop ml_rank_loop;
+    controller_->OnUrlScoringModelDone(
+        {}, base::BindLambdaForTesting([&]() {
+          AutocompleteInput input(u"text", 4, metrics::OmniboxEventProto::OTHER,
+                                  TestSchemeClassifier());
+          controller_->internal_result_.SortAndCull(
+              input, nullptr,
+              provider_client()->GetOmniboxTriggeredFeatureService());
+          ml_rank_loop.Quit();
+        }),
+        ml_results);
+    ml_rank_loop.Run();
+
+    std::vector<std::string> names;
+    for (const auto& match : controller_->internal_result_)
+      names.push_back(base::UTF16ToUTF8(match.contents));
+    return names;
+  }
+
  protected:
   std::unique_ptr<AutocompleteController> controller_;
 
@@ -252,3 +342,170 @@
           ->GetFeatureTriggeredInSession(
               metrics::OmniboxEventProto_Feature_COMPANY_ENTITY_ADJUSTMENT));
 }
+
+// Android and iOS aren't ready for ML and won't pass this test because they
+// have their own grouping code.
+#if BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_ANDROID) && \
+    !BUILDFLAG(IS_IOS)
+TEST_F(AutocompleteControllerTest, MlRanking) {
+  EXPECT_THAT(MlRank({}), testing::ElementsAre());
+
+  // Even if ML ranks a URL 0, it should still use traditional scores.
+  EXPECT_THAT(MlRank({
+                  MlMatchTestData::MakeHistory("history", true, 1400, 0),
+                  MlMatchTestData::MakeSearch("search", true, 1300),
+              }),
+              testing::ElementsAreArray({
+                  "history",
+                  "search",
+              }));
+
+  // Simple case of redistributing ranking among only URLs.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeHistory("history 1350 .5", true, 1350, .5),
+          MlMatchTestData::MakeSearch("search 1400", false, 1400),
+          MlMatchTestData::MakeSearch("search 800", true, 800),
+          MlMatchTestData::MakeSearch("search 600", false, 600),
+          MlMatchTestData::MakeHistory("history 1200 .9", true, 1200, .9),
+          MlMatchTestData::MakeHistory("history 1100 .1", false, 1100, .1),
+          MlMatchTestData::MakeHistory("history 500 .2", true, 500, .2),
+      }),
+      testing::ElementsAreArray({
+          "history 1200 .9",
+          "search 1400",
+          "search 800",
+          "search 600",
+          "history 1350 .5",
+          "history 500 .2",
+          "history 1100 .1",
+      }));
+
+  // Can change the default suggestion from 1 history to another.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeHistory("history 1400 .5", true, 1400, .5),
+          MlMatchTestData::MakeSearch("search", true, 1300),
+          MlMatchTestData::MakeHistory("history 1200 1", true, 1200, .9),
+      }),
+      testing::ElementsAreArray({
+          "history 1200 1",
+          "search",
+          "history 1400 .5",
+      }));
+
+  // Can change the default from search to history.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeSearch("search 1300", true, 1300),
+          MlMatchTestData::MakeHistory("history 1400 .5", false, 1400, .5),
+          MlMatchTestData::MakeHistory("history 1200 1", true, 1200, .9),
+      }),
+      testing::ElementsAreArray({
+          "history 1200 1",
+          "search 1300",
+          "history 1400 .5",
+      }));
+
+  // Can change the default from history to search.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeHistory("history 1400 .5", true, 1400, .5),
+          MlMatchTestData::MakeSearch("search 1300", true, 1300),
+          MlMatchTestData::MakeHistory("history 1200 1", false, 1200, .9),
+      }),
+      testing::ElementsAreArray({
+          "search 1300",
+          "history 1200 1",
+          "history 1400 .5",
+      }));
+
+  // Can redistribute shortcut boosting to non-shortcuts.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeSearch("search 1300", true, 1300),
+          MlMatchTestData::MakeShortcut("shortcut 1000 .1", 1000, .1),
+          MlMatchTestData::MakeSearch("search 1200", true, 1200),
+          MlMatchTestData::MakeHistory("history 1400 .9", false, 1400, .9),
+          MlMatchTestData::MakeHistory("history 1100 .5", true, 1100, .5),
+      }),
+      testing::ElementsAreArray({
+          "search 1300",
+          "history 1400 .9",
+          "search 1200",
+          "history 1100 .5",
+          "shortcut 1000 .1",
+      }));
+
+  // Can 'consume' shortcut boosting by assigning it to a match that's becoming
+  // default anyways.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeSearch("search 1300", true, 1300),
+          MlMatchTestData::MakeShortcut("shortcut 1000 .1", 1000, .1),
+          MlMatchTestData::MakeSearch("search 1200", true, 1200),
+          MlMatchTestData::MakeHistory("history 1400 .5", false, 1400, .5),
+          MlMatchTestData::MakeHistory("history 1100 .9", true, 1100, .9),
+      }),
+      testing::ElementsAreArray({
+          "history 1100 .9",
+          "search 1300",
+          "search 1200",
+          "history 1400 .5",
+          "shortcut 1000 .1",
+      }));
+
+  // Can increase the number of URLs above searches.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeSearch("search 1300", true, 1300),
+          MlMatchTestData::MakeShortcut("shortcut 1000 .7", 1000, .7),
+          MlMatchTestData::MakeSearch("search 1200", true, 1200),
+          MlMatchTestData::MakeHistory("history 1400 .5", false, 1400, .5),
+          MlMatchTestData::MakeHistory("history 1350 .2", false, 1350, .2),
+          MlMatchTestData::MakeHistory("history 1100 .8", true, 1100, .8),
+          MlMatchTestData::MakeHistory("history 1050 .9", false, 1050, .9),
+      }),
+      testing::ElementsAreArray({
+          "history 1100 .8",
+          "history 1050 .9",
+          "search 1300",
+          "search 1200",
+          "shortcut 1000 .7",
+          "history 1400 .5",
+          "history 1350 .2",
+      }));
+
+  // Can increase the number of URLs above searches even when the default was a
+  // URL.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeShortcut("shortcut 1450 .7", 1450, .7),
+          MlMatchTestData::MakeSearch("search 1200", true, 1200),
+          MlMatchTestData::MakeHistory("history 1400 .9", false, 1400, .9),
+      }),
+      testing::ElementsAreArray({
+          "shortcut 1450 .7",
+          "history 1400 .9",
+          "search 1200",
+      }));
+
+  // Can decrease the number of URLs above searches.
+  EXPECT_THAT(
+      MlRank({
+          MlMatchTestData::MakeHistory("history 1400 .5", true, 1400, .5),
+          MlMatchTestData::MakeShortcut("shortcut 1000 .1", 1000, .1),
+          MlMatchTestData::MakeSearch("search 1300", true, 1300),
+          MlMatchTestData::MakeSearch("search 1200", true, 1200),
+          MlMatchTestData::MakeHistory("history 1100 .9", true, 1100, .9),
+      }),
+      testing::ElementsAreArray({
+          "history 1100 .9",
+          "search 1300",
+          "search 1200",
+          "history 1400 .5",
+          "shortcut 1000 .1",
+      }));
+}
+#endif  //  BUILDFLAG(BUILD_WITH_TFLITE_LIB) && !BUILDFLAG(IS_ANDROID) &&
+        //  !BUILDFLAG(IS_IOS)
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc
index 143cd2e..177acbf9 100644
--- a/components/omnibox/browser/autocomplete_result.cc
+++ b/components/omnibox/browser/autocomplete_result.cc
@@ -1456,15 +1456,15 @@
 #if !BUILDFLAG(IS_IOS)
     // Group history cluster suggestions with searches.
     if (m.type == AutocompleteMatchType::HISTORY_CLUSTER)
-      return 1;
+      return 2;
 #endif  // !BUILDFLAG(IS_IOS)
     if (AutocompleteMatch::IsSearchType(m.type))
-      return 1;
-    // Group boosted shortcuts with searches.
+      return 2;
+    // Group boosted shortcuts above searches.
     if (omnibox_feature_configs::ShortcutBoosting::Get().group_with_searches &&
         m.shortcut_boosted) {
       return 1;
     }
-    return 2;
+    return 3;
   });
 }
diff --git a/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.cc b/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.cc
index efe92af..46ae50a 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.cc
@@ -218,4 +218,24 @@
       .should_ignore_input_context = should_ignore_input_context};
 }
 
+std::optional<proto::Any>
+OnDeviceModelExecutionConfigInterpreter::ConstructOutputMetadata(
+    proto::ModelExecutionFeature feature,
+    const std::string& output) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!HasConfigForFeature(feature)) {
+    return std::nullopt;
+  }
+
+  auto feature_config = feature_configs_.at(feature);
+  if (!feature_config.has_output_config()) {
+    return std::nullopt;
+  }
+  auto output_config = feature_config.output_config();
+
+  return SetProtoValue(output_config.proto_type(), output_config.proto_field(),
+                       output);
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.h b/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.h
index 1caf518..1fd4868 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter.h
@@ -47,6 +47,13 @@
       const google::protobuf::MessageLite& request,
       bool want_input_context) const;
 
+  // Constructs the output metadata for `feature` and `output`. Will return
+  // std::nullopt if there is not a valid config for the feature or could not be
+  // fulfilled for any reason.
+  std::optional<proto::Any> ConstructOutputMetadata(
+      proto::ModelExecutionFeature feature,
+      const std::string& output) const;
+
  private:
   // Populates `feature_configs_` based on `config`.
   void PopulateFeatureConfigs(
diff --git a/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter_unittest.cc
index b126320..af53a2e0 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter_unittest.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_execution_config_interpreter_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/task_environment.h"
 #include "base/test/test.pb.h"
+#include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/proto/features/compose.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -398,6 +399,92 @@
   EXPECT_FALSE(result->should_ignore_input_context);
 }
 
+TEST_F(OnDeviceModelExecutionConfigInterpeterTest,
+       ConstructOutputMetadataNoConfiguration) {
+  auto maybe_metadata = interpreter()->ConstructOutputMetadata(
+      proto::MODEL_EXECUTION_FEATURE_COMPOSE, "output");
+
+  EXPECT_FALSE(maybe_metadata.has_value());
+}
+
+TEST_F(OnDeviceModelExecutionConfigInterpeterTest,
+       ConstructOutputMetadataNoOnDeviceConfigForFeature) {
+  proto::OnDeviceModelExecutionConfig config;
+  config.add_feature_configs()->set_feature(
+      proto::MODEL_EXECUTION_FEATURE_TAB_ORGANIZATION);
+  UpdateInterpreterWithConfig(config);
+
+  auto maybe_metadata = interpreter()->ConstructOutputMetadata(
+      proto::MODEL_EXECUTION_FEATURE_COMPOSE, "output");
+
+  EXPECT_FALSE(maybe_metadata.has_value());
+}
+
+TEST_F(OnDeviceModelExecutionConfigInterpeterTest,
+       ConstructOutputMetadataOnDeviceConfigHasNoOutputConfig) {
+  proto::OnDeviceModelExecutionConfig config;
+  auto* fc = config.add_feature_configs();
+  fc->set_feature(proto::MODEL_EXECUTION_FEATURE_COMPOSE);
+
+  UpdateInterpreterWithConfig(config);
+
+  auto maybe_metadata = interpreter()->ConstructOutputMetadata(
+      proto::MODEL_EXECUTION_FEATURE_COMPOSE, "output");
+
+  EXPECT_FALSE(maybe_metadata.has_value());
+}
+
+TEST_F(OnDeviceModelExecutionConfigInterpeterTest,
+       ConstructOutputMetadataBadProto) {
+  proto::OnDeviceModelExecutionConfig config;
+  auto* fc = config.add_feature_configs();
+  fc->set_feature(proto::MODEL_EXECUTION_FEATURE_COMPOSE);
+  auto* oc = fc->mutable_output_config();
+  oc->set_proto_type("garbage type");
+  oc->mutable_proto_field()->add_proto_descriptors()->set_tag_number(1);
+  UpdateInterpreterWithConfig(config);
+
+  auto maybe_metadata = interpreter()->ConstructOutputMetadata(
+      proto::MODEL_EXECUTION_FEATURE_COMPOSE, "output");
+
+  EXPECT_FALSE(maybe_metadata.has_value());
+}
+
+TEST_F(OnDeviceModelExecutionConfigInterpeterTest,
+       ConstructOutputMetadataDescriptorSpecifiedNotStringValue) {
+  proto::OnDeviceModelExecutionConfig config;
+  auto* fc = config.add_feature_configs();
+  fc->set_feature(proto::MODEL_EXECUTION_FEATURE_COMPOSE);
+  auto* oc = fc->mutable_output_config();
+  oc->set_proto_type("optimization_guide.proto.ComposeRequest");
+  oc->mutable_proto_field()->add_proto_descriptors()->set_tag_number(4);
+  UpdateInterpreterWithConfig(config);
+
+  auto maybe_metadata = interpreter()->ConstructOutputMetadata(
+      proto::MODEL_EXECUTION_FEATURE_COMPOSE, "output");
+
+  EXPECT_FALSE(maybe_metadata.has_value());
+}
+
+TEST_F(OnDeviceModelExecutionConfigInterpeterTest,
+       ConstructOutputMetadataDescriptorValid) {
+  proto::OnDeviceModelExecutionConfig config;
+  auto* fc = config.add_feature_configs();
+  fc->set_feature(proto::MODEL_EXECUTION_FEATURE_COMPOSE);
+  auto* oc = fc->mutable_output_config();
+  oc->set_proto_type("optimization_guide.proto.ComposeResponse");
+  oc->mutable_proto_field()->add_proto_descriptors()->set_tag_number(1);
+  UpdateInterpreterWithConfig(config);
+
+  auto maybe_metadata = interpreter()->ConstructOutputMetadata(
+      proto::MODEL_EXECUTION_FEATURE_COMPOSE, "output");
+
+  ASSERT_TRUE(maybe_metadata.has_value());
+  EXPECT_EQ(
+      "output",
+      ParsedAnyMetadata<proto::ComposeResponse>(*maybe_metadata)->output());
+}
+
 }  // namespace
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_model_execution_proto_descriptors.h b/components/optimization_guide/core/model_execution/on_device_model_execution_proto_descriptors.h
index 3b5be10..da570fb 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_execution_proto_descriptors.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_execution_proto_descriptors.h
@@ -5,6 +5,7 @@
 #define COMPONENTS_OPTIMIZATION_GUIDE_CORE_MODEL_EXECUTION_ON_DEVICE_MODEL_EXECUTION_PROTO_DESCRIPTORS_H_
 
 #include <optional>
+#include <string>
 
 #include "components/optimization_guide/proto/model_execution.pb.h"
 
@@ -13,7 +14,12 @@
 // Returns the value of `proto_field` from `msg`.
 std::optional<proto::Value> GetProtoValue(
     const google::protobuf::MessageLite& msg,
-    const optimization_guide::proto::ProtoField& proto_field);
+    const proto::ProtoField& proto_field);
+
+// Sets `value` in `proto_field` with the type specified by `proto_name`.
+std::optional<proto::Any> SetProtoValue(const std::string& proto_name,
+                                        const proto::ProtoField& proto_field,
+                                        const std::string& value);
 
 }  // namespace optimization_guide
 
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 2b12e66..c1a3cc7c 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 2b12e66f237b926fad8f2eb19a5ef0a6d0154a9f
+Subproject commit c1a3cc7cf94d42bccee1147e2299eaafec54bf12
diff --git a/components/optimization_guide/proto/model_execution.proto b/components/optimization_guide/proto/model_execution.proto
index ca22e49..7efb336b 100644
--- a/components/optimization_guide/proto/model_execution.proto
+++ b/components/optimization_guide/proto/model_execution.proto
@@ -52,6 +52,9 @@
 
   // The config used to construct the input for on-device model execution.
   optional OnDeviceModelExecutionInputConfig input_config = 2;
+
+  // The config used to construct the output for on-device model execution.
+  optional OnDeviceModelExecutionOutputConfig output_config = 3;
 }
 
 message OnDeviceModelExecutionInputConfig {
@@ -164,3 +167,11 @@
   optional OperatorType operator_type = 2;
   optional Value value = 3;
 }
+
+message OnDeviceModelExecutionOutputConfig {
+  // The proto type to use for the response metadata.
+  optional string proto_type = 1;
+
+  // The proto field to populate the output string with.
+  optional ProtoField proto_field = 2;
+}
diff --git a/components/optimization_guide/tools/gen_on_device_proto_descriptors.py b/components/optimization_guide/tools/gen_on_device_proto_descriptors.py
index 7a6e4b3..850641b 100755
--- a/components/optimization_guide/tools/gen_on_device_proto_descriptors.py
+++ b/components/optimization_guide/tools/gen_on_device_proto_descriptors.py
@@ -81,11 +81,52 @@
     out.write('}\n\n') # End if statement
   out.write('return std::nullopt;\n')
   out.write('}\n\n') # End function
+
+  out.write("""
+    std::optional<proto::Any> SetProtoValue(const std::string& proto_name,
+                                            const proto::ProtoField& proto_field,
+                                            const std::string& value,
+                                            int32_t index) {
+      if (index >= proto_field.proto_descriptors_size()) {
+        return std::nullopt;
+      }
+  """)
+  for type_name, data in message_data.items():
+    out.write('if (proto_name == "%s") {\n' % type_name)
+    out.write('switch(proto_field.proto_descriptors(index).tag_number()) {\n')
+    for f in data['fields']:
+      if f['type'] == 9:
+        out.write('case %d: {\n' % f['tag_number'])
+        out.write('proto::Any any;\n')
+        out.write('any.set_type_url("type.googleapis.com/%s");\n' % type_name)
+        out.write('%s response_value;\n' % data['cpp_class_name'])
+        out.write('response_value.set_%s(value);' % f['name'])
+        out.write('response_value.SerializeToString(any.mutable_value());')
+        out.write('return any;')
+        out.write('}\n')
+    out.write("""
+    default:
+      return std::nullopt;\n
+    """)
+    out.write('}')
+    out.write('}\n') # End if statement
+
+  out.write("""
+      return std::nullopt;
+    }
+  """)
+
   out.write('}  // namespace\n\n')
   out.write("""
     std::optional<proto::Value> GetProtoValue(const google::protobuf::MessageLite& msg, const proto::ProtoField& proto_field) {
       return GetProtoValue(msg, proto_field, /*index=*/0);
     }
+
+    std::optional<proto::Any> SetProtoValue(const std::string& proto_name,
+                                            const proto::ProtoField& proto_field,
+                                            const std::string& value) {
+      return SetProtoValue(proto_name, proto_field, value, /*index=*/0);
+    }
   """)
   out.write('}  // namespace optimization_guide\n')
   out.write('\n')
diff --git a/components/plus_addresses_strings.grdp b/components/plus_addresses_strings.grdp
new file mode 100644
index 0000000..5fba663
--- /dev/null
+++ b/components/plus_addresses_strings.grdp
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+        <!-- Experimental enterprise plus_addresses feature strings -->
+    <message name="IDS_PLUS_ADDRESS_MODAL_TITLE" desc="Title for the experimental enterprise plus addresses feature modal" translateable="false">
+      Lorem Ipsum
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_LABEL" desc="Label for the upper portion of the plus addresses modal" translateable="false">
+      Lorem Ipsum
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PROPOSED_PLUS_ADDRESS_AND_COPY" desc="The plus address with some descriptive text around it." translateable="false">
+      Use <ph name="PLUS_ADDRESS">$1<ex>plus@plus.plus</ex>?</ph>
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_LINK_TEXT" desc="The text of the link shown on the Plus Address modal." translateable="false">
+      Settings
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION_START" desc="The start of the plus address description that has a placeholder for a link." translateable="false">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua <ph name="LINK">$1</ph>. Ut enim ad minim veniam...
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION_END" desc="The end of the plus address description that has a placeholder for the primary email address." translateable="false">
+      Lorem <ph name="REGULAR_ADDRESS">$1</ph>.
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_ERROR_MESSAGE" desc="The error message shown on the modal." translateable="false">
+      Lorem ipsum or whatever
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PROPOSED_PLUS_ADDRESS_PLACEHOLDER" desc="A placeholder for the plus address" translateable="false">
+      Lorem Ipsum or whatever
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_REGULAR_ADDRESS_LABEL" desc="A label for the regular address in the plus address modal" translateable="false">
+      For <ph name="REGULAR_ADDRESS">$1<ex>test@example.test</ex></ph>
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION" desc="An illustrative description of the plus addresses modal" translateable="false">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_CANCEL_TEXT" desc="Cancel button text for the experimental enterprise plus addresses feature modal" translateable="false">
+      Dolor
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_OK_TEXT" desc="Ok button text for the experimental enterprise plus addresses feature modal" translateable="false">
+      Sit
+    </message>
+    <message name="IDS_PLUS_ADDRESS_MODAL_PLUS_ADDRESS_DESCRIPTION_V2" desc="An illustrative description of the plus addresses modal" translateable="false">
+      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua <ph name="BEGIN_LINK">&lt;link&gt;</ph>Ut enim ad minim veniam<ph name="END_LINK">&lt;/link&gt;</ph> for <ph name="BEGIN_BOLD">&lt;b&gt;</ph><ph name="REGULAR_ADDRESS">$1<ex>test@example.test</ex></ph><ph name="END_BOLD">&lt;/b&gt;</ph>.
+    </message>
+
+</grit-part>
diff --git a/components/plus_addresses_strings_grdp/DIR_METADATA b/components/plus_addresses_strings_grdp/DIR_METADATA
new file mode 100644
index 0000000..a8bf562e
--- /dev/null
+++ b/components/plus_addresses_strings_grdp/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//components/plus_addresses/COMMON_METADATA"
diff --git a/components/plus_addresses_strings_grdp/OWNERS b/components/plus_addresses_strings_grdp/OWNERS
new file mode 100644
index 0000000..82cf094
--- /dev/null
+++ b/components/plus_addresses_strings_grdp/OWNERS
@@ -0,0 +1 @@
+file://components/plus_addresses/OWNERS
diff --git a/components/plus_addresses_strings_grdp/README.md b/components/plus_addresses_strings_grdp/README.md
new file mode 100644
index 0000000..85df098
--- /dev/null
+++ b/components/plus_addresses_strings_grdp/README.md
@@ -0,0 +1,7 @@
+Eventually, this directory will have image SHA-1 hashes used to improve translations of UI
+strings through context images for translators.
+
+For now, it is quite barebones as the enterprise plus addresses feature is experimental.
+
+See also: [Chrome Translation Screenshots - Instructions & FAQ
+](https://docs.google.com/document/d/1nwYWDny20icMSpLUuV_LgrlbWKrYpbXOERUIZNH636o/edit#heading=h.2t7lc4cxo2au)
diff --git a/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.cc b/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.cc
index a48b393..ad3793fb 100644
--- a/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.cc
+++ b/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.cc
@@ -16,12 +16,14 @@
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/functional/bind.h"
 #include "base/location.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
-#include "base/observer_list_threadsafe.h"
+#include "base/observer_list.h"
 #include "base/strings/string_split.h"
+#include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/timer/elapsed_timer.h"
@@ -101,6 +103,63 @@
   base::FilePath path_;
 };
 
+// Trigger the opening and parsing of the attestations file. Returns the
+// parsed `attestations_map_` or `absl::nullopt` when parsing fails. This
+// function should only be invoked with `kEnforcePrivacySandboxAttestations`
+// enabled. `installed_file_path` is the path to the attestations list file.
+absl::optional<PrivacySandboxAttestationsMap> LoadAttestationsInternal(
+    base::FilePath installed_file_path) {
+  // This function should only be called when the feature is enabled.
+  CHECK(base::FeatureList::IsEnabled(
+      privacy_sandbox::kEnforcePrivacySandboxAttestations));
+
+  std::ifstream stream(installed_file_path.AsUTF8Unsafe(),
+                       std::ios::binary | std::ios::in);
+  if (!stream.is_open()) {
+    // File does not exist.
+    return absl::nullopt;
+  }
+
+  SentinelFile sentinel_file(installed_file_path.DirName());
+  if (sentinel_file.IsPresent()) {
+    // An existing sentinel file implies previous parsing has crashed.
+    return absl::nullopt;
+  }
+
+  if (!sentinel_file.Create()) {
+    // Failed to create the sentinel file.
+    return absl::nullopt;
+  }
+
+  // If there is any error or crash during parsing, the sentinel file will
+  // persist in the installation directory. It will prevent this version of
+  // the attestations file from being parsed again.
+  base::ElapsedTimer parsing_timer;
+  absl::optional<PrivacySandboxAttestationsMap> attestations_map =
+      ParseAttestationsFromStream(stream);
+  if (!attestations_map.has_value()) {
+    // The parsing failed.
+    return absl::nullopt;
+  }
+
+  // For an attestations file with 10,000 entries, the average parsing time is
+  // around 240 milliseconds as per local testing on a n2-standard-128 with 128
+  // vCPUs and 512 GB memory. The estimated dynamic memory usage is around 880
+  // KB.
+  base::UmaHistogramTimes(kAttestationsFileParsingUMA, parsing_timer.Elapsed());
+  // Count up to 10000 KB with a minimum of 1 KB.
+  base::UmaHistogramCounts10000(
+      kAttestationsMapMemoryUsageUMA,
+      base::trace_event::EstimateMemoryUsage(attestations_map.value()) / 1024);
+
+  if (!sentinel_file.Remove()) {
+    // Failed to remove the sentinel file.
+    return absl::nullopt;
+  }
+
+  return attestations_map;
+}
+
 }  // namespace
 
 // static
@@ -132,6 +191,7 @@
 PrivacySandboxSettingsImpl::Status PrivacySandboxAttestations::IsSiteAttested(
     const net::SchemefulSite& site,
     PrivacySandboxAttestationsGatedAPI invoking_api) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // If attestations aren't enabled, pass the check trivially.
   if (!base::FeatureList::IsEnabled(
           privacy_sandbox::kEnforcePrivacySandboxAttestations)) {
@@ -191,16 +251,47 @@
 void PrivacySandboxAttestations::LoadAttestations(
     base::Version version,
     base::FilePath installed_file_path) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // `LoadAttestations` is invoked by `ComponentReady`, which is always run on
+  // the UI thread.
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
   // This function should only be called when the feature is enabled.
   CHECK(base::FeatureList::IsEnabled(
       privacy_sandbox::kEnforcePrivacySandboxAttestations));
   CHECK(version.IsValid());
 
-  task_runner_->PostTask(
+  // File version and attestations map should either both exist, or neither of
+  // them exist.
+  CHECK(file_version_.IsValid() == attestations_map_.has_value());
+
+  if (file_version_.IsValid() && file_version_.CompareTo(version) >= 0) {
+    // No need to parse if the incoming version is not newer than the existing
+    // one.
+    RunLoadAttestationsDoneCallbackForTesting();  // IN-TEST
+    return;
+  }
+
+  // Mark the progress as started before posting the parsing task to the
+  // sequenced task runner.
+  attestations_parse_progress_ = Progress::kStarted;
+
+  if (RunLoadAttestationsParsingStartedCallbackForTesting()) {  // IN-TEST
+    // If necessary for testing, indefinitely pause parsing once the progress
+    // is marked as started.
+    return;
+  }
+
+  // Post the parsing task to the sequenced task runner. Upon receiving the
+  // parsed attestations map, store it to `attestations_map_` in callback
+  // `OnAttestationsLoaded` on UI thread. `base::Unretained(this)` is fine in
+  // the reply callback because this is a singleton that will never be
+  // destroyed.
+  task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE,
-      base::BindOnce(&PrivacySandboxAttestations::LoadAttestationsInternal,
-                     base::Unretained(this), std::move(version),
-                     std::move(installed_file_path)));
+      base::BindOnce(&LoadAttestationsInternal, std::move(installed_file_path)),
+      base::BindOnce(&PrivacySandboxAttestations::OnAttestationsParsed,
+                     base::Unretained(this), std::move(version)));
 }
 
 void PrivacySandboxAttestations::AddOverride(const net::SchemefulSite& site) {
@@ -220,11 +311,13 @@
 
 void PrivacySandboxAttestations::SetAttestationsForTesting(
     absl::optional<PrivacySandboxAttestationsMap> attestations_map) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   attestations_map_ = std::move(attestations_map);
   NotifyObserversOnAttestationsLoaded();
 }
 
 base::Version PrivacySandboxAttestations::GetVersionForTesting() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return file_version_;
 }
 
@@ -241,118 +334,7 @@
 
 PrivacySandboxAttestations::PrivacySandboxAttestations()
     : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          {base::MayBlock(), base::TaskPriority::USER_VISIBLE})),
-      observers_(new base::ObserverListThreadSafe<
-                 content::PrivacySandboxAttestationsObserver>()) {}
-
-void PrivacySandboxAttestations::LoadAttestationsInternal(
-    base::Version version,
-    base::FilePath installed_file_path) {
-  // This function should only be called when the feature is enabled.
-  CHECK(base::FeatureList::IsEnabled(
-      privacy_sandbox::kEnforcePrivacySandboxAttestations));
-  CHECK(version.IsValid());
-
-  if (!file_version_.IsValid()) {
-    // There is no existing attestations map.
-    CHECK(!attestations_map_.has_value());
-  } else {
-    // There is an existing attestations map.
-    CHECK(attestations_map_.has_value());
-    // The progress should be `kFinished` because this function is always
-    // executed on the same SequencedTaskRunner `task_runner_`.
-    CHECK_EQ(attestations_parse_progress_, Progress::kFinished);
-
-    if (file_version_.CompareTo(version) >= 0) {
-      // The existing attestations map is of newer or same version, do not
-      // parse.
-      OnAttestationsLoaded();
-      return;
-    }
-  }
-
-  attestations_parse_progress_ = Progress::kStarted;
-
-  std::ifstream stream(installed_file_path.AsUTF8Unsafe(),
-                       std::ios::binary | std::ios::in);
-  if (!stream.is_open()) {
-    // File does not exist.
-    attestations_parse_progress_ = Progress::kFinished;
-    OnAttestationsLoaded();
-    return;
-  }
-
-  if (RunLoadAttestationsParsingStartedCallbackForTesting()) {  // IN-TEST
-    // If necessary for testing, indefinitely pause parsing once it's started.
-    return;
-  }
-
-  SentinelFile sentinel_file(installed_file_path.DirName());
-  if (sentinel_file.IsPresent()) {
-    // An existing sentinel file implies previous parsing has crashed.
-    attestations_parse_progress_ = Progress::kFinished;
-    OnAttestationsLoaded();
-    return;
-  }
-
-  if (!sentinel_file.Create()) {
-    // Failed to create the sentinel file.
-    attestations_parse_progress_ = Progress::kFinished;
-    OnAttestationsLoaded();
-    return;
-  }
-
-  // If there is any error or crash during parsing, the sentinel file will
-  // persist in the installation directory. It will prevent this version of
-  // the attestations file from being parsed again.
-  base::ElapsedTimer parsing_timer;
-  absl::optional<PrivacySandboxAttestationsMap> attestations_map =
-      ParseAttestationsFromStream(stream);
-  if (!attestations_map.has_value()) {
-    // The parsing failed.
-    attestations_parse_progress_ = Progress::kFinished;
-    OnAttestationsLoaded();
-    return;
-  }
-
-  // For an attestations file with 10,000 entries, the average parsing time is
-  // around 240 milliseconds as per local testing on a n2-standard-128 with 128
-  // vCPUs and 512 GB memory. The estimated dynamic memory usage is around 880
-  // KB.
-  base::UmaHistogramTimes(kAttestationsFileParsingUMA, parsing_timer.Elapsed());
-  // Count up to 10000 KB with a minimum of 1 KB.
-  base::UmaHistogramCounts10000(
-      kAttestationsMapMemoryUsageUMA,
-      base::trace_event::EstimateMemoryUsage(attestations_map.value()) / 1024);
-
-  if (!sentinel_file.Remove()) {
-    // Failed to remove the sentinel file.
-    attestations_parse_progress_ = Progress::kFinished;
-    OnAttestationsLoaded();
-    return;
-  }
-
-  attestations_parse_progress_ = Progress::kFinished;
-
-  // Queries on Privacy Sandbox APIs attestation status may happen on the UI
-  // thread. The final assignment of the attestations map and its version is
-  // done on the UI thread to avoid race condition.
-  content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
-      ->PostTask(
-          FROM_HERE,
-          base::BindOnce(&PrivacySandboxAttestations::SetParsedAttestations,
-                         base::Unretained(this), std::move(version),
-                         std::move(attestations_map.value())));
-}
-
-void PrivacySandboxAttestations::SetParsedAttestations(
-    base::Version version,
-    PrivacySandboxAttestationsMap attestations_map) {
-  file_version_ = std::move(version);
-  attestations_map_ = std::move(attestations_map);
-
-  OnAttestationsLoaded();
-}
+          {base::MayBlock(), base::TaskPriority::USER_VISIBLE})) {}
 
 void PrivacySandboxAttestations::RunLoadAttestationsDoneCallbackForTesting() {
   if (!load_attestations_done_callback_.is_null()) {
@@ -369,16 +351,34 @@
   return false;
 }
 
-void PrivacySandboxAttestations::OnAttestationsLoaded() {
+void PrivacySandboxAttestations::OnAttestationsParsed(
+    base::Version version,
+    absl::optional<PrivacySandboxAttestationsMap> attestations_map) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  // Queries on Privacy Sandbox APIs attestation status will happen on the UI
+  // thread. The final assignment of the attestations map and its version is
+  // done on the UI thread to avoid race condition.
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  if (attestations_map.has_value() &&
+      (!file_version_.IsValid() || file_version_.CompareTo(version) < 0)) {
+    // Parsing succeeded and the attestations file has newer version.
+    file_version_ = std::move(version);
+    attestations_map_ = std::move(attestations_map);
+  }
+
+  attestations_parse_progress_ = Progress::kFinished;
+
   NotifyObserversOnAttestationsLoaded();
 
   RunLoadAttestationsDoneCallbackForTesting();  // IN-TEST
 }
 
 void PrivacySandboxAttestations::NotifyObserversOnAttestationsLoaded() {
-  observers_->Notify(
-      FROM_HERE,
-      &content::PrivacySandboxAttestationsObserver::OnAttestationsLoaded);
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  for (auto& observer : observers_) {
+    observer.OnAttestationsLoaded();
+  }
 }
 
 bool PrivacySandboxAttestations::AddObserver(
@@ -393,7 +393,7 @@
     return true;
   }
 
-  observers_->AddObserver(observer);
+  observers_.AddObserver(observer);
 
   return IsEverLoaded();
 }
@@ -402,10 +402,11 @@
     content::PrivacySandboxAttestationsObserver* observer) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  observers_->RemoveObserver(observer);
+  observers_.RemoveObserver(observer);
 }
 
 bool PrivacySandboxAttestations::IsEverLoaded() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO(crbug.com/1498498): Add lock to `attestations_parse_progress_`.
   return attestations_map_.has_value() ||
          attestations_parse_progress_ == Progress::kFinished ||
diff --git a/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h b/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h
index a34a4bb..4b90f94 100644
--- a/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h
+++ b/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations.h
@@ -16,8 +16,10 @@
 #include "base/functional/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/no_destructor.h"
-#include "base/observer_list_threadsafe.h"
+#include "base/observer_list.h"
+#include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/thread_annotations.h"
 #include "base/version.h"
 #include "net/base/schemeful_site.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -43,6 +45,25 @@
 
 class PrivacySandboxAttestations {
  public:
+  // Describes the status of attestations file parsing.
+  enum Progress {
+    // Before we have started any parsing tasks on the attestation file.
+    kNotStarted,
+
+    // During the time when a new parsing task is running off of the main
+    // thread.
+    // TODO(crbug.com/1501408): This will no longer be true when there are two
+    // parsing tasks posted to the thread pool at the same time, in which case
+    // the progress will be `kFinished` after the first one completes. This
+    // could be fixed by keeping a counter of pending tasks, and moving the
+    // progress to kFinished when the counter reaches 0.
+    kStarted,
+
+    // When we have finished parsing the attestation file (resulting in either
+    // success or failure). See the TODO above for edge cases.
+    kFinished,
+  };
+
   // Returns the singleton instance. If there is a test instance present, return
   // the test instance.
   static PrivacySandboxAttestations* GetInstance();
@@ -91,7 +112,10 @@
   // asynchronously on the SequencedTaskRunner `task_runner_` in the thread
   // pool. This function should only be invoked with a valid version and
   // `kEnforcePrivacySandboxAttestations` enabled. `installed_file_path` should
-  // be the path to the attestations list file.
+  // be the path to the attestations list file. If there is an existing
+  // attestations map, only posts the parsing task if the incoming attestations
+  // file has a newer version. This function also validates the existing version
+  // and attestations map are in valid states.
   void LoadAttestations(base::Version version,
                         base::FilePath installed_file_path);
 
@@ -136,29 +160,10 @@
  private:
   friend class base::NoDestructor<PrivacySandboxAttestations>;
 
-  enum Progress {
-    kNotStarted,
-    kStarted,
-    kFinished,
-  };
-
   // The constructor is private to enforce the singleton requirement of this
   // class.
   PrivacySandboxAttestations();
 
-  // Trigger the opening and parsing of the attestations file. When the parsing
-  // is done, store the result to `attestations_map_`. If there is an existing
-  // attestations map, only parse if the attestations file has a newer version.
-  // This function should only be invoked with a valid version and
-  // `kEnforcePrivacySandboxAttestations` enabled. `installed_file_path` should
-  // be the path to the attestations list file.
-  void LoadAttestationsInternal(base::Version version,
-                                base::FilePath installed_file_path);
-
-  // Store the parsed attestations map and its version.
-  void SetParsedAttestations(base::Version version,
-                             PrivacySandboxAttestationsMap attestations_map);
-
   // Invoke the `attestations_loaded_callback_` registered by tests, if any.
   void RunLoadAttestationsDoneCallbackForTesting();
 
@@ -167,8 +172,12 @@
   // we're in a test). If it returns false, do nothing.
   bool RunLoadAttestationsParsingStartedCallbackForTesting();
 
-  // Called when attestations loading finishes.
-  void OnAttestationsLoaded();
+  // Called when attestations parsing finishes. Stores the parsed attestations
+  // map and its version. Also notifies the observers the attestations map has
+  // been loaded / updated.
+  void OnAttestationsParsed(
+      base::Version version,
+      absl::optional<PrivacySandboxAttestationsMap> attestations_map);
 
   // Notify observers that attestations have been loaded.
   void NotifyObserversOnAttestationsLoaded();
@@ -186,18 +195,20 @@
   // This callback is invoked when parsing for the attestations map starts.
   base::OnceClosure load_attestations_parsing_started_callback_;
 
-  Progress attestations_parse_progress_ = kNotStarted;
+  Progress attestations_parse_progress_ GUARDED_BY_CONTEXT(sequence_checker_) =
+      kNotStarted;
 
   // The attestations file from the component updater should always carry a
   // valid version. If this is a `nullopt`, this implies the attestations list
   // has not been loaded yet.
-  base::Version file_version_;
+  base::Version file_version_ GUARDED_BY_CONTEXT(sequence_checker_);
 
   // A data structure for storing and checking Privacy Sandbox attestations,
   // i.e. whether particular sites have opted in to using particular Privacy
   // Sandbox APIs. If this is a `nullopt`, this implies the attestations list
   // has not been loaded yet.
-  absl::optional<PrivacySandboxAttestationsMap> attestations_map_;
+  absl::optional<PrivacySandboxAttestationsMap> attestations_map_
+      GUARDED_BY_CONTEXT(sequence_checker_);
 
   // Overridden sites by DevTools are considered attested.
   std::vector<net::SchemefulSite> overridden_sites_;
@@ -205,9 +216,9 @@
   // If true, all Privacy Sandbox APIs are considered attested for any site.
   bool is_all_apis_attested_for_testing_ = false;
 
-  scoped_refptr<
-      base::ObserverListThreadSafe<content::PrivacySandboxAttestationsObserver>>
-      observers_;
+  base::ObserverList<content::PrivacySandboxAttestationsObserver> observers_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
 };
 
 }  // namespace privacy_sandbox
diff --git a/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_unittest.cc b/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_unittest.cc
index 605f285..4f17e38 100644
--- a/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_unittest.cc
+++ b/components/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_unittest.cc
@@ -460,8 +460,6 @@
                 net::SchemefulSite(GURL(site)),
                 PrivacySandboxAttestationsGatedAPI::kProtectedAudience),
             Status::kAllowed);
-
-  base::RunLoop().RunUntilIdle();
 }
 
 TEST_F(PrivacySandboxAttestationsFeatureEnabledTest,
diff --git a/components/test/data/url_rewrite/multiple_web_contents.html b/components/test/data/url_rewrite/multiple_web_contents.html
deleted file mode 100644
index c6b14bb..0000000
--- a/components/test/data/url_rewrite/multiple_web_contents.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<html>
-<style>
-  body {
-    margin: 0;
-  }
-
-  portal {
-    height: 300px;
-    width: 400px;
-  }
-</style>
-<body>
-  <portal src="single_web_contents.html"></portal>
-</body>
-</html>
diff --git a/components/test/data/url_rewrite/unit_tests_bundle_data.filelist b/components/test/data/url_rewrite/unit_tests_bundle_data.filelist
index f0058fc9..da5c7df 100644
--- a/components/test/data/url_rewrite/unit_tests_bundle_data.filelist
+++ b/components/test/data/url_rewrite/unit_tests_bundle_data.filelist
@@ -4,5 +4,4 @@
 # NOTE: this file is generated by build/ios/update_bundle_filelist.py
 #       If it requires updating, you should get a presubmit error with
 #       instructions on how to regenerate. Otherwise, do not edit.
-//components/test/data/url_rewrite/multiple_web_contents.html
 //components/test/data/url_rewrite/single_web_contents.html
diff --git a/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc b/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc
index d799caee..f3ae5d2f 100644
--- a/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc
+++ b/components/url_rewrite/browser/url_request_rewrite_rules_manager_browsertest.cc
@@ -21,46 +21,9 @@
 
 namespace url_rewrite {
 
-using ::testing::IsEmpty;
-using ::testing::SizeIs;
-
-// Attaches an inner WebContents to the UrlRequestRewriteRulesManager.
-class InnerWebContentsHandler : public content::WebContentsObserver {
- public:
-  InnerWebContentsHandler(
-      content::WebContents* web_contents,
-      UrlRequestRewriteRulesManager* url_request_rewrite_rules_manager)
-      : content::WebContentsObserver(web_contents),
-        url_request_rewrite_rules_manager_(url_request_rewrite_rules_manager) {}
-
-  void Wait() { run_loop_.Run(); }
-
- private:
-  // content::WebContentsObserver implementation.
-  void InnerWebContentsCreated(
-      content::WebContents* inner_web_contents) override {
-    CHECK(
-        url_request_rewrite_rules_manager_->AddWebContents(inner_web_contents));
-    run_loop_.Quit();
-  }
-
-  base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
-  raw_ptr<UrlRequestRewriteRulesManager> url_request_rewrite_rules_manager_;
-};
-
 class UrlRequestRewriteRulesManagerBrowserTest
     : public content::ContentBrowserTest {
- public:
  protected:
-  void SetUp() override {
-    // Enable portals to emulate creation of inner WebContents.
-    scoped_feature_list_.InitWithFeatures(
-        /*enabled_features=*/{blink::features::kPortals,
-                              blink::features::kPortalsCrossOrigin},
-        /*disabled_features=*/{});
-    ContentBrowserTest::SetUp();
-  }
-
   void SetUpOnMainThread() override {
     base::FilePath root_dir;
     base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &root_dir);
@@ -70,7 +33,6 @@
     ContentBrowserTest::SetUpOnMainThread();
   }
 
-  base::test::ScopedFeatureList scoped_feature_list_;
   UrlRequestRewriteRulesManager url_request_rewrite_rules_manager_;
 };
 
@@ -95,8 +57,6 @@
   ASSERT_TRUE(NavigateToURL(shell(), url));
   navigation_observer.Wait();
 
-  // Verify there were no inner WebContents created, and updaters size is 1.
-  ASSERT_THAT(shell()->web_contents()->GetInnerWebContents(), IsEmpty());
   ASSERT_EQ(url_request_rewrite_rules_manager_.GetUpdatersSizeForTesting(), 1u);
   ASSERT_TRUE(url_request_rewrite_rules_manager_.OnRulesUpdated(
       mojom::UrlRequestRewriteRules::New()));
@@ -104,23 +64,32 @@
 
 IN_PROC_BROWSER_TEST_F(UrlRequestRewriteRulesManagerBrowserTest,
                        RulesUpdatedWithMultipleWebContents) {
-  ASSERT_TRUE(url_request_rewrite_rules_manager_.AddWebContents(
-      shell()->web_contents()));
+  GURL url = embedded_test_server()->GetURL("/single_web_contents.html");
 
-  // Load an HTML page with portal element. This will results in creation of the
-  // second inner WebContents that is signaled by the
-  // |inner_web_contents_waiter|. As the operation is finished, the 2nd
-  // WebContents will be registered with |url_request_rewrite_rules_manager_|
-  // and rewrite rules' update will be verified.
-  GURL url = embedded_test_server()->GetURL("/multiple_web_contents.html");
-  InnerWebContentsHandler inner_web_contents_waiter(
-      shell()->web_contents(), &url_request_rewrite_rules_manager_);
-  content::TestNavigationObserver navigation_observer(shell()->web_contents());
-  ASSERT_TRUE(NavigateToURL(shell(), url));
-  navigation_observer.Wait();
-  inner_web_contents_waiter.Wait();
+  // Load a URL in two separate contents -- the default, and a second shell.
+  // Both contents should be registered with
+  // |url_request_rewrite_rules_manager_| and rewrite rules' update will be
+  // verified.
 
-  ASSERT_THAT(shell()->web_contents()->GetInnerWebContents(), SizeIs(1));
+  {
+    ASSERT_TRUE(url_request_rewrite_rules_manager_.AddWebContents(
+        shell()->web_contents()));
+    content::TestNavigationObserver navigation_observer(
+        shell()->web_contents());
+    ASSERT_TRUE(NavigateToURL(shell(), url));
+    navigation_observer.Wait();
+  }
+
+  {
+    content::TestNavigationObserver navigation_observer(url);
+    navigation_observer.StartWatchingNewWebContents();
+    auto* second_shell = content::Shell::CreateNewWindow(
+        shell()->web_contents()->GetBrowserContext(), url, nullptr, {800, 600});
+    ASSERT_TRUE(url_request_rewrite_rules_manager_.AddWebContents(
+        second_shell->web_contents()));
+    navigation_observer.Wait();
+  }
+
   ASSERT_EQ(url_request_rewrite_rules_manager_.GetUpdatersSizeForTesting(), 2u);
   ASSERT_TRUE(url_request_rewrite_rules_manager_.OnRulesUpdated(
       mojom::UrlRequestRewriteRules::New()));
@@ -142,9 +111,6 @@
   ASSERT_TRUE(url_request_rewrite_rules_manager_.AddWebContents(
       shell()->web_contents()));
 
-  // Verify there were no inner WebContents created, and updaters size is 1.
-  ASSERT_THAT(shell()->web_contents()->GetInnerWebContents(), IsEmpty());
-  ASSERT_EQ(url_request_rewrite_rules_manager_.GetUpdatersSizeForTesting(), 1u);
   ASSERT_TRUE(url_request_rewrite_rules_manager_.OnRulesUpdated(
       mojom::UrlRequestRewriteRules::New()));
 }
diff --git a/components/viz/service/display/overlay_candidate_factory.cc b/components/viz/service/display/overlay_candidate_factory.cc
index 1c8d3cc..60f4bee 100644
--- a/components/viz/service/display/overlay_candidate_factory.cc
+++ b/components/viz/service/display/overlay_candidate_factory.cc
@@ -379,14 +379,6 @@
     return status;
   }
 
-  // TODO(b/1471182): Render passes with transforms are complicated because
-  // clipping combined with filters that expand their bounds mean we don't know
-  // their exact size yet. Disabling them temporarily until we fix all the bugs.
-  bool is_rpdq = !!quad->DynamicCast<AggregatedRenderPassDrawQuad>();
-  if (absl::holds_alternative<gfx::Transform>(candidate.transform) && is_rpdq) {
-    return CandidateStatus::kFailRpdqWithTransform;
-  }
-
   candidate.is_opaque =
       !quad->ShouldDrawWithBlendingForReasonOtherThanMaskFilter();
 
@@ -424,11 +416,16 @@
     // Out of window clipping is enabled on Lacros only when it is supported.
     // TODO(crbug.com/1385509): Remove the condition on `quad_within_window`
     // when M117 becomes widely supported.
-    const bool can_delegate_clipping =
+    bool can_delegate_clipping =
         context_.supports_clip_rect &&
         (quad_within_window || context_.supports_out_of_window_clip_rect) &&
         transform_supports_clipping;
 
+    bool is_rpdq = !!quad->DynamicCast<AggregatedRenderPassDrawQuad>();
+    if (is_rpdq) {
+      can_delegate_clipping &= context_.transform_and_clip_rpdq;
+    }
+
     if (can_delegate_clipping) {
       // If we know the clip_rect won't intersect the display_rect at all, we
       // can skip it. We must account for any transform to the display_rect.
diff --git a/components/viz/service/display/overlay_candidate_factory.h b/components/viz/service/display/overlay_candidate_factory.h
index 414c36c..02e00cb 100644
--- a/components/viz/service/display/overlay_candidate_factory.h
+++ b/components/viz/service/display/overlay_candidate_factory.h
@@ -56,6 +56,7 @@
     bool supports_arbitrary_transform = false;
     bool supports_rounded_display_masks = false;
     bool supports_mask_filter = false;
+    bool transform_and_clip_rpdq = false;
   };
 
   // The coordinate space of |render_pass| is the target space for candidates
diff --git a/components/viz/service/display/overlay_processor_delegated.cc b/components/viz/service/display/overlay_processor_delegated.cc
index 13ff3fb..6e3914d 100644
--- a/components/viz/service/display/overlay_processor_delegated.cc
+++ b/components/viz/service/display/overlay_processor_delegated.cc
@@ -110,6 +110,7 @@
   needs_background_image_ = runtime_props.needs_background_image;
   supports_affine_transform_ = features::ShouldDelegateTransforms() &&
                                runtime_props.supports_affine_transform;
+  has_transformation_fix_ = runtime_props.has_transformation_fix;
 }
 
 OverlayProcessorDelegated::~OverlayProcessorDelegated() = default;
@@ -174,7 +175,9 @@
       .supports_clip_rect = supports_clip_rect_,
       .supports_out_of_window_clip_rect = supports_out_of_window_clip_rect_,
       .supports_arbitrary_transform = supports_affine_transform_,
-      .supports_mask_filter = true};
+      .supports_mask_filter = true,
+      .transform_and_clip_rpdq = has_transformation_fix_,
+  };
 
   OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
       render_pass, resource_provider, surface_damage_rect_list,
diff --git a/components/viz/service/display/overlay_processor_delegated.h b/components/viz/service/display/overlay_processor_delegated.h
index 889bae57..8700905 100644
--- a/components/viz/service/display/overlay_processor_delegated.h
+++ b/components/viz/service/display/overlay_processor_delegated.h
@@ -132,6 +132,7 @@
   bool supports_out_of_window_clip_rect_ = false;
   bool needs_background_image_ = false;
   bool supports_affine_transform_ = false;
+  bool has_transformation_fix_ = false;
   gfx::RectF unassigned_damage_;
   // Used to count the number of frames we should wait until allowing delegation
   // again.
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index d595b59..6e074cc 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -2982,8 +2982,8 @@
           filters->MapRect(rpdq_params.filter_bounds, local_matrix);
 
       // If after applying the filter we would be clipped out, skip the draw.
-      gfx::Rect clip_rect =
-          quad->shared_quad_state->clip_rect.value_or(current_draw_rect_);
+      gfx::Rect clip_rect = quad->shared_quad_state->clip_rect.value_or(
+          current_frame()->current_render_pass->output_rect);
       gfx::Transform transform =
           quad->shared_quad_state->quad_to_target_transform;
       transform.Flatten();
@@ -3556,8 +3556,7 @@
   absl::optional<gfx::Transform> quad_to_target_transform_inverse;
   // We cannot handle rotation with clip rect or mask filter.
   if ((shared_quad_state->clip_rect ||
-       !shared_quad_state->mask_filter_info.IsEmpty()) &&
-      shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment()) {
+       !shared_quad_state->mask_filter_info.IsEmpty())) {
     quad_to_target_transform_inverse.emplace();
     // Flatten before inverting, since we're interested in how points
     // with z=0 in local space map to the clip rect, not in how the clip
@@ -3589,8 +3588,8 @@
 
   // The |mask_filter_info| is in the device coordinate and with all transforms
   // (translation, scaling, rotation, etc), so remove them.
-  if (!shared_quad_state->mask_filter_info.IsEmpty() &&
-      shared_quad_state->quad_to_target_transform.Preserves2dAxisAlignment()) {
+  if (quad_to_target_transform_inverse &&
+      !shared_quad_state->mask_filter_info.IsEmpty()) {
     shared_quad_state->mask_filter_info.ApplyTransform(
         *quad_to_target_transform_inverse);
   }
@@ -3784,6 +3783,8 @@
     }
     OverlayCandidate::ApplyClip(*overlay, gfx::RectF(apply_clip));
     overlay->clip_rect = absl::nullopt;
+  } else {
+    overlay->display_rect = gfx::RectF(filter_bounds);
   }
 
   // Fill in |format| and |color_space| information based on selected backing.
diff --git a/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc b/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
index e339c23f..a43e274 100644
--- a/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_bundle_impl.cc
@@ -274,6 +274,12 @@
   }
 }
 
+void FrameSinkBundleImpl::SetWantsBeginFrameAcks(uint32_t sink_id) {
+  if (auto* sink = GetFrameSink(sink_id)) {
+    sink->SetWantsBeginFrameAcks();
+  }
+}
+
 void FrameSinkBundleImpl::Submit(
     std::vector<mojom::BundledFrameSubmissionPtr> submissions) {
   std::set<SinkGroup*> groups;
diff --git a/components/viz/service/frame_sinks/frame_sink_bundle_impl.h b/components/viz/service/frame_sinks/frame_sink_bundle_impl.h
index 8ae2071..9fa4672 100644
--- a/components/viz/service/frame_sinks/frame_sink_bundle_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_bundle_impl.h
@@ -67,6 +67,7 @@
       uint32_t sink_id,
       mojom::CompositorFrameSinkType type) override;
   void SetNeedsBeginFrame(uint32_t sink_id, bool needs_begin_frame) override;
+  void SetWantsBeginFrameAcks(uint32_t sink_id) override;
   void Submit(
       std::vector<mojom::BundledFrameSubmissionPtr> submissions) override;
   void DidAllocateSharedBitmap(uint32_t sink_id,
diff --git a/content/browser/media/key_system_support_android.cc b/content/browser/media/key_system_support_android.cc
index 8ca260d..5006f4b 100644
--- a/content/browser/media/key_system_support_android.cc
+++ b/content/browser/media/key_system_support_android.cc
@@ -181,12 +181,8 @@
     }
   }
 
-  // 'cenc' is always supported. 'cbcs' may or may not be available.
   capability.encryption_schemes.insert(media::EncryptionScheme::kCenc);
-  if (MediaCodecUtil::PlatformSupportsCbcsEncryption(
-          base::android::BuildInfo::GetInstance()->sdk_int())) {
-    capability.encryption_schemes.insert(media::EncryptionScheme::kCbcs);
-  }
+  capability.encryption_schemes.insert(media::EncryptionScheme::kCbcs);
 
   capability.session_types.insert(media::CdmSessionType::kTemporary);
   if (MediaDrmBridge::IsPersistentLicenseTypeSupported(key_system)) {
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 664abc4..588ff10 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -610,9 +610,9 @@
   StartDownload(std::move(parameters), nullptr);
 }
 
-// Analyzes trusted sources of a frame's trust-token-redemption Permissions
-// Policy feature to see if the feature is definitely disabled or potentially
-// enabled.
+// Analyzes trusted sources of a frame's private-state-token-redemption
+// Permissions Policy feature to see if the feature is definitely disabled or
+// potentially enabled.
 //
 // This information will be bound to a URLLoaderFactory; if the answer is
 // "definitely disabled," the network service will report a bad message if it
@@ -683,8 +683,8 @@
 }
 
 // When a frame creates its initial subresource loaders, it needs to know
-// whether the trust-token-redemption Permissions Policy feature will be enabled
-// after the commit finishes, which is a little involved (see
+// whether the private-state-token-redemption Permissions Policy feature will be
+// enabled after the commit finishes, which is a little involved (see
 // DetermineWhetherToForbidTrustTokenOperation). In contrast, if it needs to
 // make this decision once the frame has committted---for instance, to create
 // more loaders after the network service crashes---it can directly consult the
@@ -9142,7 +9142,7 @@
   // If the request is bearing Private State Tokens parameters:
   // - it must not be a main-frame navigation, and
   // - for redemption and signing operations, the frame's parent needs the
-  //   trust-token-redemption Permissions Policy feature,
+  //   private-state-token-redemption Permissions Policy feature,
   // - for issue operation, the frame's parent needs the
   //   private-state-token-issuance Permission Policy.
   if (begin_params->trust_token_params) {
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 5cfc7ea..fc2dae6 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -1742,7 +1742,7 @@
 // Test that the browser correctly reports a bad message when a child frame
 // attempts to navigate with a Private State Tokens redemption operation
 // associated with the navigation, but its parent lacks the
-// trust-token-redemption Permissions Policy feature.
+// private-state-token-redemption Permissions Policy feature.
 IN_PROC_BROWSER_TEST_F(
     SecurityExploitBrowserTestWithTrustTokensEnabled,
     BrowserForbidsTrustTokenRedemptionWithoutPermissionsPolicy) {
@@ -1781,8 +1781,8 @@
 
 // Test that the browser correctly reports a bad message when a child frame
 // attempts to navigate with a Private State Tokens signing operation associated
-// with the navigation, but its parent lacks the trust-token-redemption (sic)
-// Permissions Policy feature.
+// with the navigation, but its parent lacks the private-state-token-redemption
+// (sic) Permissions Policy feature.
 IN_PROC_BROWSER_TEST_F(
     SecurityExploitBrowserTestWithTrustTokensEnabled,
     BrowserForbidsTrustTokenSigningWithoutPermissionsPolicy) {
diff --git a/content/browser/url_loader_factory_params_helper.cc b/content/browser/url_loader_factory_params_helper.cc
index 18aa7e1..2584917 100644
--- a/content/browser/url_loader_factory_params_helper.cc
+++ b/content/browser/url_loader_factory_params_helper.cc
@@ -41,15 +41,14 @@
 //
 // network::mojom::URLLoaderNetworkServiceObserver::OnLoadingStateUpdate is
 // among the most frequent Mojo messages in traces from the field
-// (go/mojos-in-field-traces-2022). We'll disable it via a Canary/Dev-only
-// experiment to measure impact on performance. The user observable impact is
-// that the status bubble will no longer be displayed. We'll determine the next
-// steps after running the experiment.
+// (go/mojos-in-field-traces-2022). Inhibiting the messages has been tested all
+// the way to stable with no ill effect and performance gains.
 //
-// crbug.com/1433341
+// Remove when evaluation of combined performance gains is complete
+// crbug.com/1487544.
 BASE_FEATURE(kInhibitLoadingStateUpdate,
              "InhibitLoadingStateUpdate",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Helper used by the public URLLoaderFactoryParamsHelper::Create... methods.
 //
diff --git a/content/test/data/page-with-trust-token-permissions-policy-disabled.html b/content/test/data/page-with-trust-token-permissions-policy-disabled.html
index aa63cfa5..8ccc1db 100644
--- a/content/test/data/page-with-trust-token-permissions-policy-disabled.html
+++ b/content/test/data/page-with-trust-token-permissions-policy-disabled.html
@@ -3,7 +3,7 @@
 <title></title>
 
 <body>
-  <p>This page has the trust-token-redemption Feature Policy feature disabled (via its mock headers).</p>
+  <p>This page has the private-state-token-redemption Feature Policy feature disabled (via its mock headers).</p>
   <p>It also has an iframe! Yay for iframes.</p>
 
   <iframe src="/cross-site/b.com/title1.html" id="test_iframe" />
diff --git a/docs/website b/docs/website
index 83e3cf9..ca6f472 160000
--- a/docs/website
+++ b/docs/website
@@ -1 +1 @@
-Subproject commit 83e3cf98ab4c33fb95bdc40dc55912f48d9e0178
+Subproject commit ca6f472417d974ec082d6f70523b4d3fc24b0c1b
diff --git a/extensions/browser/api/feedback_private/feedback_service.cc b/extensions/browser/api/feedback_private/feedback_service.cc
index 7d2a9e53..174ade74 100644
--- a/extensions/browser/api/feedback_private/feedback_service.cc
+++ b/extensions/browser/api/feedback_private/feedback_service.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/barrier_closure.h"
+#include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
@@ -45,36 +46,57 @@
 namespace {
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+// The paths are relative to "/var/log/" by default, which can be overwritten
+// for testing purpose.
 constexpr base::FilePath::CharType kBluetoothLogsFilePath[] =
-    FILE_PATH_LITERAL("/var/log/bluetooth/log.bz2");
+    FILE_PATH_LITERAL("bluetooth/log.bz2");
 constexpr base::FilePath::CharType kBluetoothLogsFilePathOld[] =
-    FILE_PATH_LITERAL("/var/log/bluetooth/log.bz2.old");
+    FILE_PATH_LITERAL("bluetooth/log.bz2.old");
 constexpr base::FilePath::CharType kBluetoothQualityReportFilePath[] =
-    FILE_PATH_LITERAL("/var/log/bluetooth/bluetooth_quality_report");
+    FILE_PATH_LITERAL("bluetooth/bluetooth_quality_report");
+constexpr base::FilePath::CharType kWifiDebugLogsFilePath[] =
+    FILE_PATH_LITERAL("wifi/iwlwifi_firmware_dumps.tar.zst");
 
 constexpr char kBluetoothLogsAttachmentName[] = "bluetooth_logs.bz2";
 constexpr char kBluetoothLogsAttachmentNameOld[] = "bluetooth_logs.old.bz2";
 constexpr char kBluetoothQualityReportAttachmentName[] =
     "bluetooth_quality_report";
+constexpr char kWifiDebugLogsAttachmentName[] =
+    "iwlwifi_firmware_dumps.tar.zst";
 
 constexpr char kLacrosHistogramsFilename[] = "lacros_histograms.zip";
 
-void LoadBluetoothLogs(scoped_refptr<feedback::FeedbackData> feedback_data) {
-  std::string bluetooth_logs;
-  if (base::ReadFileToString(base::FilePath(kBluetoothLogsFilePath),
-                             &bluetooth_logs)) {
-    feedback_data->AddFile(kBluetoothLogsAttachmentName,
-                           std::move(bluetooth_logs));
+void AddAttachment(scoped_refptr<feedback::FeedbackData> feedback_data,
+                   const base::FilePath& root_path,
+                   const std::string& file_path,
+                   const std::string& attachment_name) {
+  std::string temp_log_content;
+  if (base::ReadFileToString(root_path.Append(file_path), &temp_log_content)) {
+    feedback_data->AddFile(attachment_name, std::move(temp_log_content));
+  } else {
+    LOG(WARNING) << "failed to add attachment " << attachment_name
+                 << ": could not read file: " << file_path << " in "
+                 << root_path.value();
   }
-  if (base::ReadFileToString(base::FilePath(kBluetoothLogsFilePathOld),
-                             &bluetooth_logs)) {
-    feedback_data->AddFile(kBluetoothLogsAttachmentNameOld,
-                           std::move(bluetooth_logs));
+}
+
+void LoadAttachmentsIfRequested(
+    scoped_refptr<feedback::FeedbackData> feedback_data,
+    const base::FilePath& root_path,
+    bool send_bluetooth_logs,
+    bool send_wifi_debug_logs) {
+  if (send_bluetooth_logs) {
+    AddAttachment(feedback_data, root_path, kBluetoothLogsFilePath,
+                  kBluetoothLogsAttachmentName);
+    AddAttachment(feedback_data, root_path, kBluetoothLogsFilePathOld,
+                  kBluetoothLogsAttachmentNameOld);
+    AddAttachment(feedback_data, root_path, kBluetoothQualityReportFilePath,
+                  kBluetoothQualityReportAttachmentName);
   }
-  if (base::ReadFileToString(base::FilePath(kBluetoothQualityReportFilePath),
-                             &bluetooth_logs)) {
-    feedback_data->AddFile(kBluetoothQualityReportAttachmentName,
-                           std::move(bluetooth_logs));
+
+  if (send_wifi_debug_logs) {
+    AddAttachment(feedback_data, root_path, kWifiDebugLogsFilePath,
+                  kWifiDebugLogsAttachmentName);
   }
 }
 #endif
@@ -111,6 +133,13 @@
                      feedback_data, std::move(callback)));
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+void FeedbackService::SetLogFilesRootPathForTesting(
+    const base::FilePath& log_file_root) {
+  log_file_root_ = log_file_root;
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 // After the attached file and screenshot if available are fetched, the callback
 // will be invoked. Other further processing will be done in background. The
 // report will be sent out once all data are in place.
@@ -262,12 +291,16 @@
     feedback_data->AddFile(kLacrosHistogramsFilename,
                            std::move(compressed_histograms));
   }
-  // TODO(b/308196190): Attach Wifi debug logs if params.send_wifi_debug_logs is
-  // true.
-  if (params.send_bluetooth_logs) {
+
+  // If at least one attachment is requested, invoke LoadAttachmentsIfRequested
+  // to add all requested attachments in a separate thread to avoid blocking the
+  // UI thread.
+  if (params.send_bluetooth_logs || params.send_wifi_debug_logs) {
     base::ThreadPool::PostTaskAndReply(
         FROM_HERE, {base::MayBlock()},
-        base::BindOnce(&LoadBluetoothLogs, feedback_data),
+        base::BindOnce(&LoadAttachmentsIfRequested, feedback_data,
+                       log_file_root_, params.send_bluetooth_logs,
+                       params.send_wifi_debug_logs),
         base::BindOnce(&FeedbackService::OnAllLogsFetched, this, params,
                        feedback_data));
   } else {
diff --git a/extensions/browser/api/feedback_private/feedback_service.h b/extensions/browser/api/feedback_private/feedback_service.h
index 8cceeeac..ef8a43f 100644
--- a/extensions/browser/api/feedback_private/feedback_service.h
+++ b/extensions/browser/api/feedback_private/feedback_service.h
@@ -5,6 +5,7 @@
 #ifndef EXTENSIONS_BROWSER_API_FEEDBACK_PRIVATE_FEEDBACK_SERVICE_H_
 #define EXTENSIONS_BROWSER_API_FEEDBACK_PRIVATE_FEEDBACK_SERVICE_H_
 
+#include "base/files/file_path.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
@@ -64,6 +65,9 @@
       SendFeedbackCallback callback);
 
   FeedbackPrivateDelegate* GetFeedbackPrivateDelegate() { return delegate_; }
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  void SetLogFilesRootPathForTesting(const base::FilePath& log_file_root);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
  protected:
   virtual ~FeedbackService();
@@ -102,6 +106,8 @@
       const FeedbackParams& params,
       scoped_refptr<feedback::FeedbackData> feedback_data,
       const std::string& compressed_histograms);
+  // Root file path for log files. It can be overwritten for testing purpose.
+  base::FilePath log_file_root_{FILE_PATH_LITERAL("/var/log/")};
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
   void OnAllLogsFetched(const FeedbackParams& params,
                         scoped_refptr<feedback::FeedbackData> feedback_data);
diff --git a/extensions/browser/api/feedback_private/feedback_service_unittest.cc b/extensions/browser/api/feedback_private/feedback_service_unittest.cc
index b828bb89..5322a74 100644
--- a/extensions/browser/api/feedback_private/feedback_service_unittest.cc
+++ b/extensions/browser/api/feedback_private/feedback_service_unittest.cc
@@ -5,6 +5,8 @@
 #include "extensions/browser/api/feedback_private/feedback_service.h"
 
 #include "base/check.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/weak_ptr.h"
 #include "base/run_loop.h"
@@ -13,6 +15,7 @@
 #include "build/build_config.h"
 #include "components/feedback/feedback_data.h"
 #include "components/feedback/feedback_report.h"
+#include "content/public/test/test_utils.h"
 #include "extensions/browser/api/feedback_private/mock_feedback_service.h"
 #include "extensions/browser/api_unittest.h"
 #include "extensions/shell/browser/api/feedback_private/shell_feedback_private_delegate.h"
@@ -88,6 +91,20 @@
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 };
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+bool AttachmentExists(const std::string& name,
+                      const scoped_refptr<FeedbackData>& feedback_data) {
+  size_t num_attachments = feedback_data->attachments();
+  for (size_t i = 0; i < num_attachments; i++) {
+    const FeedbackCommon::AttachedFile* file = feedback_data->attachment(i);
+    if (!std::strcmp(name.c_str(), file->name.c_str())) {
+      return true;
+    }
+  }
+  return false;
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 }  // namespace
 
 class FeedbackServiceTest : public ApiUnitTest {
@@ -118,6 +135,7 @@
                                 /*send_tab_titles=*/send_tab_titles,
                                 /*send_histograms=*/true,
                                 /*send_bluetooth_logs=*/true,
+                                /*send_wifi_debug_logs=*/false,
                                 /*send_autofill_metadata=*/false};
 
     EXPECT_CALL(*mock_uploader_, QueueReport).Times(1);
@@ -135,14 +153,55 @@
     EXPECT_EQ(1u, feedback_data_->sys_info()->count(kFakeKey));
   }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  void TestSendFeedbackConcerningWifiDebugLogs(bool send_wifi_debug_logs) {
+    const FeedbackParams params{/*is_internal_email=*/false,
+                                /*load_system_info=*/true,
+                                /*send_tab_titles=*/false,
+                                /*send_histograms=*/false,
+                                /*send_bluetooth_logs=*/false,
+                                /*send_wifi_debug_logs=*/send_wifi_debug_logs,
+                                /*send_autofill_metadata=*/false};
+
+    // Create a test file in sub directory "wifi".
+    const base::FilePath test_file_dir =
+        scoped_temp_dir_.GetPath().Append("wifi");
+    ASSERT_TRUE(base::CreateDirectory(test_file_dir));
+
+    const base::FilePath test_file =
+        test_file_dir.Append("iwlwifi_firmware_dumps.tar.zst");
+    ASSERT_TRUE(base::WriteFile(test_file, "Test file content"));
+
+    EXPECT_CALL(*mock_uploader_, QueueReport).Times(1);
+    base::MockCallback<SendFeedbackCallback> mock_callback;
+    EXPECT_CALL(mock_callback, Run(true));
+
+    auto mock_delegate = std::make_unique<MockFeedbackPrivateDelegate>();
+    EXPECT_CALL(*mock_delegate, FetchSystemInformation(_, _)).Times(1);
+    EXPECT_CALL(*mock_delegate, FetchExtraLogs(_, _)).Times(1);
+
+    auto feedback_service = base::MakeRefCounted<FeedbackService>(
+        browser_context(), mock_delegate.get());
+    feedback_service->SetLogFilesRootPathForTesting(scoped_temp_dir_.GetPath());
+
+    RunUntilFeedbackIsSent(feedback_service, params, mock_callback.Get());
+    EXPECT_EQ(1u, feedback_data_->sys_info()->count(kFakeKey));
+
+    // Verify the attachment is added if and only if send_wifi_debug_logs is
+    // true.
+    EXPECT_EQ(
+        send_wifi_debug_logs,
+        AttachmentExists("iwlwifi_firmware_dumps.tar.zst", feedback_data_));
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   void RunUntilFeedbackIsSent(scoped_refptr<FeedbackService> feedback_service,
                               const FeedbackParams& params,
                               SendFeedbackCallback mock_callback) {
-    base::RunLoop run_loop;
     feedback_service->RedactThenSendFeedback(params, feedback_data_,
                                              std::move(mock_callback));
     base::ThreadPoolInstance::Get()->FlushForTesting();
-    run_loop.RunUntilIdle();
+    task_environment()->RunUntilIdle();
   }
 
   base::ScopedTempDir scoped_temp_dir_;
@@ -158,6 +217,7 @@
                               /*send_tab_titles=*/true,
                               /*send_histograms=*/true,
                               /*send_bluetooth_logs=*/true,
+                              /*send_wifi_debug_logs=*/false,
                               /*send_autofill_metadata=*/false};
 
   EXPECT_CALL(*mock_uploader_, QueueReport).Times(1);
@@ -177,6 +237,7 @@
                               /*send_tab_titles=*/true,
                               /*send_histograms=*/true,
                               /*send_bluetooth_logs=*/true,
+                              /*send_wifi_debug_logs=*/false,
                               /*send_autofill_metadata=*/false};
 
   EXPECT_CALL(*mock_uploader_, QueueReport).Times(1);
@@ -219,6 +280,7 @@
                               /*send_tab_titles=*/false,
                               /*send_histograms=*/true,
                               /*send_bluetooth_logs=*/true,
+                              /*send_wifi_debug_logs=*/false,
                               /*send_autofill_metadata=*/true};
   feedback_data_->set_autofill_metadata("Autofill Metadata");
   EXPECT_CALL(*mock_uploader_, QueueReport).Times(1);
@@ -231,4 +293,14 @@
   RunUntilFeedbackIsSent(feedback_service, params, mock_callback.Get());
 }
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+TEST_F(FeedbackServiceTest, SendFeedbackWithWifiDebugLogs) {
+  TestSendFeedbackConcerningWifiDebugLogs(/*send_wifi_debug_logs=*/true);
+}
+
+TEST_F(FeedbackServiceTest, SendFeedbackWithoutWifiDebugLogs) {
+  TestSendFeedbackConcerningWifiDebugLogs(/*send_wifi_debug_logs=*/false);
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 }  // namespace extensions
diff --git "a/infra/config/generated/builders/ci/Android arm64 Builder All Targets \050dbg\051/properties.json" "b/infra/config/generated/builders/ci/Android arm64 Builder All Targets \050dbg\051/properties.json"
index 05e933b..e8d9eafb 100644
--- "a/infra/config/generated/builders/ci/Android arm64 Builder All Targets \050dbg\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Android arm64 Builder All Targets \050dbg\051/properties.json"
@@ -53,10 +53,6 @@
         {
           "builder": "android_compile_dbg",
           "group": "tryserver.chromium.android"
-        },
-        {
-          "builder": "android_compile_siso_dbg",
-          "group": "tryserver.chromium.android"
         }
       ]
     }
diff --git "a/infra/config/generated/builders/ci/Linux Builder \050dbg\051/properties.json" "b/infra/config/generated/builders/ci/Linux Builder \050dbg\051/properties.json"
index 340fd3a7..712de12 100644
--- "a/infra/config/generated/builders/ci/Linux Builder \050dbg\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Linux Builder \050dbg\051/properties.json"
@@ -79,10 +79,6 @@
           "group": "tryserver.chromium.linux"
         },
         {
-          "builder": "linux_chromium_compile_siso_dbg_ng",
-          "group": "tryserver.chromium.linux"
-        },
-        {
           "builder": "linux_chromium_dbg_ng",
           "group": "tryserver.chromium.linux"
         }
diff --git "a/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/properties.json" "b/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/properties.json"
index d2f0ed27..6b38b79 100644
--- "a/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Linux Tests \050dbg\051\0501\051/properties.json"
@@ -69,10 +69,6 @@
           "group": "tryserver.chromium.linux"
         },
         {
-          "builder": "linux_chromium_compile_siso_dbg_ng",
-          "group": "tryserver.chromium.linux"
-        },
-        {
           "builder": "linux_chromium_dbg_ng",
           "group": "tryserver.chromium.linux"
         }
diff --git a/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json b/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json
index be5e1b2..dadb5cc 100644
--- a/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json
+++ b/infra/config/generated/builders/ci/linux-chromeos-dbg/properties.json
@@ -45,10 +45,6 @@
           "group": "tryserver.chromium.chromiumos"
         },
         {
-          "builder": "linux-chromeos-compile-siso-dbg",
-          "group": "tryserver.chromium.chromiumos"
-        },
-        {
           "builder": "linux-chromeos-dbg",
           "group": "tryserver.chromium.chromiumos"
         }
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index 315fd778..f158687 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -169,7 +169,6 @@
     "android-asan-compile-dbg": "try/android-asan-compile-dbg/gn-args.json",
     "android-bfcache-rel": "try/android-bfcache-rel/gn-args.json",
     "android-binary-size": "try/android-binary-size/gn-args.json",
-    "android-binary-size-siso": "try/android-binary-size-siso/gn-args.json",
     "android-cronet-arm-dbg": "try/android-cronet-arm-dbg/gn-args.json",
     "android-cronet-arm64-dbg": "try/android-cronet-arm64-dbg/gn-args.json",
     "android-cronet-arm64-rel": "try/android-cronet-arm64-rel/gn-args.json",
@@ -208,7 +207,6 @@
     "android-x86-rel": "try/android-x86-rel/gn-args.json",
     "android_arm64_dbg_recipe": "try/android_arm64_dbg_recipe/gn-args.json",
     "android_compile_dbg": "try/android_compile_dbg/gn-args.json",
-    "android_compile_siso_dbg": "try/android_compile_siso_dbg/gn-args.json",
     "android_compile_x64_dbg": "try/android_compile_x64_dbg/gn-args.json",
     "android_compile_x86_dbg": "try/android_compile_x86_dbg/gn-args.json",
     "android_cronet": "try/android_cronet/gn-args.json"
@@ -226,7 +224,6 @@
     "linux-x64-castos-audio": "try/linux-x64-castos-audio/gn-args.json",
     "linux-x64-castos-dbg": "try/linux-x64-castos-dbg/gn-args.json",
     "linux_chromium_compile_dbg_ng": "try/linux_chromium_compile_dbg_ng/gn-args.json",
-    "linux_chromium_compile_siso_dbg_ng": "try/linux_chromium_compile_siso_dbg_ng/gn-args.json",
     "linux_chromium_dbg_ng": "try/linux_chromium_dbg_ng/gn-args.json",
     "network_service_linux": "try/network_service_linux/gn-args.json"
   },
diff --git a/infra/config/generated/builders/try/android-binary-size-siso/gn-args.json b/infra/config/generated/builders/try/android-binary-size-siso/gn-args.json
deleted file mode 100644
index 2d8abbd0..0000000
--- a/infra/config/generated/builders/try/android-binary-size-siso/gn-args.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "gn_args": {
-    "android_channel": "stable",
-    "debuggable_apks": false,
-    "ffmpeg_branding": "Chrome",
-    "is_official_build": true,
-    "is_on_release_branch": true,
-    "proprietary_codecs": true,
-    "symbol_level": 1,
-    "target_os": "android",
-    "use_dummy_lastchange": true,
-    "use_remoteexec": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android_compile_siso_dbg/gn-args.json b/infra/config/generated/builders/try/android_compile_siso_dbg/gn-args.json
deleted file mode 100644
index 35a882c..0000000
--- a/infra/config/generated/builders/try/android_compile_siso_dbg/gn-args.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "gn_args": {
-    "debuggable_apks": false,
-    "ffmpeg_branding": "Chrome",
-    "is_component_build": true,
-    "is_debug": true,
-    "proprietary_codecs": true,
-    "symbol_level": 0,
-    "target_cpu": "arm64",
-    "target_os": "android",
-    "use_dummy_lastchange": true,
-    "use_remoteexec": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/android_compile_siso_dbg/properties.json b/infra/config/generated/builders/try/android_compile_siso_dbg/properties.json
deleted file mode 100644
index ead6b94..0000000
--- a/infra/config/generated/builders/try/android_compile_siso_dbg/properties.json
+++ /dev/null
@@ -1,80 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/try/android_compile_siso_dbg/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "Android arm64 Builder All Targets (dbg)",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-android-archive",
-              "builder_group": "chromium.android",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_android_config": {
-                "config": "main_builder_mb"
-              },
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "download_xr_test_apks"
-                ],
-                "build_config": "Debug",
-                "config": "android",
-                "target_bits": 64,
-                "target_platform": "android"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "android"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "Android arm64 Builder All Targets (dbg)",
-          "project": "chromium"
-        }
-      ],
-      "is_compile_only": true
-    }
-  },
-  "$build/flakiness": {
-    "check_for_flakiness": true,
-    "check_for_flakiness_with_resultdb": true
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "jobs": 500,
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "project": "rbe-chromium-untrusted"
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.android",
-  "cq": "required",
-  "recipe": "chromium_trybot"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast-compilator/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast-compilator/properties.json
new file mode 100644
index 0000000..46b7449
--- /dev/null
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast-compilator/properties.json
@@ -0,0 +1,163 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "chromeos-amd64-generic-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-chromiumos-archive",
+              "builder_group": "chromium.chromiumos",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "cros_boards_with_qemu_images": [
+                  "amd64-generic-vm"
+                ],
+                "target_arch": "intel",
+                "target_bits": 64,
+                "target_cros_boards": [
+                  "amd64-generic"
+                ],
+                "target_platform": "chromeos"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "chromeos",
+                  "checkout_lacros_sdk"
+                ],
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "chromeos-amd64-generic-rel-gtest",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-chromiumos-archive",
+              "builder_group": "chromium.chromiumos",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "cros_boards_with_qemu_images": [
+                  "amd64-generic-vm"
+                ],
+                "target_arch": "intel",
+                "target_bits": 64,
+                "target_cros_boards": [
+                  "amd64-generic"
+                ],
+                "target_platform": "chromeos"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "chromeos",
+                  "checkout_lacros_sdk"
+                ],
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "chromeos-amd64-generic-rel",
+                "project": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "chromeos-amd64-generic-rel-tast",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-chromiumos-archive",
+              "builder_group": "chromium.chromiumos",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "cros_boards_with_qemu_images": [
+                  "amd64-generic-vm"
+                ],
+                "target_arch": "intel",
+                "target_bits": 64,
+                "target_cros_boards": [
+                  "amd64-generic"
+                ],
+                "target_platform": "chromeos"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "chromeos",
+                  "checkout_lacros_sdk"
+                ],
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "chromeos-amd64-generic-rel",
+                "project": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "chromeos-amd64-generic-rel",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "chromeos-amd64-generic-rel-gtest",
+          "project": "chromium"
+        },
+        {
+          "bucket": "ci",
+          "builder": "chromeos-amd64-generic-rel-tast",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/flakiness": {
+    "check_for_flakiness": true,
+    "check_for_flakiness_with_resultdb": true
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "jobs": 500,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.chromiumos",
+  "recipe": "chromium/compilator"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json
index f509fe1..fa512c6b 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast/properties.json
@@ -1,4 +1,8 @@
 {
+  "$build/chromium_orchestrator": {
+    "compilator": "chromeos-amd64-generic-rel-gtest-and-tast-compilator",
+    "compilator_watcher_git_revision": "e6d08be3fd589d4f222dae5d18dbc972e6117b23"
+  },
   "$build/chromium_tests_builder_config": {
     "builder_config": {
       "builder_db": {
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-compilator/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-compilator/properties.json
new file mode 100644
index 0000000..e8670139
--- /dev/null
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-compilator/properties.json
@@ -0,0 +1,118 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "chromeos-amd64-generic-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-chromiumos-archive",
+              "builder_group": "chromium.chromiumos",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "cros_boards_with_qemu_images": [
+                  "amd64-generic-vm"
+                ],
+                "target_arch": "intel",
+                "target_bits": 64,
+                "target_cros_boards": [
+                  "amd64-generic"
+                ],
+                "target_platform": "chromeos"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "chromeos",
+                  "checkout_lacros_sdk"
+                ],
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "chromeos-amd64-generic-rel-gtest",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-chromiumos-archive",
+              "builder_group": "chromium.chromiumos",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "cros_boards_with_qemu_images": [
+                  "amd64-generic-vm"
+                ],
+                "target_arch": "intel",
+                "target_bits": 64,
+                "target_cros_boards": [
+                  "amd64-generic"
+                ],
+                "target_platform": "chromeos"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "chromeos",
+                  "checkout_lacros_sdk"
+                ],
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "chromeos-amd64-generic-rel",
+                "project": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "chromeos-amd64-generic-rel",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "chromeos-amd64-generic-rel-gtest",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/flakiness": {
+    "check_for_flakiness": true,
+    "check_for_flakiness_with_resultdb": true
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "jobs": 500,
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.chromiumos",
+  "recipe": "chromium/compilator"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json
index b59e2b57..09c546e 100644
--- a/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json
+++ b/infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest/properties.json
@@ -1,4 +1,8 @@
 {
+  "$build/chromium_orchestrator": {
+    "compilator": "chromeos-amd64-generic-rel-gtest-compilator",
+    "compilator_watcher_git_revision": "e6d08be3fd589d4f222dae5d18dbc972e6117b23"
+  },
   "$build/chromium_tests_builder_config": {
     "builder_config": {
       "builder_db": {
diff --git a/infra/config/generated/builders/try/linux-chromeos-compile-siso-dbg/properties.json b/infra/config/generated/builders/try/linux-chromeos-compile-siso-dbg/properties.json
deleted file mode 100644
index c2b1a0c..0000000
--- a/infra/config/generated/builders/try/linux-chromeos-compile-siso-dbg/properties.json
+++ /dev/null
@@ -1,74 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "linux-chromeos-dbg",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-chromiumos-archive",
-              "builder_group": "chromium.chromiumos",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Debug",
-                "config": "chromium",
-                "target_arch": "intel",
-                "target_bits": 64
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "chromeos"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "linux-chromeos-dbg",
-          "project": "chromium"
-        }
-      ],
-      "is_compile_only": true
-    }
-  },
-  "$build/flakiness": {
-    "check_for_flakiness": true,
-    "check_for_flakiness_with_resultdb": true
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "jobs": 500,
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "project": "rbe-chromium-untrusted"
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.chromiumos",
-  "cq": "required",
-  "recipe": "chromium_trybot"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/gn-args.json b/infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/gn-args.json
deleted file mode 100644
index df67d1e..0000000
--- a/infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/gn-args.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "gn_args": {
-    "is_component_build": true,
-    "is_debug": true,
-    "symbol_level": 1,
-    "use_dummy_lastchange": true,
-    "use_remoteexec": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/properties.json b/infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/properties.json
deleted file mode 100644
index a2d87e6..0000000
--- a/infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/properties.json
+++ /dev/null
@@ -1,108 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "Linux Builder (dbg)",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-linux-archive",
-              "builder_group": "chromium.linux",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Debug",
-                "config": "chromium",
-                "target_bits": 64
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              }
-            }
-          },
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "Linux Tests (dbg)(1)",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-linux-archive",
-              "builder_group": "chromium.linux",
-              "execution_mode": "TEST",
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Debug",
-                "config": "chromium",
-                "target_bits": 64
-              },
-              "legacy_gclient_config": {
-                "config": "chromium"
-              },
-              "parent": {
-                "bucket": "ci",
-                "builder": "Linux Builder (dbg)",
-                "project": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "Linux Builder (dbg)",
-          "project": "chromium"
-        }
-      ],
-      "builder_ids_in_scope_for_testing": [
-        {
-          "bucket": "ci",
-          "builder": "Linux Tests (dbg)(1)",
-          "project": "chromium"
-        }
-      ],
-      "is_compile_only": true
-    }
-  },
-  "$build/flakiness": {
-    "check_for_flakiness": true,
-    "check_for_flakiness_with_resultdb": true
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "jobs": 500,
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "project": "rbe-chromium-untrusted"
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "tryserver.chromium.linux",
-  "cq": "required",
-  "recipe": "chromium_trybot"
-}
\ No newline at end of file
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md
index 550c3681..a18ccaa9 100644
--- a/infra/config/generated/cq-builders.md
+++ b/infra/config/generated/cq-builders.md
@@ -243,7 +243,7 @@
 * [chromeos-amd64-generic-rel-gtest](https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel-gtest) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""chromeos-amd64-generic-rel-gtest""))
 
   Location filters:
-  * [`//dummypath/*`](https://cs.chromium.org/search?q=+file:dummypath/*)
+  * [`//dummypath/.+`](https://cs.chromium.org/chromium/src/dummypath/)
 
 * [dawn-android-arm-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-android-arm-deps-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""dawn-android-arm-deps-rel""))
 
@@ -605,15 +605,9 @@
 * [android-arm64-siso-rel](https://ci.chromium.org/p/chromium/builders/try/android-arm64-siso-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""android-arm64-siso-rel""))
   * Experiment percentage: 10.0
 
-* [android-binary-size-siso](https://ci.chromium.org/p/chromium/builders/try/android-binary-size-siso) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""android-binary-size-siso""))
-  * Experiment percentage: 10.0
-
 * [android-x86-dual-coverage-exp-rel](https://ci.chromium.org/p/chromium/builders/try/android-x86-dual-coverage-exp-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""android-x86-dual-coverage-exp-rel""))
   * Experiment percentage: 10.0
 
-* [android_compile_siso_dbg](https://ci.chromium.org/p/chromium/builders/try/android_compile_siso_dbg) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""android_compile_siso_dbg""))
-  * Experiment percentage: 10.0
-
 * [chromeos-amd64-generic-siso-rel](https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-siso-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""chromeos-amd64-generic-siso-rel""))
   * Experiment percentage: 10.0
 
@@ -639,24 +633,15 @@
   * [`//tools/clang/scripts/update.py`](https://cs.chromium.org/search?q=+file:tools/clang/scripts/update.py)
   * [`//ui/gl/features.gni`](https://cs.chromium.org/search?q=+file:ui/gl/features.gni)
 
-* [fuchsia-binary-size-siso](https://ci.chromium.org/p/chromium/builders/try/fuchsia-binary-size-siso) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""fuchsia-binary-size-siso""))
-  * Experiment percentage: 10.0
-
 * [ios-simulator-siso](https://ci.chromium.org/p/chromium/builders/try/ios-simulator-siso) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""ios-simulator-siso""))
   * Experiment percentage: 10.0
 
-* [linux-chromeos-compile-siso-dbg](https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-compile-siso-dbg) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""linux-chromeos-compile-siso-dbg""))
-  * Experiment percentage: 10.0
-
 * [linux-siso-rel](https://ci.chromium.org/p/chromium/builders/try/linux-siso-rel) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""linux-siso-rel""))
   * Experiment percentage: 10.0
 
 * [linux_chromium_asan_siso_rel_ng](https://ci.chromium.org/p/chromium/builders/try/linux_chromium_asan_siso_rel_ng) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""linux_chromium_asan_siso_rel_ng""))
   * Experiment percentage: 10.0
 
-* [linux_chromium_compile_siso_dbg_ng](https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_siso_dbg_ng) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""linux_chromium_compile_siso_dbg_ng""))
-  * Experiment percentage: 10.0
-
 * [linux_chromium_tsan_siso_rel_ng](https://ci.chromium.org/p/chromium/builders/try/linux_chromium_tsan_siso_rel_ng) ([definition](https://cs.chromium.org/search?q=+file:/try/.*\.star$+""linux_chromium_tsan_siso_rel_ng""))
   * Experiment percentage: 10.0
 
diff --git a/infra/config/generated/cq-usage/full.cfg b/infra/config/generated/cq-usage/full.cfg
index 8636f69..f9515829 100644
--- a/infra/config/generated/cq-usage/full.cfg
+++ b/infra/config/generated/cq-usage/full.cfg
@@ -875,7 +875,7 @@
         location_filters {
           gerrit_host_regexp: ".*"
           gerrit_project_regexp: ".*"
-          path_regexp: "dummypath/*"
+          path_regexp: "dummypath/.+"
         }
         location_filters {
           gerrit_host_regexp: ".*"
diff --git a/infra/config/generated/cq-usage/mega_cq_bots.txt b/infra/config/generated/cq-usage/mega_cq_bots.txt
index 1c62d069..6004f973 100644
--- a/infra/config/generated/cq-usage/mega_cq_bots.txt
+++ b/infra/config/generated/cq-usage/mega_cq_bots.txt
@@ -26,7 +26,6 @@
 chromium/try/android-x86-rel
 chromium/try/android_arm64_dbg_recipe
 chromium/try/android_compile_dbg
-chromium/try/android_compile_siso_dbg
 chromium/try/android_compile_x64_dbg
 chromium/try/android_compile_x86_dbg
 chromium/try/chromeos-amd64-generic-asan-rel
@@ -91,7 +90,6 @@
 chromium/try/linux-cfm-rel
 chromium/try/linux-chromeos-asan-rel
 chromium/try/linux-chromeos-compile-dbg
-chromium/try/linux-chromeos-compile-siso-dbg
 chromium/try/linux-chromeos-dbg
 chromium/try/linux-chromeos-rel
 chromium/try/linux-clobber-rel
@@ -128,7 +126,6 @@
 chromium/try/linux_chromium_chromeos_msan_rel_ng
 chromium/try/linux_chromium_compile_dbg_ng
 chromium/try/linux_chromium_compile_rel_ng
-chromium/try/linux_chromium_compile_siso_dbg_ng
 chromium/try/linux_chromium_dbg_ng
 chromium/try/linux_chromium_msan_rel_ng
 chromium/try/linux_chromium_tsan_rel_ng
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 1a057d1..bfc16f4 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -590,29 +590,6 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
-        name: "chromium/try/android-binary-size-siso"
-        experiment_percentage: 10
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "docs/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/generated/builders/try/android-binary-size-siso/.+"
-        }
-        mode_allowlist: "DRY_RUN"
-        mode_allowlist: "FULL_RUN"
-      }
-      builders {
         name: "chromium/try/android-chrome-pie-x86-wpt-android-specific"
         includable_only: true
       }
@@ -1188,29 +1165,6 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
-        name: "chromium/try/android_compile_siso_dbg"
-        experiment_percentage: 10
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "docs/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/generated/builders/try/android_compile_siso_dbg/.+"
-        }
-        mode_allowlist: "DRY_RUN"
-        mode_allowlist: "FULL_RUN"
-      }
-      builders {
         name: "chromium/try/android_compile_x64_dbg"
         location_filters {
           gerrit_host_regexp: ".*"
@@ -1648,7 +1602,7 @@
         location_filters {
           gerrit_host_regexp: ".*"
           gerrit_project_regexp: ".*"
-          path_regexp: "dummypath/*"
+          path_regexp: "dummypath/.+"
         }
         location_filters {
           gerrit_host_regexp: ".*"
@@ -1671,6 +1625,14 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
+        name: "chromium/try/chromeos-amd64-generic-rel-gtest-and-tast-compilator"
+        includable_only: true
+      }
+      builders {
+        name: "chromium/try/chromeos-amd64-generic-rel-gtest-compilator"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/chromeos-amd64-generic-rel-renamed"
         location_filters {
           gerrit_host_regexp: ".*"
@@ -2393,29 +2355,6 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
-        name: "chromium/try/fuchsia-binary-size-siso"
-        experiment_percentage: 10
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "docs/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/generated/builders/try/fuchsia-binary-size-siso/.+"
-        }
-        mode_allowlist: "DRY_RUN"
-        mode_allowlist: "FULL_RUN"
-      }
-      builders {
         name: "chromium/try/fuchsia-clang-tidy-rel"
         includable_only: true
       }
@@ -3356,29 +3295,6 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
-        name: "chromium/try/linux-chromeos-compile-siso-dbg"
-        experiment_percentage: 10
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "docs/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/generated/builders/try/linux-chromeos-compile-siso-dbg/.+"
-        }
-        mode_allowlist: "DRY_RUN"
-        mode_allowlist: "FULL_RUN"
-      }
-      builders {
         name: "chromium/try/linux-chromeos-dbg"
         includable_only: true
       }
@@ -4129,29 +4045,6 @@
         includable_only: true
       }
       builders {
-        name: "chromium/try/linux_chromium_compile_siso_dbg_ng"
-        experiment_percentage: 10
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "docs/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/.+"
-          exclude: true
-        }
-        location_filters {
-          gerrit_host_regexp: ".*"
-          gerrit_project_regexp: ".*"
-          path_regexp: "infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/.+"
-        }
-        mode_allowlist: "DRY_RUN"
-        mode_allowlist: "FULL_RUN"
-      }
-      builders {
         name: "chromium/try/linux_chromium_dbg_ng"
         location_filters {
           gerrit_host_regexp: ".*"
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index c5ef650..0bbaeb3c 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -3922,7 +3922,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-arm64-all-targets-dbg\">android-arm64-all-targets-dbg</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android_compile_dbg\">android_compile_dbg</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android_compile_siso_dbg\">android_compile_siso_dbg</a></li></ul>"
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android-arm64-all-targets-dbg\">android-arm64-all-targets-dbg</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/android_compile_dbg\">android_compile_dbg</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -15743,7 +15743,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_dbg_ng\">linux_chromium_compile_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_siso_dbg_ng\">linux_chromium_compile_siso_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_dbg_ng\">linux_chromium_dbg_ng</a></li></ul>"
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_dbg_ng\">linux_chromium_compile_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_dbg_ng\">linux_chromium_dbg_ng</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -17844,7 +17844,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_dbg_ng\">linux_chromium_compile_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_siso_dbg_ng\">linux_chromium_compile_siso_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_dbg_ng\">linux_chromium_dbg_ng</a></li></ul>"
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_compile_dbg_ng\">linux_chromium_compile_dbg_ng</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux_chromium_dbg_ng\">linux_chromium_dbg_ng</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -43553,7 +43553,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-compile-dbg\">linux-chromeos-compile-dbg</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-compile-siso-dbg\">linux-chromeos-compile-siso-dbg</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-dbg\">linux-chromeos-dbg</a></li></ul>"
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-compile-dbg\">linux-chromeos-compile-dbg</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-chromeos-dbg\">linux-chromeos-dbg</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -48220,6 +48220,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -48253,6 +48254,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -48382,6 +48384,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -48415,6 +48418,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -48442,6 +48446,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -48606,6 +48611,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -48633,6 +48639,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -64179,119 +64186,6 @@
       }
     }
     builders {
-      name: "android-binary-size-siso"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:16"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "$build/binary_size": {'
-        '    "analyze_targets": ['
-        '      "//chrome/android:monochrome_public_minimal_apks",'
-        '      "//chrome/android:trichrome_32_minimal_apks",'
-        '      "//chrome/android:validate_expectations",'
-        '      "//tools/binary_size:binary_size_trybot_py"'
-        '    ],'
-        '    "compile_targets": ['
-        '      "monochrome_public_minimal_apks",'
-        '      "monochrome_static_initializers",'
-        '      "trichrome_32_minimal_apks",'
-        '      "validate_expectations"'
-        '    ]'
-        '  },'
-        '  "$build/flakiness": {'
-        '    "check_for_flakiness": true,'
-        '    "check_for_flakiness_with_resultdb": true'
-        '  },'
-        '  "$build/reclient": {'
-        '    "instance": "rbe-chromium-untrusted",'
-        '    "jobs": 500,'
-        '    "metrics_project": "chromium-reclient-metrics",'
-        '    "scandeps_server": true'
-        '  },'
-        '  "$build/siso": {'
-        '    "configs": ['
-        '      "builder"'
-        '    ],'
-        '    "enable_cloud_profiler": true,'
-        '    "enable_cloud_trace": true,'
-        '    "experiments": [],'
-        '    "project": "rbe-chromium-untrusted"'
-        '  },'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
-        '  },'
-        '  "builder_group": "tryserver.chromium.android",'
-        '  "cq": "required",'
-        '  "recipe": "binary_size_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder shadows android-binary-size builder to compare between Siso builds and Ninja builds.<br/>\nThis builder should be removed after migrating android-binary-size from Ninja to Siso. b/277863839\n"
-      contact_team_email: "chrome-build-team@google.com"
-    }
-    builders {
       name: "android-chrome-pie-x86-wpt-android-specific"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -69585,99 +69479,6 @@
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android arm64 Builder All Targets (dbg)\">Android arm64 Builder All Targets (dbg)</a></li></ul>"
     }
     builders {
-      name: "android_compile_siso_dbg"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:32"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:1"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/android_compile_siso_dbg/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.android",'
-        '  "cq": "required",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder shadows android_compile_dbg builder to compare between Siso builds and Ninja builds.<br/>\nThis builder should be removed after migrating android_compile_dbg from Ninja to Siso. b/277863839\n<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android arm64 Builder All Targets (dbg)\">Android arm64 Builder All Targets (dbg)</a></li></ul>"
-      contact_team_email: "chrome-build-team@google.com"
-    }
-    builders {
       name: "android_compile_x64_dbg"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -70856,7 +70657,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This is an Ash chrome builder which only runs gtest. This builder is the default CQ builder for non-ChromeOS engineers only. See the builder description for <a href=\"https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel-gtest-and-tast\">chromeos-amd64-generic-rel-gtest-and-tast</a> for more information<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel\">chromeos-amd64-generic-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel-gtest\">chromeos-amd64-generic-rel-gtest</a></li></ul>"
+      description_html: "This is an Ash chrome builder which only runs gtest. This builder is the default CQ builder for non-ChromeOS engineers only. See the builder description for <a href=\"https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel-gtest-and-tast\">chromeos-amd64-generic-rel-gtest-and-tast</a> for more information<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel\">chromeos-amd64-generic-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel-gtest\">chromeos-amd64-generic-rel-gtest</a></li></ul><br/>This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel-gtest-compilator\">chromeos-amd64-generic-rel-gtest-compilator</a>."
       contact_team_email: "chromeos-sw-engprod@google.com"
     }
     builders {
@@ -70948,7 +70749,201 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This is an Ash chrome builder which runs gtest and Tast tests. This builder is the default CQ builder for ChromeOS engineers only. For a CL, infra would check the CL’s owner to see if the owner is a ChromeOS org engineer or not. If the owner is a ChromeOS org engineer, the default CQ would include this builder which runs both Tast tests and gtests. Otherwise, the default CQ would include `chromeos-amd64-generic-rel-gtest` which only runs gtests.<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel\">chromeos-amd64-generic-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel-gtest\">chromeos-amd64-generic-rel-gtest</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel-tast\">chromeos-amd64-generic-rel-tast</a></li></ul>"
+      description_html: "This is an Ash chrome builder which runs gtest and Tast tests. This builder is the default CQ builder for ChromeOS engineers only. For a CL, infra would check the CL’s owner to see if the owner is a ChromeOS org engineer or not. If the owner is a ChromeOS org engineer, the default CQ would include this builder which runs both Tast tests and gtests. Otherwise, the default CQ would include `chromeos-amd64-generic-rel-gtest` which only runs gtests.<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel\">chromeos-amd64-generic-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel-gtest\">chromeos-amd64-generic-rel-gtest</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/chromeos-amd64-generic-rel-tast\">chromeos-amd64-generic-rel-tast</a></li></ul><br/>This is the orchestrator half of an orchestrator + compilator pair of builders. The compilator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel-gtest-and-tast-compilator\">chromeos-amd64-generic-rel-gtest-and-tast-compilator</a>."
+      contact_team_email: "chromeos-sw-engprod@google.com"
+    }
+    builders {
+      name: "chromeos-amd64-generic-rel-gtest-and-tast-compilator"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:chromeos-amd64-generic-rel-gtest-and-tast-compilator"
+      dimensions: "cores:16"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-and-tast-compilator/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.chromiumos",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium/compilator"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "shared_chromeos_amd64_generic_rel_cache"
+        path: "builder"
+        wait_for_warm_cache_secs: 240
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: ".<br/>This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel-gtest-and-tast\">chromeos-amd64-generic-rel-gtest-and-tast</a>."
+      contact_team_email: "chromeos-sw-engprod@google.com"
+    }
+    builders {
+      name: "chromeos-amd64-generic-rel-gtest-compilator"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builder:chromeos-amd64-generic-rel-gtest-compilator"
+      dimensions: "cores:16"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/chromeos-amd64-generic-rel-gtest-compilator/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.chromiumos",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium/compilator"'
+        '}'
+      execution_timeout_secs: 14400
+      expiration_secs: 7200
+      grace_period {
+        seconds: 120
+      }
+      caches {
+        name: "shared_chromeos_amd64_generic_rel_cache"
+        path: "builder"
+        wait_for_warm_cache_secs: 240
+      }
+      build_numbers: YES
+      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      task_template_canary_percentage {
+        value: 5
+      }
+      experiments {
+        key: "chromium_swarming.expose_merge_script_failures"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "try_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_try_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: ".<br/>This is the compilator half of an orchestrator + compilator pair of builders. The orchestrator is <a href=\"https://ci.chromium.org/p/chromium/builders/try/chromeos-amd64-generic-rel-gtest\">chromeos-amd64-generic-rel-gtest</a>."
       contact_team_email: "chromeos-sw-engprod@google.com"
     }
     builders {
@@ -73916,113 +73911,6 @@
       }
     }
     builders {
-      name: "fuchsia-binary-size-siso"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:16"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
-      }
-      properties:
-        '{'
-        '  "$build/binary_size": {'
-        '    "analyze_targets": ['
-        '      "//tools/fuchsia/size_tests:fuchsia_sizes"'
-        '    ],'
-        '    "compile_targets": ['
-        '      "fuchsia_sizes"'
-        '    ]'
-        '  },'
-        '  "$build/flakiness": {'
-        '    "check_for_flakiness": true,'
-        '    "check_for_flakiness_with_resultdb": true'
-        '  },'
-        '  "$build/reclient": {'
-        '    "instance": "rbe-chromium-untrusted",'
-        '    "jobs": 500,'
-        '    "metrics_project": "chromium-reclient-metrics",'
-        '    "scandeps_server": true'
-        '  },'
-        '  "$build/siso": {'
-        '    "configs": ['
-        '      "builder"'
-        '    ],'
-        '    "enable_cloud_profiler": true,'
-        '    "enable_cloud_trace": true,'
-        '    "experiments": [],'
-        '    "project": "rbe-chromium-untrusted"'
-        '  },'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
-        '  },'
-        '  "builder_group": "tryserver.chromium.fuchsia",'
-        '  "cq": "required",'
-        '  "recipe": "binary_size_fuchsia_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder shadows fuchsia-binary-size builder to compare between Siso builds and Ninja builds.<br/>\nThis builder should be removed after migrating size from Ninja to Siso. b/277863839\n"
-      contact_team_email: "chrome-build-team@google.com"
-    }
-    builders {
       name: "fuchsia-clang-tidy-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -84193,99 +84081,6 @@
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/linux-chromeos-dbg\">linux-chromeos-dbg</a></li></ul>"
     }
     builders {
-      name: "linux-chromeos-compile-siso-dbg"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/linux-chromeos-compile-siso-dbg/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.chromiumos",'
-        '  "cq": "required",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder shadows linux-chromeos-compile-dbg builder to compare between Siso builds and Ninja builds.<br/>\nThis builder should be removed after migrating linux-chromeos-compile-dbg from Ninja to Siso. b/277863839\n<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/linux-chromeos-dbg\">linux-chromeos-dbg</a></li></ul>"
-      contact_team_email: "chrome-build-team@google.com"
-    }
-    builders {
       name: "linux-chromeos-dbg"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -91238,103 +91033,6 @@
       description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux Builder\">Linux Builder</a></li></ul>"
     }
     builders {
-      name: "linux_chromium_compile_siso_dbg_ng"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.try"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/try/linux_chromium_compile_siso_dbg_ng/properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "tryserver.chromium.linux",'
-        '  "cq": "required",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium_trybot"'
-        '}'
-      execution_timeout_secs: 14400
-      expiration_secs: 7200
-      grace_period {
-        seconds: 120
-      }
-      caches {
-        name: "builder"
-        path: "linux_debug"
-      }
-      build_numbers: YES
-      service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-      task_template_canary_percentage {
-        value: 5
-      }
-      experiments {
-        key: "chromium_swarming.expose_merge_script_failures"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "try_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://chrome/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_try_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder shadows linux_chromium_compile_dbg_ng builder to compare between Siso builds and Ninja builds.<br/>\nThis builder should be removed after migrating linux_chromium_compile_dbg_ng from Ninja to Siso. b/277863839\n<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux Builder (dbg)\">Linux Builder (dbg)</a></li></ul>"
-      contact_team_email: "chrome-build-team@google.com"
-    }
-    builders {
       name: "linux_chromium_dbg_ng"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -92181,6 +91879,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -92214,6 +91913,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
@@ -92241,6 +91941,7 @@
         '                ],'
         '                "build_config": "Release",'
         '                "config": "chromium",'
+        '                "target_arch": "intel",'
         '                "target_bits": 64,'
         '                "target_platform": "mac"'
         '              },'
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 138ec65..f470bbe2 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -3017,9 +3017,6 @@
     name: "buildbucket/luci.chromium.try/android-binary-size"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-binary-size-siso"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-cronet-arm-dbg"
   }
   builders {
@@ -3050,9 +3047,6 @@
     name: "buildbucket/luci.chromium.try/android_compile_dbg"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android_compile_siso_dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android_compile_x64_dbg"
   }
   builders {
@@ -3086,6 +3080,12 @@
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-and-tast"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-and-tast-compilator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-compilator"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-renamed"
   }
   builders {
@@ -3200,9 +3200,6 @@
     name: "buildbucket/luci.chromium.try/linux-chromeos-compile-dbg"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-chromeos-compile-siso-dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-chromeos-rel"
   }
   builders {
@@ -3269,9 +3266,6 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_compile_dbg_ng"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux_chromium_compile_siso_dbg_ng"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_chromium_dbg_ng"
   }
   builders {
@@ -17047,9 +17041,6 @@
     name: "buildbucket/luci.chromium.try/android-binary-size"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-binary-size-siso"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-chrome-pie-x86-wpt-android-specific"
   }
   builders {
@@ -17224,9 +17215,6 @@
     name: "buildbucket/luci.chromium.try/android_compile_dbg"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android_compile_siso_dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android_compile_x64_dbg"
   }
   builders {
@@ -17269,6 +17257,12 @@
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-and-tast"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-and-tast-compilator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-compilator"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-renamed"
   }
   builders {
@@ -17365,9 +17359,6 @@
     name: "buildbucket/luci.chromium.try/fuchsia-binary-size"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/fuchsia-binary-size-siso"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-clang-tidy-rel"
   }
   builders {
@@ -17701,9 +17692,6 @@
     name: "buildbucket/luci.chromium.try/linux-chromeos-compile-dbg"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-chromeos-compile-siso-dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-chromeos-dbg"
   }
   builders {
@@ -17932,9 +17920,6 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_compile_rel_ng"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux_chromium_compile_siso_dbg_ng"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_chromium_dbg_ng"
   }
   builders {
@@ -18468,9 +18453,6 @@
     name: "buildbucket/luci.chromium.try/android-binary-size"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android-binary-size-siso"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android-chrome-pie-x86-wpt-android-specific"
   }
   builders {
@@ -18624,9 +18606,6 @@
     name: "buildbucket/luci.chromium.try/android_compile_dbg"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/android_compile_siso_dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/android_compile_x64_dbg"
   }
   builders {
@@ -18732,6 +18711,12 @@
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-and-tast"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-and-tast-compilator"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-gtest-compilator"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/chromeos-amd64-generic-rel-renamed"
   }
   builders {
@@ -18795,9 +18780,6 @@
     name: "buildbucket/luci.chromium.try/linux-chromeos-compile-dbg"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux-chromeos-compile-siso-dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux-chromeos-dbg"
   }
   builders {
@@ -18930,9 +18912,6 @@
     name: "buildbucket/luci.chromium.try/fuchsia-binary-size"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/fuchsia-binary-size-siso"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/fuchsia-code-coverage"
   }
   builders {
@@ -19257,9 +19236,6 @@
     name: "buildbucket/luci.chromium.try/linux_chromium_compile_rel_ng"
   }
   builders {
-    name: "buildbucket/luci.chromium.try/linux_chromium_compile_siso_dbg_ng"
-  }
-  builders {
     name: "buildbucket/luci.chromium.try/linux_chromium_dbg_ng"
   }
   builders {
diff --git a/infra/config/generated/testing/variants.pyl b/infra/config/generated/testing/variants.pyl
index 82b9733..6f680a7 100644
--- a/infra/config/generated/testing/variants.pyl
+++ b/infra/config/generated/testing/variants.pyl
@@ -70,16 +70,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 121.0.6119.0',
+    'description': 'Run with ash-chrome version 121.0.6120.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v121.0.6119.0',
-          'revision': 'version:121.0.6119.0',
+          'location': 'lacros_version_skew_tests_v121.0.6120.0',
+          'revision': 'version:121.0.6120.0',
         },
       ],
     },
diff --git a/infra/config/lib/orchestrator.star b/infra/config/lib/orchestrator.star
index bce1ce1..b57af9d 100644
--- a/infra/config/lib/orchestrator.star
+++ b/infra/config/lib/orchestrator.star
@@ -131,6 +131,12 @@
         if _builder_name(n) not in experimental_orchestrator_names
     ]
 
+    if len(orchestrator_nodes) != 1:
+        fail("compilator should have exactly 1 referring orchestrator, got: {}, {}".format(
+            _builder_name(node),
+            [_builder_name(n) for n in orchestrator_nodes],
+        ))
+
     return struct(
         name = compilator_name,
         builder = builder,
diff --git a/infra/config/subprojects/chromium/ci/chromium.angle.star b/infra/config/subprojects/chromium/ci/chromium.angle.star
index a88319c..f03deab 100644
--- a/infra/config/subprojects/chromium/ci/chromium.angle.star
+++ b/infra/config/subprojects/chromium/ci/chromium.angle.star
@@ -228,6 +228,7 @@
                 "mb",
             ],
             build_config = builder_config.build_config.RELEASE,
+            target_arch = builder_config.target_arch.INTEL,
             target_bits = 64,
             target_platform = builder_config.target_platform.MAC,
         ),
@@ -257,6 +258,7 @@
                 "mb",
             ],
             build_config = builder_config.build_config.RELEASE,
+            target_arch = builder_config.target_arch.INTEL,
             target_bits = 64,
             target_platform = builder_config.target_platform.MAC,
         ),
@@ -287,6 +289,7 @@
                 "mb",
             ],
             build_config = builder_config.build_config.RELEASE,
+            target_arch = builder_config.target_arch.INTEL,
             target_bits = 64,
             target_platform = builder_config.target_platform.MAC,
         ),
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 50ff30f95..7f64230 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -324,51 +324,6 @@
 )
 
 try_.builder(
-    name = "android-binary-size-siso",
-    description_html = """\
-This builder shadows android-binary-size builder to compare between Siso builds and Ninja builds.<br/>
-This builder should be removed after migrating android-binary-size from Ninja to Siso. b/277863839
-""",
-    executable = "recipe:binary_size_trybot",
-    cores = 16,
-    ssd = True,
-    contact_team_email = "chrome-build-team@google.com",
-    gn_args = gn_args.config(
-        configs = [
-            "android",
-            "chrome_with_codecs",
-            "reclient",
-            "minimal_symbols",
-            "official_optimize",
-            "stable_channel",
-            "v8_release_branch",
-            "use_dummy_lastchange",
-        ],
-    ),
-    main_list_view = "try",
-    properties = {
-        "$build/binary_size": {
-            "analyze_targets": [
-                "//chrome/android:monochrome_public_minimal_apks",
-                "//chrome/android:trichrome_32_minimal_apks",
-                "//chrome/android:validate_expectations",
-                "//tools/binary_size:binary_size_trybot_py",
-            ],
-            "compile_targets": [
-                "monochrome_public_minimal_apks",
-                "monochrome_static_initializers",
-                "trichrome_32_minimal_apks",
-                "validate_expectations",
-            ],
-        },
-    },
-    siso_enabled = True,
-    tryjob = try_.job(
-        experiment_percentage = 10,
-    ),
-)
-
-try_.builder(
     name = "android-clobber-rel",
     mirrors = [
         "ci/android-archive-rel",
@@ -1068,38 +1023,6 @@
 )
 
 try_.builder(
-    name = "android_compile_siso_dbg",
-    description_html = """\
-This builder shadows android_compile_dbg builder to compare between Siso builds and Ninja builds.<br/>
-This builder should be removed after migrating android_compile_dbg from Ninja to Siso. b/277863839
-""",
-    mirrors = builder_config.copy_from("try/android_compile_dbg"),
-    try_settings = builder_config.try_settings(
-        include_all_triggered_testers = True,
-        is_compile_only = True,
-    ),
-    cores = 32,
-    ssd = True,
-    contact_team_email = "chrome-build-team@google.com",
-    gn_args = gn_args.config(
-        configs = [
-            "android",
-            "debug_builder",
-            "reclient",
-            "compile_only",
-            "arm64",
-            "use_dummy_lastchange",
-        ],
-    ),
-    main_list_view = "try",
-    reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
-    siso_enabled = True,
-    tryjob = try_.job(
-        experiment_percentage = 10,
-    ),
-)
-
-try_.builder(
     name = "android_compile_x64_dbg",
     branch_selector = branches.selector.ANDROID_BRANCHES,
     # Since we expect this builder to compile all, let it mirror
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
index 0c704c0..82b8f27f 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -87,7 +87,7 @@
         "ci/chromeos-amd64-generic-rel",
         "ci/chromeos-amd64-generic-rel-gtest",
     ],
-    compilator = "chromeos-amd64-generic-rel-compilator",
+    compilator = "chromeos-amd64-generic-rel-gtest-compilator",
     contact_team_email = "chromeos-sw-engprod@google.com",
     main_list_view = "try",
     tryjob = try_.job(
@@ -95,7 +95,7 @@
         equivalent_builder_percentage = 100,
         equivalent_builder_whitelist = "google/chromeos-pa@google.com",
         # Use dummypath to make sure it's not auto triggered.
-        location_filters = ["dummypath/*"],
+        location_filters = ["dummypath/.+"],
     ),
 )
 
@@ -117,7 +117,7 @@
         "ci/chromeos-amd64-generic-rel-gtest",
         "ci/chromeos-amd64-generic-rel-tast",
     ],
-    compilator = "chromeos-amd64-generic-rel-compilator",
+    compilator = "chromeos-amd64-generic-rel-gtest-and-tast-compilator",
     contact_team_email = "chromeos-sw-engprod@google.com",
     main_list_view = "try",
     tryjob = try_.job(
@@ -140,13 +140,15 @@
     tryjob = try_.job(),
 )
 
+CHROMEOS_SHARED_CACHE = "shared_chromeos_amd64_generic_rel_cache"
+
 try_.compilator_builder(
     name = "chromeos-amd64-generic-rel-compilator",
     branch_selector = branches.selector.CROS_LTS_BRANCHES,
     cores = "8|16",
     caches = [
         swarming.cache(
-            name = "shared_chromeos_amd64_generic_rel_cache",
+            name = CHROMEOS_SHARED_CACHE,
             path = "builder",
             wait_for_warm_cache = 4 * time.minute,
         ),
@@ -154,6 +156,38 @@
     main_list_view = "try",
 )
 
+try_.compilator_builder(
+    name = "chromeos-amd64-generic-rel-gtest-compilator",
+    branch_selector = branches.selector.CROS_LTS_BRANCHES,
+    description_html = ".",
+    cores = "16",
+    caches = [
+        swarming.cache(
+            name = CHROMEOS_SHARED_CACHE,
+            path = "builder",
+            wait_for_warm_cache = 4 * time.minute,
+        ),
+    ],
+    contact_team_email = "chromeos-sw-engprod@google.com",
+    main_list_view = "try",
+)
+
+try_.compilator_builder(
+    name = "chromeos-amd64-generic-rel-gtest-and-tast-compilator",
+    branch_selector = branches.selector.CROS_LTS_BRANCHES,
+    description_html = ".",
+    cores = "16",
+    caches = [
+        swarming.cache(
+            name = CHROMEOS_SHARED_CACHE,
+            path = "builder",
+            wait_for_warm_cache = 4 * time.minute,
+        ),
+    ],
+    contact_team_email = "chromeos-sw-engprod@google.com",
+    main_list_view = "try",
+)
+
 try_.orchestrator_builder(
     name = "chromeos-amd64-generic-siso-rel",
     description_html = """\
@@ -313,26 +347,6 @@
 )
 
 try_.builder(
-    name = "linux-chromeos-compile-siso-dbg",
-    description_html = """\
-This builder shadows linux-chromeos-compile-dbg builder to compare between Siso builds and Ninja builds.<br/>
-This builder should be removed after migrating linux-chromeos-compile-dbg from Ninja to Siso. b/277863839
-""",
-    mirrors = builder_config.copy_from("try/linux-chromeos-compile-dbg"),
-    try_settings = builder_config.try_settings(
-        include_all_triggered_testers = True,
-        is_compile_only = True,
-    ),
-    contact_team_email = "chrome-build-team@google.com",
-    main_list_view = "try",
-    reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
-    siso_enabled = True,
-    tryjob = try_.job(
-        experiment_percentage = 10,
-    ),
-)
-
-try_.builder(
     name = "chromeos-jacuzzi-rel",
     branch_selector = branches.selector.CROS_LTS_BRANCHES,
     mirrors = [
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
index 48ada83..3c134bd 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.fuchsia.star
@@ -98,31 +98,6 @@
 )
 
 try_.builder(
-    name = "fuchsia-binary-size-siso",
-    description_html = """\
-This builder shadows fuchsia-binary-size builder to compare between Siso builds and Ninja builds.<br/>
-This builder should be removed after migrating size from Ninja to Siso. b/277863839
-""",
-    executable = "recipe:binary_size_fuchsia_trybot",
-    cores = 16,
-    contact_team_email = "chrome-build-team@google.com",
-    properties = {
-        "$build/binary_size": {
-            "analyze_targets": [
-                "//tools/fuchsia/size_tests:fuchsia_sizes",
-            ],
-            "compile_targets": [
-                "fuchsia_sizes",
-            ],
-        },
-    },
-    siso_enabled = True,
-    tryjob = try_.job(
-        experiment_percentage = 10,
-    ),
-)
-
-try_.builder(
     name = "fuchsia-compile-x64-dbg",
     mirrors = [
         "ci/fuchsia-x64-dbg",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index ec0f1c4..7627eb0 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -625,39 +625,6 @@
 )
 
 try_.builder(
-    name = "linux_chromium_compile_siso_dbg_ng",
-    description_html = """\
-This builder shadows linux_chromium_compile_dbg_ng builder to compare between Siso builds and Ninja builds.<br/>
-This builder should be removed after migrating linux_chromium_compile_dbg_ng from Ninja to Siso. b/277863839
-""",
-    mirrors = builder_config.copy_from("try/linux_chromium_compile_dbg_ng"),
-    try_settings = builder_config.try_settings(
-        include_all_triggered_testers = True,
-        is_compile_only = True,
-    ),
-    caches = [
-        swarming.cache(
-            name = "builder",
-            path = "linux_debug",
-        ),
-    ],
-    contact_team_email = "chrome-build-team@google.com",
-    gn_args = gn_args.config(
-        configs = [
-            "debug_builder",
-            "reclient",
-            "use_dummy_lastchange",
-        ],
-    ),
-    main_list_view = "try",
-    reclient_jobs = reclient.jobs.HIGH_JOBS_FOR_CQ,
-    siso_enabled = True,
-    tryjob = try_.job(
-        experiment_percentage = 10,
-    ),
-)
-
-try_.builder(
     name = "linux_chromium_compile_rel_ng",
     mirrors = [
         "ci/Linux Builder",
diff --git a/infra/config/targets/lacros-version-skew-variants.json b/infra/config/targets/lacros-version-skew-variants.json
index 290a535..d63a3f3 100644
--- a/infra/config/targets/lacros-version-skew-variants.json
+++ b/infra/config/targets/lacros-version-skew-variants.json
@@ -1,16 +1,16 @@
 {
   "LACROS_VERSION_SKEW_CANARY": {
     "args": [
-      "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+      "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
     ],
-    "description": "Run with ash-chrome version 121.0.6119.0",
+    "description": "Run with ash-chrome version 121.0.6120.0",
     "identifier": "Lacros version skew testing ash canary",
     "swarming": {
       "cipd_packages": [
         {
           "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-          "location": "lacros_version_skew_tests_v121.0.6119.0",
-          "revision": "version:121.0.6119.0"
+          "location": "lacros_version_skew_tests_v121.0.6120.0",
+          "revision": "version:121.0.6120.0"
         }
       ]
     }
diff --git a/internal b/internal
index 1b190e3..373659c 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 1b190e361c56e70d2c0e57fc9b56dc7af3a75fea
+Subproject commit 373659c559effc4f18b0e2a1a8c5ec10c86fa316
diff --git a/ios/chrome/app/spotlight/BUILD.gn b/ios/chrome/app/spotlight/BUILD.gn
index fa789f07..5cb6f0e 100644
--- a/ios/chrome/app/spotlight/BUILD.gn
+++ b/ios/chrome/app/spotlight/BUILD.gn
@@ -90,6 +90,7 @@
     "//components/favicon_base",
     "//components/reading_list/core",
     "//ios/chrome/browser/bookmarks/model",
+    "//ios/chrome/browser/bookmarks/model:test_support",
     "//ios/chrome/browser/main:main",
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/reading_list/model:test_support",
diff --git a/ios/chrome/app/spotlight/bookmark_spotlight_manager_unittest.mm b/ios/chrome/app/spotlight/bookmark_spotlight_manager_unittest.mm
index 617965e..288f453 100644
--- a/ios/chrome/app/spotlight/bookmark_spotlight_manager_unittest.mm
+++ b/ios/chrome/app/spotlight/bookmark_spotlight_manager_unittest.mm
@@ -19,12 +19,12 @@
 #import "ios/chrome/app/spotlight/fake_spotlight_interface.h"
 #import "ios/chrome/app/spotlight/spotlight_manager.h"
 #import "ios/chrome/app/spotlight/spotlight_util.h"
+#import "ios/chrome/browser/bookmarks/model/bookmark_ios_unit_test_support.h"
 #import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state.h"
 #import "net/base/mac/url_conversions.h"
 #import "testing/gmock/include/gmock/gmock.h"
 #import "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
-#import "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
 #import "third_party/ocmock/gtest_support.h"
 #import "third_party/skia/include/core/SkBitmap.h"
@@ -56,10 +56,9 @@
 
 }  // namespace
 
-class BookmarkSpotlightManagerTest : public PlatformTest {
+class BookmarkSpotlightManagerTest : public BookmarkIOSUnitTestSupport {
  public:
   BookmarkSpotlightManagerTest() {
-    bookmarkModel_ = bookmarks::TestBookmarkClient::CreateModel();
     CreateMockLargeIconService();
     spotlightInterface_ = [[FakeSpotlightInterface alloc] init];
     searchableItemFactory_ = [[FakeSearchableItemFactory alloc]
@@ -86,12 +85,9 @@
         });
   }
 
-  base::test::TaskEnvironment task_environment_;
-  std::unique_ptr<TestChromeBrowserState> browser_state_;
   testing::StrictMock<favicon::MockFaviconService> mock_favicon_service_;
   std::unique_ptr<favicon::LargeIconServiceImpl> large_icon_service_;
   base::CancelableTaskTracker cancelable_task_tracker_;
-  std::unique_ptr<bookmarks::BookmarkModel> bookmarkModel_;
   FakeSpotlightInterface* spotlightInterface_;
   FakeSearchableItemFactory* searchableItemFactory_;
 };
@@ -99,18 +95,20 @@
 /// Tests that clearAndReindexModel actually clears all bookmarks items and
 /// attempt to reindex the existing items in bookmark.
 TEST_F(BookmarkSpotlightManagerTest, testClearAndReindexModel) {
-  const std::u16string title(u"foo");
-  const GURL url("http://foo.com");
-  bookmarkModel_->AddURL(bookmarkModel_->bookmark_bar_node(), 0, title, url);
+  AddBookmark(local_or_syncable_bookmark_model_->mobile_node(), u"foo1",
+              GURL("http://foo1.com"));
+  AddBookmark(account_bookmark_model_->mobile_node(), u"foo2",
+              GURL("http://foo2.com"));
 
   FakeSpotlightInterface* fakeSpotlightInterface =
       [[FakeSpotlightInterface alloc] init];
 
   BookmarksSpotlightManager* manager = [[BookmarksSpotlightManager alloc]
-      initWithLargeIconService:large_icon_service_.get()
-                 bookmarkModel:bookmarkModel_.get()
-            spotlightInterface:fakeSpotlightInterface
-         searchableItemFactory:searchableItemFactory_];
+          initWithLargeIconService:large_icon_service_.get()
+      localOrSyncableBookmarkModel:local_or_syncable_bookmark_model_
+              accountBookmarkModel:account_bookmark_model_
+                spotlightInterface:fakeSpotlightInterface
+             searchableItemFactory:searchableItemFactory_];
 
   NSUInteger initialIndexedItemCount =
       fakeSpotlightInterface.indexSearchableItemsCallsCount;
@@ -123,9 +121,9 @@
             1u);
 
   // We expect that we will reindex the only existing item in bookmark, thus the
-  // +1 for the count.
+  // +2 for the count.
   EXPECT_EQ(fakeSpotlightInterface.indexSearchableItemsCallsCount,
-            initialIndexedItemCount + 1);
+            initialIndexedItemCount + 2);
 
   [manager shutdown];
 }
@@ -134,15 +132,17 @@
 /// returns an array of its ancestor folder names
 TEST_F(BookmarkSpotlightManagerTest, testParentFolderNamesForNode) {
   BookmarksSpotlightManager* manager = [[BookmarksSpotlightManager alloc]
-      initWithLargeIconService:large_icon_service_.get()
-                 bookmarkModel:bookmarkModel_.get()
-            spotlightInterface:spotlightInterface_
-         searchableItemFactory:searchableItemFactory_];
+          initWithLargeIconService:large_icon_service_.get()
+      localOrSyncableBookmarkModel:local_or_syncable_bookmark_model_
+              accountBookmarkModel:account_bookmark_model_
+                spotlightInterface:spotlightInterface_
+             searchableItemFactory:searchableItemFactory_];
 
-  const bookmarks::BookmarkNode* root = bookmarkModel_->bookmark_bar_node();
-  static const std::string model_string("a 1:[ b c ] d 2:[ 21:[ e ] f g ] h");
-  bookmarks::test::AddNodesFromModelString(bookmarkModel_.get(), root,
-                                           model_string);
+  const bookmarks::BookmarkNode* root =
+      local_or_syncable_bookmark_model_->mobile_node();
+  static const std::string model_string("a 1:[ b c ] d 2:[ 21:[ e ] f g ] h ");
+  bookmarks::test::AddNodesFromModelString(local_or_syncable_bookmark_model_,
+                                           root, model_string);
   const bookmarks::BookmarkNode* eNode =
       root->children()[3]->children().front()->children().front().get();
   NSMutableArray* folderNames = [manager parentFolderNamesForNode:eNode];
@@ -161,23 +161,24 @@
       [[FakeSpotlightInterface alloc] init];
 
   BookmarksSpotlightManager* manager = [[BookmarksSpotlightManager alloc]
-      initWithLargeIconService:large_icon_service_.get()
-                 bookmarkModel:bookmarkModel_.get()
-            spotlightInterface:fakeSpotlightInterface
-         searchableItemFactory:searchableItemFactory_];
+          initWithLargeIconService:large_icon_service_.get()
+      localOrSyncableBookmarkModel:local_or_syncable_bookmark_model_
+              accountBookmarkModel:account_bookmark_model_
+                spotlightInterface:fakeSpotlightInterface
+             searchableItemFactory:searchableItemFactory_];
 
   NSUInteger initialIndexedItemCount =
       fakeSpotlightInterface.indexSearchableItemsCallsCount;
 
-  const std::u16string title(u"foo");
-  const GURL url("http://foo.com");
-
-  bookmarkModel_->AddURL(bookmarkModel_->bookmark_bar_node(), 0, title, url);
+  AddBookmark(local_or_syncable_bookmark_model_->mobile_node(), u"foo1",
+              GURL("http://foo1.com"));
+  AddBookmark(account_bookmark_model_->mobile_node(), u"foo2",
+              GURL("http://foo2.com"));
 
   // We expect to call indexSearchableItems api method to add the new added
-  // bookmark item.
+  // bookmark items.
   EXPECT_EQ(fakeSpotlightInterface.indexSearchableItemsCallsCount,
-            initialIndexedItemCount + 1);
+            initialIndexedItemCount + 2);
 
   [manager shutdown];
 }
@@ -189,39 +190,44 @@
       [[FakeSpotlightInterface alloc] init];
 
   BookmarksSpotlightManager* manager = [[BookmarksSpotlightManager alloc]
-      initWithLargeIconService:large_icon_service_.get()
-                 bookmarkModel:bookmarkModel_.get()
-            spotlightInterface:fakeSpotlightInterface
-         searchableItemFactory:searchableItemFactory_];
+          initWithLargeIconService:large_icon_service_.get()
+      localOrSyncableBookmarkModel:local_or_syncable_bookmark_model_
+              accountBookmarkModel:account_bookmark_model_
+                spotlightInterface:fakeSpotlightInterface
+             searchableItemFactory:searchableItemFactory_];
 
   NSUInteger currentIndexedItemCount =
       fakeSpotlightInterface.indexSearchableItemsCallsCount;
 
-  const std::u16string title(u"foo");
-  const GURL url("http://foo.com");
-
-  const bookmarks::BookmarkNode* addedNode = bookmarkModel_->AddURL(
-      bookmarkModel_->bookmark_bar_node(), 0, title, url);
+  const bookmarks::BookmarkNode* addedNode1 =
+      AddBookmark(local_or_syncable_bookmark_model_->mobile_node(), u"foo1",
+                  GURL("http://foo1.com"));
+  const bookmarks::BookmarkNode* addedNode2 = AddBookmark(
+      account_bookmark_model_->mobile_node(), u"foo2", GURL("http://foo2.com"));
 
   // We expect to call indexSearchableItems api method to add the new added
   // bookmark item.
   EXPECT_EQ(fakeSpotlightInterface.indexSearchableItemsCallsCount,
-            currentIndexedItemCount + 1);
+            currentIndexedItemCount + 2);
 
   currentIndexedItemCount =
       fakeSpotlightInterface.indexSearchableItemsCallsCount;
 
-  bookmarkModel_->SetTitle(addedNode, u"new title",
-                           bookmarks::metrics::BookmarkEditSource::kOther);
+  local_or_syncable_bookmark_model_->SetTitle(
+      addedNode1, u"new title 1",
+      bookmarks::metrics::BookmarkEditSource::kOther);
+  account_bookmark_model_->SetTitle(
+      addedNode2, u"new title 2",
+      bookmarks::metrics::BookmarkEditSource::kOther);
 
   // We expect to delete the modified item using its identifier.
   EXPECT_EQ(
       fakeSpotlightInterface.deleteSearchableItemsWithIdentifiersCallsCount,
-      1u);
+      2u);
 
   // We expect reindexing it with the new details.
   EXPECT_EQ(fakeSpotlightInterface.indexSearchableItemsCallsCount,
-            currentIndexedItemCount + 1);
+            currentIndexedItemCount + 2);
 
   [manager shutdown];
 }
diff --git a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.h b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.h
index a0ba50b0..abcec7b 100644
--- a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.h
+++ b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.h
@@ -28,10 +28,12 @@
 @interface BookmarksSpotlightManager : BaseSpotlightManager
 
 - (instancetype)
-    initWithLargeIconService:(favicon::LargeIconService*)largeIconService
-               bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
-          spotlightInterface:(SpotlightInterface*)spotlightInterface
-       searchableItemFactory:(SearchableItemFactory*)searchableItemFactory;
+        initWithLargeIconService:(favicon::LargeIconService*)largeIconService
+    localOrSyncableBookmarkModel:
+        (bookmarks::BookmarkModel*)localOrSyncableBookmarkModel
+            accountBookmarkModel:(bookmarks::BookmarkModel*)accountBookmarkModel
+              spotlightInterface:(SpotlightInterface*)spotlightInterface
+           searchableItemFactory:(SearchableItemFactory*)searchableItemFactory;
 
 /// Number of pending large icon tasks.
 @property(nonatomic, assign) NSUInteger pendingLargeIconTasksCount;
diff --git a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
index 00fb6b1..2da5a65a 100644
--- a/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
+++ b/ios/chrome/app/spotlight/bookmarks_spotlight_manager.mm
@@ -18,6 +18,7 @@
 #import "ios/chrome/app/spotlight/searchable_item_factory.h"
 #import "ios/chrome/app/spotlight/spotlight_interface.h"
 #import "ios/chrome/app/spotlight/spotlight_logger.h"
+#import "ios/chrome/browser/bookmarks/model/account_bookmark_model_factory.h"
 #import "ios/chrome/browser/bookmarks/model/local_or_syncable_bookmark_model_factory.h"
 #import "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
 
@@ -132,13 +133,19 @@
 };
 
 @implementation BookmarksSpotlightManager {
-  // Bridge to register for bookmark changes.
-  std::unique_ptr<SpotlightBookmarkModelBridge> _bookmarkModelBridge;
+  // Bridge to register for local or syncable bookmark model changes.
+  std::unique_ptr<SpotlightBookmarkModelBridge>
+      _localOrSyncableBookmarkModelBridge;
+  // Bridge to register for account bookmark model changes.
+  std::unique_ptr<SpotlightBookmarkModelBridge> _accountBookmarkModelBridge;
 
-  // Keep a reference to detach before deallocing. Life cycle of _bookmarkModel
-  // is longer than life cycle of a SpotlightManager as
-  // `BookmarkModelBeingDeleted` will cause deletion of SpotlightManager.
-  bookmarks::BookmarkModel* _bookmarkModel;  // weak
+  // Keep a reference to detach before deallocing. Life cycle of
+  // `_localOrSyncalbeBookmarkModel` and `_accountBookmarkModel` is longer than
+  // life cycle of a SpotlightManager as `BookmarkModelBeingDeleted` will cause
+  // deletion of SpotlightManager.
+  bookmarks::BookmarkModel* _localOrSyncableBookmarkModel;  // weak
+  // `_accountBookmarkModel` can be `nullptr`.
+  bookmarks::BookmarkModel* _accountBookmarkModel;  // weak
 
   // Number of nodes indexed in initial scan.
   NSUInteger _nodesIndexed;
@@ -153,37 +160,54 @@
       IOSChromeLargeIconServiceFactory::GetForBrowserState(browserState);
 
   return [[BookmarksSpotlightManager alloc]
-      initWithLargeIconService:largeIconService
-                 bookmarkModel:ios::LocalOrSyncableBookmarkModelFactory::
-                                   GetForBrowserState(browserState)
-            spotlightInterface:[SpotlightInterface defaultInterface]
-         searchableItemFactory:
-             [[SearchableItemFactory alloc]
-                 initWithLargeIconService:largeIconService
-                                   domain:spotlight::DOMAIN_BOOKMARKS
-                    useTitleInIdentifiers:YES]];
+          initWithLargeIconService:largeIconService
+      localOrSyncableBookmarkModel:ios::LocalOrSyncableBookmarkModelFactory::
+                                       GetForBrowserState(browserState)
+              accountBookmarkModel:ios::AccountBookmarkModelFactory::
+                                       GetForBrowserState(browserState)
+                spotlightInterface:[SpotlightInterface defaultInterface]
+             searchableItemFactory:
+                 [[SearchableItemFactory alloc]
+                     initWithLargeIconService:largeIconService
+                                       domain:spotlight::DOMAIN_BOOKMARKS
+                        useTitleInIdentifiers:YES]];
 }
 
 - (instancetype)
-    initWithLargeIconService:(favicon::LargeIconService*)largeIconService
-               bookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
-          spotlightInterface:(SpotlightInterface*)spotlightInterface
-       searchableItemFactory:(SearchableItemFactory*)searchableItemFactory {
+        initWithLargeIconService:(favicon::LargeIconService*)largeIconService
+    localOrSyncableBookmarkModel:
+        (bookmarks::BookmarkModel*)localOrSyncableBookmarkModel
+            accountBookmarkModel:(bookmarks::BookmarkModel*)accountBookmarkModel
+              spotlightInterface:(SpotlightInterface*)spotlightInterface
+           searchableItemFactory:(SearchableItemFactory*)searchableItemFactory {
   self = [super initWithSpotlightInterface:spotlightInterface
                      searchableItemFactory:searchableItemFactory];
   if (self) {
     _pendingLargeIconTasksCount = 0;
-    _bookmarkModelBridge.reset(new SpotlightBookmarkModelBridge(self));
-    _bookmarkModel = bookmarkModel;
-    bookmarkModel->AddObserver(_bookmarkModelBridge.get());
+    _localOrSyncableBookmarkModelBridge =
+        std::make_unique<SpotlightBookmarkModelBridge>(self);
+    _localOrSyncableBookmarkModel = localOrSyncableBookmarkModel;
+    _localOrSyncableBookmarkModel->AddObserver(
+        _localOrSyncableBookmarkModelBridge.get());
+    if (accountBookmarkModel) {
+      _accountBookmarkModelBridge =
+          std::make_unique<SpotlightBookmarkModelBridge>(self);
+      _accountBookmarkModel = accountBookmarkModel;
+      _accountBookmarkModel->AddObserver(_accountBookmarkModelBridge.get());
+    }
   }
   return self;
 }
 
 - (void)detachBookmarkModel {
-  if (_bookmarkModelBridge.get()) {
-    _bookmarkModel->RemoveObserver(_bookmarkModelBridge.get());
-    _bookmarkModelBridge.reset();
+  if (_localOrSyncableBookmarkModelBridge.get()) {
+    _localOrSyncableBookmarkModel->RemoveObserver(
+        _localOrSyncableBookmarkModelBridge.get());
+    _localOrSyncableBookmarkModelBridge.reset();
+  }
+  if (_accountBookmarkModelBridge.get()) {
+    _accountBookmarkModel->RemoveObserver(_accountBookmarkModelBridge.get());
+    _accountBookmarkModelBridge.reset();
   }
 }
 
@@ -203,8 +227,9 @@
   }
 
   NSMutableArray* parentNames = [self parentFolderNamesForNode:node->parent()];
+  bookmarks::BookmarkModel* parentModel = [self bookmarkModelForNode:node];
 
-  if (node->is_folder() && !_bookmarkModel->is_permanent_node(node)) {
+  if (node->is_folder() && !parentModel->is_permanent_node(node)) {
     [parentNames addObject:base::SysUTF16ToNSString(node->GetTitle())];
   }
 
@@ -270,10 +295,13 @@
 }
 
 - (void)reindexBookmarksIfNeeded {
-  if (self.isShuttingDown) {
+  if (self.isShuttingDown || _initialIndexDone) {
     return;
   }
-  if (!_bookmarkModel->loaded() || _initialIndexDone) {
+  if (!_localOrSyncableBookmarkModel->loaded()) {
+    return;
+  }
+  if (_accountBookmarkModel && !_accountBookmarkModel->loaded()) {
     return;
   }
   _initialIndexDone = YES;
@@ -291,7 +319,7 @@
   }
 
   std::vector<const bookmarks::BookmarkNode*> nodesMatchingURL =
-      _bookmarkModel->GetNodesByURL(URL);
+      [self nodesByURL:URL];
 
   NSMutableArray* itemKeywords = [[NSMutableArray alloc] init];
 
@@ -376,14 +404,20 @@
   // shutdown, so the reindex can't happen until next app start. In the former
   // case, unset _initialIndexDone flag. This makes sure indexing will happen
   // once the model loads.
-  if (!_bookmarkModel->loaded()) {
+  if (!_localOrSyncableBookmarkModel->loaded()) {
+    _initialIndexDone = NO;
+  }
+  if (_accountBookmarkModel && !_accountBookmarkModel->loaded()) {
     _initialIndexDone = NO;
   }
 
   const base::Time startOfReindexing = base::Time::Now();
   _nodesIndexed = 0;
   _pendingLargeIconTasksCount = 0;
-  [self refreshNodeInIndex:_bookmarkModel->root_node()];
+  [self refreshNodeInIndex:_localOrSyncableBookmarkModel->root_node()];
+  if (_accountBookmarkModel) {
+    [self refreshNodeInIndex:_accountBookmarkModel->root_node()];
+  }
   const base::Time endOfReindexing = base::Time::Now();
 
   UMA_HISTOGRAM_TIMES("IOS.Spotlight.BookmarksIndexingDuration",
@@ -400,4 +434,26 @@
          forKey:@(spotlight::kSpotlightLastIndexingVersionKey)];
 }
 
+- (bookmarks::BookmarkModel*)bookmarkModelForNode:
+    (const bookmarks::BookmarkNode*)node {
+  if (node->HasAncestor(_localOrSyncableBookmarkModel->root_node())) {
+    return _localOrSyncableBookmarkModel;
+  }
+  DCHECK(_accountBookmarkModel &&
+         node->HasAncestor(_accountBookmarkModel->root_node()));
+  return _accountBookmarkModel;
+}
+
+- (std::vector<const bookmarks::BookmarkNode*>)nodesByURL:(const GURL&)url {
+  std::vector<const bookmarks::BookmarkNode*> localOrSyncableNodes =
+      _localOrSyncableBookmarkModel->GetNodesByURL(url);
+  if (_accountBookmarkModel) {
+    std::vector<const bookmarks::BookmarkNode*> accountNodes =
+        _accountBookmarkModel->GetNodesByURL(url);
+    localOrSyncableNodes.insert(localOrSyncableNodes.end(),
+                                accountNodes.begin(), accountNodes.end());
+  }
+  return localOrSyncableNodes;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/main/default_browser_promo_scene_agent.mm b/ios/chrome/browser/ui/main/default_browser_promo_scene_agent.mm
index ffc248ae..a2ad2f64 100644
--- a/ios/chrome/browser/ui/main/default_browser_promo_scene_agent.mm
+++ b/ios/chrome/browser/ui/main/default_browser_promo_scene_agent.mm
@@ -11,6 +11,9 @@
 #import "ios/chrome/browser/default_browser/model/utils.h"
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
 #import "ios/chrome/browser/promos_manager/constants.h"
+#import "ios/chrome/browser/shared/model/browser/browser.h"
+#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
+#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/signin/model/authentication_service.h"
 #import "ios/chrome/browser/signin/model/authentication_service_factory.h"
@@ -24,6 +27,9 @@
 // promo in the current app session.
 @property(nonatomic, assign) BOOL postRestorePromoSeenInCurrentSession;
 
+// YES if the main profile for this scene is signed in.
+@property(nonatomic, readonly, getter=isSignedIn) BOOL signedIn;
+
 @end
 
 @implementation DefaultBrowserPromoSceneAgent
@@ -57,11 +63,13 @@
   }
 }
 
-// Returns whether the user is signed in.
-- (bool)isSignedIn {
+- (BOOL)isSignedIn {
+  ChromeBrowserState* browserState =
+      self.sceneState.browserProviderInterface.mainBrowserProvider.browser
+          ->GetBrowserState();
+
   AuthenticationService* authenticationService =
-      AuthenticationServiceFactory::GetForBrowserState(
-          self.sceneState.appState.mainBrowserState);
+      AuthenticationServiceFactory::GetForBrowserState(browserState);
   DCHECK(authenticationService);
   DCHECK(authenticationService->initialized());
   return authenticationService->HasPrimaryIdentity(
@@ -71,10 +79,12 @@
 #pragma mark - BaseDefaultBrowserPromoSchedulerSceneAgent
 
 - (bool)promoCanBeDisplayed {
+  ChromeBrowserState* browserState =
+      self.sceneState.browserProviderInterface.mainBrowserProvider.browser
+          ->GetBrowserState();
   return ShouldRegisterPromoWithPromoManager(
-      [self isSignedIn], /*is_omnibox_copy_paste=*/true,
-      feature_engagement::TrackerFactory::GetForBrowserState(
-          self.sceneState.appState.mainBrowserState));
+      self.signedIn, /*is_omnibox_copy_paste=*/true,
+      feature_engagement::TrackerFactory::GetForBrowserState(browserState));
 }
 
 - (void)resetPromoHandler {
@@ -102,16 +112,16 @@
   // Post Restore promo takes priority over other default browser promos.
   [self maybeRegisterPostRestorePromo];
 
-  AppState* appState = self.sceneState.appState;
+  ChromeBrowserState* browserState =
+      self.sceneState.browserProviderInterface.mainBrowserProvider.browser
+          ->GetBrowserState();
 
   // Register default browser promo manager to the promo manager.
-  DCHECK(self.sceneState.appState.mainBrowserState);
-
   DCHECK(self.promosManager);
   if (ShouldRegisterPromoWithPromoManager(
-          [self isSignedIn], /*is_omnibox_copy_paste=*/false,
+          self.signedIn, /*is_omnibox_copy_paste=*/false,
           feature_engagement::TrackerFactory::GetForBrowserState(
-              appState.mainBrowserState))) {
+              browserState))) {
     self.promosManager->RegisterPromoForSingleDisplay(
         promos_manager::Promo::DefaultBrowser);
   } else {
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 6a6e6699..0fd7619f 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -131,7 +131,7 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-
+#if !defined(__IPHONE_16_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_16_0
   // Add Paste and Go option to the editing menu
   RegisterEditMenuItem([[UIMenuItem alloc]
       initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_IMAGE)
@@ -146,6 +146,7 @@
   RegisterEditMenuItem([[UIMenuItem alloc]
       initWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_TEXT)
              action:@selector(searchCopiedText:)]);
+#endif
 
   self.textField.placeholderTextColor = [self placeholderAndClearButtonColor];
   self.textField.placeholder = l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT);
@@ -428,6 +429,59 @@
   }
 }
 
+- (UIMenu*)textField:(UITextField*)textField
+    editMenuForCharactersInRange:(NSRange)range
+                suggestedActions:(NSArray<UIMenuElement*>*)suggestedActions
+    API_AVAILABLE(ios(16)) {
+  NSMutableArray* actions = [suggestedActions mutableCopy];
+  if ([self canPerformAction:@selector(searchCopiedImage:) withSender:nil]) {
+    UIAction* searchCopiedImage = [UIAction
+        actionWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_IMAGE)
+                  image:nil
+             identifier:nil
+                handler:^(__kindof UIAction* _Nonnull action) {
+                  [self searchCopiedImage:nil];
+                }];
+    [actions addObject:searchCopiedImage];
+  }
+
+  if ([self canPerformAction:@selector(lensCopiedImage:) withSender:nil]) {
+    UIAction* searchCopiedImageWithLens =
+        [UIAction actionWithTitle:l10n_util::GetNSString(
+                                      IDS_IOS_SEARCH_COPIED_IMAGE_WITH_LENS)
+                            image:nil
+                       identifier:nil
+                          handler:^(__kindof UIAction* _Nonnull action) {
+                            [self lensCopiedImage:nil];
+                          }];
+    [actions addObject:searchCopiedImageWithLens];
+  }
+
+  if ([self canPerformAction:@selector(visitCopiedLink:) withSender:nil]) {
+    UIAction* visitCopiedLink = [UIAction
+        actionWithTitle:l10n_util::GetNSString(IDS_IOS_VISIT_COPIED_LINK)
+                  image:nil
+             identifier:nil
+                handler:^(__kindof UIAction* _Nonnull action) {
+                  [self visitCopiedLink:nil];
+                }];
+    [actions addObject:visitCopiedLink];
+  }
+
+  if ([self canPerformAction:@selector(searchCopiedText:) withSender:nil]) {
+    UIAction* searchCopiedText = [UIAction
+        actionWithTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_COPIED_TEXT)
+                  image:nil
+             identifier:nil
+                handler:^(__kindof UIAction* _Nonnull action) {
+                  [self searchCopiedText:nil];
+                }];
+    [actions addObject:searchCopiedText];
+  }
+
+  return [UIMenu menuWithChildren:actions];
+}
+
 #pragma mark - OmniboxConsumer
 
 - (void)updateAutocompleteIcon:(UIImage*)icon
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn
index 02cc681..9a67565f 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/BUILD.gn
@@ -6,8 +6,39 @@
   sources = [
     "privacy_guide_main_coordinator.h",
     "privacy_guide_main_coordinator.mm",
+    "privacy_guide_welcome_coordinator.h",
+    "privacy_guide_welcome_coordinator.mm",
   ]
-  public_deps = [ "//ios/chrome/browser/shared/coordinator/chrome_coordinator" ]
+  deps = [
+    ":privacy_guide_ui",
+    "//base",
+    "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
+    "//ios/chrome/browser/shared/ui/table_view:utils",
+  ]
+}
+
+source_set("privacy_guide_ui") {
+  sources = [
+    "privacy_guide_welcome_view_controller.h",
+    "privacy_guide_welcome_view_controller.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/shared/ui/table_view:utils",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [ "privacy_guide_main_coordinator_unittest.mm" ]
+  deps = [
+    ":privacy_guide",
+    "//base/test:test_support",
+    "//ios/chrome/browser/shared/model/browser/test:test_support",
+    "//ios/chrome/browser/shared/model/browser_state:test_support",
+    "//ios/chrome/test:test_support",
+    "//testing/gtest",
+  ]
 }
 
 source_set("features") {
@@ -15,5 +46,5 @@
     "features.h",
     "features.mm",
   ]
-  public_deps = [ "//base" ]
+  deps = [ "//base" ]
 }
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.h b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.h
index 8266e0a..d73635e 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.h
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.h
@@ -25,4 +25,5 @@
 @property(nonatomic, weak) id<PrivacyGuideMainCoordinatorDelegate> delegate;
 
 @end
+
 #endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_GUIDE_PRIVACY_GUIDE_MAIN_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.mm b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.mm
index 0b5137dc..46e6f5d 100644
--- a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.mm
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.mm
@@ -2,11 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import <UIKit/UIKit.h>
-
 #import "ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.h"
 
+#import <UIKit/UIKit.h>
+
+#import "base/check.h"
+#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
+#import "ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.h"
+
 @interface PrivacyGuideMainCoordinator () <
+    PrivacyGuideWelcomeCoordinatorDelegate,
     UIAdaptivePresentationControllerDelegate>
 @end
 
@@ -15,14 +20,13 @@
 }
 
 - (void)start {
-  // TODO(crbug.com/1494887): Start the Welcome step before presenting the
-  // UINavigationController.
-
   _navigationController =
       [[UINavigationController alloc] initWithNavigationBarClass:nil
                                                     toolbarClass:nil];
   _navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
   _navigationController.presentationController.delegate = self;
+
+  [self startWelcomeCoordinator];
   [self.baseViewController presentViewController:_navigationController
                                         animated:YES
                                       completion:nil];
@@ -34,6 +38,17 @@
                          completion:nil];
   _navigationController.presentationController.delegate = nil;
   _navigationController = nil;
+
+  [self stopAndCleanupChildCoordinators];
+}
+
+#pragma mark - PrivacyGuideWelcomeCoordinatorDelegate
+
+- (void)privacyGuideWelcomeCoordinatorDidRemove:
+    (PrivacyGuideWelcomeCoordinator*)coordinator {
+  CHECK([self.childCoordinators containsObject:coordinator]);
+  coordinator.delegate = nil;
+  [self.childCoordinators removeObject:coordinator];
 }
 
 #pragma mark - UIAdaptivePresentationControllerDelegate
@@ -43,4 +58,26 @@
   [self.delegate privacyGuideMainCoordinatorDidRemove:self];
 }
 
+#pragma mark - Private
+
+// Initializes the Welcome step coordinator and starts it.
+- (void)startWelcomeCoordinator {
+  PrivacyGuideWelcomeCoordinator* coordinator =
+      [[PrivacyGuideWelcomeCoordinator alloc]
+          initWithBaseNavigationController:_navigationController
+                                   browser:self.browser];
+  coordinator.delegate = self;
+  [coordinator start];
+
+  [self.childCoordinators addObject:coordinator];
+}
+
+// Stops all child coordinators and clears the child coordinator list.
+- (void)stopAndCleanupChildCoordinators {
+  for (ChromeCoordinator* coordinator in self.childCoordinators) {
+    [coordinator stop];
+  }
+  [self.childCoordinators removeAllObjects];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator_unittest.mm b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator_unittest.mm
new file mode 100644
index 0000000..e556630
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator_unittest.mm
@@ -0,0 +1,52 @@
+// 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.
+
+#import "ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_main_coordinator.h"
+
+#import <UIKit/UIKit.h>
+#import <memory>
+
+#import "base/apple/foundation_util.h"
+#import "base/test/task_environment.h"
+#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
+#import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/test/scoped_key_window.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/platform_test.h"
+
+// Test fixture for the PrivacyGuideMainCoordinator.
+class PrivacyGuideMainCoordinatorTest : public PlatformTest {
+ protected:
+  PrivacyGuideMainCoordinatorTest() {
+    browser_state_ = TestChromeBrowserState::Builder().Build();
+    browser_ = std::make_unique<TestBrowser>(browser_state_.get());
+
+    root_view_controller_ = [[UIViewController alloc] init];
+    scoped_window_.Get().rootViewController = root_view_controller_;
+
+    coordinator_ = [[PrivacyGuideMainCoordinator alloc]
+        initWithBaseViewController:root_view_controller_
+                           browser:browser_.get()];
+    [coordinator_ start];
+  }
+
+  ~PrivacyGuideMainCoordinatorTest() override { [coordinator_ stop]; }
+
+  bool IsPrivacyGuidePresented() {
+    return [root_view_controller_.presentedViewController
+        isKindOfClass:[UINavigationController class]];
+  }
+
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<ChromeBrowserState> browser_state_;
+  std::unique_ptr<TestBrowser> browser_;
+  UIViewController* root_view_controller_;
+  PrivacyGuideMainCoordinator* coordinator_;
+  ScopedKeyWindow scoped_window_;
+};
+
+// Tests that the Privacy Guide correctly sets up its own navigation controller.
+TEST_F(PrivacyGuideMainCoordinatorTest, PrivacyGuidePresented) {
+  ASSERT_TRUE(IsPrivacyGuidePresented());
+}
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.h b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.h
new file mode 100644
index 0000000..c8c43b89
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.h
@@ -0,0 +1,39 @@
+// 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 IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_GUIDE_PRIVACY_GUIDE_WELCOME_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_GUIDE_PRIVACY_GUIDE_WELCOME_COORDINATOR_H_
+
+#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
+
+@class PrivacyGuideWelcomeCoordinator;
+
+// Delegate for PrivacyGuideWelcomeCoordinator.
+@protocol PrivacyGuideWelcomeCoordinatorDelegate
+
+// Called when the view controller is removed from navigation controller.
+- (void)privacyGuideWelcomeCoordinatorDidRemove:
+    (PrivacyGuideWelcomeCoordinator*)coordinator;
+
+@end
+
+// Coordinator for the Welcome step of the Privacy Guide.
+@interface PrivacyGuideWelcomeCoordinator : ChromeCoordinator
+
+@property(nonatomic, weak) id<PrivacyGuideWelcomeCoordinatorDelegate> delegate;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+// Designated initializer.
+// `navigationController`: navigation controller.
+// `browser`: browser.
+- (instancetype)initWithBaseNavigationController:
+                    (UINavigationController*)navigationController
+                                         browser:(Browser*)browser
+    NS_DESIGNATED_INITIALIZER;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_GUIDE_PRIVACY_GUIDE_WELCOME_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.mm b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.mm
new file mode 100644
index 0000000..17b8050
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.mm
@@ -0,0 +1,58 @@
+// 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.
+
+#import "ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_coordinator.h"
+
+#import "base/check_op.h"
+#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
+#import "ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.h"
+
+@interface PrivacyGuideWelcomeCoordinator () <
+    PrivacyGuideWelcomeViewControllerPresentationDelegate>
+@end
+
+@implementation PrivacyGuideWelcomeCoordinator {
+  PrivacyGuideWelcomeViewController* _viewController;
+}
+
+@synthesize baseNavigationController = _baseNavigationController;
+
+- (instancetype)initWithBaseNavigationController:
+                    (UINavigationController*)navigationController
+                                         browser:(Browser*)browser {
+  self = [super initWithBaseViewController:navigationController
+                                   browser:browser];
+  if (self) {
+    _baseNavigationController = navigationController;
+  }
+
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  _viewController = [[PrivacyGuideWelcomeViewController alloc]
+      initWithStyle:ChromeTableViewStyle()];
+  _viewController.presentationDelegate = self;
+
+  CHECK(self.baseNavigationController);
+  [self.baseNavigationController pushViewController:_viewController
+                                           animated:YES];
+}
+
+- (void)stop {
+  _viewController.presentationDelegate = nil;
+  _viewController = nil;
+}
+
+#pragma mark - PrivacyGuideWelcomeViewControllerPresentationDelegate
+
+- (void)privacyGuideWelcomeViewControllerDidRemove:
+    (PrivacyGuideWelcomeViewController*)controller {
+  CHECK_EQ(controller, _viewController);
+  [self.delegate privacyGuideWelcomeCoordinatorDidRemove:self];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.h b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.h
new file mode 100644
index 0000000..52e6b8cd9
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.h
@@ -0,0 +1,31 @@
+// 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 IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_GUIDE_PRIVACY_GUIDE_WELCOME_VIEW_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_GUIDE_PRIVACY_GUIDE_WELCOME_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+@class PrivacyGuideWelcomeViewController;
+
+// Delegate for presentation events related to PrivacyGuideWelcomeViewController
+// which is usually handled by a class that holds the view controller.
+@protocol PrivacyGuideWelcomeViewControllerPresentationDelegate
+
+// Called when the view controller is removed from its parent.
+- (void)privacyGuideWelcomeViewControllerDidRemove:
+    (PrivacyGuideWelcomeViewController*)controller;
+
+@end
+
+// View controller for the Privacy Guide Welcome step.
+@interface PrivacyGuideWelcomeViewController : UITableViewController
+
+// Presentation delegate.
+@property(nonatomic, weak)
+    id<PrivacyGuideWelcomeViewControllerPresentationDelegate>
+        presentationDelegate;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_GUIDE_PRIVACY_GUIDE_WELCOME_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.mm b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.mm
new file mode 100644
index 0000000..2794a7fd
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.mm
@@ -0,0 +1,9 @@
+// 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.
+
+#import "ios/chrome/browser/ui/settings/privacy/privacy_guide/privacy_guide_welcome_view_controller.h"
+
+@implementation PrivacyGuideWelcomeViewController
+// TODO(crbug.com/1494887): Implement the Welcome step ViewController.
+@end
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index d6e2160..bd389d1 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -413,6 +413,7 @@
     "//ios/chrome/browser/ui/settings/password/reauthentication:unit_tests",
     "//ios/chrome/browser/ui/settings/privacy:unit_tests",
     "//ios/chrome/browser/ui/settings/privacy/lockdown_mode:unit_tests",
+    "//ios/chrome/browser/ui/settings/privacy/privacy_guide:unit_tests",
     "//ios/chrome/browser/ui/settings/safety_check:unit_tests",
     "//ios/chrome/browser/ui/settings/sync:unit_tests",
     "//ios/chrome/browser/ui/settings/utils:unit_tests",
diff --git a/ios/web/public/test/fakes/fake_web_client.h b/ios/web/public/test/fakes/fake_web_client.h
index 0ee96964..92c501d9 100644
--- a/ios/web/public/test/fakes/fake_web_client.h
+++ b/ios/web/public/test/fakes/fake_web_client.h
@@ -49,6 +49,7 @@
                         int64_t navigation_id,
                         base::OnceCallback<void(NSString*)> callback) override;
   UIView* GetWindowedContainer() override;
+  bool EnableWebInspector(web::BrowserState* browser_state) const override;
   UserAgentType GetDefaultUserAgent(web::WebState* web_state,
                                     const GURL& url) const override;
 
diff --git a/ios/web/public/test/fakes/fake_web_client.mm b/ios/web/public/test/fakes/fake_web_client.mm
index f209a3f5..cdd0b19 100644
--- a/ios/web/public/test/fakes/fake_web_client.mm
+++ b/ios/web/public/test/fakes/fake_web_client.mm
@@ -76,6 +76,10 @@
   return GetAnyKeyWindow().rootViewController.view;
 }
 
+bool FakeWebClient::EnableWebInspector(web::BrowserState* browser_state) const {
+  return true;
+}
+
 UserAgentType FakeWebClient::GetDefaultUserAgent(web::WebState* web_state,
                                                  const GURL& url) const {
   return default_user_agent_;
diff --git a/ios/web_view/features.gni b/ios/web_view/features.gni
index 93f453f9..6974efc 100644
--- a/ios/web_view/features.gni
+++ b/ios/web_view/features.gni
@@ -9,3 +9,7 @@
   # Controls the output name of the built framework.
   ios_web_view_output_name = "ChromeWebView"
 }
+
+# Overridding ios_web_view_include_cronet is no longer supported,
+# set the value back to the default.
+ios_web_view_include_cronet = false
diff --git a/ios_internal b/ios_internal
index 6fedcb8..2f44627 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 6fedcb89fabbeccebd8c87753620098b0037fcfe
+Subproject commit 2f446279a877a5769e71bb6aa1433312173e9cc1
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
index 474613c..49806ad5 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -10,7 +10,6 @@
 import android.media.MediaCodec.CryptoInfo;
 import android.media.MediaCrypto;
 import android.media.MediaFormat;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -594,10 +593,6 @@
                 return MediaCodecStatus.ERROR;
             }
             boolean usesCbcs = cipherMode == MediaCodec.CRYPTO_MODE_AES_CBC;
-            if (usesCbcs && !MediaCodecUtil.platformSupportsCbcsEncryption(Build.VERSION.SDK_INT)) {
-                Log.e(TAG, "Encryption scheme 'cbcs' not supported on this platform.");
-                return MediaCodecStatus.ERROR;
-            }
             CryptoInfo cryptoInfo = new CryptoInfo();
             cryptoInfo.set(numSubSamples, numBytesOfClearData, numBytesOfEncryptedData, keyId, iv,
                     cipherMode);
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
index d43b070..ba84f755e 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -649,17 +649,6 @@
     }
 
     /**
-     * Returns true if and only if a platform with the given SDK API level supports the 'cbcs'
-     * encryption scheme, specifically AES CBC encryption with possibility of pattern encryption.
-     * While 'cbcs' scheme was originally implemented in N, there was a bug (in the
-     * DRM code) which means that it didn't really work properly until N-MR1).
-     */
-    @CalledByNative
-    static boolean platformSupportsCbcsEncryption(int sdk) {
-        return sdk >= Build.VERSION_CODES.N_MR1;
-    }
-
-    /**
      * Sets the encryption pattern value if and only if CryptoInfo.setPattern method is
      * supported.
      * Note that if platformSupportsCbcsEncryption returns true, then this function will set the
diff --git a/media/base/android/media_codec_util.cc b/media/base/android/media_codec_util.cc
index ca4b04e..18886a7 100644
--- a/media/base/android/media_codec_util.cc
+++ b/media/base/android/media_codec_util.cc
@@ -171,12 +171,6 @@
 }
 
 // static
-bool MediaCodecUtil::PlatformSupportsCbcsEncryption(int sdk) {
-  JNIEnv* env = AttachCurrentThread();
-  return Java_MediaCodecUtil_platformSupportsCbcsEncryption(env, sdk);
-}
-
-// static
 std::set<int> MediaCodecUtil::GetEncoderColorFormats(
     const std::string& mime_type) {
   std::set<int> color_formats;
diff --git a/media/base/android/media_codec_util.h b/media/base/android/media_codec_util.h
index f214021..70575982 100644
--- a/media/base/android/media_codec_util.h
+++ b/media/base/android/media_codec_util.h
@@ -33,9 +33,6 @@
                                             SampleFormat sample_format);
   static std::string CodecToAndroidMimeType(VideoCodec codec);
 
-  // Returns true if MediaCodec supports CBCS Encryption.
-  static bool PlatformSupportsCbcsEncryption(int sdk);
-
   // Indicates if the vp8 decoder or encoder is available on this device.
   static bool IsVp8DecoderAvailable();
   static bool IsVp8EncoderAvailable();
diff --git a/media/base/android/media_codec_util_unittest.cc b/media/base/android/media_codec_util_unittest.cc
index 1e987e2..bde5fc7 100644
--- a/media/base/android/media_codec_util_unittest.cc
+++ b/media/base/android/media_codec_util_unittest.cc
@@ -24,13 +24,6 @@
  public:
 };
 
-TEST_F(MediaCodecUtilTest, TestCbcsAvailableIfNewerVersion) {
-  EXPECT_FALSE(
-      MediaCodecUtil::PlatformSupportsCbcsEncryption(SDK_VERSION_NOUGAT));
-  EXPECT_TRUE(
-      MediaCodecUtil::PlatformSupportsCbcsEncryption(SDK_VERSION_NOUGAT_MR1));
-}
-
 TEST_F(MediaCodecUtilTest, GuessCodedSizeAlignment) {
   EXPECT_EQ(absl::nullopt,
             MediaCodecUtil::LookupCodedSizeAlignment("c2.fake.h264.decoder"));
diff --git a/media/filters/hls_manifest_demuxer_engine.cc b/media/filters/hls_manifest_demuxer_engine.cc
index c987da3..29a8534 100644
--- a/media/filters/hls_manifest_demuxer_engine.cc
+++ b/media/filters/hls_manifest_demuxer_engine.cc
@@ -64,7 +64,7 @@
   return true;
 }
 
-hls::RenditionSelector::CodecSupportType GetSupportedTypes(
+hls::RenditionManager::CodecSupportType GetSupportedTypes(
     base::StringPiece container,
     base::span<const std::string> codecs) {
   std::vector<VideoType> video_formats;
@@ -93,15 +93,15 @@
   bool video_support = AreAllVideoCodecsSupported(std::move(video_formats));
 
   if (audio_support && video_support) {
-    return hls::RenditionSelector::CodecSupportType::kSupportedAudioVideo;
+    return hls::RenditionManager::CodecSupportType::kSupportedAudioVideo;
   }
   if (audio_support) {
-    return hls::RenditionSelector::CodecSupportType::kSupportedAudioOnly;
+    return hls::RenditionManager::CodecSupportType::kSupportedAudioOnly;
   }
   if (video_support) {
-    return hls::RenditionSelector::CodecSupportType::kSupportedVideoOnly;
+    return hls::RenditionManager::CodecSupportType::kSupportedVideoOnly;
   }
-  return hls::RenditionSelector::CodecSupportType::kUnsupported;
+  return hls::RenditionManager::CodecSupportType::kUnsupported;
 }
 
 }  // namespace
@@ -308,7 +308,7 @@
   weak_factory_.InvalidateWeakPtrs();
 
   multivariant_root_.reset();
-  rendition_selector_.reset();
+  rendition_manager_.reset();
   renditions_.clear();
   host_ = nullptr;
 }
@@ -465,65 +465,58 @@
     PipelineStatusCallback parse_complete_cb,
     scoped_refptr<hls::MultivariantPlaylist> playlist) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
-  CHECK(!rendition_selector_);
+  CHECK(!rendition_manager_);
   multivariant_root_ = std::move(playlist);
-  rendition_selector_ = std::make_unique<hls::RenditionSelector>(
-      multivariant_root_, base::BindRepeating(&GetSupportedTypes));
+  rendition_manager_ = std::make_unique<hls::RenditionManager>(
+      multivariant_root_,
+      base::BindRepeating(&HlsManifestDemuxerEngine::OnRenditionsSelected,
+                          weak_factory_.GetWeakPtr()),
+      base::BindRepeating(&GetSupportedTypes));
 
-  hls::RenditionSelector::PreferredVariants streams =
-      rendition_selector_->GetPreferredVariants(video_preferences_,
-                                                audio_preferences_);
-
-  // Possible outcomes of the rendition selector:
-  // | AOVariant | SelVariant | AORend  | primary=? | secondary=? |
-  // |-----------|------------|---------|-----------|-------------|
-  // | null      | null       | null    | X         | X           |
-  // |-----------|------------|---------|-----------|-------------|
-  // | null      | present    | null    | SV        | X           |
-  // |-----------|------------|---------|-----------|-------------|
-  // | present   | null       | present | AOV       | X           |
-  // |-----------|------------|---------|-----------|-------------|
-  // | present   | present    | null    | SV        | X           |
-  // |-----------|------------|---------|-----------|-------------|
-  // | present   | present    | present | SV        | AOV         |
-  // |-----------|------------|---------|-----------|-------------|
-  absl::optional<GURL> audio_override_uri;
-  const GURL& primary_uri = streams.selected_variant->GetPrimaryRenditionUri();
-  if (streams.audio_override_rendition) {
-    CHECK_NE(streams.audio_override_variant, nullptr);
-    audio_override_uri = streams.audio_override_rendition->GetUri().value_or(
-        streams.audio_override_variant->GetPrimaryRenditionUri());
-  }
-
-  std::vector<PlaylistParseInfo> renditions_to_parse;
-  std::vector<std::string> no_codecs;
-
-  if (streams.selected_variant) {
-    renditions_to_parse.emplace_back(
-        streams.selected_variant->GetPrimaryRenditionUri(),
-        streams.selected_variant->GetCodecs().value_or(no_codecs), kPrimary);
-
-    if (streams.audio_override_rendition &&
-        primary_uri != audio_override_uri.value_or(primary_uri)) {
-      CHECK_NE(streams.audio_override_variant, nullptr);
-      renditions_to_parse.emplace_back(
-          *audio_override_uri,
-          streams.audio_override_variant->GetCodecs().value_or(no_codecs),
-          kAudioOverride);
-    }
-  } else if (streams.audio_override_rendition &&
-             primary_uri != audio_override_uri.value_or(primary_uri)) {
-    renditions_to_parse.emplace_back(
-        *audio_override_uri,
-        streams.audio_override_variant->GetCodecs().value_or(no_codecs),
-        kPrimary);
-  } else {
+  if (!rendition_manager_->HasAnyVariants()) {
+    // This will abort the pending init, and `parse_complete_cb` will not need
+    // to be called.
     Abort(HlsDemuxerStatus::Codes::kNoRenditions);
     return;
   }
 
-  SetStreams(std::move(renditions_to_parse), std::move(parse_complete_cb),
-             PIPELINE_OK);
+  multivariant_parse_complete_cb_ = std::move(parse_complete_cb);
+  rendition_manager_->Reselect(
+      base::BindOnce(&HlsManifestDemuxerEngine::OnRenditionsSelected,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void HlsManifestDemuxerEngine::OnRenditionsSelected(
+    const hls::VariantStream* variant,
+    const hls::AudioRendition* audio_override_rendition) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(media_sequence_checker_);
+  CHECK(variant);
+
+  // When `multivariant_parse_complete_cb_` is set, it means that we need to
+  // finish responding to the init cb after setting up our renditions. Otherwise
+  // it means that a midstream adjustment is taking place and the renditions
+  // need to be updated.
+  if (multivariant_parse_complete_cb_) {
+    std::vector<PlaylistParseInfo> renditions_to_parse;
+    std::vector<std::string> no_codecs;
+    renditions_to_parse.emplace_back(variant->GetPrimaryRenditionUri(),
+                                     variant->GetCodecs().value_or(no_codecs),
+                                     kPrimary);
+
+    // There need not be an audio override rendition, either due to not having
+    // any, or there being no change to the override rendition when the variant
+    // changes.
+    if (audio_override_rendition) {
+      CHECK(audio_override_rendition->GetUri().has_value());
+      renditions_to_parse.emplace_back(*audio_override_rendition->GetUri(),
+                                       variant->GetCodecs().value_or(no_codecs),
+                                       kAudioOverride);
+    }
+    SetStreams(std::move(renditions_to_parse),
+               std::move(multivariant_parse_complete_cb_), PIPELINE_OK);
+    return;
+  }
+  // TODO(crbug/1266991): handle a mid-playback adjustment.
 }
 
 void HlsManifestDemuxerEngine::SetStreams(
diff --git a/media/filters/hls_manifest_demuxer_engine.h b/media/filters/hls_manifest_demuxer_engine.h
index 4fd72d3..ee6fadaf 100644
--- a/media/filters/hls_manifest_demuxer_engine.h
+++ b/media/filters/hls_manifest_demuxer_engine.h
@@ -22,7 +22,7 @@
 #include "media/filters/manifest_demuxer.h"
 #include "media/formats/hls/media_playlist.h"
 #include "media/formats/hls/parse_status.h"
-#include "media/formats/hls/rendition_selector.h"
+#include "media/formats/hls/rendition_manager.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace media {
@@ -39,7 +39,7 @@
                            MediaLog* media_log);
   ~HlsManifestDemuxerEngine() override;
 
-  // HlsRenditionHost implementation
+  // ManifestDemuxer::Engine implementation
   std::string GetName() const override;
   void Initialize(ManifestDemuxerEngineHost* host,
                   PipelineStatusCallback status_cb) override;
@@ -52,6 +52,8 @@
   bool IsSeekable() const override;
   int64_t GetMemoryUsage() const override;
   void Stop() override;
+
+  // HlsRenditionHost implementation.
   void ReadFromUrl(GURL uri,
                    bool read_chunked,
                    absl::optional<hls::types::ByteRange> range,
@@ -143,6 +145,8 @@
   void OnMultivariantPlaylist(
       PipelineStatusCallback parse_complete_cb,
       scoped_refptr<hls::MultivariantPlaylist> playlist);
+  void OnRenditionsSelected(const hls::VariantStream* variant,
+                            const hls::AudioRendition* rendition);
   void SetStreams(std::vector<PlaylistParseInfo> playlists,
                   PipelineStatusCallback cb,
                   PipelineStatus exit_on_error);
@@ -193,7 +197,9 @@
   // dependant media playlists.
   scoped_refptr<hls::MultivariantPlaylist> multivariant_root_
       GUARDED_BY_CONTEXT(media_sequence_checker_);
-  std::unique_ptr<hls::RenditionSelector> rendition_selector_
+  PipelineStatusCallback multivariant_parse_complete_cb_
+      GUARDED_BY_CONTEXT(media_sequence_checker_);
+  std::unique_ptr<hls::RenditionManager> rendition_manager_
       GUARDED_BY_CONTEXT(media_sequence_checker_);
 
   // Multiple renditions are allowed, and have to be synchronized.
@@ -204,15 +210,6 @@
   // liveness, and allows access to the liveness check later.
   absl::optional<bool> is_seekable_ = absl::nullopt;
 
-  // Preferences for selecting optimal renditions. Storing them allows them
-  // to be changed later due to network constraints or user changes.
-  hls::RenditionSelector::VideoPlaybackPreferences video_preferences_
-      GUARDED_BY_CONTEXT(media_sequence_checker_) = {absl::nullopt,
-                                                     absl::nullopt};
-  hls::RenditionSelector::AudioPlaybackPreferences audio_preferences_
-      GUARDED_BY_CONTEXT(media_sequence_checker_) = {absl::nullopt,
-                                                     absl::nullopt};
-
   // Ensure that safe member fields are only accessed on the media sequence.
   SEQUENCE_CHECKER(media_sequence_checker_);
 
diff --git a/media/formats/BUILD.gn b/media/formats/BUILD.gn
index dcf1908..80cff50 100644
--- a/media/formats/BUILD.gn
+++ b/media/formats/BUILD.gn
@@ -215,8 +215,8 @@
     "hls/playlist.h",
     "hls/playlist_common.cc",
     "hls/playlist_common.h",
-    "hls/rendition_selector.cc",
-    "hls/rendition_selector.h",
+    "hls/rendition_manager.cc",
+    "hls/rendition_manager.h",
     "hls/source_string.cc",
     "hls/source_string.h",
     "hls/tag_name.cc",
@@ -366,7 +366,7 @@
     "hls/multivariant_playlist_unittest.cc",
     "hls/playlist_test_builder.h",
     "hls/playlist_unittest.cc",
-    "hls/rendition_selector_unittest.cc",
+    "hls/rendition_manager_unittest.cc",
     "hls/tags_unittest.cc",
     "hls/test_util.h",
     "hls/types_unittest.cc",
diff --git a/media/formats/hls/rendition_manager.cc b/media/formats/hls/rendition_manager.cc
new file mode 100644
index 0000000..ab33ebd
--- /dev/null
+++ b/media/formats/hls/rendition_manager.cc
@@ -0,0 +1,419 @@
+// 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 "media/formats/hls/rendition_manager.h"
+
+#include "base/logging.h"
+#include "media/formats/hls/audio_rendition.h"
+#include "media/formats/hls/multivariant_playlist.h"
+#include "media/formats/hls/types.h"
+#include "media/formats/hls/variant_stream.h"
+
+namespace media::hls {
+
+namespace {
+
+RenditionManager::CodecSupportType VariantTypeSupported(
+    RenditionManager::IsTypeSupportedCallback is_type_supported_cb,
+    const VariantStream& variant) {
+  // Check if the codecs reported by this variant can be played at all. If
+  // this variant does not report its codecs, we'll assume its supported until
+  // proven otherwise.
+  auto codecs = variant.GetCodecs();
+  if (!codecs) {
+    return RenditionManager::CodecSupportType::kSupportedAudioVideo;
+  }
+
+  RenditionManager::CodecSupportType mp4 =
+      is_type_supported_cb.Run("video/mp4", *codecs);
+  if (mp4 != RenditionManager::CodecSupportType::kUnsupported) {
+    return mp4;
+  }
+
+  RenditionManager::CodecSupportType mp2t =
+      is_type_supported_cb.Run("video/mp2t", *codecs);
+  return mp2t;
+}
+
+std::string GetVariantDisplayString(const VariantStream* variant) {
+  // TODO(crbug/1266991): implement.
+  return "variant";
+}
+
+std::string GetAudioRenditionDisplayString(const AudioRendition* rendition) {
+  // TODO(crbug/1266991): Consider displaying characteristics / channels /
+  // language and other things rather than just the name.
+  return rendition->GetName();
+}
+
+}  // namespace
+
+RenditionManager::UpdatedSelections::~UpdatedSelections() = default;
+RenditionManager::UpdatedSelections::UpdatedSelections() = default;
+RenditionManager::UpdatedSelections::UpdatedSelections(
+    const UpdatedSelections&) = default;
+
+RenditionManager::VariantStatistics::~VariantStatistics() = default;
+RenditionManager::VariantStatistics::VariantStatistics(
+    const VariantStatistics&) = default;
+RenditionManager::VariantStatistics::VariantStatistics(
+    const VariantStream* stream,
+    const AudioRenditionGroup* group)
+    : stream(stream), audio_rendition_group(group) {}
+
+RenditionManager::~RenditionManager() = default;
+RenditionManager::RenditionManager(scoped_refptr<MultivariantPlaylist> playlist,
+                                   SelectedCB on_variant_selected,
+                                   IsTypeSupportedCallback is_type_supported_cb)
+    : playlist_(std::move(playlist)),
+      on_variant_selected_(std::move(on_variant_selected)) {
+  InitializeVariantMaps(std::move(is_type_supported_cb));
+}
+
+std::vector<RenditionManager::SelectableOption<RenditionManager::VariantID>>
+RenditionManager::GetSelectableVariants() const {
+  std::vector<SelectableOption<VariantID>> result;
+  for (const auto& [variant_id, stats] : selectable_variants_) {
+    result.emplace_back(variant_id, GetVariantDisplayString(stats.stream));
+  }
+  return result;
+}
+
+std::vector<RenditionManager::SelectableOption<RenditionManager::RenditionID>>
+RenditionManager::GetSelectableAudioRenditions() const {
+  std::vector<SelectableOption<RenditionID>> result;
+  if (!selected_variant_.has_value()) {
+    return result;
+  }
+  const auto& stats = selectable_variants_.at(*selected_variant_);
+  for (auto id : stats.audio_renditions) {
+    result.emplace_back(
+        id, GetAudioRenditionDisplayString(selectable_renditions_.at(id)));
+  }
+  return result;
+}
+
+void RenditionManager::Reselect(SelectedCallonce callback) {
+  auto selections = GetUpdatedSelectionIds();
+  if (!selections.variant.has_value() &&
+      !selections.audio_rendition.has_value()) {
+    std::move(callback).Run(nullptr, nullptr);
+    return;
+  }
+
+  bool variant_change = false;
+  bool rendition_change = false;
+  if (selections.variant != selected_variant_) {
+    variant_change = true;
+    selected_variant_ = selections.variant;
+  }
+  if (selections.audio_rendition != selected_audio_rendition_) {
+    rendition_change = true;
+    selected_audio_rendition_ = selections.audio_rendition;
+  }
+
+  if (!(variant_change || rendition_change)) {
+    // No changes, so do not run the callback.
+    return;
+  }
+
+  CHECK(selections.variant.has_value());
+  const VariantStream* selected_variant =
+      selectable_variants_.at(*selected_variant_).stream;
+  const AudioRendition* audio_override = nullptr;
+  if (selected_audio_rendition_.has_value()) {
+    audio_override = selectable_renditions_[*selected_audio_rendition_];
+    if (!audio_override->GetUri().has_value()) {
+      // An audio rendition with no uri just plays the content from the
+      // selected variant. See section 4.4.6.2.1 of the HLS spec for details.
+      // The URI attribute is OPTIONAL unless the TYPE is CLOSED-CAPTIONS, in
+      // which case the URI attribute must not be present.
+      audio_override = nullptr;
+    }
+  }
+
+  if (variant_change || (rendition_change && audio_override)) {
+    std::move(callback).Run(selected_variant, audio_override);
+  }
+}
+
+void RenditionManager::SetPreferredVariant(
+    std::optional<RenditionManager::VariantID> id) {
+  preferred_variant_ = id;
+  Reselect(on_variant_selected_);
+}
+
+void RenditionManager::SetPreferredAudioRendition(
+    std::optional<RenditionManager::RenditionID> id) {
+  preferred_audio_rendition_ = id;
+  Reselect(on_variant_selected_);
+}
+
+void RenditionManager::UpdatePlayerResolution(const gfx::Size& resolution) {
+  player_resolution_ = resolution;
+  Reselect(on_variant_selected_);
+}
+
+void RenditionManager::UpdateNetworkSpeed(uint64_t network_bps) {
+  network_bps_ = network_bps;
+  Reselect(on_variant_selected_);
+}
+
+bool RenditionManager::HasAnyVariants() const {
+  return !selectable_variants_.empty();
+}
+
+void RenditionManager::InitializeVariantMaps(
+    IsTypeSupportedCallback is_type_supported_cb) {
+  bool was_audio_only = false;
+  bool has_video = false;
+  std::vector<VariantStatistics> variant_ordering;
+
+  // From the spec:
+  //   The EXT-X-STREAM-INF tag specifies a Variant Stream, which is a set of
+  //   Renditions that can be combined to play the presentation.
+  // This player does _not_ support alternative video renditions for variants
+  // which only have audio content. If we find any variants with video after
+  // finding any audio-only variants, drop all the audio-specific variants and
+  // renditions. If we find any audio-only variants after a video variant, do
+  // not consider it.
+  // TODO(crbug/1266991): Is this correct? The spec does not say anything about
+  // playlists which have audio-only variants next to variants with video. It
+  // might be used in the wild to play only video if the network is truly in
+  // bad shape, but it's not clear. I've not run into any playlists in the wild
+  // which have this.
+  for (const VariantStream& variant : playlist_->GetVariants()) {
+    switch (VariantTypeSupported(is_type_supported_cb, variant)) {
+      case CodecSupportType::kSupportedAudioVideo:
+      case CodecSupportType::kSupportedVideoOnly: {
+        has_video = true;
+        if (was_audio_only) {
+          variant_ordering.clear();
+          selectable_variants_.clear();
+          selectable_renditions_.clear();
+          was_audio_only = false;
+        }
+        break;
+      }
+      case CodecSupportType::kSupportedAudioOnly: {
+        if (has_video) {
+          // Don't add this variant.
+          continue;
+        }
+        was_audio_only = true;
+        break;
+      }
+      case CodecSupportType::kUnsupported: {
+        continue;
+      }
+    }
+    VariantStatistics stats{&variant, variant.GetAudioRenditionGroup().get()};
+    if (auto group = variant.GetAudioRenditionGroup()) {
+      // If there is an audio rendition group associated, then get all of its
+      // renditions, ID them, and track them in the selectable variant.
+      for (auto& rendition : group->GetRenditions()) {
+        auto rendition_id = LookupRendition(&rendition);
+        if (!rendition_id.has_value()) {
+          rendition_id = rendition_id_gen_.GenerateNextId();
+          selectable_renditions_[*rendition_id] = &rendition;
+        }
+        stats.audio_renditions.insert(*rendition_id);
+      }
+    }
+    variant_ordering.push_back(std::move(stats));
+  }
+
+  constexpr auto compare = [](const VariantStatistics& lhs,
+                              const VariantStatistics& rhs) {
+    // First compare by bandwidth
+    if (lhs.stream->GetBandwidth() != rhs.stream->GetBandwidth()) {
+      return lhs.stream->GetBandwidth() < rhs.stream->GetBandwidth();
+    }
+
+    // Then compare by the HLS SCORE property, if available.
+    if (lhs.stream->GetScore().has_value() &&
+        rhs.stream->GetScore().has_value()) {
+      return *lhs.stream->GetScore() < *rhs.stream->GetScore();
+    }
+
+    // Prefer the side which has a score, otherwise, consider LHS better.
+    return rhs.stream->GetScore().has_value();
+  };
+
+  // All variants are now added, and should be sorted.
+  base::ranges::sort(variant_ordering, compare);
+  for (const VariantStatistics& stats : std::move(variant_ordering)) {
+    selectable_variants_.try_emplace(variant_id_gen_.GenerateNextId(), stats);
+  }
+}
+
+RenditionManager::UpdatedSelections RenditionManager::GetUpdatedSelectionIds() {
+  UpdatedSelections selections;
+  selections.variant = preferred_variant_;
+  selections.audio_rendition = preferred_audio_rendition_;
+
+  if (!selections.variant.has_value()) {
+    // If the user did not specify a video variant, select the "best" one
+    // ourselves.
+    selections.variant = SelectBestVariant();
+  }
+
+  if (!selections.variant.has_value()) {
+    // Only when there are no valid variants to play can selections.variant be
+    // nullopt at this point. If that is the case, then we should select nothing
+    // return both nullopts, and let whoever is responsible for loading the
+    // content report an error.
+    return selections;
+  }
+
+  // The user may have selected a preferece for rendition that was present in a
+  // different variant - for example the user may have been playing a stereo
+  // rendition for some variant, but then decided to change to a variant which
+  // only has audio from a 5.1 surround stream. In this case, we want to select
+  // a rendition that is most similar to the user provided one.
+  selections.audio_rendition =
+      SelectBestRendition(*selections.variant, selections.audio_rendition);
+
+  return selections;
+}
+
+std::optional<RenditionManager::VariantID>
+RenditionManager::SelectBestVariant() {
+  std::optional<VariantID> best = absl::nullopt;
+  if (selectable_variants_.size()) {
+    // If there is at least one thing in the list, then consider the lowest
+    // quality entry to be selectable, even if poor performance would otherwise
+    // preclude it from selection.
+    best = std::get<0>(*selectable_variants_.begin());
+  }
+
+  for (auto& [variant_id, stats] : selectable_variants_) {
+    const auto& variant_resolution = stats.stream->GetResolution();
+    if (variant_resolution.has_value() &&
+        variant_resolution->Area() > player_resolution_.Area64()) {
+      // This resolution is too large, so return the previous one.
+      return best;
+    }
+
+    const auto& variant_bps = stats.stream->GetBandwidth();
+    if (network_bps_ < variant_bps) {
+      // The network is likely not able to keep up with the download rate, so
+      // return the previous one.
+      return best;
+    }
+
+    // This variant is selectable, but keep checking for better ones.
+    best = variant_id;
+  }
+
+  return best;
+}
+
+std::optional<RenditionManager::RenditionID>
+RenditionManager::SelectRenditionBasedOnLanguage(
+    const VariantStatistics& variant,
+    std::optional<std::string> language,
+    bool only_autoselect) {
+  // Check to see if the default rendition exists and matches the language, if
+  // the language is specified. If language is specified but the default does
+  // not specify a language at all, consider it a match.
+  if (auto* def = variant.audio_rendition_group->GetDefaultRendition()) {
+    // The rendition is guaranteed to be present.
+    RenditionID id = LookupRendition(def).value();
+    if (!language.has_value()) {
+      return id;
+    }
+
+    auto default_lang = def->GetLanguage();
+    if (!default_lang.has_value() || *default_lang == *language) {
+      return id;
+    }
+  }
+
+  // Check the remaining renditions - on this pass, they must be auto-selectable
+  // and match the language if it is specified. Return the first match.
+  for (const auto id : variant.audio_renditions) {
+    if (only_autoselect && !selectable_renditions_[id]->MayAutoSelect()) {
+      continue;
+    }
+    const auto& rendition_lang = selectable_renditions_[id]->GetLanguage();
+    if (!language.has_value() || rendition_lang == language) {
+      return id;
+    }
+  }
+
+  if (auto* def = variant.audio_rendition_group->GetDefaultRendition()) {
+    // Nothing acceptable matched our language, so select the default, if it
+    // exists. The default rendition is guaranteed to exist in the map.
+    return LookupRendition(def).value();
+  }
+
+  // Select the first remotely acceptable rendition.
+  for (const auto id : variant.audio_renditions) {
+    if (only_autoselect && !selectable_renditions_[id]->MayAutoSelect()) {
+      continue;
+    }
+    return id;
+  }
+
+  return absl::nullopt;
+}
+
+std::optional<RenditionManager::RenditionID>
+RenditionManager::SelectBestRendition(
+    VariantID variant_id,
+    std::optional<RenditionID> maybe_rendition) {
+  const auto& variant = selectable_variants_.at(variant_id);
+
+  // If there are no renditions attached to this variant, then select nothing.
+  if (!variant.audio_renditions.size() || !variant.audio_rendition_group) {
+    return absl::nullopt;
+  }
+
+  if (!maybe_rendition.has_value()) {
+    // The user did not select anything, so we first try to get a default from
+    // the group.
+    if (auto* def = variant.audio_rendition_group->GetDefaultRendition()) {
+      std::optional<RenditionID> id = LookupRendition(def);
+      if (id.has_value()) {
+        return id.value();
+      }
+    }
+
+    // Because there is no selected rendition, there is no language default
+    // to consider. This should change if we can get a system default language.
+    // Also, we should only be considering auto-selectable renditions.
+    return SelectRenditionBasedOnLanguage(variant, absl::nullopt, true);
+  }
+
+  // The user did select a rendition, but it's possible that a user could, say
+  // select a stereo variant, then a german rendition, then a 5.1 surround
+  // variant. The german rendition is likely to be different in this new variant
+  // but we probably want to match it as closely as possible.
+  if (variant.audio_renditions.find(*maybe_rendition) !=
+      variant.audio_renditions.end()) {
+    // The user's rendition is still here, use it.
+    return maybe_rendition;
+  }
+
+  auto ideal_rendition = selectable_renditions_[*maybe_rendition];
+
+  // Use the language from the user's selected rendition (might be nullopt),
+  // and also don't consider only auto-selectable things, as the user has made
+  // a selection.
+  return SelectRenditionBasedOnLanguage(variant, ideal_rendition->GetLanguage(),
+                                        false);
+}
+
+std::optional<RenditionManager::RenditionID> RenditionManager::LookupRendition(
+    const AudioRendition* rendition) {
+  for (const auto& [id, selectable] : selectable_renditions_) {
+    if (selectable == rendition) {
+      return id;
+    }
+  }
+  return absl::nullopt;
+}
+
+}  // namespace media::hls
diff --git a/media/formats/hls/rendition_manager.h b/media/formats/hls/rendition_manager.h
new file mode 100644
index 0000000..2599dd0
--- /dev/null
+++ b/media/formats/hls/rendition_manager.h
@@ -0,0 +1,191 @@
+// 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 MEDIA_FORMATS_HLS_RENDITION_MANAGER_H_
+#define MEDIA_FORMATS_HLS_RENDITION_MANAGER_H_
+
+#include <deque>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "base/containers/span.h"
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+#include "base/types/id_type.h"
+#include "media/base/demuxer.h"
+#include "media/base/limits.h"
+#include "media/base/media_export.h"
+#include "media/formats/hls/types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/gfx/geometry/size.h"
+#include "url/gurl.h"
+
+namespace media::hls {
+
+class MultivariantPlaylist;
+class VariantStream;
+class AudioRendition;
+class AudioRenditionGroup;
+
+// Class responsible for tracking playability state of all variants and
+// renditions in a multivariant playlist. It will always select a preferred
+// variant, and then from within that variant, select an optional audio-override
+// rendition. The selection depends on user preference, network speed, frame
+// drops, underflow events, and player resolution.
+class MEDIA_EXPORT RenditionManager {
+ public:
+  using VariantID = base::IdType32<VariantStream>;
+  using RenditionID = base::IdType32<AudioRendition>;
+
+  // We want to ask if a codec string is supported, but also if it contains
+  // audio, video, or both types of content, allowing us to sort our variants
+  // into groups.
+  enum class CodecSupportType {
+    kUnsupported,
+    kSupportedAudioVideo,
+    kSupportedAudioOnly,
+    kSupportedVideoOnly,
+  };
+
+  // A SelectableOption consists of an ID representing either a variant or a
+  // rendition, as well as a string that should be displayed to a user in a menu
+  // for selecting a preferred rendition or variant.
+  template <typename T>
+  using SelectableOption = std::tuple<T, std::string>;
+
+  // Callback used to query whether the given MIME+codec string is supported,
+  // and what types of content we can expect.
+  // The first argument is the name of the container, ie "video/mp4" or
+  // "video/mp2t", and the second argument is a list of codecs to check support
+  // for, such as ["mp4a.40.2", "avc1.4d4015"]
+  using IsTypeSupportedCallback =
+      base::RepeatingCallback<CodecSupportType(std::string_view,
+                                               base::span<const std::string>)>;
+
+  // The VariantStream ptr can be null if there is not supposed to be a change
+  // in the URI of the primary variant when this callback is run.
+  // The AudioRendition ptr can be null if there is no audio override rendition
+  // selected.
+  using SelectedCB = base::RepeatingCallback<void(const VariantStream*,
+                                                  const AudioRendition*)>;
+  using SelectedCallonce =
+      base::OnceCallback<void(const VariantStream*, const AudioRendition*)>;
+
+  ~RenditionManager();
+  RenditionManager(scoped_refptr<MultivariantPlaylist> playlist,
+                   SelectedCB on_variant_selected,
+                   IsTypeSupportedCallback is_type_supported_cb);
+
+  void UpdatePlayerResolution(const gfx::Size& resolution);
+  void UpdateNetworkSpeed(uint64_t network_bps);
+
+  // After initialization, is there anything to select?
+  bool HasAnyVariants() const;
+
+  std::vector<SelectableOption<VariantID>> GetSelectableVariants() const;
+  std::vector<SelectableOption<RenditionID>> GetSelectableAudioRenditions()
+      const;
+
+  // Uses player state and user preferences to trigger `on_variant_selected`
+  // calls with preferred playback uris.
+  void Reselect(SelectedCallonce callback);
+
+  // Set preferred variants. A nullopt means that no preference is set, and
+  // automatic selection can take place.
+  void SetPreferredAudioRendition(std::optional<RenditionID> rendition_index);
+  void SetPreferredVariant(std::optional<VariantID> variant_index);
+
+ private:
+  struct UpdatedSelections {
+    ~UpdatedSelections();
+    UpdatedSelections();
+    UpdatedSelections(const UpdatedSelections&);
+    std::optional<VariantID> variant;
+    std::optional<RenditionID> audio_rendition;
+  };
+
+  struct VariantStatistics {
+    ~VariantStatistics();
+    VariantStatistics(const VariantStatistics&);
+    VariantStatistics(const VariantStream*, const AudioRenditionGroup*);
+
+    raw_ptr<const VariantStream> stream;
+    raw_ptr<const AudioRenditionGroup> audio_rendition_group;
+    base::flat_set<RenditionID> audio_renditions;
+  };
+
+  // Called during construction. This creates the per-variant statistics
+  // trackers and all the maps where variants can reference IDs.
+  void InitializeVariantMaps(IsTypeSupportedCallback callback);
+
+  // Helper for `Reselect()` which computes what the best rendition and variant
+  // are, so that they can be compared to what is currently selected and a diff
+  // can be returned.
+  UpdatedSelections GetUpdatedSelectionIds();
+
+  // Runs the variant selection algorithm, which tries to select the highest
+  // bitrate variant which isn't precluded by some combination of player
+  // resolution and network speed.
+  std::optional<VariantID> SelectBestVariant();
+
+  // Given the selected variant, select the best audio rendition that could be
+  // used as an audio-override rendition. It first tries to use any
+  // user-selected rendition, but if it's not available, tries to find the most
+  // similar rendition to the user's selection. If there is no user selection,
+  // it uses defaults and autoselect tags to determine which rendition to pick.
+  std::optional<RenditionID> SelectBestRendition(VariantID,
+                                                 std::optional<RenditionID>);
+
+  // Backwards lookup of RenditionID from `selectable_renditions_`. This map is
+  // usually so small that an iteration is not significantly slow.
+  std::optional<RenditionID> LookupRendition(const AudioRendition* rendition);
+
+  // Selects the best rendition based with an optionally given language, and a
+  // flag stating whether only renditions tagged with "AUTOSELECT=TRUE" may be
+  // selected. The premise here is to prefer renditions in order of:
+  //  - variant->Default (guaranteed autoselect always) if language is null
+  //  - variant->Default if language matches
+  //  - any rendition with matching language and matching selectability
+  //  - the default rendition if no renditions languages match
+  //  - any rendition with matching selectability
+  //  - nothing
+  std::optional<RenditionManager::RenditionID> SelectRenditionBasedOnLanguage(
+      const VariantStatistics& variant,
+      std::optional<std::string> maybe_language,
+      bool only_autoselect);
+
+  // The playlist owns all VariantStream and VideoRendition instances, which is
+  // what allows us to store raw_ptr references to those throughout the rest
+  // of the member variables here.
+  scoped_refptr<MultivariantPlaylist> playlist_;
+
+  // Fired whenever a variant or rendition changes.
+  SelectedCB on_variant_selected_;
+
+  base::flat_map<VariantID, VariantStatistics> selectable_variants_;
+  base::flat_map<RenditionID, raw_ptr<const AudioRendition>>
+      selectable_renditions_;
+
+  std::optional<VariantID> selected_variant_;
+  std::optional<VariantID> preferred_variant_;
+
+  std::optional<RenditionID> selected_audio_rendition_;
+  std::optional<RenditionID> preferred_audio_rendition_;
+
+  VariantID::Generator variant_id_gen_;
+  RenditionID::Generator rendition_id_gen_;
+
+  // Playback qualities not tied to a specific variant.
+  gfx::Size player_resolution_ = {limits::kMaxDimension, limits::kMaxDimension};
+  uint64_t network_bps_ = 0xFFFFFFFFFF;
+};
+
+}  // namespace media::hls
+
+#endif  // define MEDIA_FORMATS_HLS_RENDITION_MANAGER_H_
diff --git a/media/formats/hls/rendition_manager_unittest.cc b/media/formats/hls/rendition_manager_unittest.cc
new file mode 100644
index 0000000..f1e9d75
--- /dev/null
+++ b/media/formats/hls/rendition_manager_unittest.cc
@@ -0,0 +1,278 @@
+// 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 "media/formats/hls/rendition_manager.h"
+
+#include "base/logging.h"
+#include "base/test/gmock_callback_support.h"
+#include "media/base/test_helpers.h"
+#include "media/formats/hls/multivariant_playlist_test_builder.h"
+#include "media/formats/hls/parse_status.h"
+#include "media/formats/hls/types.h"
+#include "media/formats/hls/variant_stream.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media::hls {
+
+namespace {
+
+RenditionManager::CodecSupportType GetCodecSupportType(
+    base::StringPiece container,
+    base::span<const std::string> codecs) {
+  bool has_audio = false;
+  bool has_video = false;
+  for (const auto& codec : codecs) {
+    if (codec == "V") {
+      has_video = true;
+    } else if (codec == "A") {
+      has_audio = true;
+    } else if (codec == "audio.codec") {
+      has_audio = true;
+    } else if (codec == "video.codec") {
+      has_video = true;
+    } else if (codec == "av.codec") {
+      return RenditionManager::CodecSupportType::kSupportedAudioVideo;
+    }
+  }
+  if (has_audio && has_video) {
+    return RenditionManager::CodecSupportType::kSupportedAudioVideo;
+  } else if (has_audio) {
+    return RenditionManager::CodecSupportType::kSupportedAudioOnly;
+  } else if (has_video) {
+    return RenditionManager::CodecSupportType::kSupportedVideoOnly;
+  }
+  return RenditionManager::CodecSupportType::kUnsupported;
+}
+
+}  // namespace
+
+using testing::_;
+
+class HlsRenditionManagerTest : public testing::Test {
+ public:
+  MOCK_METHOD(void, VariantSelected, (std::string, std::string), ());
+
+  void _VariantSelected(const VariantStream* vs, const AudioRendition* ar) {
+    std::string variant_path = "NONE";
+    std::string rendition_path = "NONE";
+    if (vs) {
+      variant_path = vs->GetPrimaryRenditionUri().path();
+    }
+    if (ar) {
+      CHECK(ar->GetUri().has_value());
+      rendition_path = ar->GetUri()->path();
+    }
+    VariantSelected(variant_path, rendition_path);
+  }
+
+  decltype(auto) GetVariantCb() {
+    return base::BindRepeating(&HlsRenditionManagerTest::_VariantSelected,
+                               base::Unretained(this));
+  }
+
+  template <typename... Strings>
+  RenditionManager GetRenditionManager(Strings... strings) {
+    MultivariantPlaylistTestBuilder builder;
+    builder.AppendLine("#EXTM3U");
+    ([&] { builder.AppendLine(strings); }(), ...);
+    return RenditionManager(builder.Parse(),
+                            base::BindRepeating(GetVariantCb()),
+                            base::BindRepeating(&GetCodecSupportType));
+  }
+};
+
+TEST_F(HlsRenditionManagerTest, MixedAVTypes) {
+  auto rm = GetRenditionManager(
+      "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000",
+      "http://example.com/low.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000",
+      "http://example.com/mid.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000",
+      "http://example.com/hi.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"audio.codec\"",
+      "http://example.com/audio-only.m3u8");
+
+  EXPECT_CALL(*this, VariantSelected("/hi.m3u8", "NONE"));
+  rm.Reselect(GetVariantCb());
+}
+
+TEST_F(HlsRenditionManagerTest, NoSupportedCodecs) {
+  auto rm = GetRenditionManager(
+      "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"vvc1.00.00\"",
+      "http://example.com/audio-only.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"sheet.music\"",
+      "http://example.com/audio-only.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"av02.00.00\"",
+      "http://example.com/audio-only.m3u8");
+  ASSERT_FALSE(rm.HasAnyVariants());
+  EXPECT_CALL(*this, VariantSelected("NONE", "NONE"));
+  rm.Reselect(GetVariantCb());
+}
+
+TEST_F(HlsRenditionManagerTest, MultipleVariantResolutions) {
+  auto rm = GetRenditionManager(
+      "#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS=\"V\",RESOLUTION=320x200",
+      "video/cga.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS=\"V\",RESOLUTION=480x320",
+      "video/hvga.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS=\"V\",RESOLUTION=800x480",
+      "video/wvga.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS=\"V\",RESOLUTION=1920x1080",
+      "video/fhd.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS=\"V\",RESOLUTION=2560x1440",
+      "video/wqhd.m3u8",
+      "#EXT-X-STREAM-INF:BANDWIDTH=10,CODECS=\"V\",RESOLUTION=7680x4320",
+      "video/8kuhd.m3u8");
+
+  EXPECT_CALL(*this, VariantSelected("/video/8kuhd.m3u8", "NONE"));
+  rm.Reselect(GetVariantCb());
+
+  EXPECT_CALL(*this, VariantSelected("/video/fhd.m3u8", "NONE"));
+  rm.UpdatePlayerResolution({1920, 1080});
+
+  EXPECT_CALL(*this, VariantSelected("/video/wvga.m3u8", "NONE"));
+  rm.UpdatePlayerResolution({1000, 1000});
+
+  // The comparison is area based.
+  EXPECT_CALL(*this, VariantSelected("/video/fhd.m3u8", "NONE"));
+  rm.UpdatePlayerResolution({1080, 1920});
+
+  EXPECT_CALL(*this, VariantSelected("/video/hvga.m3u8", "NONE"));
+  rm.UpdatePlayerResolution({400, 600});
+
+  EXPECT_CALL(*this, VariantSelected("/video/8kuhd.m3u8", "NONE"));
+  rm.UpdatePlayerResolution({8192, 8192});
+}
+
+TEST_F(HlsRenditionManagerTest, MultipleRenditionGroupsVariantsOutOfOrder) {
+  auto rm = GetRenditionManager(
+      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"stereo\",LANGUAGE=\"en\",NAME="
+      "\"English\",DEFAULT=YES,AUTOSELECT=YES,URI=\"audio/stereo/en/"
+      "128kbit.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"stereo\",LANGUAGE=\"dubbing\",NAME="
+      "\"Dubbing\",DEFAULT=NO,AUTOSELECT=YES,URI=\"audio/stereo/none/"
+      "128kbit.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"stereo\",LANGUAGE=\"de\",NAME="
+      "\"German\",DEFAULT=YES,AUTOSELECT=YES,URI=\"audio/stereo/de/"
+      "128kbit.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"surround\",LANGUAGE=\"en\",NAME="
+      "\"English\",DEFAULT=YES,AUTOSELECT=YES,URI=\"audio/surround/en/"
+      "320kbit.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"surround\",LANGUAGE=\"dubbing\",NAME="
+      "\"Dubbing\",DEFAULT=NO,AUTOSELECT=YES,URI=\"audio/surround/none/"
+      "320kbit.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"Deutsch\",DEFAULT="
+      "NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE=\"de\",URI=\"subtitles_de.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"English\",DEFAULT="
+      "YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE=\"en\",URI=\"subtitles_en.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"Espanol\",DEFAULT="
+      "NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE=\"es\",URI=\"subtitles_es.m3u8\"",
+      "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"Français\",DEFAULT="
+      "NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE=\"fr\",URI=\"subtitles_fr.m3u8\"",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=258157,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"stereo\",RESOLUTION=422x180,SUBTITLES=\"subs\"",
+      "video/250kbit.m3u8",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=520929,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"stereo\",RESOLUTION=638x272,SUBTITLES=\"subs\"",
+      "video/500kbit.m3u8",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=831270,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"stereo\",RESOLUTION=638x272,SUBTITLES=\"subs\"",
+      "video/800kbit.m3u8",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1144430,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"surround\",RESOLUTION=958x408,SUBTITLES=\"subs\"",
+      "video/1100kbit.m3u8",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1558322,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"surround\",RESOLUTION=1277x554,SUBTITLES=\"subs\"",
+      "video/1500kbit.m3u8",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=4149264,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"surround\",RESOLUTION=1921x818,SUBTITLES=\"subs\"",
+      "video/4000kbit.m3u8",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=10285391,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"surround\",RESOLUTION=4096x1744,SUBTITLES="
+      "\"subs\"",
+      "video/10000kbit.m3u8",
+      "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=6214307,CODECS=\"video.codec,"
+      "audio.codec\",AUDIO=\"surround\",RESOLUTION=1921x818,SUBTITLES=\"subs\"",
+      "video/6000kbit.m3u8");
+
+  // All variants are playable, so the best one selected. The default audio
+  // override is also selected.
+  EXPECT_CALL(*this, VariantSelected("/video/10000kbit.m3u8",
+                                     "/audio/surround/en/320kbit.m3u8"));
+  rm.Reselect(GetVariantCb());
+
+  // Notify a network downgrade, but not one that would preclude our 10285kbps
+  // stream. Verify no response.
+  EXPECT_CALL(*this, VariantSelected(_, _)).Times(0);
+  rm.UpdateNetworkSpeed(10285395);
+
+  // Notify a network downgrade which would knock us down to a lower bitrate
+  // video
+  EXPECT_CALL(*this, VariantSelected("/video/6000kbit.m3u8",
+                                     "/audio/surround/en/320kbit.m3u8"));
+  rm.UpdateNetworkSpeed(10285300);
+
+  // Notify a network upgrade, and go back up to the highest level.
+  EXPECT_CALL(*this, VariantSelected("/video/10000kbit.m3u8",
+                                     "/audio/surround/en/320kbit.m3u8"));
+  rm.UpdateNetworkSpeed(10285395);
+
+  // This network downgrade pushes us into the stereo variants, so a new audio
+  // override rendition is selected as well.
+  EXPECT_CALL(*this, VariantSelected("/video/800kbit.m3u8",
+                                     "/audio/stereo/en/128kbit.m3u8"));
+  rm.UpdateNetworkSpeed(831280);
+
+  // Now lets check the available renditions for this selected variant. These
+  // Should be in the same order as the manifest.
+  const auto renditions = rm.GetSelectableAudioRenditions();
+  ASSERT_EQ(renditions.size(), 3u);
+  ASSERT_EQ(std::get<1>(renditions[0]), "English");
+  ASSERT_EQ(std::get<1>(renditions[1]), "Dubbing");
+  ASSERT_EQ(std::get<1>(renditions[2]), "German");
+
+  // Select the dubbing rendition, and get a change.
+  const auto dubbing_id = std::get<0>(renditions[1]);
+  EXPECT_CALL(*this, VariantSelected("/video/800kbit.m3u8",
+                                     "/audio/stereo/none/128kbit.m3u8"));
+  rm.SetPreferredAudioRendition(dubbing_id);
+
+  // Increase the network speed to full again. Because the user has selected
+  // the dubbing track, we try to match the language.
+  EXPECT_CALL(*this, VariantSelected("/video/10000kbit.m3u8",
+                                     "/audio/surround/none/320kbit.m3u8"));
+  rm.UpdateNetworkSpeed(10285395);
+
+  // Drop the network speed again to ensure we stick to dubbing ways.
+  EXPECT_CALL(*this, VariantSelected("/video/800kbit.m3u8",
+                                     "/audio/stereo/none/128kbit.m3u8"));
+  rm.UpdateNetworkSpeed(831280);
+
+  // Select the german rendition, and get a change.
+  const auto german_id = std::get<0>(renditions[2]);
+  EXPECT_CALL(*this, VariantSelected("/video/800kbit.m3u8",
+                                     "/audio/stereo/de/128kbit.m3u8"));
+  rm.SetPreferredAudioRendition(german_id);
+
+  // Increase the network speed to full again. Because the user has selected
+  // the german track, but the surround sound has no german audio, we switch
+  // back to whatever the default is.
+  EXPECT_CALL(*this, VariantSelected("/video/10000kbit.m3u8",
+                                     "/audio/surround/en/320kbit.m3u8"));
+  rm.UpdateNetworkSpeed(10285395);
+
+  // Finally, drop back down low network again, and ensure we switch back to
+  // german.
+  EXPECT_CALL(*this, VariantSelected("/video/800kbit.m3u8",
+                                     "/audio/stereo/de/128kbit.m3u8"));
+  rm.UpdateNetworkSpeed(831280);
+
+  // Unselect a preferred rendition, which switches back to english.
+  EXPECT_CALL(*this, VariantSelected("/video/800kbit.m3u8",
+                                     "/audio/stereo/en/128kbit.m3u8"));
+  rm.SetPreferredAudioRendition(absl::nullopt);
+}
+
+}  // namespace media::hls
diff --git a/media/formats/hls/rendition_selector.cc b/media/formats/hls/rendition_selector.cc
deleted file mode 100644
index 4997231..0000000
--- a/media/formats/hls/rendition_selector.cc
+++ /dev/null
@@ -1,313 +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 "media/formats/hls/rendition_selector.h"
-
-#include <string>
-
-#include "base/ranges/algorithm.h"
-#include "media/formats/hls/audio_rendition.h"
-#include "media/formats/hls/multivariant_playlist.h"
-#include "media/formats/hls/types.h"
-#include "media/formats/hls/variant_stream.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace media::hls {
-
-namespace {
-
-RenditionSelector::CodecSupportType VariantTypeSupported(
-    RenditionSelector::IsTypeSupportedCallback is_type_supported_cb,
-    const VariantStream& variant) {
-  // Check if the codecs reported by this variant can be played at all. If
-  // this variant does not report its codecs, we'll assume its supported until
-  // proven otherwise.
-  auto codecs = variant.GetCodecs();
-  if (!codecs) {
-    return RenditionSelector::CodecSupportType::kSupportedAudioVideo;
-  }
-
-  RenditionSelector::CodecSupportType mp4 =
-      is_type_supported_cb.Run("video/mp4", *codecs);
-  if (mp4 != RenditionSelector::CodecSupportType::kUnsupported) {
-    return mp4;
-  }
-
-  RenditionSelector::CodecSupportType mp2t =
-      is_type_supported_cb.Run("video/mp2t", *codecs);
-  return mp2t;
-}
-
-// |OptimalFilter| will apply an |IsAcceptable (T)->Bool| filter function over
-// a set of |options| and filter them based on on the result. However, in the
-// case where nothing in |options| is acceptable, it will use a
-// |ComputeFallbackCandidate (T, T) -> T| function to ensure that the resulting
-// list always has at least one item in it.
-template <typename IsAcceptable, typename ComputeFallbackCandidate>
-std::vector<const VariantStream*> OptimalFilter(
-    const std::vector<const VariantStream*>& options,
-    IsAcceptable filter_functor,
-    ComputeFallbackCandidate compare_functor) {
-  std::vector<const VariantStream*> acceptable;
-  const VariantStream* best_fallback_candidate = nullptr;
-
-  for (const VariantStream* test : options) {
-    if (filter_functor(test)) {
-      acceptable.push_back(test);
-    }
-    if (acceptable.empty()) {
-      if (best_fallback_candidate == nullptr) {
-        best_fallback_candidate = test;
-      } else {
-        best_fallback_candidate =
-            compare_functor(best_fallback_candidate, test);
-      }
-    }
-  }
-
-  if (acceptable.empty() && best_fallback_candidate != nullptr) {
-    acceptable.push_back(best_fallback_candidate);
-  }
-
-  return acceptable;
-}
-
-// Use the |OptimalFilter| function above to find all Variants under a max bit
-// rate, or, if everything is over the max bitrate, find the lowest one.
-std::vector<const VariantStream*> GetPreferredVariantsByBitrate(
-    absl::optional<types::DecimalInteger> max_bitrate,
-    std::vector<const VariantStream*> inputs) {
-  if (!max_bitrate.has_value()) {
-    return inputs;
-  }
-
-  // TODO: Prefer to use average bandwidth, if the streams have that calculated.
-  types::DecimalInteger bitrate = *max_bitrate;
-  return OptimalFilter(
-      inputs,
-      /*filter_functor=*/
-      [bitrate](const VariantStream* test) {
-        return test->GetBandwidth() <= bitrate;
-      },
-      /*compare_functor=*/
-      [](const VariantStream* A, const VariantStream* B) {
-        return A->GetBandwidth() > B->GetBandwidth() ? B : A;
-      });
-}
-
-}  // namespace
-
-RenditionSelector::RenditionSelector(
-    scoped_refptr<MultivariantPlaylist> playlist,
-    IsTypeSupportedCallback is_type_supported_cb)
-    : playlist_(std::move(playlist)) {
-  DCHECK(playlist_);
-  DCHECK(is_type_supported_cb);
-
-  for (const VariantStream& variant : playlist_->GetVariants()) {
-    bool has_video = false;
-    bool has_audio = false;
-
-    switch (VariantTypeSupported(is_type_supported_cb, variant)) {
-      case CodecSupportType::kUnsupported:
-        break;
-      case CodecSupportType::kSupportedAudioVideo:
-        has_audio = true;
-        has_video = true;
-        break;
-      case CodecSupportType::kSupportedAudioOnly:
-        has_audio = true;
-        break;
-      case CodecSupportType::kSupportedVideoOnly:
-        has_video = true;
-        break;
-    }
-
-    // Don't consider unsupported types!
-    if (has_audio || has_video) {
-      if (variant.GetAudioRenditionGroup()) {
-        has_audio = true;
-      }
-
-      if (variant.GetVideoRenditionGroupName().has_value()) {
-        has_video = true;
-      }
-
-      if (has_video) {
-        video_variants_.push_back(&variant);
-      }
-
-      if (has_audio) {
-        audio_variants_.push_back(&variant);
-      }
-    }
-  }
-
-  // Sort variants by score and bandwidth (descending)
-  constexpr auto compare = [](const VariantStream* lhs,
-                              const VariantStream* rhs) {
-    // First compare by |BANDWIDTH|
-    if (lhs->GetBandwidth() != rhs->GetBandwidth()) {
-      return lhs->GetBandwidth() > rhs->GetBandwidth();
-    }
-
-    // Then compare by SCORE
-    if (lhs->GetScore().has_value() && rhs->GetScore().has_value()) {
-      return *lhs->GetScore() > *rhs->GetScore();
-    }
-
-    // If |lhs| has a score, but |rhs| doesn't, then lhs should be preferred.
-    return lhs->GetScore().has_value();
-  };
-
-  base::ranges::sort(video_variants_, compare);
-  base::ranges::sort(audio_variants_, compare);
-}
-
-RenditionSelector::RenditionSelector(const RenditionSelector&) = default;
-
-RenditionSelector::RenditionSelector(RenditionSelector&&) = default;
-
-RenditionSelector& RenditionSelector::operator=(const RenditionSelector&) =
-    default;
-
-RenditionSelector& RenditionSelector::operator=(RenditionSelector&&) = default;
-
-RenditionSelector::~RenditionSelector() = default;
-
-RenditionSelector::PreferredVariants RenditionSelector::GetPreferredVariants(
-    VideoPlaybackPreferences video_preferences,
-    AudioPlaybackPreferences audio_preferences) const {
-  // Start by selecting the preferred video variant, which might also be an
-  // audio variant. If it's not an audio variant, we have to select the audio
-  // variant after. Get the acceptable bitrate variants first, then filter by
-  // resolution. From there, we select the highest bitrate one, which should be
-  // first, as they are sorted descending by bitrate.
-  std::vector<const VariantStream*> acceptable_bitrates =
-      GetPreferredVariantsByBitrate(video_preferences.max_smooth_bitrate,
-                                    video_variants_);
-
-  // TODO: prefer variants which have the same aspect ratio, even if they are
-  // a bit larger, as that will probably look better.
-  std::vector<const VariantStream*> acceptable_resolutions;
-  if (video_preferences.video_player_resolution.has_value()) {
-    gfx::Size resolution = *video_preferences.video_player_resolution;
-    acceptable_resolutions = OptimalFilter(
-        acceptable_bitrates,
-        /*filter_functor=*/
-        [resolution](const VariantStream* test) {
-          auto stream_res = test->GetResolution();
-          if (!stream_res.has_value()) {
-            return true;
-          }
-          if (stream_res->Area() > resolution.Area64()) {
-            return false;
-          }
-          return true;
-        },
-        /*compare_functor=*/
-        [](const VariantStream* A, const VariantStream* B) {
-          // If either A or B didn't have a resolution, then there is at least
-          // A or B selected already, and this compare_function shouldn't be
-          // getting called.
-          DCHECK(A->GetResolution().has_value());
-          DCHECK(B->GetResolution().has_value());
-
-          // If we have to select something and all the resolutions are too
-          // large, select the smallest resolution.
-          return A->GetResolution()->Area() > B->GetResolution()->Area() ? B
-                                                                         : A;
-        });
-  } else {
-    acceptable_resolutions = acceptable_bitrates;
-  }
-
-  PreferredVariants result;
-
-  // This could be audio only, so it's not quite an error yet. Lets grab the
-  // optimal audio variant, and try that. If that's also null, then we have no
-  // good variant to play, and we should return an error.
-  if (acceptable_resolutions.empty()) {
-    DCHECK(video_variants_.empty());
-    auto acceptable_audio = GetPreferredVariantsByBitrate(
-        video_preferences.max_smooth_bitrate, audio_variants_);
-    if (acceptable_audio.empty()) {
-      return result;
-    }
-    result.selected_variant = acceptable_audio[0];
-    result.audio_override_rendition =
-        TryFindAudioOverride(audio_preferences, result.selected_variant);
-
-    // If our selected variant is audio, but we got a rendition that overrides
-    // this, then this selected variant won't actually be selected, since it
-    // would be overridden by the audio.
-    if (result.audio_override_rendition != nullptr) {
-      result.audio_override_variant = result.selected_variant;
-      result.selected_variant = nullptr;
-    }
-
-    return result;
-  }
-
-  // For now, since we only select a potential override from the selected
-  // variant, the audio override variant is always the same.
-  result.selected_variant = acceptable_resolutions[0];
-  result.audio_override_variant = acceptable_resolutions[0];
-
-  // If our selected variant is an audio/video stream, we should use its audio
-  // group to determine what rendition to pick as an override, if it has an
-  // audio group. If we don't suspect it of being an audio stream, then we have
-  // to decide what to do - most implementations just end up with silent video.
-  // Should we consider finding the best audio variant as well?
-  result.audio_override_rendition =
-      TryFindAudioOverride(audio_preferences, result.selected_variant);
-  return result;
-}
-
-const AudioRendition* RenditionSelector::TryFindAudioOverride(
-    AudioPlaybackPreferences audio_preferences,
-    const VariantStream* variant) const {
-  // Note that we're not considering channel count, which should probably be
-  // changed at some point.
-  CHECK(variant);
-
-  // There are no audio renditions related to this variant anyway.
-  if (!variant->GetAudioRenditionGroup()) {
-    return nullptr;
-  }
-
-  // If there is no preference for language, then we can just use the default.
-  if (!audio_preferences.language.has_value()) {
-    audio_preferences.language = default_audio_language_;
-  }
-
-  // If we have a preference, check all the renditions that are connected to our
-  // variant.
-  if (audio_preferences.language.has_value()) {
-    const auto& renditions = variant->GetAudioRenditionGroup()->GetRenditions();
-    for (const auto& rendition : renditions) {
-      if (rendition.GetAssociatedLanguage() == *audio_preferences.language) {
-        return &rendition;
-      }
-    }
-  }
-
-  // Otherwise, we should just select the one that is marked as DEFAULT (which
-  // implies that AUTOSELECT is also true)
-  if (variant->GetAudioRenditionGroup()->GetDefaultRendition()) {
-    return variant->GetAudioRenditionGroup()->GetDefaultRendition();
-  }
-
-  // If there is no default, and we have no preference, then just grab the
-  // first thing that was present in the manifest.
-  const auto& renditions = variant->GetAudioRenditionGroup()->GetRenditions();
-  if (renditions.size()) {
-    return &renditions.front();
-  }
-
-  // nothing to select!
-  return nullptr;
-}
-
-}  // namespace media::hls
diff --git a/media/formats/hls/rendition_selector.h b/media/formats/hls/rendition_selector.h
deleted file mode 100644
index e13c8835..0000000
--- a/media/formats/hls/rendition_selector.h
+++ /dev/null
@@ -1,134 +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 MEDIA_FORMATS_HLS_RENDITION_SELECTOR_H_
-#define MEDIA_FORMATS_HLS_RENDITION_SELECTOR_H_
-
-#include <string>
-#include <vector>
-
-#include "base/containers/flat_set.h"
-#include "base/containers/span.h"
-#include "base/functional/callback.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/strings/string_piece.h"
-#include "media/base/media_export.h"
-#include "media/formats/hls/types.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-#include "ui/gfx/geometry/size.h"
-#include "url/gurl.h"
-
-namespace media::hls {
-
-class MultivariantPlaylist;
-class VariantStream;
-class AudioRendition;
-
-// Class responsible for tracking playability state of all variants and
-// renditions in a multivariant playlist.
-class MEDIA_EXPORT RenditionSelector {
- public:
-  // We want to ask if a codec string is supported, but also if it contains
-  // audio, video, or both types of content, allowing us to sort our variants
-  // into groups.
-  enum CodecSupportType {
-    kUnsupported,
-    kSupportedAudioVideo,
-    kSupportedAudioOnly,
-    kSupportedVideoOnly,
-  };
-
-  // Callback used to query whether the given MIME+codec string is supported,
-  // and what types of content we can expect.
-  // The first argument is the name of the container, ie "video/mp4" or
-  // "video/mp2t", and the second argument is a list of codecs to check support
-  // for, such as ["mp4a.40.2", "avc1.4d4015"]
-  using IsTypeSupportedCallback =
-      base::RepeatingCallback<CodecSupportType(base::StringPiece,
-                                               base::span<const std::string>)>;
-
-  // Contains the set of constraints that may be used when selecting an audio
-  // rendition, in the order they're used to filter available renditions.
-  struct AudioPlaybackPreferences {
-    // The preferred audio language, in the format given by
-    // https://datatracker.ietf.org/doc/html/rfc5646. If this is
-    // `absl::nullopt` or the language does not exist in any rendition, the
-    // playlist's default is used.
-    absl::optional<base::StringPiece> language;
-
-    // The preferred number of audio channels. If this is `absl::nullopt` or
-    // does not exist in any rendition that meets the previous preference(s),
-    // its ignored.
-    absl::optional<uint32_t> channel_count;
-  };
-
-  // Contains the set of constraints and values related to video playback
-  // on the current system.
-  struct VideoPlaybackPreferences {
-    // The max bitrate supported for smooth playback.
-    absl::optional<types::DecimalInteger> max_smooth_bitrate;
-
-    // The max resolution for the player - this might change on player resize,
-    // but generally there's no point in playing a 4k video on a 1080p screen.
-    absl::optional<gfx::Size> video_player_resolution;
-  };
-
-  // Contains the preferred video variant, and optionally a preferred audio
-  // rendition if the preferred variant has an audio group associated.
-  // If there is no associated audio group with the selected rendition, then
-  // the variant default will be used.
-  struct PreferredVariants {
-    // Use this playlist for video, and use it for audio too if the audio-only
-    // variant is nullptr.
-    raw_ptr<const VariantStream> selected_variant = nullptr;
-
-    // Use this variant for audio content if it is not nullptr.
-    raw_ptr<const VariantStream> audio_override_variant = nullptr;
-    raw_ptr<const AudioRendition> audio_override_rendition = nullptr;
-  };
-
-  RenditionSelector(scoped_refptr<MultivariantPlaylist> playlist,
-                    IsTypeSupportedCallback is_type_supported_cb);
-  RenditionSelector(const RenditionSelector&);
-  RenditionSelector(RenditionSelector&&);
-  RenditionSelector& operator=(const RenditionSelector&);
-  RenditionSelector& operator=(RenditionSelector&&);
-  ~RenditionSelector();
-
-  // Select a preferred variant based on bandwidth constraints, codec support,
-  // and video resolution. Unsupported codecs will never be considered for
-  // selection, while resolution and bandwidth constraints that are above
-  // the optionally provided maximums might still be considered if nothing else
-  // is available. If scores are provided for variants, then the highest scoring
-  // variant from among the possibilities is selected.
-  PreferredVariants GetPreferredVariants(
-      VideoPlaybackPreferences video_preferences,
-      AudioPlaybackPreferences audio_preferences) const;
-
- private:
-  const AudioRendition* TryFindAudioOverride(
-      AudioPlaybackPreferences audio_preferences,
-      const VariantStream* variant) const;
-
-  scoped_refptr<MultivariantPlaylist> playlist_;
-
-  // All variants which we suspect have an audio component.
-  std::vector<const VariantStream*> audio_variants_;
-
-  // All variants which we suspect have a video component.
-  std::vector<const VariantStream*> video_variants_;
-
-  // The default audio language that was present in the manifest.
-  // This is a singular value, relying on the fact the spec says that all groups
-  // should have the same set of renditions differing only by URI and CHANNELS.
-  // If this is `absl::nullopt`, there was no playable audio
-  // rendition with the DEFAULT=YES and LANGUAGE attributes. If this is an empty
-  // string, a default rendition exists but it had no LANGUAGE attribute.
-  absl::optional<base::StringPiece> default_audio_language_;
-};
-
-}  // namespace media::hls
-
-#endif
diff --git a/media/formats/hls/rendition_selector_unittest.cc b/media/formats/hls/rendition_selector_unittest.cc
deleted file mode 100644
index 22c62619..0000000
--- a/media/formats/hls/rendition_selector_unittest.cc
+++ /dev/null
@@ -1,205 +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 "media/formats/hls/rendition_selector.h"
-
-#include "media/formats/hls/multivariant_playlist_test_builder.h"
-#include "media/formats/hls/parse_status.h"
-#include "media/formats/hls/types.h"
-#include "media/formats/hls/variant_stream.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace media::hls {
-
-namespace {
-
-RenditionSelector::CodecSupportType GetCodecSupportType(
-    base::StringPiece container,
-    base::span<const std::string> codecs) {
-  bool has_audio = false;
-  bool has_video = false;
-  for (const auto& codec : codecs) {
-    if (codec == "audio.codec") {
-      has_audio = true;
-    } else if (codec == "video.codec") {
-      has_video = true;
-    } else if (codec == "av.codec") {
-      return RenditionSelector::CodecSupportType::kSupportedAudioVideo;
-    }
-  }
-  if (has_audio && has_video) {
-    return RenditionSelector::CodecSupportType::kSupportedAudioVideo;
-  } else if (has_audio) {
-    return RenditionSelector::CodecSupportType::kSupportedAudioOnly;
-  } else if (has_video) {
-    return RenditionSelector::CodecSupportType::kSupportedVideoOnly;
-  }
-  return RenditionSelector::CodecSupportType::kUnsupported;
-}
-
-template <typename... Strings>
-RenditionSelector GetRenditionSelector(Strings... strings) {
-  MultivariantPlaylistTestBuilder builder;
-  builder.AppendLine("#EXTM3U");
-  ([&] { builder.AppendLine(strings); }(), ...);
-  return RenditionSelector(builder.Parse(),
-                           base::BindRepeating(&GetCodecSupportType));
-}
-
-void CheckSelections(const RenditionSelector& selector,
-                     RenditionSelector::VideoPlaybackPreferences video_prefs,
-                     RenditionSelector::AudioPlaybackPreferences audio_prefs,
-                     absl::optional<std::string> selected,
-                     absl::optional<std::string> audio_override) {
-  auto variants = selector.GetPreferredVariants(video_prefs, audio_prefs);
-  if (selected.has_value()) {
-    ASSERT_NE(variants.selected_variant, nullptr);
-    ASSERT_EQ(variants.selected_variant->GetPrimaryRenditionUri(),
-              GURL(*selected));
-  } else {
-    ASSERT_EQ(variants.selected_variant, nullptr);
-  }
-
-  if (audio_override.has_value()) {
-    ASSERT_NE(variants.audio_override_rendition, nullptr);
-    ASSERT_NE(variants.audio_override_rendition->GetUri(), absl::nullopt);
-    ASSERT_EQ(*variants.audio_override_rendition->GetUri(),
-              GURL(*audio_override));
-  } else {
-    ASSERT_EQ(variants.audio_override_rendition, nullptr);
-  }
-}
-
-}  // namespace
-
-TEST(HlsRenditionSelectorTest, SimpleVideoStreamSelection) {
-  auto rs = GetRenditionSelector(
-      "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000",
-      "http://example.com/low.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000",
-      "http://example.com/mid.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000",
-      "http://example.com/high.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"audio.codec\"",
-      "http://example.com/audio-only.m3u8");
-
-  // With no preferences for bandwidth, resolution, or language, we pick the
-  // highest bandwidth stream. Since that stream has no audio group, we don't
-  // do any audio rendition override selection.
-  CheckSelections(rs,
-                  /*video_prefs=*/{absl::nullopt, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://example.com/high.m3u8", absl::nullopt);
-
-  // If we have a bandwidth cap, filter out everything too high, and pick the
-  // highest remaining variant. Again, there is no audio group, so there are no
-  // audio override renditions.
-
-  CheckSelections(rs,
-                  /*video_prefs=*/{2600000, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://example.com/mid.m3u8", absl::nullopt);
-
-  // If we have a bandwidth cap, filter out everything too high, and pick the
-  // highest remaining variant. This time, there's no video stream under the
-  // cap, but we have to pick _something_, so pick the lowest. Note that
-  // although the audio codec is lower than our preference, it's got a codec
-  // line which we don't detect as having audio present, so it can't be used
-  // for video. And finally, we still have no audio group on the selected
-  // variant, so there is no audio override.
-  CheckSelections(rs,
-                  /*video_prefs=*/{70000, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://example.com/low.m3u8", absl::nullopt);
-
-  // If we have a bandwidth cap, filter out everything too high, and pick the
-  // highest remaining variant. This time, there's no video stream OR audio
-  // stream under the cap, but we have to pick _something_, so pick the lowest.
-  CheckSelections(rs,
-                  /*video_prefs=*/{4, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://example.com/low.m3u8", absl::nullopt);
-}
-
-TEST(HlsRenditionSelectorTest, SimpleAudioOnlyStreamSelection) {
-  auto rs = GetRenditionSelector(
-      "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"audio.codec\"",
-      "http://example.com/audio1.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"audio.codec\"",
-      "http://example.com/audio2.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"audio.codec\"",
-      "http://example.com/audio3.m3u8");
-
-  // There are no variants that we've detected as having video, so we end up
-  // with the fallback to selecting from a bunch of audio-only variants. There
-  // aren't any renditions either, so this will count as a primary variant.
-  CheckSelections(rs,
-                  /*video_prefs=*/{absl::nullopt, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://example.com/audio1.m3u8", absl::nullopt);
-
-  // Try the selection again, but this time only have one variant under the
-  // bandwidth cap.
-  CheckSelections(rs,
-                  /*video_prefs=*/{1280001, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://example.com/audio3.m3u8", absl::nullopt);
-
-  // Finally, try selecting where the bandwidth cap is too low to select any
-  // variant by normal means, and default to the lowest.
-  CheckSelections(rs,
-                  /*video_prefs=*/{5, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://example.com/audio3.m3u8", absl::nullopt);
-}
-
-TEST(HlsRenditionSelectorTest, AlternativeAudioSelection) {
-  auto rs = GetRenditionSelector(
-      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Eng\",DEFAULT=YES,"
-      "AUTOSELECT=YES,LANGUAGE=\"en\",URI=\"eng-audio.m3u8\"",
-      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Ger\",DEFAULT=NO,"
-      "AUTOSELECT=YES,LANGUAGE=\"en\",URI=\"ger-audio.m3u8\"",
-      "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Com\",DEFAULT=NO,"
-      "AUTOSELECT=NO,LANGUAGE=\"en\",URI=\"eng-comments.m3u8\"",
-      "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"video.codec\",AUDIO="
-      "\"aac\"",
-      "low/video-only.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"video.codec\",AUDIO="
-      "\"aac\"",
-      "mid/video-only.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"video.codec\",AUDIO="
-      "\"aac\"",
-      "hi/video-only.m3u8",
-      "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"audio.codec\",AUDIO=\"aac\"",
-      "main/english-audio.m3u8");
-
-  // With no preferences for bandwidth, resolution, or language, we pick the
-  // highest bandwidth stream. Since that stream has an audio group, we then
-  // look at all the matching renditions and select the one tagged as DEFAULT.
-  CheckSelections(rs,
-                  /*video_prefs=*/{absl::nullopt, absl::nullopt},
-                  /*audio_prefs=*/{absl::nullopt, absl::nullopt},
-                  "http://localhost/hi/video-only.m3u8",
-                  "http://localhost/eng-audio.m3u8");
-
-  // With no preferences for bandwidth, resolution, or language, we pick the
-  // highest bandwidth stream. Since that stream has an audio group, we then
-  // look at all the matching renditions and see if anything matches our
-  // language criteria. First, we try english, which lets us select the DEFAULT
-  // one again, but then if we set it to prefer german, we will select german
-  // audio.
-  CheckSelections(rs,
-                  /*video_prefs=*/{absl::nullopt, absl::nullopt},
-                  /*audio_prefs=*/{"en", absl::nullopt},
-                  "http://localhost/hi/video-only.m3u8",
-                  "http://localhost/eng-audio.m3u8");
-  CheckSelections(rs,
-                  /*video_prefs=*/{absl::nullopt, absl::nullopt},
-                  /*audio_prefs=*/{"de", absl::nullopt},
-                  "http://localhost/hi/video-only.m3u8",
-                  "http://localhost/eng-audio.m3u8");
-}
-
-}  // namespace media::hls
diff --git a/media/gpu/chromeos/BUILD.gn b/media/gpu/chromeos/BUILD.gn
index 730f805a..dfbfbe8 100644
--- a/media/gpu/chromeos/BUILD.gn
+++ b/media/gpu/chromeos/BUILD.gn
@@ -90,8 +90,8 @@
     "vda_video_frame_pool.cc",
     "vda_video_frame_pool.h",
     "video_decoder_pipeline.h",
-    "vulkan_image_processor_backend.cc",
-    "vulkan_image_processor_backend.h",
+    "vulkan_image_processor.cc",
+    "vulkan_image_processor.h",
   ]
 
   # TODO(crbug.com/1012587): Merge :fourcc to :common.
diff --git a/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.cc b/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.cc
index 8c699d8..d385c71 100644
--- a/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.cc
+++ b/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.cc
@@ -22,14 +22,6 @@
 
 FakeChromeOSIntelCompressedGpuMemoryBuffer::
     FakeChromeOSIntelCompressedGpuMemoryBuffer(const gfx::Size& size,
-                                               gfx::BufferFormat format)
-    : FakeChromeOSIntelCompressedGpuMemoryBuffer(
-          size,
-          format,
-          I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS) {}
-
-FakeChromeOSIntelCompressedGpuMemoryBuffer::
-    FakeChromeOSIntelCompressedGpuMemoryBuffer(const gfx::Size& size,
                                                gfx::BufferFormat format,
                                                uint64_t modifier)
     : size_(size), format_(format) {
diff --git a/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.h b/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.h
index 7d93f9c8..b3ff0ee 100644
--- a/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.h
+++ b/media/gpu/chromeos/fake_chromeos_intel_compressed_gpu_memory_buffer.h
@@ -16,8 +16,6 @@
 class FakeChromeOSIntelCompressedGpuMemoryBuffer : public gfx::GpuMemoryBuffer {
  public:
   FakeChromeOSIntelCompressedGpuMemoryBuffer(const gfx::Size& size,
-                                             gfx::BufferFormat format);
-  FakeChromeOSIntelCompressedGpuMemoryBuffer(const gfx::Size& size,
                                              gfx::BufferFormat format,
                                              uint64_t modifier);
 
diff --git a/media/gpu/chromeos/image_processor_test.cc b/media/gpu/chromeos/image_processor_test.cc
index 9d771bc5..ebe46c2a 100644
--- a/media/gpu/chromeos/image_processor_test.cc
+++ b/media/gpu/chromeos/image_processor_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <sys/mman.h>
+#include <sys/poll.h>
 #include <memory>
 #include <string>
 #include <tuple>
@@ -19,6 +20,7 @@
 #include "base/test/test_suite.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "gpu/command_buffer/common/mailbox.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
 #include "media/base/video_types.h"
@@ -28,7 +30,7 @@
 #include "media/gpu/chromeos/image_processor_backend.h"
 #include "media/gpu/chromeos/image_processor_factory.h"
 #include "media/gpu/chromeos/platform_video_frame_utils.h"
-#include "media/gpu/chromeos/vulkan_image_processor_backend.h"
+#include "media/gpu/chromeos/vulkan_image_processor.h"
 #include "media/gpu/test/image.h"
 #include "media/gpu/test/image_processor/image_processor_client.h"
 #include "media/gpu/test/video_frame_file_writer.h"
@@ -718,41 +720,37 @@
       test::CreateVideoFrameLayout(input_image.PixelFormat(), coded_size);
   auto output_layout = test::CreateVideoFrameLayout(
       output_fourcc.ToVideoPixelFormat(), output_size);
-  ImageProcessor::PortConfig input_config(
-      Fourcc(Fourcc::MM21), coded_size, input_layout->planes(), visible_rect,
-      {VideoFrame::STORAGE_GPU_MEMORY_BUFFER});
-  ImageProcessor::PortConfig output_config(
-      output_fourcc, output_size, output_layout->planes(), output_visible_rect,
-      {VideoFrame::STORAGE_GPU_MEMORY_BUFFER});
-  base::RunLoop run_loop;
-  base::RepeatingClosure quit_closure = run_loop.QuitClosure();
-  bool image_processor_error = false;
-  auto client_task_runner = base::SequencedTaskRunner::GetCurrentDefault();
-  ImageProcessor::ErrorCB error_cb = base::BindRepeating(
-      [](scoped_refptr<base::SequencedTaskRunner> client_task_runner,
-         base::RepeatingClosure quit_closure, bool* image_processor_error) {
-        CHECK(client_task_runner->RunsTasksInCurrentSequence());
-        *image_processor_error = true;
-        quit_closure.Run();
+  auto in_gmb = CreateGpuMemoryBufferHandle(mm21_frame.get());
+  auto out_gmb = CreateGpuMemoryBufferHandle(vulkan_output_frame.get());
+  gpu::Mailbox input_mailbox = gpu::Mailbox::GenerateForSharedImage();
+  gpu::Mailbox output_mailbox = gpu::Mailbox::GenerateForSharedImage();
+
+  VulkanImageProcessor::BackingCB input_backing_cb = base::BindRepeating(
+      [](gpu::Mailbox expected_mailbox_, gfx::GpuMemoryBufferHandle* in_gmb_,
+         gpu::Mailbox& mailbox) {
+        assert(mailbox == expected_mailbox_);
+        return std::move(*in_gmb_);
       },
-      client_task_runner, quit_closure, &image_processor_error);
-  auto vulkan_image_processor = VulkanImageProcessorBackend::Create(
-      input_config, output_config, ImageProcessorBackend::OutputMode::IMPORT,
-      error_cb);
+      input_mailbox, &in_gmb);
+  VulkanImageProcessor::ReleaseCB in_release_cb =
+      base::BindOnce([](gpu::Mailbox& mailbox) {});
+  VulkanImageProcessor::BackingCB output_backing_cb = base::BindRepeating(
+      [](gpu::Mailbox expected_mailbox_, gfx::GpuMemoryBufferHandle* out_gmb_,
+         gpu::Mailbox& mailbox) {
+        assert(mailbox == expected_mailbox_);
+        return std::move(*out_gmb_);
+      },
+      output_mailbox, &out_gmb);
+  auto vulkan_image_processor =
+      VulkanImageProcessor::Create(input_backing_cb, output_backing_cb);
   ASSERT_TRUE(vulkan_image_processor);
 
-  ImageProcessor::FrameReadyCB vulkan_callback = base::BindOnce(
-      [](scoped_refptr<base::SequencedTaskRunner> client_task_runner,
-         base::RepeatingClosure quit_closure,
-         scoped_refptr<VideoFrame>* output_frame,
-         scoped_refptr<VideoFrame> frame) {
-        CHECK(client_task_runner->RunsTasksInCurrentSequence());
-        quit_closure.Run();
-      },
-      client_task_runner, quit_closure, &vulkan_output_frame);
-  vulkan_image_processor->Process(mm21_frame, vulkan_output_frame,
-                                  std::move(vulkan_callback));
-  run_loop.Run();
+  gpu::SemaphoreHandle done_semaphore = vulkan_image_processor->Process(
+      input_mailbox, coded_size, visible_rect.size(), std::move(in_release_cb),
+      output_mailbox, output_size, output_visible_rect.size(), absl::nullopt);
+
+  struct pollfd poll_request = {done_semaphore.TakeHandle().get(), -0x7FFF, 0};
+  poll(&poll_request, 1, 1000);
 
   // Replicate this operation using LibYUV. Note that we don't use the image
   // processor since we need to do a very specific conversion and scale
diff --git a/media/gpu/chromeos/platform_video_frame_pool_unittest.cc b/media/gpu/chromeos/platform_video_frame_pool_unittest.cc
index fcb7316..76a10977 100644
--- a/media/gpu/chromeos/platform_video_frame_pool_unittest.cc
+++ b/media/gpu/chromeos/platform_video_frame_pool_unittest.cc
@@ -47,7 +47,8 @@
 }
 
 CroStatus::Or<scoped_refptr<VideoFrame>>
-CreateChromeOSCompressedGpuMemoryBufferVideoFrame(VideoPixelFormat format,
+CreateChromeOSCompressedGpuMemoryBufferVideoFrame(uint64_t modifier,
+                                                  VideoPixelFormat format,
                                                   const gfx::Size& coded_size,
                                                   const gfx::Rect& visible_rect,
                                                   const gfx::Size& natural_size,
@@ -59,8 +60,8 @@
   DCHECK(gfx_format);
   return WrapChromeOSCompressedGpuMemoryBufferAsVideoFrame(
       visible_rect, natural_size,
-      std::make_unique<FakeChromeOSIntelCompressedGpuMemoryBuffer>(coded_size,
-                                                                   *gfx_format),
+      std::make_unique<FakeChromeOSIntelCompressedGpuMemoryBuffer>(
+          coded_size, *gfx_format, modifier),
       timestamp);
 }
 
@@ -348,8 +349,39 @@
   EXPECT_TRUE(GetFrame(10));
 }
 
-TEST_P(PlatformVideoFramePoolTest, CompressedGpuMemoryBufferIsPassed) {
-  const VideoPixelFormat pixel_format = GetParam();
+class PlatformVideoFramePoolWithMediaCompressionTest
+    : public PlatformVideoFramePoolTestBase,
+      public testing::WithParamInterface<
+          std::tuple<VideoPixelFormat, uint64_t>> {
+ public:
+  PlatformVideoFramePoolWithMediaCompressionTest() = default;
+  ~PlatformVideoFramePoolWithMediaCompressionTest() override = default;
+
+  struct PrintToStringParamName {
+    template <class ParamType>
+    std::string operator()(
+        const testing::TestParamInfo<ParamType>& info) const {
+      return base::StringPrintf(
+          "%s_%s", VideoPixelFormatToString(std::get<0>(info.param)).c_str(),
+          IntelMediaCompressedModifierToString(std::get<1>(info.param))
+              .c_str());
+    }
+  };
+};
+
+constexpr uint64_t kCompressedBufferModifiers[] = {
+    I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS, I915_FORMAT_MOD_4_TILED_MTL_MC_CCS};
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    PlatformVideoFramePoolWithMediaCompressionTest,
+    testing::Combine(testing::ValuesIn(kPixelFormats),
+                     testing::ValuesIn(kCompressedBufferModifiers)),
+    PlatformVideoFramePoolWithMediaCompressionTest::PrintToStringParamName());
+
+TEST_P(PlatformVideoFramePoolWithMediaCompressionTest,
+       CompressedGpuMemoryBufferIsPassed) {
+  const VideoPixelFormat pixel_format = std::get<0>(GetParam());
+  const uint64_t modifier = std::get<1>(GetParam());
   if (pixel_format != PIXEL_FORMAT_NV12 &&
       pixel_format != PIXEL_FORMAT_P016LE) {
     GTEST_SKIP() << "Pixel format doesn't support compressed GPU memory buffer";
@@ -357,11 +389,11 @@
   const auto fourcc = Fourcc::FromVideoPixelFormat(pixel_format);
   ASSERT_TRUE(fourcc.has_value());
 
-  SetCreateFrameCB(
-      base::BindRepeating(&CreateChromeOSCompressedGpuMemoryBufferVideoFrame));
-  ASSERT_TRUE(Initialize(fourcc.value()));
+  SetCreateFrameCB(base::BindRepeating(
+      &CreateChromeOSCompressedGpuMemoryBufferVideoFrame, modifier));
 
-  EXPECT_EQ(layout_->modifier(), I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS);
+  ASSERT_TRUE(Initialize(fourcc.value()));
+  EXPECT_EQ(layout_->modifier(), modifier);
   constexpr size_t kExpectedNumberOfPlanes = 4u;
   EXPECT_EQ(layout_->planes().size(), kExpectedNumberOfPlanes);
   scoped_refptr<VideoFrame> frame = GetFrame(10);
diff --git a/media/gpu/chromeos/vulkan_image_processor_backend.cc b/media/gpu/chromeos/vulkan_image_processor.cc
similarity index 73%
rename from media/gpu/chromeos/vulkan_image_processor_backend.cc
rename to media/gpu/chromeos/vulkan_image_processor.cc
index f7c65b1..b0b6cc94 100644
--- a/media/gpu/chromeos/vulkan_image_processor_backend.cc
+++ b/media/gpu/chromeos/vulkan_image_processor.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/gpu/chromeos/vulkan_image_processor_backend.h"
+#include "media/gpu/chromeos/vulkan_image_processor.h"
 
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
@@ -18,7 +18,7 @@
 
 namespace media {
 
-class VulkanImageProcessorBackend::VulkanRenderPass {
+class VulkanImageProcessor::VulkanRenderPass {
  public:
   VulkanRenderPass(const VulkanRenderPass&) = delete;
   VulkanRenderPass& operator=(const VulkanRenderPass&) = delete;
@@ -38,7 +38,7 @@
   const VkDevice logical_device_;
 };
 
-class VulkanImageProcessorBackend::VulkanShader {
+class VulkanImageProcessor::VulkanShader {
  public:
   VulkanShader(const VulkanShader&) = delete;
   VulkanShader& operator=(const VulkanShader&) = delete;
@@ -59,7 +59,7 @@
   const VkDevice logical_device_;
 };
 
-class VulkanImageProcessorBackend::VulkanPipeline {
+class VulkanImageProcessor::VulkanPipeline {
  public:
   VulkanPipeline(const VulkanPipeline&) = delete;
   VulkanPipeline& operator=(const VulkanPipeline&) = delete;
@@ -73,7 +73,6 @@
       const std::vector<VkDescriptorSetLayoutBinding>& ubo_bindings,
       std::unique_ptr<VulkanShader> vert_shader,
       std::unique_ptr<VulkanShader> frag_shader,
-      const gfx::Size& output_size,
       size_t push_constants_size,
       VkRenderPass render_pass,
       VkDevice logical_device);
@@ -99,7 +98,7 @@
   const VkDevice logical_device_;
 };
 
-class VulkanImageProcessorBackend::VulkanDescriptorPool {
+class VulkanImageProcessor::VulkanDescriptorPool {
  public:
   VulkanDescriptorPool(const VulkanDescriptorPool&) = delete;
   VulkanDescriptorPool& operator=(const VulkanDescriptorPool&) = delete;
@@ -125,7 +124,7 @@
   const VkDevice logical_device_;
 };
 
-class VulkanImageProcessorBackend::VulkanDeviceQueueWrapper {
+class VulkanImageProcessor::VulkanDeviceQueueWrapper {
  public:
   VulkanDeviceQueueWrapper(const VulkanDeviceQueueWrapper&) = delete;
   VulkanDeviceQueueWrapper& operator=(const VulkanDeviceQueueWrapper&) = delete;
@@ -149,7 +148,7 @@
   const std::unique_ptr<gpu::VulkanDeviceQueue> vulkan_device_queue_;
 };
 
-class VulkanImageProcessorBackend::VulkanCommandPoolWrapper {
+class VulkanImageProcessor::VulkanCommandPoolWrapper {
  public:
   VulkanCommandPoolWrapper(const VulkanCommandPoolWrapper&) = delete;
   VulkanCommandPoolWrapper& operator=(const VulkanCommandPoolWrapper&) = delete;
@@ -159,18 +158,16 @@
   static std::unique_ptr<VulkanCommandPoolWrapper> Create(
       gpu::VulkanDeviceQueue* device_queue);
 
-  gpu::VulkanCommandBuffer* GetPrimaryCommandBuffer();
+  std::unique_ptr<gpu::VulkanCommandBuffer> CreatePrimaryCommandBuffer();
 
  private:
   VulkanCommandPoolWrapper(
-      std::unique_ptr<gpu::VulkanCommandPool> command_pool,
-      std::unique_ptr<gpu::VulkanCommandBuffer> primary_command_buf);
+      std::unique_ptr<gpu::VulkanCommandPool> command_pool);
 
   const std::unique_ptr<gpu::VulkanCommandPool> command_pool_;
-  std::unique_ptr<gpu::VulkanCommandBuffer> primary_command_buf_;
 };
 
-class VulkanImageProcessorBackend::VulkanTextureImage {
+class VulkanImageProcessor::VulkanTextureImage {
  public:
   VulkanTextureImage(const VulkanTextureImage&) = delete;
   VulkanTextureImage& operator=(const VulkanTextureImage&) = delete;
@@ -210,22 +207,22 @@
   const VkDevice logical_device_;
 };
 
-VulkanImageProcessorBackend::VulkanRenderPass::VulkanRenderPass(
+VulkanImageProcessor::VulkanRenderPass::VulkanRenderPass(
     VkDevice logical_device,
     VkRenderPass render_pass)
     : render_pass_(render_pass), logical_device_(logical_device) {}
 
-VulkanImageProcessorBackend::VulkanRenderPass::~VulkanRenderPass() {
+VulkanImageProcessor::VulkanRenderPass::~VulkanRenderPass() {
   vkDestroyRenderPass(logical_device_, render_pass_, nullptr);
 }
 
-VkRenderPass VulkanImageProcessorBackend::VulkanRenderPass::Get() {
+VkRenderPass VulkanImageProcessor::VulkanRenderPass::Get() {
   return render_pass_;
 }
 
-std::unique_ptr<VulkanImageProcessorBackend::VulkanRenderPass>
-VulkanImageProcessorBackend::VulkanRenderPass::Create(VkFormat format,
-                                                      VkDevice logical_device) {
+std::unique_ptr<VulkanImageProcessor::VulkanRenderPass>
+VulkanImageProcessor::VulkanRenderPass::Create(VkFormat format,
+                                               VkDevice logical_device) {
   VkAttachmentDescription color_attachment{};
   color_attachment.format = format;
   color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
@@ -261,22 +258,22 @@
   return base::WrapUnique(new VulkanRenderPass(logical_device, render_pass));
 }
 
-VulkanImageProcessorBackend::VulkanShader::VulkanShader(VkDevice logical_device,
-                                                        VkShaderModule shader)
+VulkanImageProcessor::VulkanShader::VulkanShader(VkDevice logical_device,
+                                                 VkShaderModule shader)
     : shader_(shader), logical_device_(logical_device) {}
 
-VulkanImageProcessorBackend::VulkanShader::~VulkanShader() {
+VulkanImageProcessor::VulkanShader::~VulkanShader() {
   vkDestroyShaderModule(logical_device_, shader_, nullptr);
 }
 
-VkShaderModule VulkanImageProcessorBackend::VulkanShader::Get() {
+VkShaderModule VulkanImageProcessor::VulkanShader::Get() {
   return shader_;
 }
 
-std::unique_ptr<VulkanImageProcessorBackend::VulkanShader>
-VulkanImageProcessorBackend::VulkanShader::Create(const uint32_t* shader_code,
-                                                  size_t shader_code_size,
-                                                  VkDevice logical_device) {
+std::unique_ptr<VulkanImageProcessor::VulkanShader>
+VulkanImageProcessor::VulkanShader::Create(const uint32_t* shader_code,
+                                           size_t shader_code_size,
+                                           VkDevice logical_device) {
   VkShaderModuleCreateInfo shader_info{};
   shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
   shader_info.codeSize = shader_code_size;
@@ -292,12 +289,12 @@
   return base::WrapUnique(new VulkanShader(logical_device, shader));
 }
 
-VulkanImageProcessorBackend::VulkanPipeline::VulkanPipeline(
+VulkanImageProcessor::VulkanPipeline::VulkanPipeline(
     VkPipeline pipeline,
     VkDescriptorSetLayout descriptor_set_layout,
     VkPipelineLayout pipeline_layout,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanShader> vert_shader,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanShader> frag_shader,
+    std::unique_ptr<VulkanImageProcessor::VulkanShader> vert_shader,
+    std::unique_ptr<VulkanImageProcessor::VulkanShader> frag_shader,
     VkDevice logical_device)
     : pipeline_(pipeline),
       descriptor_set_layout_(descriptor_set_layout),
@@ -306,36 +303,34 @@
       frag_shader_(std::move(frag_shader)),
       logical_device_(logical_device) {}
 
-VulkanImageProcessorBackend::VulkanPipeline::~VulkanPipeline() {
+VulkanImageProcessor::VulkanPipeline::~VulkanPipeline() {
   vkDestroyPipeline(logical_device_, pipeline_, nullptr);
   vkDestroyPipelineLayout(logical_device_, pipeline_layout_, nullptr);
   vkDestroyDescriptorSetLayout(logical_device_, descriptor_set_layout_,
                                nullptr);
 }
 
-VkPipeline VulkanImageProcessorBackend::VulkanPipeline::Get() {
+VkPipeline VulkanImageProcessor::VulkanPipeline::Get() {
   return pipeline_;
 }
 
 VkDescriptorSetLayout
-VulkanImageProcessorBackend::VulkanPipeline::GetDescriptorSetLayout() {
+VulkanImageProcessor::VulkanPipeline::GetDescriptorSetLayout() {
   return descriptor_set_layout_;
 }
 
-VkPipelineLayout
-VulkanImageProcessorBackend::VulkanPipeline::GetPipelineLayout() {
+VkPipelineLayout VulkanImageProcessor::VulkanPipeline::GetPipelineLayout() {
   return pipeline_layout_;
 }
 
-std::unique_ptr<VulkanImageProcessorBackend::VulkanPipeline>
-VulkanImageProcessorBackend::VulkanPipeline::Create(
+std::unique_ptr<VulkanImageProcessor::VulkanPipeline>
+VulkanImageProcessor::VulkanPipeline::Create(
     const std::vector<VkVertexInputBindingDescription>& binding_descriptions,
     const std::vector<VkVertexInputAttributeDescription>&
         attribute_descriptions,
     const std::vector<VkDescriptorSetLayoutBinding>& ubo_bindings,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanShader> vert_shader,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanShader> frag_shader,
-    const gfx::Size& output_size,
+    std::unique_ptr<VulkanImageProcessor::VulkanShader> vert_shader,
+    std::unique_ptr<VulkanImageProcessor::VulkanShader> frag_shader,
     size_t push_constants_size,
     VkRenderPass render_pass,
     VkDevice logical_device) {
@@ -407,15 +402,14 @@
   VkViewport viewport{};
   viewport.x = 0.0f;
   viewport.y = 0.0f;
-  viewport.width = (float)output_size.width();
-  viewport.height = (float)output_size.height();
+  viewport.width = 1.0;
+  viewport.height = 1.0;
   viewport.minDepth = 0.0f;
   viewport.maxDepth = 1.0f;
 
   VkRect2D scissor{};
   scissor.offset = {0, 0};
-  scissor.extent = {static_cast<uint32_t>(output_size.width()),
-                    static_cast<uint32_t>(output_size.height())};
+  scissor.extent = {1, 1};
 
   VkPipelineViewportStateCreateInfo viewport_state{};
   viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@@ -491,7 +485,7 @@
       std::move(frag_shader), logical_device));
 }
 
-VulkanImageProcessorBackend::VulkanDescriptorPool::VulkanDescriptorPool(
+VulkanImageProcessor::VulkanDescriptorPool::VulkanDescriptorPool(
     std::vector<VkDescriptorSet> descriptor_sets,
     VkDescriptorPool descriptor_pool,
     VkDevice logical_device)
@@ -499,17 +493,17 @@
       descriptor_pool_(descriptor_pool),
       logical_device_(logical_device) {}
 
-VulkanImageProcessorBackend::VulkanDescriptorPool::~VulkanDescriptorPool() {
+VulkanImageProcessor::VulkanDescriptorPool::~VulkanDescriptorPool() {
   vkDestroyDescriptorPool(logical_device_, descriptor_pool_, nullptr);
 }
 
 const std::vector<VkDescriptorSet>&
-VulkanImageProcessorBackend::VulkanDescriptorPool::Get() {
+VulkanImageProcessor::VulkanDescriptorPool::Get() {
   return descriptor_sets_;
 }
 
-std::unique_ptr<VulkanImageProcessorBackend::VulkanDescriptorPool>
-VulkanImageProcessorBackend::VulkanDescriptorPool::Create(
+std::unique_ptr<VulkanImageProcessor::VulkanDescriptorPool>
+VulkanImageProcessor::VulkanDescriptorPool::Create(
     size_t num_descriptor_sets,
     std::vector<VkDescriptorType> descriptor_types,
     VkDescriptorSetLayout descriptor_set_layout,
@@ -554,7 +548,7 @@
       descriptor_sets, descriptor_pool, logical_device));
 }
 
-VulkanImageProcessorBackend::VulkanTextureImage::VulkanTextureImage(
+VulkanImageProcessor::VulkanTextureImage::VulkanTextureImage(
     std::unique_ptr<gpu::VulkanImage> image,
     const std::vector<VkImageView>& image_views,
     const std::vector<VkFramebuffer>& framebuffers,
@@ -566,7 +560,7 @@
       current_layout_(initial_layout),
       logical_device_(logical_device) {}
 
-VulkanImageProcessorBackend::VulkanTextureImage::~VulkanTextureImage() {
+VulkanImageProcessor::VulkanTextureImage::~VulkanTextureImage() {
   for (VkFramebuffer framebuffer : framebuffers_) {
     vkDestroyFramebuffer(logical_device_, framebuffer, nullptr);
   }
@@ -578,21 +572,21 @@
   image_->Destroy();
 }
 
-VkImage VulkanImageProcessorBackend::VulkanTextureImage::GetImage() {
+VkImage VulkanImageProcessor::VulkanTextureImage::GetImage() {
   return image_->image();
 }
 
 const std::vector<VkImageView>&
-VulkanImageProcessorBackend::VulkanTextureImage::GetImageViews() {
+VulkanImageProcessor::VulkanTextureImage::GetImageViews() {
   return image_views_;
 }
 
 const std::vector<VkFramebuffer>&
-VulkanImageProcessorBackend::VulkanTextureImage::GetFramebuffers() {
+VulkanImageProcessor::VulkanTextureImage::GetFramebuffers() {
   return framebuffers_;
 }
 
-void VulkanImageProcessorBackend::VulkanTextureImage::TransitionImageLayout(
+void VulkanImageProcessor::VulkanTextureImage::TransitionImageLayout(
     gpu::VulkanCommandBuffer* command_buf,
     VkImageLayout new_layout,
     uint32_t src_queue_family_index,
@@ -608,8 +602,8 @@
   current_layout_ = new_layout;
 }
 
-std::unique_ptr<VulkanImageProcessorBackend::VulkanTextureImage>
-VulkanImageProcessorBackend::VulkanTextureImage::Create(
+std::unique_ptr<VulkanImageProcessor::VulkanTextureImage>
+VulkanImageProcessor::VulkanTextureImage::Create(
     std::unique_ptr<gpu::VulkanImage> image,
     const std::vector<VkFormat>& formats,
     const std::vector<gfx::Size>& sizes,
@@ -672,47 +666,43 @@
       logical_device));
 }
 
-VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::VulkanDeviceQueueWrapper(
+VulkanImageProcessor::VulkanDeviceQueueWrapper::VulkanDeviceQueueWrapper(
     std::unique_ptr<gpu::VulkanDeviceQueue> vulkan_device_queue)
     : vulkan_device_queue_(std::move(vulkan_device_queue)) {}
 
-VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::
-    ~VulkanDeviceQueueWrapper() {
+VulkanImageProcessor::VulkanDeviceQueueWrapper::~VulkanDeviceQueueWrapper() {
   vulkan_device_queue_->Destroy();
 }
 
 gpu::VulkanDeviceQueue*
-VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::GetVulkanDeviceQueue() {
+VulkanImageProcessor::VulkanDeviceQueueWrapper::GetVulkanDeviceQueue() {
   return vulkan_device_queue_.get();
 }
 
-VkPhysicalDevice VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::
-    GetVulkanPhysicalDevice() {
+VkPhysicalDevice
+VulkanImageProcessor::VulkanDeviceQueueWrapper::GetVulkanPhysicalDevice() {
   return vulkan_device_queue_->GetVulkanPhysicalDevice();
 }
 
-VkPhysicalDeviceProperties VulkanImageProcessorBackend::
-    VulkanDeviceQueueWrapper::GetVulkanPhysicalDeviceProperties() {
+VkPhysicalDeviceProperties VulkanImageProcessor::VulkanDeviceQueueWrapper::
+    GetVulkanPhysicalDeviceProperties() {
   return vulkan_device_queue_->vk_physical_device_properties();
 }
 
-VkDevice
-VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::GetVulkanDevice() {
+VkDevice VulkanImageProcessor::VulkanDeviceQueueWrapper::GetVulkanDevice() {
   return vulkan_device_queue_->GetVulkanDevice();
 }
 
-VkQueue
-VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::GetVulkanQueue() {
+VkQueue VulkanImageProcessor::VulkanDeviceQueueWrapper::GetVulkanQueue() {
   return vulkan_device_queue_->GetVulkanQueue();
 }
 
-uint32_t
-VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::GetVulkanQueueIndex() {
+uint32_t VulkanImageProcessor::VulkanDeviceQueueWrapper::GetVulkanQueueIndex() {
   return vulkan_device_queue_->GetVulkanQueueIndex();
 }
 
-std::unique_ptr<VulkanImageProcessorBackend::VulkanDeviceQueueWrapper>
-VulkanImageProcessorBackend::VulkanDeviceQueueWrapper::Create(
+std::unique_ptr<VulkanImageProcessor::VulkanDeviceQueueWrapper>
+VulkanImageProcessor::VulkanDeviceQueueWrapper::Create(
     gpu::VulkanImplementation* implementation) {
   auto vulkan_device_queue = CreateVulkanDeviceQueue(
       implementation,
@@ -722,61 +712,43 @@
       new VulkanDeviceQueueWrapper(std::move(vulkan_device_queue)));
 }
 
-VulkanImageProcessorBackend::VulkanCommandPoolWrapper::VulkanCommandPoolWrapper(
-    std::unique_ptr<gpu::VulkanCommandPool> command_pool,
-    std::unique_ptr<gpu::VulkanCommandBuffer> primary_command_buf)
-    : command_pool_(std::move(command_pool)),
-      primary_command_buf_(std::move(primary_command_buf)) {}
+VulkanImageProcessor::VulkanCommandPoolWrapper::VulkanCommandPoolWrapper(
+    std::unique_ptr<gpu::VulkanCommandPool> command_pool)
+    : command_pool_(std::move(command_pool)) {}
 
-VulkanImageProcessorBackend::VulkanCommandPoolWrapper::
-    ~VulkanCommandPoolWrapper() {
-  primary_command_buf_->Destroy();
-  primary_command_buf_ = nullptr;
+VulkanImageProcessor::VulkanCommandPoolWrapper::~VulkanCommandPoolWrapper() {
   command_pool_->Destroy();
 }
 
-gpu::VulkanCommandBuffer* VulkanImageProcessorBackend::
-    VulkanCommandPoolWrapper::GetPrimaryCommandBuffer() {
-  return primary_command_buf_.get();
+std::unique_ptr<gpu::VulkanCommandBuffer>
+VulkanImageProcessor::VulkanCommandPoolWrapper::CreatePrimaryCommandBuffer() {
+  return command_pool_->CreatePrimaryCommandBuffer();
 }
 
-std::unique_ptr<VulkanImageProcessorBackend::VulkanCommandPoolWrapper>
-VulkanImageProcessorBackend::VulkanCommandPoolWrapper::Create(
+std::unique_ptr<VulkanImageProcessor::VulkanCommandPoolWrapper>
+VulkanImageProcessor::VulkanCommandPoolWrapper::Create(
     gpu::VulkanDeviceQueue* device_queue) {
   std::unique_ptr<gpu::VulkanCommandPool> command_pool =
       base::WrapUnique(new gpu::VulkanCommandPool(device_queue));
   command_pool->Initialize();
-  std::unique_ptr<gpu::VulkanCommandBuffer> primary_command_buf =
-      command_pool->CreatePrimaryCommandBuffer();
 
-  return base::WrapUnique(new VulkanCommandPoolWrapper(
-      std::move(command_pool), std::move(primary_command_buf)));
+  return base::WrapUnique(
+      new VulkanCommandPoolWrapper(std::move(command_pool)));
 }
 
-VulkanImageProcessorBackend::VulkanImageProcessorBackend(
-    gfx::Size input_size,
-    gfx::Size output_size,
+VulkanImageProcessor::VulkanImageProcessor(
+    BackingCB input_backing_cb,
+    BackingCB output_backing_cb,
     std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanDeviceQueueWrapper>
+    std::unique_ptr<VulkanImageProcessor::VulkanDeviceQueueWrapper>
         vulkan_device_queue,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanCommandPoolWrapper>
+    std::unique_ptr<VulkanImageProcessor::VulkanCommandPoolWrapper>
         command_pool,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanRenderPass> render_pass,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanPipeline> pipeline,
-    std::unique_ptr<VulkanImageProcessorBackend::VulkanDescriptorPool>
-        descriptor_pool,
-    const PortConfig& input_config,
-    const PortConfig& output_config,
-    OutputMode output_mode,
-    ErrorCB error_cb)
-    : ImageProcessorBackend(input_config,
-                            output_config,
-                            output_mode,
-                            std::move(error_cb),
-                            base::ThreadPool::CreateSingleThreadTaskRunner(
-                                {base::TaskPriority::USER_VISIBLE})),
-      input_size_(input_size),
-      output_size_(output_size),
+    std::unique_ptr<VulkanImageProcessor::VulkanRenderPass> render_pass,
+    std::unique_ptr<VulkanImageProcessor::VulkanPipeline> pipeline,
+    std::unique_ptr<VulkanImageProcessor::VulkanDescriptorPool> descriptor_pool)
+    : input_backing_cb_(input_backing_cb),
+      output_backing_cb_(output_backing_cb),
       vulkan_implementation_(std::move(vulkan_implementation)),
       vulkan_device_queue_(std::move(vulkan_device_queue)),
       command_pool_(std::move(command_pool)),
@@ -784,57 +756,17 @@
       pipeline_(std::move(pipeline)),
       descriptor_pool_(std::move(descriptor_pool)) {}
 
-VulkanImageProcessorBackend::~VulkanImageProcessorBackend() = default;
-
-std::string VulkanImageProcessorBackend::type() const {
-  return "VukanImageProcessor";
+VulkanImageProcessor::~VulkanImageProcessor() {
+  // Make sure there aren't any pending cleanup jobs before we start destroying
+  // stuff.
+  vulkan_device_queue_->GetVulkanDeviceQueue()
+      ->GetFenceHelper()
+      ->PerformImmediateCleanup();
 }
 
-bool VulkanImageProcessorBackend::IsSupported(const PortConfig& input_config,
-                                              const PortConfig& output_config,
-                                              OutputMode output_mode) {
-  if (output_mode != OutputMode::IMPORT) {
-    return false;
-  }
-
-  if (!input_config.visible_rect.origin().IsOrigin() ||
-      !output_config.visible_rect.origin().IsOrigin()) {
-    VLOGF(2)
-        << "The VulkanImageProcessorBackend does not support transposition.";
-    return false;
-  }
-
-  if (input_config.fourcc != Fourcc(Fourcc::MM21) ||
-      output_config.fourcc != Fourcc(Fourcc::AR24)) {
-    VLOGF(2) << "The VulkanImageProcessorBackend only supports MM21->AR24 "
-                "conversion.";
-    return false;
-  }
-
-  // TODO(b/251458823): We want to support arbitrary scaling long term, this is
-  // just a short term hack because we need to revamp our bilinear filtering
-  // algorithm.
-  if (output_config.visible_rect.width() <=
-          input_config.visible_rect.width() / 2 ||
-      output_config.visible_rect.height() <=
-          input_config.visible_rect.height() / 2) {
-    VLOGF(2) << "The VulkanImageProcessorBackend cannot downsample by more "
-                "than 2 in each dimension.";
-    return false;
-  }
-
-  return true;
-}
-
-std::unique_ptr<VulkanImageProcessorBackend>
-VulkanImageProcessorBackend::Create(const PortConfig& input_config,
-                                    const PortConfig& output_config,
-                                    OutputMode output_mode,
-                                    ErrorCB error_cb) {
-  if (!IsSupported(input_config, output_config, output_mode)) {
-    return nullptr;
-  }
-
+std::unique_ptr<VulkanImageProcessor> VulkanImageProcessor::Create(
+    BackingCB input_backing_cb,
+    BackingCB output_backing_cb) {
   auto vulkan_implementation = gpu::CreateVulkanImplementation(
       /*use_swiftshader=*/false, /*allow_protected_memory=*/false);
   vulkan_implementation->InitializeVulkanInstance(/*using_surface=*/false);
@@ -886,8 +818,8 @@
   auto pipeline = VulkanPipeline::Create(
       binding_descriptions, attribute_descriptions, descriptor_bindings,
       std::move(vert_shader), std::move(frag_shader),
-      output_config.visible_rect.size(), 2 * 2 * sizeof(int),
-      render_pass->Get(), vulkan_device_queue->GetVulkanDevice());
+      2 * 2 * sizeof(int), render_pass->Get(),
+      vulkan_device_queue->GetVulkanDevice());
   if (!pipeline) {
     return nullptr;
   }
@@ -900,44 +832,46 @@
     return nullptr;
   }
 
-  return base::WrapUnique(new VulkanImageProcessorBackend(
-      input_config.size, output_config.visible_rect.size(),
-      std::move(vulkan_implementation), std::move(vulkan_device_queue),
-      std::move(command_pool), std::move(render_pass), std::move(pipeline),
-      std::move(descriptor_pool), input_config, output_config, output_mode,
-      std::move(error_cb)));
+  return base::WrapUnique(new VulkanImageProcessor(
+      input_backing_cb, output_backing_cb, std::move(vulkan_implementation),
+      std::move(vulkan_device_queue), std::move(command_pool),
+      std::move(render_pass), std::move(pipeline), std::move(descriptor_pool)));
 }
 
-void VulkanImageProcessorBackend::Process(
-    scoped_refptr<VideoFrame> input_frame,
-    scoped_refptr<VideoFrame> output_frame,
-    FrameReadyCB cb) {
+gpu::SemaphoreHandle VulkanImageProcessor::Process(
+    gpu::Mailbox in_mailbox,
+    const gfx::Size& input_coded_size,
+    const gfx::Size& input_visible_size,
+    ReleaseCB input_release_cb,
+    gpu::Mailbox out_mailbox,
+    const gfx::Size& output_coded_size,
+    const gfx::Size& output_visible_size,
+    absl::optional<gpu::SemaphoreHandle> in_semaphore_handle) {
+  auto in_gmb = input_backing_cb_.Run(in_mailbox);
+  auto out_gmb = output_backing_cb_.Run(out_mailbox);
+
   // Import output_frame into vulkan.
-  gfx::GpuMemoryBufferHandle out_gmb =
-      CreateGpuMemoryBufferHandle(output_frame.get());
   auto out_image = vulkan_implementation_->CreateImageFromGpuMemoryHandle(
       vulkan_device_queue_->GetVulkanDeviceQueue(), std::move(out_gmb),
-      output_size_, VK_FORMAT_B8G8R8A8_UNORM,
+      output_coded_size, VK_FORMAT_B8G8R8A8_UNORM,
       gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT709,
                       gfx::ColorSpace::TransferID::BT709));
   auto out_texture = VulkanTextureImage::Create(
-      std::move(out_image), {VK_FORMAT_B8G8R8A8_UNORM}, {output_size_},
+      std::move(out_image), {VK_FORMAT_B8G8R8A8_UNORM}, {output_coded_size},
       {VK_IMAGE_ASPECT_COLOR_BIT},
       /*is_framebuffer=*/true, render_pass_->Get(),
       vulkan_device_queue_->GetVulkanDevice());
 
-  gfx::GpuMemoryBufferHandle in_gmb =
-      CreateGpuMemoryBufferHandle(input_frame.get());
   auto in_image = vulkan_implementation_->CreateImageFromGpuMemoryHandle(
       vulkan_device_queue_->GetVulkanDeviceQueue(), std::move(in_gmb),
-      input_size_, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
+      input_coded_size, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
       gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT709,
                       gfx::ColorSpace::TransferID::BT709));
-  gfx::Size uv_plane_size =
-      gfx::Size((input_size_.width() + 1) / 2, (input_size_.height() + 1) / 2);
+  gfx::Size uv_plane_size = gfx::Size((input_coded_size.width() + 1) / 2,
+                                      (input_coded_size.height() + 1) / 2);
   auto in_texture = VulkanTextureImage::Create(
       std::move(in_image), {VK_FORMAT_R8_UNORM, VK_FORMAT_R8G8_UNORM},
-      {input_size_, uv_plane_size},
+      {input_coded_size, uv_plane_size},
       {VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT},
       /*is_framebuffer=*/false, render_pass_->Get(),
       vulkan_device_queue_->GetVulkanDevice());
@@ -968,26 +902,26 @@
                          descriptor_write.size(), descriptor_write.data(), 0,
                          nullptr);
 
+  auto command_buf = command_pool_->CreatePrimaryCommandBuffer();
   {
-    gpu::ScopedSingleUseCommandBufferRecorder record(
-        *command_pool_->GetPrimaryCommandBuffer());
+    gpu::ScopedSingleUseCommandBufferRecorder record(*command_buf);
 
     VkViewport viewport{};
     viewport.x = 0.0f;
     viewport.y = 0.0f;
-    viewport.width = static_cast<float>(output_size_.width());
-    viewport.height = static_cast<float>(output_size_.height());
+    viewport.width = static_cast<float>(output_visible_size.width());
+    viewport.height = static_cast<float>(output_visible_size.height());
     viewport.minDepth = 0.0f;
     viewport.maxDepth = 1.0f;
     vkCmdSetViewport(record.handle(), 0, 1, &viewport);
 
     VkRect2D scissor{};
     scissor.offset = {0, 0};
-    scissor.extent.width = static_cast<float>(output_size_.width());
-    scissor.extent.height = static_cast<float>(output_size_.height());
+    scissor.extent.width = static_cast<float>(output_visible_size.width());
+    scissor.extent.height = static_cast<float>(output_visible_size.height());
     vkCmdSetScissor(record.handle(), 0, 1, &scissor);
 
-    in_texture->TransitionImageLayout(command_pool_->GetPrimaryCommandBuffer(),
+    in_texture->TransitionImageLayout(command_buf.get(),
                                       VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
 
     VkRenderPassBeginInfo render_pass_info{};
@@ -996,8 +930,8 @@
     render_pass_info.framebuffer = out_texture->GetFramebuffers()[0];
     render_pass_info.renderArea.offset = {0, 0};
     render_pass_info.renderArea.extent = {
-        static_cast<uint32_t>(output_size_.width()),
-        static_cast<uint32_t>(output_size_.height())};
+        static_cast<uint32_t>(output_visible_size.width()),
+        static_cast<uint32_t>(output_visible_size.height())};
     VkClearValue clear_color = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
     render_pass_info.clearValueCount = 1;
     render_pass_info.pClearValues = &clear_color;
@@ -1012,10 +946,9 @@
                             pipeline_->GetPipelineLayout(), 0, 1,
                             descriptor_pool_->Get().data(), 0, nullptr);
 
-    int push_constants[4] = {input_config_.size.width(),
-                             input_config_.size.height(),
-                             input_config_.visible_rect.width(),
-                             input_config_.visible_rect.height()};
+    int push_constants[4] = {
+        input_coded_size.width(), input_coded_size.height(),
+        input_visible_size.width(), input_visible_size.height()};
     vkCmdPushConstants(record.handle(), pipeline_->GetPipelineLayout(),
                        VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_constants),
                        push_constants);
@@ -1025,14 +958,42 @@
     vkCmdEndRenderPass(record.handle());
 
     out_texture->TransitionImageLayout(
-        command_pool_->GetPrimaryCommandBuffer(), VK_IMAGE_LAYOUT_UNDEFINED,
-        VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_FOREIGN_EXT);
+        command_buf.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_QUEUE_FAMILY_IGNORED,
+        VK_QUEUE_FAMILY_FOREIGN_EXT);
   }
-  command_pool_->GetPrimaryCommandBuffer()->Submit(0, nullptr, 0, nullptr);
-  // TODO(b/251458823): We may not need to block here, see if we can use
-  // a semaphore or a fence.
-  command_pool_->GetPrimaryCommandBuffer()->Wait(UINT64_MAX);
-  std::move(cb).Run(std::move(output_frame));
+
+  auto* fence_helper =
+      vulkan_device_queue_->GetVulkanDeviceQueue()->GetFenceHelper();
+  VkSemaphore out_semaphore = vulkan_implementation_->CreateExternalSemaphore(
+      vulkan_device_queue_->GetVulkanDevice());
+  if (in_semaphore_handle) {
+    VkSemaphore in_semaphore = vulkan_implementation_->ImportSemaphoreHandle(
+        vulkan_device_queue_->GetVulkanDevice(),
+        std::move(*in_semaphore_handle));
+
+    command_buf->Submit(1, &in_semaphore, 1, &out_semaphore);
+
+    fence_helper->EnqueueSemaphoresCleanupForSubmittedWork(
+        {out_semaphore, in_semaphore});
+  } else {
+    command_buf->Submit(0, nullptr, 1, &out_semaphore);
+
+    fence_helper->EnqueueSemaphoresCleanupForSubmittedWork({out_semaphore});
+  }
+
+  fence_helper->EnqueueVulkanObjectCleanupForSubmittedWork(
+      std::move(command_buf));
+  fence_helper->EnqueueCleanupTaskForSubmittedWork(base::BindOnce(
+      [](std::unique_ptr<VulkanTextureImage> in_texture,
+         std::unique_ptr<VulkanTextureImage> out_texture,
+         gpu::Mailbox in_mailbox, ReleaseCB input_release_cb,
+         gpu::VulkanDeviceQueue* device_queue,
+         bool device_lost) { std::move(input_release_cb).Run(in_mailbox); },
+      std::move(in_texture), std::move(out_texture), in_mailbox,
+      std::move(input_release_cb)));
+
+  return vulkan_implementation_->GetSemaphoreHandle(
+      vulkan_device_queue_->GetVulkanDevice(), out_semaphore);
 }
 
 }  // namespace media
diff --git a/media/gpu/chromeos/vulkan_image_processor.h b/media/gpu/chromeos/vulkan_image_processor.h
new file mode 100644
index 0000000..9b283be
--- /dev/null
+++ b/media/gpu/chromeos/vulkan_image_processor.h
@@ -0,0 +1,87 @@
+// 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 MEDIA_GPU_CHROMEOS_VULKAN_IMAGE_PROCESSOR_H_
+#define MEDIA_GPU_CHROMEOS_VULKAN_IMAGE_PROCESSOR_H_
+
+#include <vulkan/vulkan_core.h>
+#include <memory>
+#include <vector>
+
+#include "gpu/vulkan/vulkan_command_buffer.h"
+#include "gpu/vulkan/vulkan_command_pool.h"
+#include "gpu/vulkan/vulkan_device_queue.h"
+#include "gpu/vulkan/vulkan_image.h"
+#include "gpu/vulkan/vulkan_implementation.h"
+#include "media/gpu/chromeos/image_processor_backend.h"
+#include "media/gpu/media_gpu_export.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace media {
+
+// An image processor using Vulkan to perform MM21 detiling.
+class MEDIA_GPU_EXPORT VulkanImageProcessor {
+ public:
+  // Abstraction for the mapping from Mailbox to GpuMemoryBuffer.
+  using BackingCB =
+      base::RepeatingCallback<gfx::GpuMemoryBufferHandle(gpu::Mailbox&)>;
+  // Abstraction for the recycling of the input frame.
+  using ReleaseCB = base::OnceCallback<void(gpu::Mailbox&)>;
+
+  VulkanImageProcessor(const VulkanImageProcessor&) = delete;
+  VulkanImageProcessor& operator=(const VulkanImageProcessor&) = delete;
+
+  ~VulkanImageProcessor();
+
+  static std::unique_ptr<VulkanImageProcessor> Create(
+      BackingCB input_backing_cb,
+      BackingCB output_backing_cb);
+
+  gpu::SemaphoreHandle Process(
+      gpu::Mailbox in_mailbox,
+      const gfx::Size& input_coded_size,
+      const gfx::Size& input_visible_size,
+      ReleaseCB input_release_cb,
+      gpu::Mailbox out_mailbox,
+      const gfx::Size& output_coded_size,
+      const gfx::Size& output_visible_size,
+      absl::optional<gpu::SemaphoreHandle> in_semaphore_handle);
+
+ private:
+  class VulkanRenderPass;
+  class VulkanShader;
+  class VulkanPipeline;
+  class VulkanDescriptorPool;
+  class VulkanDeviceQueueWrapper;
+  class VulkanCommandBufferWrapper;
+  class VulkanCommandPoolWrapper;
+  class VulkanTextureImage;
+
+  VulkanImageProcessor(
+      BackingCB input_backing_cb,
+      BackingCB output_backing_cb,
+      std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation,
+      std::unique_ptr<VulkanImageProcessor::VulkanDeviceQueueWrapper>
+          vulkan_device_queue,
+      std::unique_ptr<VulkanImageProcessor::VulkanCommandPoolWrapper>
+          command_pool,
+      std::unique_ptr<VulkanImageProcessor::VulkanRenderPass> render_pass,
+      std::unique_ptr<VulkanImageProcessor::VulkanPipeline> pipeline,
+      std::unique_ptr<VulkanImageProcessor::VulkanDescriptorPool>
+          descriptor_pool);
+
+  BackingCB input_backing_cb_;
+  BackingCB output_backing_cb_;
+  std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation_;
+  std::unique_ptr<VulkanImageProcessor::VulkanDeviceQueueWrapper>
+      vulkan_device_queue_;
+  std::unique_ptr<VulkanImageProcessor::VulkanCommandPoolWrapper> command_pool_;
+  std::unique_ptr<VulkanImageProcessor::VulkanRenderPass> render_pass_;
+  std::unique_ptr<VulkanImageProcessor::VulkanPipeline> pipeline_;
+  std::unique_ptr<VulkanImageProcessor::VulkanDescriptorPool> descriptor_pool_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_GPU_CHROMEOS_VULKAN_IMAGE_PROCESSOR_H_
diff --git a/media/gpu/chromeos/vulkan_image_processor_backend.h b/media/gpu/chromeos/vulkan_image_processor_backend.h
deleted file mode 100644
index b70ed07..0000000
--- a/media/gpu/chromeos/vulkan_image_processor_backend.h
+++ /dev/null
@@ -1,91 +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 MEDIA_GPU_CHROMEOS_VULKAN_IMAGE_PROCESSOR_BACKEND_H_
-#define MEDIA_GPU_CHROMEOS_VULKAN_IMAGE_PROCESSOR_BACKEND_H_
-
-#include <vulkan/vulkan_core.h>
-#include <memory>
-#include <vector>
-
-#include "gpu/vulkan/vulkan_command_buffer.h"
-#include "gpu/vulkan/vulkan_command_pool.h"
-#include "gpu/vulkan/vulkan_device_queue.h"
-#include "gpu/vulkan/vulkan_image.h"
-#include "gpu/vulkan/vulkan_implementation.h"
-#include "media/gpu/chromeos/image_processor_backend.h"
-#include "media/gpu/media_gpu_export.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace media {
-
-// An image processor using Vulkan to perform MM21 detiling.
-class MEDIA_GPU_EXPORT VulkanImageProcessorBackend
-    : public ImageProcessorBackend {
- public:
-  VulkanImageProcessorBackend(const VulkanImageProcessorBackend&) = delete;
-  VulkanImageProcessorBackend& operator=(const VulkanImageProcessorBackend&) =
-      delete;
-
-  ~VulkanImageProcessorBackend() override;
-
-  static std::unique_ptr<VulkanImageProcessorBackend> Create(
-      const PortConfig& input_config,
-      const PortConfig& output_config,
-      OutputMode output_mode,
-      ErrorCB error_cb);
-
-  void Process(scoped_refptr<VideoFrame> input_frame,
-               scoped_refptr<VideoFrame> output_frame,
-               FrameReadyCB cb) override;
-
-  std::string type() const override;
-
- private:
-  class VulkanRenderPass;
-  class VulkanShader;
-  class VulkanPipeline;
-  class VulkanDescriptorPool;
-  class VulkanDeviceQueueWrapper;
-  class VulkanCommandPoolWrapper;
-  class VulkanTextureImage;
-
-  VulkanImageProcessorBackend(
-      gfx::Size input_size,
-      gfx::Size output_size,
-      std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation,
-      std::unique_ptr<VulkanImageProcessorBackend::VulkanDeviceQueueWrapper>
-          vulkan_device_queue,
-      std::unique_ptr<VulkanImageProcessorBackend::VulkanCommandPoolWrapper>
-          command_pool,
-      std::unique_ptr<VulkanImageProcessorBackend::VulkanRenderPass>
-          render_pass,
-      std::unique_ptr<VulkanImageProcessorBackend::VulkanPipeline> pipeline,
-      std::unique_ptr<VulkanImageProcessorBackend::VulkanDescriptorPool>
-          descriptor_pool,
-      const PortConfig& input_config,
-      const PortConfig& output_config,
-      OutputMode output_mode,
-      ErrorCB error_cb);
-
-  static bool IsSupported(const PortConfig& input_config,
-                          const PortConfig& output_config,
-                          OutputMode output_mode);
-
-  gfx::Size input_size_;
-  gfx::Size output_size_;
-  std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation_;
-  std::unique_ptr<VulkanImageProcessorBackend::VulkanDeviceQueueWrapper>
-      vulkan_device_queue_;
-  std::unique_ptr<VulkanImageProcessorBackend::VulkanCommandPoolWrapper>
-      command_pool_;
-  std::unique_ptr<VulkanImageProcessorBackend::VulkanRenderPass> render_pass_;
-  std::unique_ptr<VulkanImageProcessorBackend::VulkanPipeline> pipeline_;
-  std::unique_ptr<VulkanImageProcessorBackend::VulkanDescriptorPool>
-      descriptor_pool_;
-};
-
-}  // namespace media
-
-#endif  // MEDIA_GPU_CHROMEOS_VULKAN_IMAGE_PROCESSOR_BACKEND_H_
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc
index 677e910..df9247af7 100644
--- a/net/http/http_server_properties_manager.cc
+++ b/net/http/http_server_properties_manager.cc
@@ -315,10 +315,6 @@
 
   // Set the properties loaded from prefs on |http_server_properties_impl_|.
 
-  // TODO(mmenke): Rename this once more information is stored in this map.
-  UMA_HISTOGRAM_COUNTS_1M("Net.HttpServerProperties.CountOfServers",
-                          (*server_info_map)->size());
-
   UMA_HISTOGRAM_COUNTS_1000("Net.CountOfQuicServerInfos",
                             (*quic_server_info_map)->size());
 
diff --git a/remoting/host/setup/corp_host_starter.cc b/remoting/host/setup/corp_host_starter.cc
index 1adea603..4536009 100644
--- a/remoting/host/setup/corp_host_starter.cc
+++ b/remoting/host/setup/corp_host_starter.cc
@@ -221,7 +221,7 @@
   authorization_code_ = internal::GetAuthorizationCode(*response);
   if (authorization_code_.empty()) {
     LOG(ERROR) << "No authorization code returned by the Directory.";
-    std::move(on_done_).Run(START_ERROR);
+    std::move(on_done_).Run(REGISTRATION_ERROR);
     return;
   }
 
@@ -304,8 +304,7 @@
     LOG(ERROR) << "Failed to start host: " << result;
     // TODO(joedow): Decide whether to delete the host instance or update its
     // offline reason in the Directory instead.
-    // TODO(joedow): Check to see if we need to run on_done here. If so, also
-    // check to see if the OAuth-based HostStarter needs that change as well.
+    std::move(on_done_).Run(START_ERROR);
     return;
   }
   std::move(on_done_).Run(START_COMPLETE);
@@ -352,9 +351,10 @@
     }
   }
 
-  // TODO(joedow): Add more error codes to HostStarter and use them here.
   switch (error_code) {
     case ProtobufHttpStatus::Code::PERMISSION_DENIED:
+      std::move(on_done_).Run(PERMISSION_DENIED);
+      return;
     case ProtobufHttpStatus::Code::UNAUTHENTICATED:
       std::move(on_done_).Run(OAUTH_ERROR);
       return;
diff --git a/remoting/host/setup/host_starter.cc b/remoting/host/setup/host_starter.cc
index 19a1d90..cf1503e 100644
--- a/remoting/host/setup/host_starter.cc
+++ b/remoting/host/setup/host_starter.cc
@@ -308,6 +308,7 @@
     unregistering_host_ = true;
     service_client_->UnregisterHost(start_host_params_.id,
                                     directory_access_token_, this);
+    // `on_done_` will be run after the UnregisterHost() call returns.
     return;
   }
   std::move(on_done_).Run(START_COMPLETE);
diff --git a/remoting/host/setup/host_starter.h b/remoting/host/setup/host_starter.h
index a71df47..abc01b2 100644
--- a/remoting/host/setup/host_starter.h
+++ b/remoting/host/setup/host_starter.h
@@ -22,9 +22,11 @@
 class HostStarter {
  public:
   enum Result {
-    START_COMPLETE,
     NETWORK_ERROR,
     OAUTH_ERROR,
+    PERMISSION_DENIED,
+    REGISTRATION_ERROR,
+    START_COMPLETE,
     START_ERROR,
   };
   struct Params {
diff --git a/remoting/host/setup/start_host_as_root.cc b/remoting/host/setup/start_host_as_root.cc
index 70c1866..fcea16c 100644
--- a/remoting/host/setup/start_host_as_root.cc
+++ b/remoting/host/setup/start_host_as_root.cc
@@ -18,10 +18,20 @@
   DCHECK(getuid() == 0);
 
   base::CommandLine command_line(argc, argv);
-  std::string user_name = command_line.GetSwitchValueASCII("user-name");
+  std::string user_name;
+  if (command_line.HasSwitch("corp-user")) {
+    std::string corp_user_email = command_line.GetSwitchValueASCII("corp-user");
+    size_t at_symbol_pos = corp_user_email.find("@");
+    if (at_symbol_pos != std::string::npos) {
+      user_name = corp_user_email.substr(0, at_symbol_pos);
+    }
+  } else if (command_line.HasSwitch("user_name")) {
+    user_name = command_line.GetSwitchValueASCII("user-name");
+  }
   if (user_name.empty()) {
     fprintf(stderr,
-            "Must specify the --user-name option when running as root.\n");
+            "Must specify the --user-name or --corp-user option when running "
+            "as root.\n");
     return 1;
   }
 
diff --git a/remoting/host/setup/start_host_main.cc b/remoting/host/setup/start_host_main.cc
index 4a0cd39..7a04dfc9 100644
--- a/remoting/host/setup/start_host_main.cc
+++ b/remoting/host/setup/start_host_main.cc
@@ -147,11 +147,15 @@
     case HostStarter::OAUTH_ERROR:
       fprintf(stderr, "Couldn't start host: OAuth error.\n");
       break;
+    case HostStarter::PERMISSION_DENIED:
+      fprintf(stderr, "Couldn't start host: Permission denied.\n");
+      break;
+    case HostStarter::REGISTRATION_ERROR:
+      fprintf(stderr, "Couldn't start host: Registration error.\n");
+      break;
     case HostStarter::START_ERROR:
       fprintf(stderr, "Couldn't start host.\n");
       break;
-    default:
-      NOTREACHED() << "Unexpected HostStarter result: " << result;
   }
 
   g_active_run_loop->Quit();
@@ -251,8 +255,7 @@
 
   // google_apis::GetOAuth2ClientID/Secret need a static CommandLine.
   base::CommandLine::Init(argc, argv);
-  const base::CommandLine* command_line =
-      base::CommandLine::ForCurrentProcess();
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
 
   // This object instance is required by Chrome code (for example,
   // FilePath, LazyInstance, MessageLoop).
@@ -283,6 +286,8 @@
     // Linux-specific, it isn't plumbed through the platform-independent daemon
     // controller code, and must be configured on the Linux delegate explicitly.
     DaemonControllerDelegateLinux::set_start_host_after_setup(false);
+    // Remove the switch from the command line to simplify arg count checks.
+    command_line->RemoveSwitch("no-start");
   }
 #endif  // BUILDFLAG(IS_LINUX)
 #if BUILDFLAG(IS_WIN)
diff --git a/services/viz/public/mojom/compositing/frame_sink_bundle.mojom b/services/viz/public/mojom/compositing/frame_sink_bundle.mojom
index eb4c66b..e0b505f 100644
--- a/services/viz/public/mojom/compositing/frame_sink_bundle.mojom
+++ b/services/viz/public/mojom/compositing/frame_sink_bundle.mojom
@@ -59,6 +59,10 @@
   // for the identified sink.
   SetNeedsBeginFrame(uint32 sink_id, bool needs_begin_frame);
 
+  // Corresponds to a single message of the same name on CompositorFrameSink
+  // for the identified sink.
+  SetWantsBeginFrameAcks(uint32 sink_id);
+
   // Corresponds to a series of batched requests equivalent to calls across
   // arbitrary CompositorFrameSinks for frame sinks belonging to this bundle.
   // Can be used to substantially reduce IPC traffic when many related sinks are
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index e34bd15..ee3734d 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -7568,8 +7568,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/android_32_google_atd_x64_foldable.textpb",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter"
+          "--avd-config=../../tools/android/avd/proto/android_32_google_atd_x64_foldable.textpb"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 3533b28..726c6c5 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -13339,8 +13339,7 @@
         "args": [
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices",
-          "--avd-config=../../tools/android/avd/proto/generic_android32_foldable.textpb",
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter"
+          "--avd-config=../../tools/android/avd/proto/generic_android32_foldable.textpb"
         ],
         "merge": {
           "args": [
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 7b84ddf..6725a96 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -6097,9 +6097,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -6109,8 +6109,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
@@ -6247,9 +6247,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -6259,8 +6259,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index dda97f42..3faccb2 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -20451,9 +20451,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20463,8 +20463,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
@@ -20601,9 +20601,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -20613,8 +20613,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 47cd5cc..6eca522 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -43561,9 +43561,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -43572,8 +43572,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
@@ -43711,9 +43711,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -43722,8 +43722,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
@@ -45020,9 +45020,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -45031,8 +45031,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
@@ -45170,9 +45170,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -45181,8 +45181,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
@@ -45865,9 +45865,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
         },
@@ -45876,8 +45876,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index f1e0015..3f91c81 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -16249,12 +16249,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16264,8 +16264,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
@@ -16419,12 +16419,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.filter;../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 121.0.6119.0",
+        "description": "Run with ash-chrome version 121.0.6120.0",
         "isolate_profile_data": true,
         "merge": {
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -16434,8 +16434,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v121.0.6119.0",
-              "revision": "version:121.0.6119.0"
+              "location": "lacros_version_skew_tests_v121.0.6120.0",
+              "revision": "version:121.0.6120.0"
             }
           ],
           "dimensions": {
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index b76c45e..9ea5cf3 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -397,6 +397,5 @@
 
   data = [
     "//testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter",
-    "//testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter",
   ]
 }
diff --git a/testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter b/testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter
deleted file mode 100644
index ca6970c..0000000
--- a/testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter
+++ /dev/null
@@ -1,14 +0,0 @@
-# crbug.com/1420820
--org.chromium.android_webview.test.AwContentsClientFullScreenTest.testOnShowCustomViewAndPlayWithHtmlControl_video
--org.chromium.android_webview.test.AwContentsClientFullScreenTest.testPowerSaveBlockerIsEnabledDuringFullscreenPlayback_video
-
-# crbug.com/1420821
--org.chromium.android_webview.test.AwAutofillTest.testAutofillTriggersAfterReload
--org.chromium.android_webview.test.AwAutofillTest.testFocusRemovedAndRestored
--org.chromium.android_webview.test.AwAutofillTest.testTouchingFormWithAdjustPan
--org.chromium.android_webview.test.AwAutofillTest.testTouchingFormWithAdjustResize
--org.chromium.android_webview.test.AwAutofillTest.testTouchingPasswordFieldTriggerQuery
-
-# crbug.com/1417038
--org.chromium.android_webview.test.PopupWindowTest.testPopupWindowTextHandle
--org.chromium.android_webview.test.PopupWindowTest.testSingleWindowModeJsInjection
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 11ec4aba..905c1d2 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -5078,17 +5078,11 @@
         },
       },
       'android-12l-x64-dbg-tests': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter',
-        ],
         'swarming': {
           'shards': 9,
         },
       },
       'android-12l-x64-fyi-dbg': {
-        'args': [
-          '--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_12l.webview_instrumentation_test_apk.filter',
-        ],
         'swarming': {
           'shards': 9,
         },
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 82b9733..6f680a7 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -70,16 +70,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'identifier': 'Lacros version skew testing ash canary',
-    'description': 'Run with ash-chrome version 121.0.6119.0',
+    'description': 'Run with ash-chrome version 121.0.6120.0',
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6119.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v121.0.6120.0/test_ash_chrome',
     ],
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v121.0.6119.0',
-          'revision': 'version:121.0.6119.0',
+          'location': 'lacros_version_skew_tests_v121.0.6120.0',
+          'revision': 'version:121.0.6120.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index f8b638dd..722b379 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5125,6 +5125,21 @@
             ]
         }
     ],
+    "DeferTabSwitcherLayoutCreation": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DeferTabSwitcherLayoutCreation"
+                    ]
+                }
+            ]
+        }
+    ],
     "DeferredSyncStartupCustomDelay": [
         {
             "platforms": [
@@ -13314,6 +13329,26 @@
             ]
         }
     ],
+    "PrivacySandboxEnrollment": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "PrivacySandboxAttestationsHigherComponentRegistrationPriority",
+                    "enable_features": [
+                        "PrivacySandboxAttestationsHigherComponentRegistrationPriority"
+                    ]
+                }
+            ]
+        }
+    ],
     "PrivacySandboxHatsForDesktopM1": [
         {
             "platforms": [
diff --git a/third_party/angle b/third_party/angle
index f773a79..aec4ffa 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit f773a79fef3814af079b9648e9c4395c5a5123b3
+Subproject commit aec4ffac02c0a093c7ff03d2069c4660ce552599
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index e06849c3..9f2da5cd 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -771,7 +771,7 @@
 // this feature does not automatically expose this element to the web, it only
 // allows the element to be enabled by the runtime enabled feature, for origin
 // trials.
-BASE_FEATURE(kFencedFrames, "FencedFrames", base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kFencedFrames, "FencedFrames", base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enable the new fenced frame-related features in M120. (These are
 // conditionally dependent on other fenced frame-related feature flags being
diff --git a/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc b/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc
index 7b8b127f..9dc9eae 100644
--- a/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc
+++ b/third_party/blink/renderer/core/fetch/trust_token_to_mojom.cc
@@ -106,7 +106,7 @@
             DOMExceptionCode::kNotAllowedError,
             "Private State Token Redemption ('token-redemption') and signing "
             "('send-redemption-record') operations require that the "
-            "trust-token-redemption "
+            "private-state-token-redemption "
             "Permissions Policy feature be enabled.");
         return false;
       }
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index e0e1436..912946b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -88,6 +88,7 @@
 #include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
+#include "third_party/blink/renderer/core/frame/web_frame_widget_impl.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
 #include "third_party/blink/renderer/core/highlight/highlight_registry.h"
@@ -343,6 +344,7 @@
   visitor->Trace(pending_opacity_updates_);
   visitor->Trace(pending_sticky_updates_);
   visitor->Trace(pending_snap_updates_);
+  visitor->Trace(pending_perform_snap_);
   visitor->Trace(disconnected_elements_with_remembered_size_);
 }
 
@@ -1215,13 +1217,16 @@
   auto* layout_view = GetLayoutView();
   if (layout_view) {
     // If this is the outermost main frame, we might have got here by
-    // hiding/showing the top controls. In that case, layout won't be
-    // triggered, so we need to clamp the scroll offset here.
+    // hiding/showing the top controls. In that case, layout might not be
+    // triggered, so some things that normally hook into layout need to be
+    // specially notified.
     // TODO(bokan): IsOutermostMainFrame may need to be reevaluated for
     // portals.
     if (GetFrame().IsOutermostMainFrame()) {
-      if (auto* scrollable_area = layout_view->GetScrollableArea())
+      if (auto* scrollable_area = layout_view->GetScrollableArea()) {
         scrollable_area->ClampScrollOffsetAfterOverflowChange();
+        scrollable_area->EnqueueForSnapUpdateIfNeeded();
+      }
     }
 
     layout_view->Layer()->SetNeedsCompositingInputsUpdate();
@@ -2448,6 +2453,18 @@
   });
 }
 
+bool LocalFrameView::ShouldDeferLayoutSnap() const {
+  // Scrollers that are snap containers normally need to re-snap after layout
+  // changes, but we defer the snap until the user is done scrolling to avoid
+  // fighting with snap animations on the compositor thread.
+  if (auto* web_frame = WebLocalFrameImpl::FromFrame(frame_)) {
+    if (auto* widget = web_frame->LocalRootFrameWidget()) {
+      return widget->IsScrollGestureActive();
+    }
+  }
+  return false;
+}
+
 bool LocalFrameView::RunStyleAndLayoutLifecyclePhases(
     DocumentLifecycle::LifecycleState target_state) {
   TRACE_EVENT0("blink,benchmark",
@@ -5066,10 +5083,23 @@
     for (PaintLayerScrollableArea* scrollable_area : *pending_snap_updates_) {
       auto* snap_container = scrollable_area->GetLayoutBox();
       DCHECK(snap_container->IsScrollContainer());
-      SnapCoordinator::UpdateSnapContainerData(*snap_container);
+      if (SnapCoordinator::UpdateSnapContainerData(*snap_container)) {
+        if (!pending_perform_snap_) {
+          pending_perform_snap_ = MakeGarbageCollected<
+              HeapHashSet<Member<PaintLayerScrollableArea>>>();
+        }
+        pending_perform_snap_->insert(scrollable_area);
+      }
     }
     pending_snap_updates_->clear();
   }
+
+  if (pending_perform_snap_ && !ShouldDeferLayoutSnap()) {
+    for (PaintLayerScrollableArea* scrollable_area : *pending_perform_snap_) {
+      scrollable_area->SnapAfterLayout();
+    }
+    pending_perform_snap_->clear();
+  }
 }
 
 void LocalFrameView::NotifyElementWithRememberedSizeDisconnected(
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 1e5b1d96..4483799 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -961,6 +961,7 @@
   void DeliverSynchronousIntersectionObservations();
 
   bool RunScrollSnapshotClientSteps();
+  bool ShouldDeferLayoutSnap() const;
 
   bool NotifyResizeObservers();
   bool RunResizeObserverSteps(DocumentLifecycle::LifecycleState target_state);
@@ -1189,6 +1190,11 @@
   // registered during style/layout, and deferred until the end of layout.
   Member<HeapHashSet<Member<PaintLayerScrollableArea>>> pending_snap_updates_;
 
+  // These are scrollers that had their SnapContainerData changed but still need
+  // to have SnapAfterLayout called. We defer the SnapAfterLayout until the user
+  // has stopped scrolling.
+  Member<HeapHashSet<Member<PaintLayerScrollableArea>>> pending_perform_snap_;
+
   // These are elements that were disconnected while having a remembered
   // size. We need to clear the remembered at resize observer timing,
   // assuming they are still disconnected.
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 46076e1..c565059 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1339,8 +1339,9 @@
 
 void WebFrameWidgetImpl::UpdateCompositorScrollState(
     const cc::CompositorCommitData& commit_data) {
+  is_scroll_gesture_active_ = commit_data.is_scroll_active;
   if (WebDevToolsAgentImpl* devtools = LocalRootImpl()->DevToolsAgentImpl())
-    devtools->SetPageIsScrolling(commit_data.is_scroll_active);
+    devtools->SetPageIsScrolling(is_scroll_gesture_active_);
 
   RecordManipulationTypeCounts(commit_data.manipulation_info);
 
@@ -1363,6 +1364,10 @@
   }
 }
 
+bool WebFrameWidgetImpl::IsScrollGestureActive() const {
+  return is_scroll_gesture_active_;
+}
+
 WebInputMethodController*
 WebFrameWidgetImpl::GetActiveWebInputMethodController() const {
   WebLocalFrameImpl* local_frame =
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index dcb7c4e..7acbacc 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -691,6 +691,8 @@
   // widget.
   bool WillBeDestroyed() const;
 
+  bool IsScrollGestureActive() const;
+
  protected:
   // WidgetBaseClient overrides:
   void WillBeginMainFrame() override;
@@ -1019,6 +1021,8 @@
   float page_scale_factor_in_mainframe_ = 1.f;
   bool is_pinch_gesture_active_in_mainframe_ = false;
 
+  bool is_scroll_gesture_active_ = false;
+
   // If set, the (plugin) element which has mouse capture.
   Member<HTMLPlugInElement> mouse_capture_element_;
 
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index a14d8ab7..61496e8 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -557,12 +557,12 @@
 
   if (!GetExecutionContext()->IsFeatureEnabled(
           mojom::blink::PermissionsPolicyFeature::kTrustTokenRedemption)) {
-    GetExecutionContext()->AddConsoleMessage(
-        MakeGarbageCollected<ConsoleMessage>(
-            mojom::blink::ConsoleMessageSource::kOther,
-            mojom::blink::ConsoleMessageLevel::kError,
-            "Trust Tokens: Attempted redemption or signing without the "
-            "trust-token-redemption Permissions Policy feature present."));
+    GetExecutionContext()->AddConsoleMessage(MakeGarbageCollected<
+                                             ConsoleMessage>(
+        mojom::blink::ConsoleMessageSource::kOther,
+        mojom::blink::ConsoleMessageLevel::kError,
+        "Trust Tokens: Attempted redemption or signing without the "
+        "private-state-token-redemption Permissions Policy feature present."));
     return nullptr;
   }
 
diff --git a/third_party/blink/renderer/core/input/scroll_snap_test.cc b/third_party/blink/renderer/core/input/scroll_snap_test.cc
index 7053c4f..612ede1e 100644
--- a/third_party/blink/renderer/core/input/scroll_snap_test.cc
+++ b/third_party/blink/renderer/core/input/scroll_snap_test.cc
@@ -432,4 +432,58 @@
   ASSERT_EQ(body->scrollTop(), 200);
 }
 
+TEST_F(ScrollSnapTest, ResizeDuringGesture) {
+  ResizeView(gfx::Size(400, 400));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    ::-webkit-scrollbar { display: none; }
+    html { scroll-snap-type: both mandatory; }
+    body { margin: 0; width: 600px; height: 600px; }
+    #a1 { position: absolute; left: 0; top: 0; background: blue;
+          width: 100px; height: 100px; scroll-snap-align: start; }
+    #a2 { position: absolute; left: 400px; top: 400px; background: blue;
+          width: 100px; height: 100px; scroll-snap-align: end; }
+    </style>
+    <div id='a1'></div>
+    <div id='a2'></div>
+  )HTML");
+
+  Compositor().BeginFrame();
+
+  Element* viewport = GetDocument().scrollingElement();
+  ASSERT_EQ(viewport->scrollLeft(), 0);
+  ASSERT_EQ(viewport->scrollTop(), 0);
+
+  ScrollBegin(10, 10, -75, -75);
+  ScrollUpdate(10, 10, -75, -75);
+
+  Compositor().BeginFrame();
+
+  ASSERT_EQ(viewport->scrollLeft(), 75);
+  ASSERT_EQ(viewport->scrollTop(), 75);
+
+  ResizeView(gfx::Size(450, 450));
+  Compositor().BeginFrame();
+
+  // After mid-gesture resize, we should still be at 75.
+  ASSERT_EQ(viewport->scrollLeft(), 75);
+  ASSERT_EQ(viewport->scrollTop(), 75);
+
+  ScrollEnd(10, 10);
+
+  // The scrollend is deferred for the snap animation in cc::InputHandler; wait
+  // for the animation to finish.  (We pss raster = true to ensure that we call
+  // LayerTreeHostImpl::UpdateAnimationState, which will set start time and
+  // transition to KeyframeModel::RUNNING.)
+  Compositor().BeginFrame(0.016, true);
+  Compositor().BeginFrame(0.3);
+
+  // Once the snap animation is finished, we run a deferred SnapAfterLayout.
+  ASSERT_EQ(viewport->scrollLeft(), 50);
+  ASSERT_EQ(viewport->scrollTop(), 50);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
index a1f6518..8adb8b7 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
@@ -59,7 +59,7 @@
 // TODO(sunyunjia): Move the static functions to an anonymous namespace.
 
 // static
-void SnapCoordinator::UpdateSnapContainerData(LayoutBox& snap_container) {
+bool SnapCoordinator::UpdateSnapContainerData(LayoutBox& snap_container) {
   ScrollableArea* scrollable_area =
       ScrollableArea::GetForScrolling(&snap_container);
   const auto* old_snap_container_data = scrollable_area->GetSnapContainerData();
@@ -76,7 +76,7 @@
       scrollable_area->UpdateSnappedTargetsAndEnqueueSnapChanged();
       scrollable_area->SetSnappedTargetData(absl::nullopt);
     }
-    return;
+    return false;
   }
 
   cc::SnapContainerData snap_container_data(snap_type);
@@ -93,7 +93,8 @@
   // https://drafts.csswg.org/css-overflow-3/#scrollport. So we use the
   // LayoutRect of the padding box here. The coordinate is relative to the
   // container's border box.
-  PhysicalRect container_rect(snap_container.PhysicalPaddingBoxRect());
+  PhysicalRect container_rect(
+      snap_container.OverflowClipRect(PhysicalOffset()));
 
   const ComputedStyle* container_style = snap_container.Style();
   // The percentage of scroll-padding is different from that of normal
@@ -164,8 +165,9 @@
       *old_snap_container_data != snap_container_data) {
     snap_container.SetNeedsPaintPropertyUpdate();
     scrollable_area->SetSnapContainerData(snap_container_data);
-    scrollable_area->SnapAfterLayout();
+    return true;
   }
+  return false;
 }
 
 // https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
index 4761ee76..c728ffe 100644
--- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
+++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.h
@@ -41,7 +41,8 @@
       const LayoutBox& snap_area,
       const LayoutBox& snap_container);
 
-  static void UpdateSnapContainerData(LayoutBox&);
+  // Returns true if the SnapContainerData actually changed.
+  static bool UpdateSnapContainerData(LayoutBox&);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index 8f0484f3..c9a73db6 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -351,6 +351,11 @@
     HTMLMediaElement& html_media_element,
     const WebMediaPlayerSource& source,
     WebMediaPlayerClient* media_player_client) const {
+  // TODO(crbug.com/1345473): Various checks to prove or disprove theories about
+  // why media players might not have been destroyed by ContextDestroyed()
+  // notifications.
+  CHECK(!html_media_element.GetDocument().domWindow()->IsContextDestroyed());
+  CHECK(html_media_element.GetDocument().IsActive());
   HTMLMediaElementEncryptedMedia& encrypted_media =
       HTMLMediaElementEncryptedMedia::From(html_media_element);
   WebString sink_id(
diff --git a/third_party/blink/renderer/platform/graphics/test/mock_frame_sink_bundle.h b/third_party/blink/renderer/platform/graphics/test/mock_frame_sink_bundle.h
index 5392004..59d405f 100644
--- a/third_party/blink/renderer/platform/graphics/test/mock_frame_sink_bundle.h
+++ b/third_party/blink/renderer/platform/graphics/test/mock_frame_sink_bundle.h
@@ -39,6 +39,7 @@
   MOCK_METHOD2(InitializeCompositorFrameSinkType,
                void(uint32_t, viz::mojom::CompositorFrameSinkType));
   MOCK_METHOD2(SetNeedsBeginFrame, void(uint32_t, bool));
+  MOCK_METHOD1(SetWantsBeginFrameAcks, void(uint32_t));
   MOCK_METHOD1(Submit,
                void(WTF::Vector<viz::mojom::blink::BundledFrameSubmissionPtr>));
   MOCK_METHOD3(DidAllocateSharedBitmap,
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.cc b/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.cc
index 0f2bfb3..02ff5d75 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.cc
@@ -151,6 +151,11 @@
   bundle_->SetNeedsBeginFrame(sink_id, needs_begin_frame);
 }
 
+void VideoFrameSinkBundle::SetWantsBeginFrameAcks(uint32_t sink_id) {
+  // These messages are not sent often, so we don't bother batching them.
+  bundle_->SetWantsBeginFrameAcks(sink_id);
+}
+
 void VideoFrameSinkBundle::SubmitCompositorFrame(
     uint32_t sink_id,
     const viz::LocalSurfaceId& local_surface_id,
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.h b/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.h
index 88b870f4..6bb4465 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.h
+++ b/third_party/blink/renderer/platform/graphics/video_frame_sink_bundle.h
@@ -114,6 +114,7 @@
       uint32_t sink_id,
       viz::mojom::blink::CompositorFrameSinkType);
   void SetNeedsBeginFrame(uint32_t sink_id, bool needs_begin_frame);
+  void SetWantsBeginFrameAcks(uint32_t sink_id);
   void SubmitCompositorFrame(
       uint32_t sink_id,
       const viz::LocalSurfaceId& local_surface_id,
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index 5fad80d..3602323 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -93,9 +93,16 @@
     bundle_->SetNeedsBeginFrame(frame_sink_id_.sink_id(), needs_begin_frame);
   }
 
+  void SetWantsBeginFrameAcks() override {
+    if (!bundle_) {
+      return;
+    }
+
+    bundle_->SetWantsBeginFrameAcks(frame_sink_id_.sink_id());
+  }
+
   // Not used by VideoFrameSubmitter.
   void SetWantsAnimateOnlyBeginFrames() override { NOTREACHED(); }
-  void SetWantsBeginFrameAcks() override { NOTREACHED(); }
   void SetAutoNeedsBeginFrame() override { NOTREACHED(); }
 
   void SubmitCompositorFrame(
@@ -550,6 +557,7 @@
         remote_frame_sink_.BindNewPipeAndPassReceiver());
     compositor_frame_sink_ = remote_frame_sink_.get();
   }
+  compositor_frame_sink_->SetWantsBeginFrameAcks();
 
   if (!surface_embedder_.is_bound()) {
     provider->ConnectToEmbedder(frame_sink_id_,
diff --git a/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc b/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc
index da47f27..c0bbb8a 100644
--- a/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc
+++ b/third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc
@@ -28,7 +28,6 @@
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/webrtc/api/async_dns_resolver.h"
 #include "third_party/webrtc/rtc_base/async_packet_socket.h"
-#include "third_party/webrtc/rtc_base/voucher.h"
 
 namespace blink {
 
@@ -102,16 +101,11 @@
   // send. The information tracked here will be used to match with the
   // P2PSendPacketMetrics from the underneath system socket.
   struct InFlightPacketRecord {
-    InFlightPacketRecord(uint64_t packet_id,
-                         size_t packet_size,
-                         webrtc::Voucher::Ptr voucher)
-        : packet_id(packet_id),
-          packet_size(packet_size),
-          voucher(std::move(voucher)) {}
+    InFlightPacketRecord(uint64_t packet_id, size_t packet_size)
+        : packet_id(packet_id), packet_size(packet_size) {}
 
     uint64_t packet_id;
     size_t packet_size;
-    webrtc::Voucher::Ptr voucher;
   };
 
   typedef std::list<InFlightPacketRecord> InFlightPacketList;
@@ -475,8 +469,8 @@
   // Ensure packet_id is not 0. It can't be the case according to
   // P2PSocketClientImpl::Send().
   DCHECK_NE(packet_id, 0uL);
-  in_flight_packet_records_.emplace_back(packet_id, data_size,
-                                         webrtc::Voucher::Current());
+  in_flight_packet_records_.push_back(
+      InFlightPacketRecord(packet_id, data_size));
   TraceSendThrottlingState();
 
   // Fake successful send. The caller ignores result anyway.
@@ -633,7 +627,7 @@
 
   CHECK(!in_flight_packet_records_.empty());
 
-  InFlightPacketRecord& record = in_flight_packet_records_.front();
+  const InFlightPacketRecord& record = in_flight_packet_records_.front();
 
   // Tracking is not turned on for TCP so it's always 0. For UDP, this will
   // cause a crash when the packet ids don't match.
@@ -643,8 +637,6 @@
   send_bytes_available_ = std::min(send_bytes_available_ + record.packet_size,
                                    max_in_flight_bytes_);
 
-  webrtc::Voucher::ScopedSetter setter(std::move(record.voucher));
-
   in_flight_packet_records_.pop_front();
   TraceSendThrottlingState();
 
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
index b406cb5..187844f 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -62,7 +62,6 @@
 #include "third_party/webrtc/modules/video_coding/codecs/h264/include/h264.h"
 #include "third_party/webrtc/modules/video_coding/include/video_error_codes.h"
 #include "third_party/webrtc/rtc_base/time_utils.h"
-#include "third_party/webrtc/rtc_base/voucher.h"
 
 namespace {
 
@@ -270,12 +269,6 @@
 };
 
 template <>
-struct CrossThreadCopier<webrtc::Voucher::Ptr>
-    : public CrossThreadCopierPassThrough<webrtc::Voucher::Ptr> {
-  STATIC_ONLY(CrossThreadCopier);
-};
-
-template <>
 struct CrossThreadCopier<
     std::vector<media::VideoEncodeAccelerator::Config::SpatialLayer>>
     : public CrossThreadCopierPassThrough<
@@ -473,20 +466,17 @@
   FrameInfo(const base::TimeDelta& media_timestamp,
             int32_t rtp_timestamp,
             int64_t capture_time_ms,
-            const ActiveSpatialLayers& active_spatial_layers,
-            webrtc::Voucher::Ptr voucher)
+            const ActiveSpatialLayers& active_spatial_layers)
       : media_timestamp_(media_timestamp),
         rtp_timestamp_(rtp_timestamp),
         capture_time_ms_(capture_time_ms),
-        active_spatial_layers_(active_spatial_layers),
-        voucher_(std::move(voucher)) {}
+        active_spatial_layers_(active_spatial_layers) {}
 
   const base::TimeDelta media_timestamp_;
   const int32_t rtp_timestamp_;
   const int64_t capture_time_ms_;
   const ActiveSpatialLayers active_spatial_layers_;
   size_t produced_frames_ = 0;
-  webrtc::Voucher::Ptr voucher_;
 };
 
 webrtc::VideoCodecType ProfileToWebRtcVideoCodecType(
@@ -613,7 +603,7 @@
   // Enqueue a frame from WebRTC for encoding. This function is called
   // asynchronously from webrtc encoder thread. When the error is caused, it is
   // reported by NotifyErrorStatus().
-  void Enqueue(FrameChunk frame_chunk, webrtc::Voucher::Ptr voucher);
+  void Enqueue(FrameChunk frame_chunk);
 
   // Request encoding parameter change for the underlying encoder.
   void RequestEncodingParametersChange(
@@ -642,12 +632,11 @@
   };
 
   // Perform encoding on an input frame from the input queue.
-  void EncodeOneFrame(FrameChunk frame_chunk, webrtc::Voucher::Ptr voucher);
+  void EncodeOneFrame(FrameChunk frame_chunk);
 
   // Perform encoding on an input frame from the input queue using VEA native
   // input mode.  The input frame must be backed with GpuMemoryBuffer buffers.
-  void EncodeOneFrameWithNativeInput(FrameChunk frame_chunk,
-                                     webrtc::Voucher::Ptr voucher);
+  void EncodeOneFrameWithNativeInput(FrameChunk frame_chunk);
 
   // Creates a GpuMemoryBuffer frame filled with black pixels. Returns true if
   // the frame is successfully created; false otherwise.
@@ -891,8 +880,7 @@
           preferred_pixel_formats_.begin(), preferred_pixel_formats_.end()));
 }
 
-void RTCVideoEncoder::Impl::Enqueue(FrameChunk frame_chunk,
-                                    webrtc::Voucher::Ptr voucher) {
+void RTCVideoEncoder::Impl::Enqueue(FrameChunk frame_chunk) {
   TRACE_EVENT1("webrtc", "RTCVideoEncoder::Impl::Enqueue", "timestamp",
                frame_chunk.timestamp_us);
   DVLOG(3) << __func__;
@@ -905,7 +893,7 @@
 
   if (use_native_input_) {
     DCHECK(pending_frames_.empty());
-    EncodeOneFrameWithNativeInput(std::move(frame_chunk), std::move(voucher));
+    EncodeOneFrameWithNativeInput(std::move(frame_chunk));
     return;
   }
 
@@ -915,13 +903,7 @@
   while (!pending_frames_.empty() && !input_buffers_free_.empty()) {
     auto chunk = std::move(pending_frames_.front());
     pending_frames_.pop_front();
-    // Use the voucher for the `frame_chunk` that was submitted into this
-    // method.
-    webrtc::Voucher::Ptr voucher_ptr;
-    if (pending_frames_.empty()) {
-      voucher_ptr = std::move(voucher);
-    }
-    EncodeOneFrame(std::move(chunk), std::move(voucher_ptr));
+    EncodeOneFrame(std::move(chunk));
   }
 }
 
@@ -1122,7 +1104,6 @@
   absl::optional<uint32_t> rtp_timestamp;
   absl::optional<int64_t> capture_timestamp_ms;
   absl::optional<ActiveSpatialLayers> expected_active_spatial_layers;
-  absl::optional<webrtc::Voucher::ScopedSetter> voucher_setter;
   if (!failed_timestamp_match_) {
     // Pop timestamps until we have a match.
     while (!submitted_frames_.empty()) {
@@ -1153,9 +1134,6 @@
                  "missing resolution"});
             return;
           }
-          if (front_frame.voucher_) {
-            voucher_setter.emplace(std::move(front_frame.voucher_));
-          }
           submitted_frames_.pop_front();
         }
         break;
@@ -1414,8 +1392,7 @@
   weak_this_factory_.InvalidateWeakPtrs();
 }
 
-void RTCVideoEncoder::Impl::EncodeOneFrame(FrameChunk frame_chunk,
-                                           webrtc::Voucher::Ptr voucher) {
+void RTCVideoEncoder::Impl::EncodeOneFrame(FrameChunk frame_chunk) {
   DVLOG(3) << "Impl::EncodeOneFrame()";
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!input_buffers_free_.empty());
@@ -1567,9 +1544,9 @@
   if (!failed_timestamp_match_) {
     DCHECK(!base::Contains(submitted_frames_, timestamp,
                            &FrameInfo::media_timestamp_));
-    submitted_frames_.emplace_back(
-        timestamp, frame_chunk.timestamp, frame_chunk.render_time_ms,
-        GetActiveSpatialLayers(), std::move(voucher));
+    submitted_frames_.emplace_back(timestamp, frame_chunk.timestamp,
+                                   frame_chunk.render_time_ms,
+                                   GetActiveSpatialLayers());
   }
 
   // Call UseOutputBitstreamBuffer() for pending output buffers.
@@ -1583,8 +1560,7 @@
 }
 
 void RTCVideoEncoder::Impl::EncodeOneFrameWithNativeInput(
-    FrameChunk frame_chunk,
-    webrtc::Voucher::Ptr voucher) {
+    FrameChunk frame_chunk) {
   DVLOG(3) << "Impl::EncodeOneFrameWithNativeInput()";
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(input_buffers_.empty() && input_buffers_free_.empty());
@@ -1627,9 +1603,9 @@
   if (!failed_timestamp_match_) {
     DCHECK(!base::Contains(submitted_frames_, frame->timestamp(),
                            &FrameInfo::media_timestamp_));
-    submitted_frames_.emplace_back(
-        frame->timestamp(), frame_chunk.timestamp, frame_chunk.render_time_ms,
-        GetActiveSpatialLayers(), std::move(voucher));
+    submitted_frames_.emplace_back(frame->timestamp(), frame_chunk.timestamp,
+                                   frame_chunk.render_time_ms,
+                                   GetActiveSpatialLayers());
   }
 
   // Call UseOutputBitstreamBuffer() for pending output buffers.
@@ -1686,7 +1662,7 @@
   while (!pending_frames_.empty() && !input_buffers_free_.empty()) {
     auto chunk = std::move(pending_frames_.front());
     pending_frames_.pop_front();
-    EncodeOneFrame(std::move(chunk), webrtc::Voucher::Current());
+    EncodeOneFrame(std::move(chunk));
   }
 }
 
@@ -1983,8 +1959,7 @@
   PostCrossThreadTask(
       *gpu_task_runner_.get(), FROM_HERE,
       CrossThreadBindOnce(&RTCVideoEncoder::Impl::Enqueue, weak_impl_,
-                          FrameChunk(input_image, want_key_frame),
-                          webrtc::Voucher::Current()));
+                          FrameChunk(input_image, want_key_frame)));
   return WEBRTC_VIDEO_CODEC_OK;
 }
 
diff --git a/third_party/blink/web_tests/PRESUBMIT.py b/third_party/blink/web_tests/PRESUBMIT.py
index 03cedd37..e54c03f 100644
--- a/third_party/blink/web_tests/PRESUBMIT.py
+++ b/third_party/blink/web_tests/PRESUBMIT.py
@@ -297,6 +297,11 @@
 def _CheckForExtraVirtualBaselines(input_api, output_api):
     """Checks that expectations in virtual test suites are for virtual test suites that exist
     """
+    # This test fails on Windows because win32pipe is not available and
+    # other errors.
+    if os.name == 'nt':
+        return []
+
     os_path = input_api.os_path
 
     local_dir = os_path.relpath(
@@ -323,11 +328,6 @@
     if not check_all and len(check_files) == 0:
         return []
 
-    # The rest of this test fails on Windows because win32pipe is not available
-    # and other errors.
-    if os.name == 'nt':
-        return []
-
     from blinkpy.common.host import Host
     port_factory = Host().port_factory
     known_virtual_suites = [
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 3ce1251..2d93e08 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -168,6 +168,7 @@
 crbug.com/1280882 http/tests/inspector-protocol/network/cors-errors.js [ Slow ]
 crbug.com/1289238 http/tests/inspector-protocol/network/same-site-issue-warn-cookie-navigation-context-downgrade.js [ Slow ]
 crbug.com/1300352 http/tests/inspector-protocol/network/blocked-cookie-not-on-path.js [ Slow ]
+crbug.com/1498414 http/tests/inspector-protocol/network/auction-network-events-handler.js [ Slow ]
 crbug.com/1318449 http/tests/inspector-protocol/window-open-effective-opener.js [ Slow ]
 crbug.com/1412923 [ Win ] http/tests/inspector-protocol/network/load-network-resource-errors.js [ Slow ]
 crbug.com/1412923 [ Win ] http/tests/inspector-protocol/network/load-network-resource-different-frame.js [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 7b2e9d9..a2c80ff 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -6890,7 +6890,8 @@
 crbug.com/1464614 [ Win11-arm64 ] virtual/threaded/external/wpt/css/css-transforms/animation/canvas-webgl-translate-in-animation.html [ Failure Timeout ]
 crbug.com/1464614 [ Win11-arm64 ] external/wpt/density-size-correction/density-corrected-image-in-canvas.html [ Failure Timeout ]
 crbug.com/1464614 [ Win11-arm64 ] external/wpt/mediacapture-streams/MediaDevices-enumerateDevices-per-origin-ids.sub.https.html [ Failure Timeout ]
-
+crbug.com/1464614 [ Win11-arm64 ] http/tests/images/force-reload-image-document.html [ Failure Timeout ]
+crbug.com/1464614 [ Win11-arm64 ] http/tests/images/force-reload.html [ Failure Timeout ]
 
 crbug.com/1411760 [ Mac ] virtual/composite-clip-path-animation/external/wpt/css/css-masking/clip-path/animations/clip-path-animation-fixed-position.html [ Failure Pass ]
 
@@ -7079,6 +7080,7 @@
 crbug.com/1499775 [ Chrome ] external/wpt/background-fetch/fetch.https.window.html [ Timeout ]
 crbug.com/1499775 [ Chrome ] external/wpt/background-fetch/match.https.window.html [ Timeout ]
 crbug.com/1499775 [ Chrome ] external/wpt/background-fetch/update-ui.https.window.html [ Timeout ]
+crbug.com/1499775 [ Chrome ] external/wpt/client-hints/accept-ch/cache-revalidation.https.html [ Failure ]  # Flaky output
 crbug.com/1499775 [ Chrome ] external/wpt/compat/webkit-box-rtl-flex.html [ Failure ]  # Reftest image failure
 crbug.com/1499775 [ Chrome ] external/wpt/content-security-policy/inheritance/blob-inherits-from-meta-http-equiv-with-invalid-characters.html [ Failure ]  # Flaky output
 crbug.com/1499775 [ Chrome ] external/wpt/credential-management/fedcm-abort.https.html [ Timeout ]
@@ -7266,6 +7268,9 @@
 crbug.com/1499775 [ Chrome ] external/wpt/eventsource/eventsource-request-cancellation.window.any.worker.html [ Failure ]  # Flaky output
 crbug.com/1499775 [ Chrome ] external/wpt/eventsource/request-credentials.window.any.html [ Failure ]  # Flaky output
 crbug.com/1499775 [ Chrome ] external/wpt/fetch/api/abort/serviceworker-intercepted.https.html [ Timeout ]
+crbug.com/1499775 [ Chrome ] external/wpt/fetch/stale-while-revalidate/fetch.any.html [ Failure ]  # Flaky output
+crbug.com/1499775 [ Chrome ] external/wpt/fetch/stale-while-revalidate/fetch-sw.https.html [ Failure ]  # Flaky output
+crbug.com/1499775 [ Chrome ] external/wpt/fetch/stale-while-revalidate/fetch.any.sharedworker.html [ Failure ]  # Flaky output
 crbug.com/1499775 [ Chrome ] external/wpt/fetch/stale-while-revalidate/fetch.any.serviceworker.html [ Failure ]  # Flaky output
 crbug.com/1499775 [ Chrome ] external/wpt/fullscreen/api/element-request-fullscreen-non-top.html [ Timeout ]
 crbug.com/1499775 [ Chrome ] external/wpt/fullscreen/api/element-request-fullscreen-options.tentative.https.html [ Timeout ]
@@ -7291,6 +7296,8 @@
 crbug.com/1499775 [ Chrome ] external/wpt/fullscreen/rendering/fullscreen-root-block-scroll.html [ Timeout ] # pass on wpt.fyi
 crbug.com/1499775 [ Chrome ] external/wpt/fullscreen/rendering/fullscreen-root-block-size.html [ Timeout ]
 crbug.com/1499775 [ Chrome ] external/wpt/fullscreen/rendering/ua-style-iframe.html [ Timeout ] # pass on wpt.fyi
+crbug.com/1499775 [ Chrome ] external/wpt/html/browsers/history/the-location-interface/assign_before_load.html [ Failure ]  # Flaky output
+crbug.com/1499775 [ Chrome ] external/wpt/html/browsers/history/the-location-interface/assign_after_load.html [ Failure ]  # Flaky output
 crbug.com/1499775 [ Chrome ] external/wpt/html/browsers/browsing-the-web/navigating-across-documents/cross-origin-top-navigation-with-user-activation-in-parent.window.html [ Timeout ]
 crbug.com/1499775 [ Chrome ] external/wpt/html/canvas/element/manual/filters/tentative/canvas-filter-object-turbulence.html [ Failure ]  # Reftest image failure
 crbug.com/1499775 [ Chrome ] external/wpt/html/canvas/element/text/direction-inherit-rtl.html [ Failure ]  # Reftest image failure
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/currency.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/currency.https.window.js
index 6caba210..9a33d12 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/currency.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/currency.https.window.js
@@ -45,7 +45,7 @@
     allowComponentAuction: true,
     reportWin: `
         sendReportTo('${createBidderReportURL(uuid, /*id=*/ '')}' +
-                     browserSignals.bid + encodeURIComponent(browserSignals.bidCurrency));`,
+                     browserSignals.bid + browserSignals.bidCurrency);`,
   });
 }
 
@@ -57,12 +57,17 @@
     bid: bid,
     bidCurrency: currency,
     allowComponentAuction: true,
+    generateBid: `
+      forDebuggingOnly.reportAdAuctionWin(
+          '${createBidderReportURL(uuid, /*id=*/ 'dbg_')}' +
+          '\${winningBid}\${winningBidCurrency}_' +
+          '\${highestScoringOtherBid}\${highestScoringOtherBidCurrency}');`,
     reportWin: `
         sendReportTo(
             '${createBidderReportURL(uuid, /*id=*/ '')}' +
-            browserSignals.bid + encodeURIComponent(browserSignals.bidCurrency) +
+            browserSignals.bid + browserSignals.bidCurrency +
             '_' + browserSignals.highestScoringOtherBid +
-            encodeURIComponent(browserSignals.highestScoringOtherBidCurrency));`,
+            browserSignals.highestScoringOtherBidCurrency);`,
   });
 }
 
@@ -73,7 +78,7 @@
               throw 'Wrong currency';`,
     reportResult: `
           sendReportTo('${createSellerReportURL(uuid, /*id=*/ '')}' +
-                         browserSignals.bid + encodeURIComponent(browserSignals.bidCurrency)   );`,
+                         browserSignals.bid + browserSignals.bidCurrency);`,
   });
 }
 
@@ -87,6 +92,10 @@
     uuid, conversion = '', suffix = '') {
   return createDecisionScriptURL(uuid, {
     scoreAd: `
+      forDebuggingOnly.reportAdAuctionWin(
+          '${createSellerReportURL(uuid, /*id=*/ 'dbg_')}' + '${suffix}' +
+          '\${winningBid}\${winningBidCurrency}_' +
+          '\${highestScoringOtherBid}\${highestScoringOtherBidCurrency}');
       let converted = undefined;
       let modified = undefined;
       let modifiedCurrency = undefined;
@@ -100,9 +109,9 @@
     reportResult: `
         sendReportTo(
             '${createSellerReportURL(uuid, /*id=*/ '')}' + '${suffix}' +
-            browserSignals.bid + encodeURIComponent(browserSignals.bidCurrency) +
+            browserSignals.bid + browserSignals.bidCurrency +
             '_' + browserSignals.highestScoringOtherBid +
-            encodeURIComponent(browserSignals.highestScoringOtherBidCurrency));`,
+            browserSignals.highestScoringOtherBidCurrency);`,
   });
 }
 
@@ -127,7 +136,7 @@
     decisionLogicURL: createDecisionScriptURL(uuid, {
       reportResult: `
         sendReportTo('${createSellerReportURL(uuid, 'top_')}' +
-                     browserSignals.bid + encodeURIComponent(browserSignals.bidCurrency))`,
+                     browserSignals.bid + browserSignals.bidCurrency)`,
       ...params.topLevelSellerScriptParamsOverride
     }),
     componentAuctions: [{
@@ -135,7 +144,7 @@
       decisionLogicURL: createDecisionScriptURL(uuid, {
         reportResult: `
           sendReportTo('${createSellerReportURL(uuid, 'component_')}' +
-                       browserSignals.bid + encodeURIComponent(browserSignals.bidCurrency))`,
+                       browserSignals.bid + browserSignals.bidCurrency)`,
         ...params.componentSellerScriptParamsOverride
       }),
       interestGroupBuyers: [ORIGIN],
@@ -688,9 +697,13 @@
       {decisionLogicURL: createDecisionURLForHighestScoringOther(uuid)});
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, '10???_9???'),
-    createBidderReportURL(uuid, '10???_9???')
+    createBidderReportURL(uuid, '10???_9???'),
+    // w/o sellerCurrency set, forDebuggingOnly reports original values and ???
+    // as tags.
+    createSellerReportURL(uuid, 'dbg_10???_9???'),
+    createBidderReportURL(uuid, 'dbg_10???_9???')
   ]);
-}, 'highestScoringOtherBid with no sellerCurrency set.');
+}, 'Converted currency use with no sellerCurrency set.');
 
 subsetTest(promise_test, async test => {
   const uuid = generateUuid(test);
@@ -701,9 +714,13 @@
   });
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, '10???_9USD'),
-    createBidderReportURL(uuid, '10???_9USD')
+    createBidderReportURL(uuid, '10???_9USD'),
+    // w/sellerCurrency set, forDebuggingOnly reports converted bids +
+    // sellerCurrency.
+    createSellerReportURL(uuid, 'dbg_10USD_9USD'),
+    createBidderReportURL(uuid, 'dbg_10USD_9USD')
   ]);
-}, 'highestScoringOtherBid with sellerCurrency set matching.');
+}, 'Converted currency use with sellerCurrency set matching.');
 
 subsetTest(promise_test, async test => {
   const uuid = generateUuid(test);
@@ -714,9 +731,12 @@
   });
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, '10???_0EUR'),
-    createBidderReportURL(uuid, '10???_0EUR')
+    createBidderReportURL(uuid, '10???_0EUR'),
+    // sellerCurrency set, and no bid available in it: get 0s.
+    createSellerReportURL(uuid, 'dbg_0EUR_0EUR'),
+    createBidderReportURL(uuid, 'dbg_0EUR_0EUR')
   ]);
-}, 'highestScoringOtherBid with sellerCurrency different, no conversion.');
+}, 'Converted currency use with sellerCurrency different, no conversion.');
 
 subsetTest(promise_test, async test => {
   const uuid = generateUuid(test);
@@ -728,9 +748,12 @@
   });
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, '10???_27EUR'),
-    createBidderReportURL(uuid, '10???_27EUR')
+    createBidderReportURL(uuid, '10???_27EUR'),
+    // sellerCurrency set, converted bids.
+    createSellerReportURL(uuid, 'dbg_30EUR_27EUR'),
+    createBidderReportURL(uuid, 'dbg_30EUR_27EUR')
   ]);
-}, 'highestScoringOtherBid with sellerCurrency different, conversion.');
+}, 'Converted currency use with sellerCurrency different, conversion.');
 
 subsetTest(promise_test, async test => {
   const uuid = generateUuid(test);
@@ -747,7 +770,12 @@
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, 'top_11EUR_0???'),
     createSellerReportURL(uuid, 'component_10???_0EUR'),
-    createBidderReportURL(uuid, '10???_0EUR')
+    createBidderReportURL(uuid, '10???_0EUR'),
+    // forDebuggingOnly info w/sellerCurrency set relies on conversion;
+    // but sellerCurrency is on component auction only.
+    createBidderReportURL(uuid, 'dbg_0EUR_0EUR'),
+    createSellerReportURL(uuid, 'dbg_component_0EUR_0EUR'),
+    createSellerReportURL(uuid, 'dbg_top_11???_0???'),
   ]);
 }, 'Modified bid does not act in place of incomingBidInSellerCurrency.');
 
@@ -767,7 +795,12 @@
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, 'top_11EUR_0???'),
     createSellerReportURL(uuid, 'component_10???_8EUR'),
-    createBidderReportURL(uuid, '10???_8EUR')
+    createBidderReportURL(uuid, '10???_8EUR'),
+    // Debug at component shows converted; top-level has no sellerCurrency,
+    // so shows modified.
+    createBidderReportURL(uuid, 'dbg_9EUR_8EUR'),
+    createSellerReportURL(uuid, 'dbg_component_9EUR_8EUR'),
+    createSellerReportURL(uuid, 'dbg_top_11???_0???'),
   ]);
 }, 'Both modified bid and incomingBidInSellerCurrency.');
 
@@ -782,15 +815,17 @@
         topLevelAuctionConfigOverrides: {sellerCurrency: 'EUR'},
         topLevelConversion: `converted = 3 * bid;`,
       });
-  // Note that since highestScoringOtherBid isn't available at top-level, one
-  // can't actually observe the effect of conversion with just sendReportTo(),
-  // but error-checking for it still happens.
   expectSuccess(result);
   createAndNavigateFencedFrame(test, result);
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, 'top_11???_0???'),
     createSellerReportURL(uuid, 'component_10???_9???'),
-    createBidderReportURL(uuid, '10???_9???')
+    createBidderReportURL(uuid, '10???_9???'),
+    // No sellerCurrency at component; debug at top-level shows the result of
+    // conversion.
+    createBidderReportURL(uuid, 'dbg_10???_9???'),
+    createSellerReportURL(uuid, 'dbg_component_10???_9???'),
+    createSellerReportURL(uuid, 'dbg_top_33EUR_0???'),
   ]);
 }, 'incomingBidInSellerCurrency at top-level trying to convert is OK.');
 
@@ -826,7 +861,12 @@
   await waitForObservedRequests(uuid, [
     createSellerReportURL(uuid, 'top_11???_0???'),
     createSellerReportURL(uuid, 'component_10???_9???'),
-    createBidderReportURL(uuid, '10???_9???')
+    createBidderReportURL(uuid, '10???_9???'),
+    // No sellerCurrency at component; debug at top-level shows the result of
+    // no-op conversion.
+    createBidderReportURL(uuid, 'dbg_10???_9???'),
+    createSellerReportURL(uuid, 'dbg_component_10???_9???'),
+    createSellerReportURL(uuid, 'dbg_top_11EUR_0???'),
   ]);
 }, 'incomingBidInSellerCurrency at top-level doing a no-op conversion OK.');
 
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js
index 91c5c0fa..e9513ec 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/resources/fledge-util.sub.js
@@ -34,10 +34,10 @@
 // `id` will always be the last query parameter.
 function createTrackerURL(origin, uuid, dispatch, id = null) {
   let url = new URL(`${origin}${BASE_PATH}resources/request-tracker.py`);
-  url.searchParams.append('uuid', uuid);
-  url.searchParams.append('dispatch', dispatch);
+  let search = `uuid=${uuid}&dispatch=${dispatch}`;
   if (id)
-    url.searchParams.append('id', id);
+    search += `&id=${id}`;
+  url.search = search;
   return url.toString();
 }
 
diff --git a/third_party/blink/web_tests/http/tests/cookies/resources/echo-json.php b/third_party/blink/web_tests/http/tests/cookies/resources/echo-json.php
index 5ee6af6..f6cf4523 100644
--- a/third_party/blink/web_tests/http/tests/cookies/resources/echo-json.php
+++ b/third_party/blink/web_tests/http/tests/cookies/resources/echo-json.php
@@ -1,8 +1,9 @@
 <?php
+$request_origin_value = $_SERVER["HTTP_ORIGIN"] ?? null;
 header("Content-Type: application/json");
 header("Access-Control-Allow-Credentials: true");
 header("Access-Control-Allow-External: true");
-header("Access-Control-Allow-Origin: ${_SERVER['HTTP_ORIGIN']}");
+header("Access-Control-Allow-Origin: " . $request_origin_value);
 
 echo json_encode($_COOKIE);
 ?>
diff --git a/third_party/blink/web_tests/http/tests/cookies/same-site/basics.https.html b/third_party/blink/web_tests/http/tests/cookies/same-site/basics.https.html
index 379d564..876f5bd 100644
--- a/third_party/blink/web_tests/http/tests/cookies/same-site/basics.https.html
+++ b/third_party/blink/web_tests/http/tests/cookies/same-site/basics.https.html
@@ -41,7 +41,7 @@
 
     promise_test(_ => {
         return fetch("https://subdomain." + TEST_HOST + ":8443/cookies/resources/echo-json.php", {"credentials": "include"})
-            .then(r => r.json())
+        .then(r => r.json())
             .then(j => {
                 assert_equals(j[STRICT_DOM], "2", "strict");
                 assert_equals(j[LAX_DOM], "2", "lax");
@@ -52,7 +52,7 @@
 
     promise_test(_ => {
         return fetch("https://" + TEST_ROOT + ":8443/cookies/resources/echo-json.php", {"credentials": "include"})
-            .then(r => r.json())
+        .then(r => r.json())
             .then(j => {
                 assert_equals(j[STRICT_DOM], undefined, "strict");
                 assert_equals(j[LAX_DOM], undefined, "lax");
@@ -63,7 +63,7 @@
 
     promise_test(_ => {
         return fetch("https://" + ORIGINAL_HOST + ":8443/cookies/resources/echo-json.php", {"credentials": "include"})
-            .then(r => r.json())
+        .then(r => r.json())
             .then(j => {
                 assert_equals(j[STRICT_DOM], undefined, "strict");
                 assert_equals(j[LAX_DOM], undefined, "lax");
diff --git a/third_party/blink/web_tests/http/tests/fetch/resources/redirect-loop.php b/third_party/blink/web_tests/http/tests/fetch/resources/redirect-loop.php
index 3912c7b..28bf5eb 100644
--- a/third_party/blink/web_tests/http/tests/fetch/resources/redirect-loop.php
+++ b/third_party/blink/web_tests/http/tests/fetch/resources/redirect-loop.php
@@ -1,5 +1,5 @@
 <?php
-$url = $_GET['Redirect'];
+$url = $_GET['Redirect'] ?? null;
 $path = '/fetch/resources/redirect-loop.php';
 if (isset($_GET['Count'])) {
   $count = intval($_GET['Count']) - 1;
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/auction-network-events-handler.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/auction-network-events-handler.js
index 9b1d3727..b240584 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/network/auction-network-events-handler.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/auction-network-events-handler.js
@@ -9,14 +9,15 @@
 
   await dp.Emulation.setUserAgentOverride({ userAgent: 'Vending Machine', acceptLanguage: 'ar' });
 
-  let finishedRequests = 0;
+  let finishedLoadingRequests = 0;
+  let finishedReceivingExtraInfo = 0;
   let resolveAllRequestsCompleted = null;
   const allRequestsCompleted = new Promise((resolve) => {
     resolveAllRequestsCompleted = resolve;
   });
 
   function checkIfAllRequestsCompleted() {
-    if (finishedRequests > 4) {
+    if (finishedLoadingRequests > 4 && finishedReceivingExtraInfo > 4) {
       resolveAllRequestsCompleted(true);
     }
   }
@@ -58,12 +59,15 @@
   dp.Network.onResponseReceivedExtraInfo(async event => {
     const requestId = event.params.requestId;
     requestsById[requestId].responseExtraInfoReceived = true;
+    finishedReceivingExtraInfo++;
+    checkIfAllRequestsCompleted();
+
   });
 
   dp.Network.onLoadingFinished(async event => {
     const requestId = event.params.requestId;
     requestsById[requestId].finished = true;
-    finishedRequests++;
+    finishedLoadingRequests++;
     checkIfAllRequestsCompleted();
   });
 
diff --git a/third_party/blink/web_tests/http/tests/xmlhttprequest/resources/redirect-loop.php b/third_party/blink/web_tests/http/tests/xmlhttprequest/resources/redirect-loop.php
index 171b4ed..c380485 100644
--- a/third_party/blink/web_tests/http/tests/xmlhttprequest/resources/redirect-loop.php
+++ b/third_party/blink/web_tests/http/tests/xmlhttprequest/resources/redirect-loop.php
@@ -2,7 +2,7 @@
 header("HTTP/1.1 307");
 header("Access-Control-Allow-Origin: *");
 
-$url = $_GET['Redirect'];
+$url = $_GET['Redirect'] ?? null;
 $path = '/xmlhttprequest/resources/redirect-loop.php';
 $count = intval($_GET['Count']) - 1;
 if ($count >= 0) {
diff --git a/third_party/blink/web_tests/platform/PRESUBMIT.py b/third_party/blink/web_tests/platform/PRESUBMIT.py
new file mode 100644
index 0000000..936493f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/PRESUBMIT.py
@@ -0,0 +1,72 @@
+# 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.
+"""Top-level presubmit script for `web_tests/platform/`.
+
+See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+import os
+
+
+def _CheckForExtraPlatformBaselines(input_api, output_api):
+    """Checks that expectations are not added/modified for platforms that do not exist
+    """
+    # This test does not work on Windows because of the dependencies of
+    # the imported blinkpy code below.
+    if os.name == 'nt':
+        return []
+
+    os_path = input_api.os_path
+
+    local_dir = os_path.relpath(
+        os_path.normpath('{0}/'.format(input_api.PresubmitLocalPath().replace(
+            os_path.sep, '/'))), input_api.change.RepositoryRoot())
+
+    check_files = []
+    for f in input_api.AffectedFiles(include_deletes=False):
+        local_path = f.LocalPath()
+        assert local_path.startswith(local_dir)
+        local_path = os_path.relpath(local_path, local_dir)
+        path_components = local_path.split(os_path.sep)
+        if len(path_components) > 1:
+            check_files.append((local_path, path_components[0]))
+
+    if len(check_files) == 0:
+        return []
+
+    from blinkpy.common.host import Host
+    port_factory = Host().port_factory
+    all_ports = [
+        port_factory.get(port_name)
+        for port_name in port_factory.all_port_names()
+    ]
+    known_platforms = set([
+        fallback_path for port in all_ports
+        for fallback_path in port.FALLBACK_PATHS[port.version()]
+    ])
+
+    results = []
+    for (f, platform) in check_files:
+        if not platform in known_platforms:
+            path = os_path.relpath(
+                os_path.join(input_api.PresubmitLocalPath(), f),
+                input_api.change.RepositoryRoot())
+            results.append(
+                output_api.PresubmitError(
+                    "This CL adds a new baseline %s, but %s is not a known platform."
+                    % (path, platform)))
+    return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+    results = []
+    results.extend(_CheckForExtraPlatformBaselines(input_api, output_api))
+    return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+    results = []
+    results.extend(_CheckForExtraPlatformBaselines(input_api, output_api))
+    return results
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-flexbox/intrinsic-size/row-use-cases-001-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-flexbox/intrinsic-size/row-use-cases-001-expected.txt
new file mode 100644
index 0000000..225480f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-flexbox/intrinsic-size/row-use-cases-001-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+[PASS] left < right
+[PASS] no overflow
+[PASS] same widths
+[FAIL] zero width
+  assert_equals: expected 0 but got 200
+[FAIL] same heights
+  assert_equals: expected 28 but got 40
+[FAIL] no overflow 2
+  assert_less_than_equal: expected a number less than or equal to 85.65625 but got 129.3125
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-fonts/variations/font-weight-matching-installed-fonts-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-fonts/variations/font-weight-matching-installed-fonts-expected.txt
new file mode 100644
index 0000000..d0a93663
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-fonts/variations/font-weight-matching-installed-fonts-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+[FAIL] Test native font matching on "CSSTest Weights W2569" for weight 375
+  assert_approx_equals: @font-face should be mapped to "CSSTest Weights W2569". expected 90 +/- 2 but got 78.21875
+[FAIL] Test native font matching on "CSSTest Weights Full" for weight 375
+  assert_approx_equals: @font-face should be mapped to "CSSTest Weights Full". expected 90 +/- 2 but got 78.21875
+[FAIL] Test native font matching on "CSSTest Weights W1479" for weight 475
+  assert_approx_equals: @font-face should be mapped to "CSSTest Weights W1479". expected 90 +/- 2 but got 78.21875
+[FAIL] Test native font matching on "CSSTest Weights Full" for weight 425
+  assert_approx_equals: @font-face should be mapped to "CSSTest Weights Full". expected 90 +/- 2 but got 78.21875
+[FAIL] Test native font matching on "CSSTest Weights Full" for weight 525
+  assert_approx_equals: @font-face should be mapped to "CSSTest Weights Full". expected 90 +/- 2 but got 78.21875
+[FAIL] Test native font matching on "CSSTest Weights Full" for weight 675
+  assert_approx_equals: @font-face should be mapped to "CSSTest Weights Full". expected 90 +/- 2 but got 78.21875
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-overscroll-behavior/overscroll-behavior-root-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-overscroll-behavior/overscroll-behavior-root-expected.txt
new file mode 100644
index 0000000..e8b0115
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-overscroll-behavior/overscroll-behavior-root-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] overscroll-behavior in root works
+  assert_equals: overscroll-behavior should work on the root expected 774 but got 674
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/ja/css-text-line-break-ja-pr-normal-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/ja/css-text-line-break-ja-pr-normal-expected.txt
new file mode 100644
index 0000000..e7585d3
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/ja/css-text-line-break-ja-pr-normal-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+[FAIL] 00B1  PLUS-MINUS SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 20AC  EURO SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 2116  NUMERO SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 100 +/- 1 but got 70
+[FAIL] FE69  SMALL DOLLAR SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE6  FULLWIDTH WON SIGN may NOT appear at line start if ja and normal
+  assert_approx_equals: expected 99 +/- 1 but got 69
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/ja/css-text-line-break-ja-pr-strict-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/ja/css-text-line-break-ja-pr-strict-expected.txt
new file mode 100644
index 0000000..57da61eb
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/ja/css-text-line-break-ja-pr-strict-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+[FAIL] 00B1  PLUS-MINUS SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 20AC  EURO SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 2116  NUMERO SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 100 +/- 1 but got 70
+[FAIL] FE69  SMALL DOLLAR SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE6  FULLWIDTH WON SIGN may NOT appear at line start if ja and strict
+  assert_approx_equals: expected 99 +/- 1 but got 69
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-pr-normal-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-pr-normal-expected.txt
new file mode 100644
index 0000000..22f7811
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-pr-normal-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+[FAIL] 00B1  PLUS-MINUS SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 20AC  EURO SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 2116  NUMERO SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 100 +/- 1 but got 70
+[FAIL] FE69  SMALL DOLLAR SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE6  FULLWIDTH WON SIGN may NOT appear at line start if zh and normal
+  assert_approx_equals: expected 99 +/- 1 but got 69
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-pr-strict-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-pr-strict-expected.txt
new file mode 100644
index 0000000..3e33d95
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/i18n/zh/css-text-line-break-zh-pr-strict-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+[FAIL] 00B1  PLUS-MINUS SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 20AC  EURO SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 92 +/- 1 but got 62
+[FAIL] 2116  NUMERO SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 100 +/- 1 but got 70
+[FAIL] FE69  SMALL DOLLAR SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FF04  FULLWIDTH DOLLAR SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE1  FULLWIDTH POUND SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 80 +/- 1 but got 50
+[FAIL] FFE5  FULLWIDTH YEN SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 99 +/- 1 but got 69
+[FAIL] FFE6  FULLWIDTH WON SIGN may NOT appear at line start if zh and strict
+  assert_approx_equals: expected 99 +/- 1 but got 69
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-001.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-001.tentative-expected.txt
new file mode 100644
index 0000000..e7c11cf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-001.tentative-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+[FAIL] linebreak only
+  assert_equals: expected 108 but got 115
+[FAIL] spaces linebreak
+  assert_equals: expected 108 but got 115
+[FAIL] linebreak spaces
+  assert_equals: expected 108 but got 115
+[FAIL] spaces linebreak spaces
+  assert_equals: expected 108 but got 115
+[FAIL] multiple linebreaks
+  assert_equals: expected 108 but got 115
+[FAIL] multiple linebreaks + spaces
+  assert_equals: expected 108 but got 115
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-002.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-002.tentative-expected.txt
new file mode 100644
index 0000000..89721ea5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-002.tentative-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+[FAIL] linebreak only
+  assert_equals: expected 162 but got 169
+[FAIL] spaces linebreak
+  assert_equals: expected 162 but got 169
+[FAIL] linebreak spaces
+  assert_equals: expected 162 but got 169
+[FAIL] spaces linebreak spaces
+  assert_equals: expected 162 but got 169
+[FAIL] multiple linebreaks
+  assert_equals: expected 162 but got 169
+[FAIL] multiple linebreaks + spaces
+  assert_equals: expected 162 but got 169
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-003.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-003.tentative-expected.txt
new file mode 100644
index 0000000..a3e0b30
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-003.tentative-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+[FAIL] linebreak only
+  assert_equals: expected 72 but got 79
+[FAIL] spaces linebreak
+  assert_equals: expected 72 but got 79
+[FAIL] linebreak spaces
+  assert_equals: expected 72 but got 79
+[FAIL] spaces linebreak spaces
+  assert_equals: expected 72 but got 79
+[FAIL] multiple linebreaks
+  assert_equals: expected 72 but got 79
+[FAIL] multiple linebreaks + spaces
+  assert_equals: expected 72 but got 79
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-004.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-004.tentative-expected.txt
new file mode 100644
index 0000000..0adfba70
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-004.tentative-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+[FAIL] linebreak only â‚©ï¼’ï¼”
+  assert_equals: expected 60 but got 67
+[FAIL] spaces linebreak â‚©ï¼’ï¼”
+  assert_equals: expected 60 but got 67
+[FAIL] linebreak spaces â‚©ï¼’ï¼”
+  assert_equals: expected 60 but got 67
+[FAIL] spaces linebreak spaces â‚©ï¼’ï¼”
+  assert_equals: expected 60 but got 67
+[FAIL] multiple linebreaks â‚©ï¼’ï¼”
+  assert_equals: expected 60 but got 67
+[FAIL] multiple linebreaks + spaces â‚©ï¼’ï¼”
+  assert_equals: expected 60 but got 67
+[FAIL] linebreak only 24₩
+  assert_equals: expected 60 but got 66
+[FAIL] spaces linebreak 24₩
+  assert_equals: expected 60 but got 66
+[FAIL] linebreak spaces 24₩
+  assert_equals: expected 60 but got 66
+[FAIL] spaces linebreak spaces 24₩
+  assert_equals: expected 60 but got 66
+[FAIL] multiple linebreaks 24₩
+  assert_equals: expected 60 but got 66
+[FAIL] multiple linebreaks + spaces 24₩
+  assert_equals: expected 60 but got 66
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-008.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-008.tentative-expected.txt
new file mode 100644
index 0000000..e738d96
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-008.tentative-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+[FAIL] linebreak only
+  assert_equals: expected 144 but got 151
+[FAIL] spaces linebreak
+  assert_equals: expected 144 but got 151
+[FAIL] linebreak spaces
+  assert_equals: expected 144 but got 151
+[FAIL] spaces linebreak spaces
+  assert_equals: expected 144 but got 151
+[FAIL] multiple linebreaks
+  assert_equals: expected 144 but got 151
+[FAIL] multiple linebreaks + spaces
+  assert_equals: expected 144 but got 151
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-009.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-009.tentative-expected.txt
new file mode 100644
index 0000000..e7c11cf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/seg-break-transformation-009.tentative-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+[FAIL] linebreak only
+  assert_equals: expected 108 but got 115
+[FAIL] spaces linebreak
+  assert_equals: expected 108 but got 115
+[FAIL] linebreak spaces
+  assert_equals: expected 108 but got 115
+[FAIL] spaces linebreak spaces
+  assert_equals: expected 108 but got 115
+[FAIL] multiple linebreaks
+  assert_equals: expected 108 but got 115
+[FAIL] multiple linebreaks + spaces
+  assert_equals: expected 108 but got 115
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/white-space-collapse-002-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/white-space-collapse-002-expected.txt
new file mode 100644
index 0000000..45b28c7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-text/white-space/white-space-collapse-002-expected.txt
@@ -0,0 +1,38 @@
+This is a testharness.js-based test.
+[PASS] RLO 1
+[FAIL] RLO 2
+  assert_equals: expected 105 but got 112
+[FAIL] RLO 3
+  assert_equals: expected 105 but got 112
+[FAIL] RLO 4
+  assert_equals: expected 105 but got 112
+[FAIL] RLO 5
+  assert_equals: expected 105 but got 112
+[PASS] RLE 1
+[FAIL] RLE 2
+  assert_equals: expected 103 but got 109
+[FAIL] RLE 3
+  assert_equals: expected 103 but got 109
+[FAIL] RLE 4
+  assert_equals: expected 103 but got 109
+[FAIL] RLE 5
+  assert_equals: expected 103 but got 109
+[PASS] RLI 1
+[FAIL] RLI 2
+  assert_equals: expected 93 but got 100
+[FAIL] RLI 3
+  assert_equals: expected 93 but got 100
+[FAIL] RLI 4
+  assert_equals: expected 93 but got 100
+[FAIL] RLI 5
+  assert_equals: expected 93 but got 100
+[PASS] RLM 1
+[FAIL] RLM 2
+  assert_equals: expected 111 but got 117
+[FAIL] RLM 3
+  assert_equals: expected 111 but got 117
+[FAIL] RLM 4
+  assert_equals: expected 111 but got 117
+[FAIL] RLM 5
+  assert_equals: expected 111 but got 117
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-variables/variable-presentation-attribute-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-variables/variable-presentation-attribute-expected.txt
new file mode 100644
index 0000000..45152e3d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/css-variables/variable-presentation-attribute-expected.txt
@@ -0,0 +1,65 @@
+This is a testharness.js-based test.
+[PASS] Testing 'stroke-width' on '#box1'.
+[PASS] Testing 'stroke-width' on '#box2'.
+[PASS] Testing 'stroke-width' on '#box3'.
+[PASS] Testing 'clip' on '#test4'.
+[FAIL] Testing 'alignment-baseline'.
+  assert_equals: Default value. expected "baseline" but got "auto"
+[FAIL] Testing 'baseline-shift'.
+  assert_equals: Default value. expected "baseline" but got "0px"
+[PASS] Testing 'clip-rule'.
+[PASS] Testing 'color'.
+[FAIL] Testing 'color-interpolation-filters'.
+  assert_equals: Default value. expected "" but got "linearrgb"
+[PASS] Testing 'cursor'.
+[PASS] Testing 'direction'.
+[PASS] Testing 'display'.
+[PASS] Testing 'dominant-baseline'.
+[FAIL] Testing 'fill'.
+  assert_equals: Default value. expected "black" but got "rgb(0, 0, 0)"
+[PASS] Testing 'fill-opacity'.
+[PASS] Testing 'fill-rule'.
+[PASS] Testing 'filter'.
+[FAIL] Testing 'flood-color'.
+  assert_equals: Default value. expected "" but got "rgb(0, 0, 0)"
+[PASS] Testing 'flood-opacity'.
+[FAIL] Testing 'font-family'.
+  assert_equals: Default value. expected "Times New Roman" but got "\\"Times New Roman\\""
+[PASS] Testing 'font-size'.
+[PASS] Testing 'font-size-adjust'.
+[PASS] Testing 'font-stretch'.
+[PASS] Testing 'font-style'.
+[PASS] Testing 'font-weight'.
+[FAIL] Testing 'glyph-orientation-horizontal'.
+  assert_equals: Default value. expected "0deg" but got ""
+[FAIL] Testing 'glyph-orientation-vertical'.
+  assert_equals: Default value. expected "auto" but got ""
+[FAIL] Testing 'kerning'.
+  assert_equals: Default value. expected "auto" but got ""
+[PASS] Testing 'letter-spacing'.
+[FAIL] Testing 'lighting-color'.
+  assert_equals: Default value. expected "" but got "rgb(255, 255, 255)"
+[PASS] Testing 'opacity'.
+[PASS] Testing 'overflow'.
+[FAIL] Testing 'pointer-events'.
+  assert_equals: Default value. expected "visiblePainted" but got "auto"
+[FAIL] Testing 'stop-color'.
+  assert_equals: Default value. expected "" but got "rgb(0, 0, 0)"
+[PASS] Testing 'stop-opacity'.
+[FAIL] Testing 'stroke'.
+  assert_equals: Default value. expected "" but got "none"
+[PASS] Testing 'stroke-dasharray'.
+[PASS] Testing 'stroke-dashoffset'.
+[PASS] Testing 'stroke-linecap'.
+[PASS] Testing 'stroke-linejoin'.
+[PASS] Testing 'stroke-miterlimit'.
+[PASS] Testing 'stroke-opacity'.
+[PASS] Testing 'stroke-width'.
+[PASS] Testing 'text-anchor'.
+[PASS] Testing 'text-decoration-line'.
+[PASS] Testing 'text-decoration-style'.
+[PASS] Testing 'visibility'.
+[PASS] Testing 'word-spacing'.
+[FAIL] Testing 'writing-mode'.
+  assert_equals: Default value. expected "lr-tb" but got "horizontal-tb"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom-view/range-bounding-client-rect-with-display-contents-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom-view/range-bounding-client-rect-with-display-contents-expected.txt
new file mode 100644
index 0000000..7dd6dd5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom-view/range-bounding-client-rect-with-display-contents-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] the space between elements using a range should be the same as using another method
+  assert_equals: range.getBoundingClientRect().height expected 47 but got 60
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom-view/scrollIntoView-stuck.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom-view/scrollIntoView-stuck.tentative-expected.txt
new file mode 100644
index 0000000..da423e23a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/css/cssom-view/scrollIntoView-stuck.tentative-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] CSSOM View - scrollIntoView doesn't consider scroll-padding when target is stuck
+  assert_equals: Shouldn't have scrolled expected 0 but got 2421
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/delete-in-child-of-html.tentative_designMode=off_method=forwarddelete-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/delete-in-child-of-html.tentative_designMode=off_method=forwarddelete-expected.txt
new file mode 100644
index 0000000..2b09e81
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/delete-in-child-of-html.tentative_designMode=off_method=forwarddelete-expected.txt
@@ -0,0 +1,22 @@
+This is a testharness.js-based test.
+[PASS] forwarddelete in <div> elements after <body> should join them
+[FAIL] forwarddelete should merge <div> after <body> into the <body>
+  assert_in_array: The text should be merged value "<head><title>iframe</title></head><body>abc</body><div>def</div>" not in array ["<head><title>iframe</title></head><body>abcdef</body>", "<head><title>iframe</title></head><body>abcdef<br></body>"]
+[FAIL] forwarddelete should merge <div> after <body> into the <div> in the <body>
+  assert_in_array: The <div> elements should be merged value "<head><title>iframe</title></head><body><div>abc</div></body><div>def</div>" not in array ["<head><title>iframe</title></head><body><div>abcdef</div></body>", "<head><title>iframe</title></head><body><div>abcdef<br></div></body>"]
+[FAIL] forwarddelete should merge <div> after <body> into the empty <body>
+  assert_in_array: The <div> element should be merged into the <body> value "<head><title>iframe</title></head><body><br></body><div>abc</div>" not in array ["<head><title>iframe</title></head><body>abc</body>", "<head><title>iframe</title></head><body>abc<br></body>"]
+[FAIL] forwarddelete should merge <div> before <body> into the <body>
+  assert_in_array: The text should be merged value "<head><title>iframe</title></head><div>abcdef</div><body></body>" not in array ["<head><title>iframe</title></head><body>abcdef</body>", "<head><title>iframe</title></head><body>abcdef<br></body>"]
+[FAIL] forwarddelete should merge <div> before <body> into the <div> in the <body>
+  assert_in_array: The <div> elements should be merged value "<head><title>iframe</title></head><div>abcdef</div><body></body>" not in array ["<head><title>iframe</title></head><body><div>abcdef</div></body>", "<head><title>iframe</title></head><body><div>abcdef<br></div></body>"]
+[FAIL] forwarddelete should merge <div> before <body> into the empty <body>
+  assert_in_array: The <div> element should be merged into the <body> value "<head><title>iframe</title></head><div>abc</div><body></body>" not in array ["<head><title>iframe</title></head><body>abc</body>", "<head><title>iframe</title></head><body>abc<br></body>"]
+[FAIL] forwarddelete from <div> around invisible <head> element should not delete the <head>
+  assert_in_array: The <div> element should be merged into the left <div> without deleting the <head> value "<div>abcdef</div><body><br></body>" not in array ["<div>abcdef</div><head><title>iframe</title></head><body><br></body>", "<div>abcdef<br></div><head><title>iframe</title></head><body><br></body>", "<head><title>iframe</title></head><div>abcdef</div><body><br></body>", "<head><title>iframe</title></head><div>abcdef<br></div><body><br></body>"]
+[PASS] forwarddelete from <div> following visible <head> element should be merged with the <div> in the <head>
+[FAIL] forwarddelete from <div> following visible <head> element should be merged with the visible <style> in the <head>
+  assert_equals: The <div> element should not be merged with the <style> in the <head> expected "<head><title>iframe</title><style>abc</style></head><div>def</div><body><br></body>" but got "<head><title>iframe</title><style>abcdef</style></head><body><br></body>"
+[FAIL] forwarddelete from <div> following visible <script> element should be merged with the visible <script> in the <head>
+  assert_equals: The <div> element should not be merged with the <script> in the <head> expected "<head><title>iframe</title><script>// abc</script></head><div>def</div><body><br></body>" but got "<head><title>iframe</title><script>// abcdef</script></head><body><br></body>"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/delete-in-child-of-html.tentative_designMode=on_method=forwarddelete-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/delete-in-child-of-html.tentative_designMode=on_method=forwarddelete-expected.txt
new file mode 100644
index 0000000..2b09e81
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/delete-in-child-of-html.tentative_designMode=on_method=forwarddelete-expected.txt
@@ -0,0 +1,22 @@
+This is a testharness.js-based test.
+[PASS] forwarddelete in <div> elements after <body> should join them
+[FAIL] forwarddelete should merge <div> after <body> into the <body>
+  assert_in_array: The text should be merged value "<head><title>iframe</title></head><body>abc</body><div>def</div>" not in array ["<head><title>iframe</title></head><body>abcdef</body>", "<head><title>iframe</title></head><body>abcdef<br></body>"]
+[FAIL] forwarddelete should merge <div> after <body> into the <div> in the <body>
+  assert_in_array: The <div> elements should be merged value "<head><title>iframe</title></head><body><div>abc</div></body><div>def</div>" not in array ["<head><title>iframe</title></head><body><div>abcdef</div></body>", "<head><title>iframe</title></head><body><div>abcdef<br></div></body>"]
+[FAIL] forwarddelete should merge <div> after <body> into the empty <body>
+  assert_in_array: The <div> element should be merged into the <body> value "<head><title>iframe</title></head><body><br></body><div>abc</div>" not in array ["<head><title>iframe</title></head><body>abc</body>", "<head><title>iframe</title></head><body>abc<br></body>"]
+[FAIL] forwarddelete should merge <div> before <body> into the <body>
+  assert_in_array: The text should be merged value "<head><title>iframe</title></head><div>abcdef</div><body></body>" not in array ["<head><title>iframe</title></head><body>abcdef</body>", "<head><title>iframe</title></head><body>abcdef<br></body>"]
+[FAIL] forwarddelete should merge <div> before <body> into the <div> in the <body>
+  assert_in_array: The <div> elements should be merged value "<head><title>iframe</title></head><div>abcdef</div><body></body>" not in array ["<head><title>iframe</title></head><body><div>abcdef</div></body>", "<head><title>iframe</title></head><body><div>abcdef<br></div></body>"]
+[FAIL] forwarddelete should merge <div> before <body> into the empty <body>
+  assert_in_array: The <div> element should be merged into the <body> value "<head><title>iframe</title></head><div>abc</div><body></body>" not in array ["<head><title>iframe</title></head><body>abc</body>", "<head><title>iframe</title></head><body>abc<br></body>"]
+[FAIL] forwarddelete from <div> around invisible <head> element should not delete the <head>
+  assert_in_array: The <div> element should be merged into the left <div> without deleting the <head> value "<div>abcdef</div><body><br></body>" not in array ["<div>abcdef</div><head><title>iframe</title></head><body><br></body>", "<div>abcdef<br></div><head><title>iframe</title></head><body><br></body>", "<head><title>iframe</title></head><div>abcdef</div><body><br></body>", "<head><title>iframe</title></head><div>abcdef<br></div><body><br></body>"]
+[PASS] forwarddelete from <div> following visible <head> element should be merged with the <div> in the <head>
+[FAIL] forwarddelete from <div> following visible <head> element should be merged with the visible <style> in the <head>
+  assert_equals: The <div> element should not be merged with the <style> in the <head> expected "<head><title>iframe</title><style>abc</style></head><div>def</div><body><br></body>" but got "<head><title>iframe</title><style>abcdef</style></head><body><br></body>"
+[FAIL] forwarddelete from <div> following visible <script> element should be merged with the visible <script> in the <head>
+  assert_equals: The <div> element should not be merged with the <script> in the <head> expected "<head><title>iframe</title><script>// abc</script></head><div>def</div><body><br></body>" but got "<head><title>iframe</title><script>// abcdef</script></head><body><br></body>"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/empty-elements-insertion-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/empty-elements-insertion-expected.txt
new file mode 100644
index 0000000..ca4a3162
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/editing/other/empty-elements-insertion-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+[PASS] Insert text into the inline element styled with border
+[PASS] Insert text into the inline element styled with padding
+[FAIL] Insert text into the unstyled inline element
+  assert_greater_than: The text should be inserted into the unstyled <strong> element expected a number greater than 0 but got 0
+[FAIL] Insert text into the unstyled inline element with the styled ::before pseudoelement
+  assert_greater_than: The text should be inserted into the <strong> element expected a number greater than 0 but got 0
+[FAIL] Insert text into the unstyled inline element with the styled ::after pseudoelement
+  assert_greater_than: The text should be inserted into the <strong> element expected a number greater than 0 but got 0
+[FAIL] Insert text into the unstyled inline element with the styled ::before and ::after pseudoelements
+  assert_greater_than: The text should be inserted into the <strong> element expected a number greater than 0 but got 0
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-no-preflight-required.tentative.https.window-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-no-preflight-required.tentative.https.window-expected.txt
new file mode 100644
index 0000000..ccfccf9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-no-preflight-required.tentative.https.window-expected.txt
@@ -0,0 +1,18 @@
+This is a testharness.js-based test.
+[FAIL] local to local: no preflight required.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] local to private: no preflight required.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] local to public: no preflight required.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] private to private: no preflight required.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] private to public: no preflight required.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to public: no preflight required.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to public: no preflight required.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to local: optional preflight
+  navigator.joinAdInterestGroup is not a function
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=baseline-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=baseline-expected.txt
new file mode 100644
index 0000000..3c256bc7
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=baseline-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+[FAIL] local to public: PUT preflight failure.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] local to public: PUT preflight success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-local-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-local-expected.txt
new file mode 100644
index 0000000..0b8c582
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-local-expected.txt
@@ -0,0 +1,8 @@
+This is a testharness.js-based test.
+[FAIL] local to local: no preflight required.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] local to private: no preflight required.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] local to public: no preflight required.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-private-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-private-expected.txt
new file mode 100644
index 0000000..27fd60e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-private-expected.txt
@@ -0,0 +1,26 @@
+This is a testharness.js-based test.
+[FAIL] private to local: failed preflight.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: missing CORS headers on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: missing PNA header on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: missing CORS headers on final response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: PUT success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: no-CORS mode failed preflight.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: no-CORS mode missing CORS headers on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: no-CORS mode missing PNA header on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to local: no-CORS mode success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to private: no preflight required.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] private to public: no preflight required.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-public-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-public-expected.txt
new file mode 100644
index 0000000..c0e46e47
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame-subresource-fetch.tentative.https.window_include=from-public-expected.txt
@@ -0,0 +1,44 @@
+This is a testharness.js-based test.
+[FAIL] public to local: failed preflight.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: missing CORS headers on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: missing PNA header on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: missing CORS headers on final response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: PUT success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: no-CORS mode failed preflight.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: no-CORS mode missing CORS headers on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: no-CORS mode missing PNA header on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to local: no-CORS mode success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: failed preflight.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: missing CORS headers on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: missing PNA header on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: missing CORS headers on final response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: PUT success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: no-CORS mode failed preflight.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: no-CORS mode missing CORS headers on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: no-CORS mode missing PNA header on preflight response.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to private: no-CORS mode success.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+[FAIL] public to public: no preflight required.
+  promise_test: Unhandled rejection with value: object "TypeError: navigator.joinAdInterestGroup is not a function"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame.tentative.https.window-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame.tentative.https.window-expected.txt
new file mode 100644
index 0000000..8c5d1a8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fetch/private-network-access/fenced-frame.tentative.https.window-expected.txt
@@ -0,0 +1,44 @@
+This is a testharness.js-based test.
+[FAIL] private to local: failed preflight.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] private to local: missing CORS headers.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] private to local: missing PNA header.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] private to local: failed because fenced frames are incompatible with PNA.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to local: failed preflight.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to local: missing CORS headers.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to local: missing PNA header.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to local: failed because fenced frames are incompatible with PNA.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to private: failed preflight.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to private: missing CORS headers.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to private: missing PNA header.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] public to private: failed because fenced frames are incompatible with PNA.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to local: failed preflight.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to local: missing CORS headers.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to local: missing PNA header.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to local: failed because fenced frames are incompatible with PNA.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to local (same-origin): fenced frame embedder initiated navigation has opaque origin.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to private: failed preflight.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to private: missing CORS headers.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to private: missing PNA header.
+  navigator.joinAdInterestGroup is not a function
+[FAIL] treat-as-public-address to private: failed because fenced frames are incompatible with PNA.
+  navigator.joinAdInterestGroup is not a function
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt
new file mode 100644
index 0000000..e9236768
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/fullscreen/api/element-ready-check-not-allowed-cross-origin.sub-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] Cross-origin element ready check with no allowfullscreen or allow attribute
+  promise_test: Unhandled rejection with value: object "Error: Timed out waiting for message"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html-aam/roles-dynamic-switch.tentative.window-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html-aam/roles-dynamic-switch.tentative.window-expected.txt
new file mode 100644
index 0000000..7cca95d5
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html-aam/roles-dynamic-switch.tentative.window-expected.txt
@@ -0,0 +1,14 @@
+This is a testharness.js-based test.
+[FAIL] Disconnected <input type=checkbox switch>
+  assert_equals: expected "" but got "none"
+[FAIL] Connected <input type=checkbox switch>
+  assert_equals: expected "switch" but got "checkbox"
+[FAIL] Connected <input type=checkbox switch>: adding switch attribute
+  assert_equals: expected "switch" but got "checkbox"
+[FAIL] Connected <input type=checkbox switch>: removing switch attribute
+  assert_equals: expected "switch" but got "checkbox"
+[FAIL] Connected <input type=checkbox switch>: removing type attribute
+  assert_equals: expected "switch" but got "checkbox"
+[FAIL] Connected <input type=checkbox switch>: adding type attribute
+  assert_equals: expected "switch" but got "checkbox"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-2.sub-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-2.sub-expected.txt
new file mode 100644
index 0000000..1c6dea3e
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/browsers/browsing-the-web/overlapping-navigations-and-traversals/nav-cancelation-2.sub-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] grandparent cancels a pending navigation in a cross-origin grandchild
+  assert_false: Grandchild <iframe>'s load event does not fire at least one task after the grandparent's window load event fires. It should only fire when its subsequent navigation is complete expected false got true
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sans-fieldset-display-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sans-fieldset-display-expected.txt
new file mode 100644
index 0000000..c861cf8
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-sans-fieldset-display-expected.txt
@@ -0,0 +1,23 @@
+This is a testharness.js-based test.
+[FAIL] .table
+  assert_equals: .table height expected "36px" but got "54px"
+[FAIL] .caption
+  assert_equals: .caption display expected "table-caption" but got "block"
+[FAIL] .col
+  assert_equals: .col display expected "table-column" but got "block"
+[FAIL] .tbody
+  assert_equals: .tbody display expected "table-row-group" but got "block"
+[FAIL] .tr
+  assert_equals: .tr display expected "table-row" but got "block"
+[FAIL] .td
+  assert_equals: .td display expected "table-cell" but got "block"
+[PASS] .li
+[FAIL] .inline
+  assert_equals: .inline display expected "inline" but got "block"
+[FAIL] .inline-block
+  assert_equals: .inline-block display expected "inline-block" but got "block"
+[FAIL] .ruby
+  assert_equals: .ruby display expected "inline" but got "block"
+[FAIL] .rt
+  assert_equals: .rt width expected "29.3281px" but got "800px"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/widgets/baseline-alignment-and-overflow.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/widgets/baseline-alignment-and-overflow.tentative-expected.txt
new file mode 100644
index 0000000..943c08e6
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/widgets/baseline-alignment-and-overflow.tentative-expected.txt
@@ -0,0 +1,163 @@
+This is a testharness.js-based test.
+Found 132 tests; 104 PASS, 28 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[PASS] <input type="text" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="text" value="x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="text" value="x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="text" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="text" value="x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="text" value="x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="search" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="search" value="x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="search" value="x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="search" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="search" value="x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="search" value="x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="tel" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="tel" value="x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="tel" value="x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="tel" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="tel" value="x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="tel" value="x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="url" value="data:,x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="url" value="data:,x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="url" value="data:,x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="url" value="data:,x" style="overflow: visible; appearance: none;">
+[PASS] <input type="url" value="data:,x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="url" value="data:,x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="email" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="email" value="x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="email" value="x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="email" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="email" value="x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="email" value="x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="password" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="password" value="x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="password" value="x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="password" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="password" value="x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="password" value="x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="date" value="2020-01-01" style="overflow: visible; appearance: auto;">
+[PASS] <input type="date" value="2020-01-01" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="date" value="2020-01-01" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="date" value="2020-01-01" style="overflow: visible; appearance: none;">
+[PASS] <input type="date" value="2020-01-01" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="date" value="2020-01-01" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="month" value="2020-01" style="overflow: visible; appearance: auto;">
+[PASS] <input type="month" value="2020-01" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="month" value="2020-01" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="month" value="2020-01" style="overflow: visible; appearance: none;">
+[PASS] <input type="month" value="2020-01" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="month" value="2020-01" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="week" value="2020-W01" style="overflow: visible; appearance: auto;">
+[PASS] <input type="week" value="2020-W01" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="week" value="2020-W01" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="week" value="2020-W01" style="overflow: visible; appearance: none;">
+[PASS] <input type="week" value="2020-W01" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="week" value="2020-W01" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="time" value="00:00" style="overflow: visible; appearance: auto;">
+[PASS] <input type="time" value="00:00" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="time" value="00:00" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="time" value="00:00" style="overflow: visible; appearance: none;">
+[PASS] <input type="time" value="00:00" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="time" value="00:00" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="datetime-local" value="2020-01-01T00:00" style="overflow: visible; appearance: auto;">
+[PASS] <input type="datetime-local" value="2020-01-01T00:00" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="datetime-local" value="2020-01-01T00:00" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="datetime-local" value="2020-01-01T00:00" style="overflow: visible; appearance: none;">
+[PASS] <input type="datetime-local" value="2020-01-01T00:00" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="datetime-local" value="2020-01-01T00:00" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="number" value="0" style="overflow: visible; appearance: auto;">
+[PASS] <input type="number" value="0" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="number" value="0" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="number" value="0" style="overflow: visible; appearance: none;">
+[PASS] <input type="number" value="0" style="overflow: hidden; appearance: none;">
+[PASS] <input type="number" value="0" style="overflow: scroll; appearance: none;">
+[FAIL] <input type="range" style="overflow: visible; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 71 but got 61
+[FAIL] <input type="range" style="overflow: hidden; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 71 but got 61
+[FAIL] <input type="range" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 71 but got 61
+[FAIL] <input type="range" style="overflow: visible; appearance: none;">
+  assert_equals: <span>.offsetTop expected 71 but got 61
+[FAIL] <input type="range" style="overflow: hidden; appearance: none;">
+  assert_equals: <span>.offsetTop expected 71 but got 61
+[FAIL] <input type="range" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 71 but got 61
+[FAIL] <input type="color" value="#000000" style="overflow: visible; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 49 but got 46
+[FAIL] <input type="color" value="#000000" style="overflow: hidden; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 49 but got 71
+[FAIL] <input type="color" value="#000000" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 49 but got 71
+[FAIL] <input type="color" value="#000000" style="overflow: visible; appearance: none;">
+  assert_equals: <span>.offsetTop expected 49 but got 46
+[FAIL] <input type="color" value="#000000" style="overflow: hidden; appearance: none;">
+  assert_equals: <span>.offsetTop expected 49 but got 71
+[FAIL] <input type="color" value="#000000" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 49 but got 71
+[PASS] <input type="checkbox" style="overflow: visible; appearance: auto;">
+[PASS] <input type="checkbox" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="checkbox" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="checkbox" style="overflow: visible; appearance: none;">
+[PASS] <input type="checkbox" style="overflow: hidden; appearance: none;">
+[PASS] <input type="checkbox" style="overflow: scroll; appearance: none;">
+[PASS] <input type="radio" style="overflow: visible; appearance: auto;">
+[PASS] <input type="radio" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="radio" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="radio" style="overflow: visible; appearance: none;">
+[PASS] <input type="radio" style="overflow: hidden; appearance: none;">
+[PASS] <input type="radio" style="overflow: scroll; appearance: none;">
+[PASS] <input type="file" style="overflow: visible; appearance: auto;">
+[PASS] <input type="file" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="file" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="file" style="overflow: visible; appearance: none;">
+[PASS] <input type="file" style="overflow: hidden; appearance: none;">
+[PASS] <input type="file" style="overflow: scroll; appearance: none;">
+[PASS] <input type="submit" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="submit" value="x" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="submit" value="x" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="submit" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="submit" value="x" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="submit" value="x" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="image" src="data:,broken" alt="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="image" src="data:,broken" alt="x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="image" src="data:,broken" alt="x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="image" src="data:,broken" alt="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="image" src="data:,broken" alt="x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="image" src="data:,broken" alt="x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="image" src="data:(png)" alt="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="image" src="data:(png)" alt="x" style="overflow: hidden; appearance: auto;">
+[PASS] <input type="image" src="data:(png)" alt="x" style="overflow: scroll; appearance: auto;">
+[PASS] <input type="image" src="data:(png)" alt="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="image" src="data:(png)" alt="x" style="overflow: hidden; appearance: none;">
+[PASS] <input type="image" src="data:(png)" alt="x" style="overflow: scroll; appearance: none;">
+[PASS] <input type="reset" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="reset" value="x" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="reset" value="x" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="reset" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="reset" value="x" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="reset" value="x" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="button" value="x" style="overflow: visible; appearance: auto;">
+[PASS] <input type="button" value="x" style="overflow: hidden; appearance: auto;">
+[FAIL] <input type="button" value="x" style="overflow: scroll; appearance: auto;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+[PASS] <input type="button" value="x" style="overflow: visible; appearance: none;">
+[PASS] <input type="button" value="x" style="overflow: hidden; appearance: none;">
+[FAIL] <input type="button" value="x" style="overflow: scroll; appearance: none;">
+  assert_equals: <span>.offsetTop expected 35 but got 27
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/widgets/the-select-element/select-as-listbox-default-styles.tentative-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/widgets/the-select-element/select-as-listbox-default-styles.tentative-expected.txt
new file mode 100644
index 0000000..ae7ce31
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/html/rendering/widgets/the-select-element/select-as-listbox-default-styles.tentative-expected.txt
@@ -0,0 +1,235 @@
+This is a testharness.js-based test.
+Found 193 tests; 154 PASS, 39 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[PASS] <select multiple=""><option>1 - display
+[PASS] <select multiple=""><option>1 - margin-top
+[PASS] <select multiple=""><option>1 - margin-right
+[PASS] <select multiple=""><option>1 - margin-bottom
+[PASS] <select multiple=""><option>1 - margin-left
+[PASS] <select multiple=""><option>1 - padding-top
+[PASS] <select multiple=""><option>1 - padding-right
+[PASS] <select multiple=""><option>1 - padding-bottom
+[PASS] <select multiple=""><option>1 - padding-left
+[PASS] <select multiple=""><option>1 - letter-spacing
+[PASS] <select multiple=""><option>1 - word-spacing
+[PASS] <select multiple=""><option>1 - text-transform
+[PASS] <select multiple=""><option>1 - text-indent
+[PASS] <select multiple=""><option>1 - text-shadow
+[PASS] <select multiple=""><option>1 - appearance
+[PASS] <select multiple=""><option>1 - box-sizing
+[FAIL] <select multiple=""><option>1 - border-top-width
+  assert_equals: expected "0px" but got "1px"
+[FAIL] <select multiple=""><option>1 - border-right-width
+  assert_equals: expected "0px" but got "1px"
+[FAIL] <select multiple=""><option>1 - border-bottom-width
+  assert_equals: expected "0px" but got "1px"
+[FAIL] <select multiple=""><option>1 - border-left-width
+  assert_equals: expected "0px" but got "1px"
+[FAIL] <select multiple=""><option>1 - border-top-style
+  assert_equals: expected "none" but got "solid"
+[FAIL] <select multiple=""><option>1 - border-right-style
+  assert_equals: expected "none" but got "solid"
+[FAIL] <select multiple=""><option>1 - border-bottom-style
+  assert_equals: expected "none" but got "solid"
+[FAIL] <select multiple=""><option>1 - border-left-style
+  assert_equals: expected "none" but got "solid"
+[FAIL] <select multiple=""><option>1 - border-top-color
+  assert_equals: expected "rgb(0, 0, 0)" but got "rgb(118, 118, 118)"
+[FAIL] <select multiple=""><option>1 - border-right-color
+  assert_equals: expected "rgb(0, 0, 0)" but got "rgb(118, 118, 118)"
+[FAIL] <select multiple=""><option>1 - border-bottom-color
+  assert_equals: expected "rgb(0, 0, 0)" but got "rgb(118, 118, 118)"
+[FAIL] <select multiple=""><option>1 - border-left-color
+  assert_equals: expected "rgb(0, 0, 0)" but got "rgb(118, 118, 118)"
+[FAIL] <select multiple=""><option>1 - align-items
+  assert_equals: expected "normal" but got "flex-start"
+[FAIL] <select multiple=""><option>1 - white-space
+  assert_equals: expected "normal" but got "nowrap"
+[PASS] <select multiple=""><option>1 - color
+[FAIL] <select multiple=""><option>1 - background-color
+  assert_equals: expected "rgba(0, 0, 0, 0)" but got "rgb(255, 255, 255)"
+[FAIL] <select multiple=""><option>1 - cursor
+  assert_equals: expected "auto" but got "default"
+[PASS] <select multiple=""><option>1 - font-style
+[PASS] <select multiple=""><option>1 - font-weight
+[FAIL] <select multiple=""><option>1 - font-size
+  assert_equals: expected "16px" but got "13.3333px"
+[FAIL] <select multiple=""><option>1 - font-family
+  assert_equals: expected "\\"Times New Roman\\"" but got "Arial"
+[PASS] <select multiple=""><option>1 - writing-mode
+[PASS] <select multiple=""><option>1 - scrollbar-width
+[FAIL] <select multiple=""><option>1 - overflow
+  assert_equals: expected "visible" but got "hidden scroll"
+[FAIL] <select multiple=""><option>1 - vertical-align
+  assert_equals: expected "baseline" but got "text-bottom"
+[PASS] <select multiple=""><option>1 - user-select
+[PASS] <select multiple=""><option>1 - page-break-inside
+[PASS] <select multiple=""><option>1 - overflow-clip-box
+[PASS] <select multiple=""><option>1 - font-variant-ligatures
+[PASS] <select multiple=""><option>1 - font-variant-caps
+[PASS] <select multiple=""><option>1 - font-variant-numeric
+[PASS] <select multiple=""><option>1 - font-variant-east-asian
+[PASS] <select multiple=""><option>1 - text-rendering
+[PASS] <option>1 (in <select multiple="">) - margin-top
+[PASS] <option>1 (in <select multiple="">) - margin-right
+[PASS] <option>1 (in <select multiple="">) - margin-bottom
+[PASS] <option>1 (in <select multiple="">) - margin-left
+[PASS] <option>1 (in <select multiple="">) - padding-top
+[FAIL] <option>1 (in <select multiple="">) - padding-right
+  assert_equals: expected "0px" but got "2px"
+[FAIL] <option>1 (in <select multiple="">) - padding-bottom
+  assert_equals: expected "0px" but got "1px"
+[FAIL] <option>1 (in <select multiple="">) - padding-left
+  assert_equals: expected "0px" but got "2px"
+[PASS] <option>1 (in <select multiple="">) - letter-spacing
+[PASS] <option>1 (in <select multiple="">) - word-spacing
+[PASS] <option>1 (in <select multiple="">) - text-transform
+[PASS] <option>1 (in <select multiple="">) - text-indent
+[PASS] <option>1 (in <select multiple="">) - text-shadow
+[PASS] <option>1 (in <select multiple="">) - appearance
+[PASS] <option>1 (in <select multiple="">) - box-sizing
+[PASS] <option>1 (in <select multiple="">) - border-top-width
+[PASS] <option>1 (in <select multiple="">) - border-right-width
+[PASS] <option>1 (in <select multiple="">) - border-bottom-width
+[PASS] <option>1 (in <select multiple="">) - border-left-width
+[PASS] <option>1 (in <select multiple="">) - border-top-style
+[PASS] <option>1 (in <select multiple="">) - border-right-style
+[PASS] <option>1 (in <select multiple="">) - border-bottom-style
+[PASS] <option>1 (in <select multiple="">) - border-left-style
+[PASS] <option>1 (in <select multiple="">) - border-top-color
+[PASS] <option>1 (in <select multiple="">) - border-right-color
+[PASS] <option>1 (in <select multiple="">) - border-bottom-color
+[PASS] <option>1 (in <select multiple="">) - border-left-color
+[PASS] <option>1 (in <select multiple="">) - align-items
+[FAIL] <option>1 (in <select multiple="">) - white-space
+  assert_equals: expected "normal" but got "nowrap"
+[PASS] <option>1 (in <select multiple="">) - color
+[PASS] <option>1 (in <select multiple="">) - background-color
+[FAIL] <option>1 (in <select multiple="">) - cursor
+  assert_equals: expected "auto" but got "default"
+[PASS] <option>1 (in <select multiple="">) - font-style
+[PASS] <option>1 (in <select multiple="">) - font-weight
+[FAIL] <option>1 (in <select multiple="">) - font-size
+  assert_equals: expected "16px" but got "13.3333px"
+[FAIL] <option>1 (in <select multiple="">) - font-family
+  assert_equals: expected "\\"Times New Roman\\"" but got "Arial"
+[PASS] <option>1 (in <select multiple="">) - writing-mode
+[PASS] <option>1 (in <select multiple="">) - scrollbar-width
+[PASS] <option>1 (in <select multiple="">) - overflow
+[PASS] <option>1 (in <select multiple="">) - vertical-align
+[PASS] <option>1 (in <select multiple="">) - user-select
+[PASS] <option>1 (in <select multiple="">) - page-break-inside
+[PASS] <option>1 (in <select multiple="">) - overflow-clip-box
+[PASS] <option>1 (in <select multiple="">) - font-variant-ligatures
+[PASS] <option>1 (in <select multiple="">) - font-variant-caps
+[PASS] <option>1 (in <select multiple="">) - font-variant-numeric
+[PASS] <option>1 (in <select multiple="">) - font-variant-east-asian
+[PASS] <option>1 (in <select multiple="">) - text-rendering
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - margin-top
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - margin-right
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - margin-bottom
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - margin-left
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - padding-top
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - padding-right
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - padding-bottom
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - padding-left
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - letter-spacing
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - word-spacing
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - text-transform
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - text-indent
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - text-shadow
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - appearance
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - box-sizing
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-top-width
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-right-width
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-bottom-width
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-left-width
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-top-style
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-right-style
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-bottom-style
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-left-style
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-top-color
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-right-color
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-bottom-color
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - border-left-color
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - align-items
+[FAIL] <optgroup label="2"><option>3 (in <select multiple="">) - white-space
+  assert_equals: expected "normal" but got "nowrap"
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - color
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - background-color
+[FAIL] <optgroup label="2"><option>3 (in <select multiple="">) - cursor
+  assert_equals: expected "auto" but got "default"
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - font-style
+[FAIL] <optgroup label="2"><option>3 (in <select multiple="">) - font-weight
+  assert_equals: expected "400" but got "700"
+[FAIL] <optgroup label="2"><option>3 (in <select multiple="">) - font-size
+  assert_equals: expected "16px" but got "13.3333px"
+[FAIL] <optgroup label="2"><option>3 (in <select multiple="">) - font-family
+  assert_equals: expected "\\"Times New Roman\\"" but got "Arial"
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - writing-mode
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - scrollbar-width
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - overflow
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - vertical-align
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - user-select
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - page-break-inside
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - overflow-clip-box
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - font-variant-ligatures
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - font-variant-caps
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - font-variant-numeric
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - font-variant-east-asian
+[PASS] <optgroup label="2"><option>3 (in <select multiple="">) - text-rendering
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - margin-top
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - margin-right
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - margin-bottom
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - margin-left
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - padding-top
+[FAIL] <option>3 (in <select multiple=""><optgroup label="2">) - padding-right
+  assert_equals: expected "0px" but got "2px"
+[FAIL] <option>3 (in <select multiple=""><optgroup label="2">) - padding-bottom
+  assert_equals: expected "0px" but got "1px"
+[FAIL] <option>3 (in <select multiple=""><optgroup label="2">) - padding-left
+  assert_equals: expected "0px" but got "20px"
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - letter-spacing
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - word-spacing
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - text-transform
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - text-indent
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - text-shadow
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - appearance
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - box-sizing
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-top-width
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-right-width
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-bottom-width
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-left-width
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-top-style
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-right-style
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-bottom-style
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-left-style
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-top-color
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-right-color
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-bottom-color
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - border-left-color
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - align-items
+[FAIL] <option>3 (in <select multiple=""><optgroup label="2">) - white-space
+  assert_equals: expected "normal" but got "nowrap"
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - color
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - background-color
+[FAIL] <option>3 (in <select multiple=""><optgroup label="2">) - cursor
+  assert_equals: expected "auto" but got "default"
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - font-style
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - font-weight
+[FAIL] <option>3 (in <select multiple=""><optgroup label="2">) - font-size
+  assert_equals: expected "16px" but got "13.3333px"
+[FAIL] <option>3 (in <select multiple=""><optgroup label="2">) - font-family
+  assert_equals: expected "\\"Times New Roman\\"" but got "Arial"
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - writing-mode
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - scrollbar-width
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - overflow
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - vertical-align
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - user-select
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - page-break-inside
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - overflow-clip-box
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - font-variant-ligatures
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - font-variant-caps
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - font-variant-numeric
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - font-variant-east-asian
+[PASS] <option>3 (in <select multiple=""><optgroup label="2">) - text-rendering
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/payment-request/payment-request-disallowed-when-hidden.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/payment-request/payment-request-disallowed-when-hidden.https-expected.txt
new file mode 100644
index 0000000..602c101f
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/payment-request/payment-request-disallowed-when-hidden.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] PaymentRequest.show() cannot be triggered from a hidden context
+  assert_equals: expected true but got false
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/pointerevents/pointerevent_constructor-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/pointerevents/pointerevent_constructor-expected.txt
new file mode 100644
index 0000000..cc350ae2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/pointerevents/pointerevent_constructor-expected.txt
@@ -0,0 +1,44 @@
+This is a testharness.js-based test.
+[PASS] PointerEvent: Constructor test
+[FAIL] getCoalescedEvents in event
+  assert_equals: expected false but got true
+[PASS] getPredictedEvents().length
+[PASS] event.target
+[PASS] event.currentTarget
+[PASS] event.eventPhase
+[PASS] event.clientX
+[PASS] event.pointerType
+[PASS] getPredictedEvents()[0].clientX
+[PASS] getPredictedEvents()[1].clientX
+[PASS] getPredictedEvents()[0].pointerId
+[PASS] getPredictedEvents()[0].pointerType
+[PASS] getPredictedEvents()[0].isPrimary
+[PASS] getPredictedEvents()[0].getPredictedEvents().length
+[FAIL] getPredictedEvents()[0].target
+  assert_equals: expected null but got Element node <div id="target0"></div>
+[PASS] getPredictedEvents()[0].currentTarget
+[PASS] getPredictedEvents()[0].eventPhase
+[PASS] getPredictedEvents()[0].cancelable
+[PASS] getPredictedEvents()[0].bubbles
+[FAIL] getPredictedEvents()[0].offsetX
+  assert_equals: expected 320 but got 312
+[FAIL] getPredictedEvents()[0].offsetY
+  assert_equals: expected 0 but got -119.140625
+[PASS] getPredictedEvents()[1].pointerId
+[PASS] getPredictedEvents()[1].pointerType
+[PASS] getPredictedEvents()[1].isPrimary
+[PASS] getPredictedEvents()[1].getPredictedEvents().length
+[FAIL] getPredictedEvents()[1].target
+  assert_equals: expected null but got Element node <div id="target0"></div>
+[PASS] getPredictedEvents()[1].currentTarget
+[PASS] getPredictedEvents()[1].eventPhase
+[PASS] getPredictedEvents()[1].cancelable
+[PASS] getPredictedEvents()[1].bubbles
+[FAIL] getPredictedEvents()[1].offsetX
+  assert_equals: expected 330 but got 322
+[FAIL] getPredictedEvents()[1].offsetY
+  assert_equals: expected 0 but got -119.140625
+[PASS] default event.pointerType
+[PASS] default getPredictedEvents().length
+[PASS] type event.pointerType
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/pointerevents/pointerevent_constructor.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/pointerevents/pointerevent_constructor.https-expected.txt
new file mode 100644
index 0000000..236d744b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/pointerevents/pointerevent_constructor.https-expected.txt
@@ -0,0 +1,79 @@
+This is a testharness.js-based test.
+Found 64 tests; 52 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
+[PASS] PointerEvent: Constructor test
+[PASS] getCoalescedEvents().length
+[PASS] getPredictedEvents().length
+[PASS] event.target
+[PASS] event.currentTarget
+[PASS] event.eventPhase
+[PASS] event.clientX
+[PASS] event.pointerType
+[PASS] getCoalescedEvents()[0].clientX
+[PASS] getCoalescedEvents()[1].clientX
+[PASS] getPredictedEvents()[0].clientX
+[PASS] getPredictedEvents()[1].clientX
+[PASS] getCoalescedEvents()[0].pointerId
+[PASS] getCoalescedEvents()[0].pointerType
+[PASS] getCoalescedEvents()[0].isPrimary
+[PASS] getCoalescedEvents()[0].getCoalescedEvents().length
+[PASS] getCoalescedEvents()[0].getPredictedEvents().length
+[FAIL] getCoalescedEvents()[0].target
+  assert_equals: expected null but got Element node <div id="target0"></div>
+[PASS] getCoalescedEvents()[0].currentTarget
+[PASS] getCoalescedEvents()[0].eventPhase
+[PASS] getCoalescedEvents()[0].cancelable
+[PASS] getCoalescedEvents()[0].bubbles
+[FAIL] getCoalescedEvents()[0].offsetX
+  assert_equals: expected 300 but got 292
+[FAIL] getCoalescedEvents()[0].offsetY
+  assert_equals: expected 0 but got -119.140625
+[PASS] getCoalescedEvents()[1].pointerId
+[PASS] getCoalescedEvents()[1].pointerType
+[PASS] getCoalescedEvents()[1].isPrimary
+[PASS] getCoalescedEvents()[1].getCoalescedEvents().length
+[PASS] getCoalescedEvents()[1].getPredictedEvents().length
+[FAIL] getCoalescedEvents()[1].target
+  assert_equals: expected null but got Element node <div id="target0"></div>
+[PASS] getCoalescedEvents()[1].currentTarget
+[PASS] getCoalescedEvents()[1].eventPhase
+[PASS] getCoalescedEvents()[1].cancelable
+[PASS] getCoalescedEvents()[1].bubbles
+[FAIL] getCoalescedEvents()[1].offsetX
+  assert_equals: expected 310 but got 302
+[FAIL] getCoalescedEvents()[1].offsetY
+  assert_equals: expected 0 but got -119.140625
+[PASS] getPredictedEvents()[0].pointerId
+[PASS] getPredictedEvents()[0].pointerType
+[PASS] getPredictedEvents()[0].isPrimary
+[PASS] getPredictedEvents()[0].getCoalescedEvents().length
+[PASS] getPredictedEvents()[0].getPredictedEvents().length
+[FAIL] getPredictedEvents()[0].target
+  assert_equals: expected null but got Element node <div id="target0"></div>
+[PASS] getPredictedEvents()[0].currentTarget
+[PASS] getPredictedEvents()[0].eventPhase
+[PASS] getPredictedEvents()[0].cancelable
+[PASS] getPredictedEvents()[0].bubbles
+[FAIL] getPredictedEvents()[0].offsetX
+  assert_equals: expected 320 but got 312
+[FAIL] getPredictedEvents()[0].offsetY
+  assert_equals: expected 0 but got -119.140625
+[PASS] getPredictedEvents()[1].pointerId
+[PASS] getPredictedEvents()[1].pointerType
+[PASS] getPredictedEvents()[1].isPrimary
+[PASS] getPredictedEvents()[1].getCoalescedEvents().length
+[PASS] getPredictedEvents()[1].getPredictedEvents().length
+[FAIL] getPredictedEvents()[1].target
+  assert_equals: expected null but got Element node <div id="target0"></div>
+[PASS] getPredictedEvents()[1].currentTarget
+[PASS] getPredictedEvents()[1].eventPhase
+[PASS] getPredictedEvents()[1].cancelable
+[PASS] getPredictedEvents()[1].bubbles
+[FAIL] getPredictedEvents()[1].offsetX
+  assert_equals: expected 330 but got 322
+[FAIL] getPredictedEvents()[1].offsetY
+  assert_equals: expected 0 but got -119.140625
+[PASS] default event.pointerType
+[PASS] default getCoalescedEvents().length
+[PASS] default getPredictedEvents().length
+[PASS] type event.pointerType
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-accepted.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-accepted.https-expected.txt
new file mode 100644
index 0000000..dab58ce
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-accepted.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] Successful SPC authentication
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-cannot-bypass-spc.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-cannot-bypass-spc.https-expected.txt
new file mode 100644
index 0000000..6ab4a62
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-cannot-bypass-spc.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] Cannot bypass SPC authentication UI via navigator.credentials.get
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-cross-origin.sub.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-cross-origin.sub.https-expected.txt
new file mode 100644
index 0000000..ad0e9f96
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-cross-origin.sub.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] Cross-origin SPC authentication ceremony
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-icon-data-url.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-icon-data-url.https-expected.txt
new file mode 100644
index 0000000..4814e6a1
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-icon-data-url.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] SPC authentication with data URL instrument icon
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-in-iframe.sub.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-in-iframe.sub.https-expected.txt
new file mode 100644
index 0000000..29b52935
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-in-iframe.sub.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+[FAIL] SPC authentication ceremony in cross-origin iframe
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+[FAIL] SPC authentication ceremony in cross-origin iframe without payment permission
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-invalid-icon.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-invalid-icon.https-expected.txt
new file mode 100644
index 0000000..18f5b5b
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-invalid-icon.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+[FAIL] SPC authentication with an invalid icon
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+[FAIL] SPC authentication allowing an invalid icon with iconMustBeShown option.
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-optout.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-optout.https-expected.txt
new file mode 100644
index 0000000..2f8fa15d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-optout.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] SPC opt-out returns OptOutError
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-rejected.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-rejected.https-expected.txt
new file mode 100644
index 0000000..79dc3f2a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-rejected.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] Rejected SPC authentication
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt
new file mode 100644
index 0000000..8d1c623
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/authentication-requires-user-activation.https-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] SPC authentication not allowed without a user activation
+  promise_test: Unhandled rejection with value: "error: Action set_spc_transaction_mode not implemented"
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/enrollment-in-iframe.sub.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/enrollment-in-iframe.sub.https-expected.txt
new file mode 100644
index 0000000..651914b0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/secure-payment-confirmation/enrollment-in-iframe.sub.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+[FAIL] SPC enrollment in cross-origin iframe
+  assert_equals: expected null but got object "NotSupportedError: The user agent does not support public key credentials."
+[PASS] SPC enrollment in cross-origin iframe fails without user activation
+[PASS] SPC enrollment in cross-origin iframe without payment permission
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/service-workers/service-worker/navigate-window.https-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/service-workers/service-worker/navigate-window.https-expected.txt
new file mode 100644
index 0000000..88c2a93
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/service-workers/service-worker/navigate-window.https-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+[FAIL] Clients.matchAll() should not show an old window as controlled after it navigates.
+  assert_unreached: unexpected rejection: assert_equals: client should have expected frame type expected "auxiliary" but got "top-level" Reached unreachable code
+[FAIL] Clients.matchAll() should not show an old window after it navigates.
+  assert_unreached: unexpected rejection: assert_equals: expected number of clients expected 3 but got 4 Reached unreachable code
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/storage-access-api/requestStorageAccess-dedicated-worker.tentative.sub.https.window-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/storage-access-api/requestStorageAccess-dedicated-worker.tentative.sub.https.window-expected.txt
index 8bd56bd..dfe658f 100644
--- a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/storage-access-api/requestStorageAccess-dedicated-worker.tentative.sub.https.window-expected.txt
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/storage-access-api/requestStorageAccess-dedicated-worker.tentative.sub.https.window-expected.txt
@@ -2,5 +2,5 @@
 [FAIL] Workers inherit storage access
   assert_true: requestStorageAccess resolves without requiring a gesture. expected true got false
 [FAIL] Workers don't observe parent's storage access
-  assert_equals: Worker's first fetch is uncredentialed. expected "" but got "cookie=unpartitioned"
+  assert_false: Worker's first fetch is uncredentialed. expected false got true
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/websockets/cookies/007_wpt_flags=h2-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/websockets/cookies/007_wpt_flags=h2-expected.txt
new file mode 100644
index 0000000..c6e1cc49
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/websockets/cookies/007_wpt_flags=h2-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] WebSockets: when to process set-cookie fields in ws response
+  assert_unreached: cookie was set during script execution Reached unreachable code
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/external/wpt/websockets/unload-a-document/002_wpt_flags=h2-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/websockets/unload-a-document/002_wpt_flags=h2-expected.txt
new file mode 100644
index 0000000..b9637bf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/external/wpt/websockets/unload-a-document/002_wpt_flags=h2-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+[FAIL] WebSockets: navigating top-level browsing context with closed websocket
+  assert_unreached: document was discarded Reached unreachable code
+Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/linux-chrome/virtual/saa-beyond-cookies-disabled/external/wpt/storage-access-api/requestStorageAccess-dedicated-worker.tentative.sub.https.window-expected.txt b/third_party/blink/web_tests/platform/linux-chrome/virtual/saa-beyond-cookies-disabled/external/wpt/storage-access-api/requestStorageAccess-dedicated-worker.tentative.sub.https.window-expected.txt
new file mode 100644
index 0000000..8bd56bd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux-chrome/virtual/saa-beyond-cookies-disabled/external/wpt/storage-access-api/requestStorageAccess-dedicated-worker.tentative.sub.https.window-expected.txt
@@ -0,0 +1,6 @@
+This is a testharness.js-based test.
+[FAIL] Workers inherit storage access
+  assert_true: requestStorageAccess resolves without requiring a gesture. expected true got false
+[FAIL] Workers don't observe parent's storage access
+  assert_equals: Worker's first fetch is uncredentialed. expected "" but got "cookie=unpartitioned"
+Harness: the test ran to completion.
diff --git a/third_party/chromite b/third_party/chromite
index 65c1393..095fc4f4 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 65c1393ab1f7ae98bb8a60ca0cfb1ad5e2d57e9f
+Subproject commit 095fc4f448750165f1e6546410069fcdaceaf617
diff --git a/third_party/dawn b/third_party/dawn
index e30eacb..18ac67f 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit e30eacb0b6e8725e79c283908b7cb0c7f219ac44
+Subproject commit 18ac67fc72dfff47efec97c2a8bcf654a13a9e37
diff --git a/third_party/devtools-frontend-internal b/third_party/devtools-frontend-internal
index b32c6af..a3d3736 160000
--- a/third_party/devtools-frontend-internal
+++ b/third_party/devtools-frontend-internal
@@ -1 +1 @@
-Subproject commit b32c6af7c0da8b57e81dd7217f1a519e3b8805e0
+Subproject commit a3d37368696ffdbea916cb1114c64f06755a6f79
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index c3e6c9a..97d7a06 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit c3e6c9af4f1260fce7879783b5fb8bbcf0b273bd
+Subproject commit 97d7a065030a8930d942dc0b3ee66e79e89c6c78
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index b88a34b..dc4928f 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby
-Version: 629e7ba7c964a62c97f6a882bffaec3c8490bb1e
+Version: a83fbe3ebb47980e204c39cf23f20813e2048a2f
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/nearby/src b/third_party/nearby/src
index 629e7ba..a83fbe3 160000
--- a/third_party/nearby/src
+++ b/third_party/nearby/src
@@ -1 +1 @@
-Subproject commit 629e7ba7c964a62c97f6a882bffaec3c8490bb1e
+Subproject commit a83fbe3ebb47980e204c39cf23f20813e2048a2f
diff --git a/third_party/skia b/third_party/skia
index ae6df72..e653081 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit ae6df7264c6eedbe988e05a12455c619e1a0f242
+Subproject commit e653081ca3138dda1a9abaa9e90cdbf5ae980523
diff --git a/third_party/webrtc b/third_party/webrtc
index 283a5fd..2baa7ae 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 283a5fd7ec64d231b1369843ac24d18222ecfde7
+Subproject commit 2baa7ae9e03553da5da3b2c232431602a64fcd50
diff --git a/third_party/webrtc_overrides/BUILD.gn b/third_party/webrtc_overrides/BUILD.gn
index e726b676..ba8a413 100644
--- a/third_party/webrtc_overrides/BUILD.gn
+++ b/third_party/webrtc_overrides/BUILD.gn
@@ -125,7 +125,6 @@
   "//third_party/webrtc/rtc_base:threading",
   "//third_party/webrtc/rtc_base:timestamp_aligner",
   "//third_party/webrtc/rtc_base:timeutils",
-  "//third_party/webrtc/rtc_base:voucher",
   "//third_party/webrtc/rtc_base/system:rtc_export",
   "//third_party/webrtc/rtc_base/third_party/base64",
   "//third_party/webrtc/rtc_base/third_party/sigslot",
diff --git a/third_party/win_virtual_display/3pp/3pp.pb b/third_party/win_virtual_display/3pp/3pp.pb
new file mode 100644
index 0000000..23ecc35
--- /dev/null
+++ b/third_party/win_virtual_display/3pp/3pp.pb
@@ -0,0 +1,16 @@
+create {
+  platform_re: "windows-amd64"
+  source {
+    script { name: "fetch.py" }
+    unpack_archive: true
+    subdir: "third_party/win_virtual_display"
+  }
+  build {
+    install: "build.py"
+    no_toolchain: true
+  }
+}
+
+upload {
+  pkg_prefix: "chromium/third_party"
+}
diff --git a/third_party/win_virtual_display/3pp/build.py b/third_party/win_virtual_display/3pp/build.py
new file mode 100755
index 0000000..f4cf7c8
--- /dev/null
+++ b/third_party/win_virtual_display/3pp/build.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+# 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.
+
+import hashlib
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+import urllib.request
+
+# Windows 11, version 22H2 EWDK with Visual Studio Build Tools 17.1.5
+EWDK_URL = "https://go.microsoft.com/fwlink/?linkid=2195661"
+# SHA256 hash of the ISO that we expect.
+EWDK_HASH = "887d484454c677db191bf444380a3059a20dd87ee56b1028b12fd8cb52b997f0"
+# Script in the wdk to run to set up the build environment.
+BUILD_ENV = "BuildEnv\\SetupBuildEnv.cmd"
+# MSVC++ project file to build.
+BUILD_FILE = "third_party\\win_virtual_display\\driver\\ChromiumVirtualDisplayDriver.vcxproj"
+
+
+def get_ewdk_iso_path():
+    return os.path.join(tempfile.gettempdir(), "ewdk.iso")
+
+
+def get_ewdk_iso_extract_path():
+    return os.path.join(tempfile.gettempdir(), "wdk")
+
+
+def hash_file(file_path):
+    """Returns SHA256 hash of a specified file."""
+    sha256 = hashlib.sha256()
+    with open(file_path, "rb") as f:
+        for b in iter(lambda: f.read(2048), b""):
+            sha256.update(b)
+    return sha256.hexdigest()
+
+
+def fetch_and_mount_ewdk():
+    """ Fetches the EWK ISO and mounts it as a drive.
+    Returns the drive letter that it was mounted to (e.g. "D")."""
+    iso_path = get_ewdk_iso_path()
+    print(f"Downloading iso to: {iso_path}")
+    if os.path.isfile(iso_path) == False:
+        urllib.request.urlretrieve(EWDK_URL, iso_path)
+    else:
+        print(f"File exists. Skipping ISO download.")
+    # Check the hash of the download so that unexpected changes are flagged.
+    iso_hash = hash_file(iso_path)
+    if iso_hash != EWDK_HASH:
+        print(f"iso hash mismatch. Expected: {EWDK_HASH}. Got: {iso_hash}")
+    cmd = [
+        "powershell.exe",
+        "-command",
+        "Mount-DiskImage",
+        "-ImagePath",
+        iso_path,
+        "|",
+        "Get-Volume",
+        "|ForEach-Object",
+        "DriveLetter",
+    ]
+    result = subprocess.run(cmd, capture_output=True, text=True)
+    if (result.returncode!=0):
+      raise Exception(f"Failed to mount ISO: {result.stdout}")
+    output_drive = result.stdout.strip()
+    if (len(output_drive)!=1):
+      raise Exception(f"Failed to mount ISO: No drive letter obtained.")
+    print(f"ISO mounted to drive letter: {output_drive}")
+    return output_drive
+
+
+def unmount_ewdk():
+    """Unmounts the ISO from its drive letter."""
+    iso_path = get_ewdk_iso_path()
+    cmd = ["powershell.exe", "-command", "Dismount-DiskImage", iso_path]
+    subprocess.run(cmd)
+
+
+def build(ewdk_path, output_path):
+    """Build the vcxproj using the specified ewdk path and output path."""
+    build_env = os.path.join(ewdk_path, BUILD_ENV)
+    build_path = tempfile.gettempdir() + "\\build\\"
+    command = (f"{build_env} && msbuild {BUILD_FILE} /t:build "
+              f"/property:Platform=x64 /p:OutDir={build_path}")
+    cmd = ["cmd", "/c", command]
+    result = subprocess.run(cmd, capture_output=True, text=True)
+    if "Build succeeded" not in result.stdout:
+        print(result.stdout)
+        raise Exception("Build failed.")
+    # Copy compilation output and test certificate files to the output path.
+    print(f"Copying build output to {output_path}")
+    shutil.copytree(
+        os.path.join(build_path, "ChromiumVirtualDisplayDriver"),
+        output_path,
+        dirs_exist_ok=True,
+    )
+    shutil.copy(os.path.join(build_path, "ChromiumVirtualDisplayDriver.cer"),
+                output_path)
+
+
+def main():
+    mounted_drive_letter = fetch_and_mount_ewdk()
+    try:
+        # msbuild fails with "Access Denied" if executing directly on the mounted ISO.
+        # Copy the contents to the real filesystem.
+        ewdk_path = get_ewdk_iso_extract_path()
+        src_path = mounted_drive_letter + ":\\"
+        print(f"Copying directory {src_path} to {ewdk_path}")
+        shutil.copytree(src_path, ewdk_path, dirs_exist_ok=True)
+        # If second argument exists, copy build output to it, otherwise use the
+        # current directory.
+        build(ewdk_path, sys.argv[2] if len(sys.argv) > 2 else os.getcwd())
+    finally:
+        print("Unmounting ISO")
+        unmount_ewdk()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/third_party/win_virtual_display/3pp/fetch.py b/third_party/win_virtual_display/3pp/fetch.py
new file mode 100755
index 0000000..1c402567
--- /dev/null
+++ b/third_party/win_virtual_display/3pp/fetch.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# 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.
+
+import argparse
+import json
+import os
+import urllib.request
+
+_GIT_HOST = "https://chromium.googlesource.com/chromium/src.git/"
+_COMMIT_URL = _GIT_HOST + "+log/refs/heads/main/third_party/win_virtual_display?n=1&format=json"
+_ARCHIVE_URL = _GIT_HOST + "+archive/{}/third_party/win_virtual_display.tar.gz"
+
+
+def get_last_commit():
+    """Returns the last commit of third_party/win_virtual_display."""
+    content = urllib.request.urlopen(_COMMIT_URL).read().decode("utf-8")
+    # Remove the leading json safety prefix.
+    assert content.startswith(")]}'")
+    return json.loads(content[4:])["log"][0]["commit"]
+
+
+def get_download_url(sha):
+    """Returns the manifest for the following download procedure."""
+    partial_manifest = {
+        "url": [_ARCHIVE_URL.format(sha)],
+        "ext": ".tar.gz",
+    }
+    print(json.dumps(partial_manifest))
+
+
+def main():
+    ap = argparse.ArgumentParser()
+    sub = ap.add_subparsers()
+
+    latest = sub.add_parser("latest")
+    latest.set_defaults(func=lambda: print(get_last_commit()))
+
+    download = sub.add_parser("get_url")
+    download.set_defaults(func=lambda: get_download_url(
+        os.environ.get("_3PP_VERSION", get_last_commit())))
+
+    ap.parse_args().func()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py
index a4f759c4..f29e902 100644
--- a/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py
+++ b/third_party/wpt_tools/wpt/tools/wptrunner/wptrunner/testrunner.py
@@ -703,6 +703,13 @@
                 expected_fail_message = test.expected_fail_message(result.name)
                 if expected_fail_message is not None and result.message.strip() != expected_fail_message:
                     is_unexpected = True
+                    if result.status in known_intermittent:
+                        known_intermittent.remove(result.status)
+                    elif len(known_intermittent) > 0:
+                        expected = known_intermittent[0]
+                        known_intermittent = known_intermittent[1:]
+                    else:
+                        expected = "PASS"
 
             if is_unexpected:
                 subtest_unexpected = True
diff --git a/tools/clang/plugins/RawPtrManualPathsToIgnore.h b/tools/clang/plugins/RawPtrManualPathsToIgnore.h
index 3694d3d..954c3d7 100644
--- a/tools/clang/plugins/RawPtrManualPathsToIgnore.h
+++ b/tools/clang/plugins/RawPtrManualPathsToIgnore.h
@@ -102,9 +102,6 @@
                                       // to",
                                       // public/renderer?",
 
-    // Moved from //third_party/blink/renderer/platform/image-decoders/
-    "components/image_decoders/",
-
     // Contains sysroot dirs like debian_bullseye_amd64-sysroot/ that are not
     // part of the repository.
     "build/linux/",
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 2a2ff49f..4a07720 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -122,7 +122,7 @@
       'fuchsia-angle-builder': 'angle_deqp_release_bot_dcheck_always_on_fuchsia_reclient',
       'ios-angle-builder': 'angle_deqp_release_bot_dcheck_always_on_ios_reclient',
       'linux-angle-chromium-builder': 'gpu_tests_release_trybot_minimal_symbols_reclient',
-      'mac-angle-chromium-builder': 'gpu_tests_release_trybot_minimal_symbols_reclient',
+      'mac-angle-chromium-builder': 'gpu_tests_release_trybot_minimal_symbols_x64_reclient',
       'win-angle-chromium-x64-builder': 'gpu_tests_release_trybot_minimal_symbols_reclient',
       'win-angle-chromium-x86-builder': 'gpu_tests_release_bot_dcheck_always_on_x86_reclient',
     },
@@ -905,7 +905,7 @@
       'fuchsia-angle-try': 'angle_deqp_release_trybot_fuchsia_reclient',
       'ios-angle-try-intel': 'angle_deqp_release_trybot_ios_reclient',
       'linux-angle-chromium-try': 'gpu_tests_release_trybot_reclient',
-      'mac-angle-chromium-try': 'gpu_tests_release_trybot_reclient',
+      'mac-angle-chromium-try': 'gpu_tests_release_trybot_x64_reclient',
       'win-angle-chromium-x64-try': 'gpu_tests_release_trybot_reclient',
       'win-angle-chromium-x86-try': 'gpu_tests_release_trybot_x86_reclient',
     },
@@ -2331,6 +2331,10 @@
       'gpu_tests', 'release_trybot_minimal_symbols_reclient',
     ],
 
+    'gpu_tests_release_trybot_minimal_symbols_x64_reclient': [
+      'gpu_tests', 'release_trybot_minimal_symbols_reclient', 'x64',
+    ],
+
     'gpu_tests_release_trybot_no_symbols_mac_code_coverage_x64_reclient': [
       'gpu_tests', 'release_trybot_reclient', 'no_symbols',
       'use_clang_coverage', 'partial_code_coverage_instrumentation',
@@ -2398,6 +2402,10 @@
       'gpu_tests', 'release_trybot_reclient', 'resource_allowlisting',
     ],
 
+    'gpu_tests_release_trybot_x64_reclient': [
+      'gpu_tests', 'release_trybot_reclient', 'x64',
+    ],
+
     'gpu_tests_release_trybot_x86_reclient': [
       'gpu_tests', 'release_trybot_reclient', 'x86',
     ],
diff --git a/tools/mb/mb_config_expectations/chromium.angle.json b/tools/mb/mb_config_expectations/chromium.angle.json
index 3255b3c..db36fc16 100644
--- a/tools/mb/mb_config_expectations/chromium.angle.json
+++ b/tools/mb/mb_config_expectations/chromium.angle.json
@@ -62,6 +62,7 @@
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 1,
+      "target_cpu": "x64",
       "use_dummy_lastchange": true,
       "use_remoteexec": true
     }
diff --git a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
index d3fe6733..6eb8668 100644
--- a/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
+++ b/tools/mb/mb_config_expectations/tryserver.chromium.angle.json
@@ -63,6 +63,7 @@
       "is_debug": false,
       "proprietary_codecs": true,
       "symbol_level": 0,
+      "target_cpu": "x64",
       "use_dummy_lastchange": true,
       "use_remoteexec": true
     }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 70e9609..f093dd6 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7090,6 +7090,7 @@
   <int value="7" label="PVM_SCRIPTED_PRINT_FENCED_FRAME"/>
   <int value="8" label="PVMB_SCRIPTED_PRINT_FENCED_FRAME"/>
   <int value="9" label="SSI_CREATE_FENCED_FRAME"/>
+  <int value="10" label="CCU_SUPERFLUOUS_BIND"/>
 </enum>
 
 <enum name="BadMessageReasonContent">
@@ -8162,11 +8163,6 @@
   <int value="1" label="Foreground"/>
 </enum>
 
-<enum name="BooleanInherited">
-  <int value="0" label="Not inherited"/>
-  <int value="1" label="Inherited"/>
-</enum>
-
 <enum name="BooleanInteractive">
   <int value="0" label="Non-interactive"/>
   <int value="1" label="Interactive"/>
@@ -8280,11 +8276,6 @@
   <int value="1" label="Ordered"/>
 </enum>
 
-<enum name="BooleanOverlap">
-  <int value="0" label="No overlap"/>
-  <int value="1" label="Overlapped"/>
-</enum>
-
 <enum name="BooleanOverlayDamageRect">
   <int value="0" label="Zero damage rect"/>
   <int value="1" label="Non-Zero damage rect"/>
@@ -10439,17 +10430,6 @@
   <int value="1" label="Advanced"/>
 </enum>
 
-<enum name="ClearDataSiteBlacklistCrossedReason">
-  <int value="0" label="Durable"/>
-  <int value="1" label="Notifications"/>
-  <int value="2" label="Engagement"/>
-  <int value="3" label="Notifications and Engagement"/>
-  <int value="4" label="Durable and Engagement"/>
-  <int value="5" label="Notifications and Durable"/>
-  <int value="6" label="Notifications, Durable, and Engagement"/>
-  <int value="7" label="Unknown"/>
-</enum>
-
 <enum name="ClearOnExitSyncEvent">
   <int value="0" label="Profile startup, sync is disabled"/>
   <int value="1" label="Profile startup, sync is paused"/>
@@ -10469,42 +10449,6 @@
   <int value="9" label="Profile shutdown, sync is paused"/>
 </enum>
 
-<enum name="ClearSiteDataParameters">
-  <int value="0" label="No valid types"/>
-  <int value="1" label="Cookies"/>
-  <int value="2" label="Storage"/>
-  <int value="3" label="Cookies and Storage"/>
-  <int value="4" label="Cache"/>
-  <int value="5" label="Cookies and Cache"/>
-  <int value="6" label="Storage and Cache"/>
-  <int value="7" label="Cookies, Storage, and Cache"/>
-  <int value="8" label="Storage buckets"/>
-  <int value="9" label="Storage buckets and Cookies"/>
-  <int value="10" label="Storage buckets and Storage"/>
-  <int value="11" label="Storage buckets, Cookies, and Storage"/>
-  <int value="12" label="Storage buckets and Cache"/>
-  <int value="13" label="Storage buckets, Cookies, and Cache"/>
-  <int value="14" label="Storage buckets, Storage, and Cache"/>
-  <int value="15" label="Storage buckets, Cookies, Storage, and Cache"/>
-  <int value="16" label="Client Hints"/>
-  <int value="17" label="Client Hints and Cookies"/>
-  <int value="18" label="Client Hints and Storage"/>
-  <int value="19" label="Client Hints, Cookies, and Storage"/>
-  <int value="20" label="Client Hints and Cache"/>
-  <int value="21" label="Client Hints, Cookies, and Cache"/>
-  <int value="22" label="Client Hints, Storage, and Cache"/>
-  <int value="23" label="Client Hints, Cookies, Storage, and Cache"/>
-  <int value="24" label="Client Hints and Storage buckets"/>
-  <int value="25" label="Client Hints, Storage buckets, and Cookies"/>
-  <int value="26" label="Client Hints, Storage buckets, and Storage"/>
-  <int value="27" label="Client Hints, Storage buckets, Cookies, and Storage"/>
-  <int value="28" label="Client Hints, Storage buckets, and Cache"/>
-  <int value="29" label="Client Hints, Storage buckets, Cookies and Cache"/>
-  <int value="30" label="Client Hints, Storage buckets, Storage and Cache"/>
-  <int value="31"
-      label="Client Hints, Storage buckets, Cookies, Storage, and Cache"/>
-</enum>
-
 <enum name="ClientAppId">
   <int value="0" label="Other"/>
   <int value="1" label="Gmail"/>
@@ -10536,33 +10480,6 @@
   <int value="4" label="User selection not permitted"/>
 </enum>
 
-<enum name="ClipboardFormatRead">
-  <int value="0" label="Text"/>
-  <int value="1" label="Html"/>
-  <int value="2" label="Rtf"/>
-  <int value="3" label="Image"/>
-  <int value="4" label="Bookmark"/>
-  <int value="5" label="Data"/>
-  <int value="6" label="CustomData"/>
-  <int value="7" label="WebSmartPaste"/>
-  <int value="8" label="Svg"/>
-  <int value="9" label="Filenames"/>
-  <int value="10" label="Png"/>
-</enum>
-
-<enum name="ClipboardFormatWrite">
-  <int value="0" label="Text"/>
-  <int value="1" label="Html"/>
-  <int value="2" label="Rtf"/>
-  <int value="3" label="Image"/>
-  <int value="4" label="Bookmark"/>
-  <int value="5" label="Data"/>
-  <int value="6" label="CustomData"/>
-  <int value="7" label="WebSmartPaste"/>
-  <int value="8" label="Svg"/>
-  <int value="9" label="Filenames"/>
-</enum>
-
 <enum name="CloudProvider">
   <int value="0" label="None"/>
   <int value="1" label="Unknown"/>
@@ -11501,14 +11418,6 @@
   <int value="29" label="Search web for... in new tab"/>
 </enum>
 
-<enum name="ContextRedirectTypeBug1221316">
-  <int value="0" label="Unset"/>
-  <int value="1" label="No Redirect"/>
-  <int value="2" label="Cross-Site Redirect"/>
-  <int value="3" label="Partially Same-Site Redirect"/>
-  <int value="4" label="Fully Same-Site Redirect"/>
-</enum>
-
 <enum name="ContextualSearchCardTag">
   <int value="0" label="None"/>
   <int value="1" label="CT_OTHER"/>
@@ -11914,14 +11823,6 @@
   <int value="4" label="kCacheMissAfterSet"/>
 </enum>
 
-<enum name="CookieCommitProblem">
-  <int value="0" label="Entry encryption failed"/>
-  <int value="1" label="Adding cookie to DB failed."/>
-  <int value="2" label="Updating access time of cookie failed."/>
-  <int value="3" label="Deleting cookie failed."/>
-  <int value="4" label="Committing the transaction failed"/>
-</enum>
-
 <enum name="CookieControlsMode">
   <int value="0" label="Off"/>
   <int value="1" label="BlockThirdParty"/>
@@ -11938,25 +11839,6 @@
   <int value="6" label="Eligible: Forced via kForceEligibleForTesting"/>
 </enum>
 
-<enum name="CookieEffectiveSameSite">
-  <int value="0" label="No restriction"/>
-  <int value="1" label="Lax mode"/>
-  <int value="2" label="Strict mode"/>
-  <int value="3" label="Lax mode allow unsafe"/>
-  <int value="4" label="Undefined"/>
-</enum>
-
-<enum name="CookieLoadProblem">
-  <int value="0" label="Entry decryption failed"/>
-  <int value="1"
-      label="(Obsolete) Entry decryption taking over a minute. Removed
-             03/2021. The problem rarely happens and can be reported through
-             other mechanisms."/>
-  <int value="2" label="Cookie canonical form check failed"/>
-  <int value="3" label="Could not open or initialize database"/>
-  <int value="4" label="Attempt to delete broken (and related) rows failed"/>
-</enum>
-
 <enum name="CookieOrCacheDeletion">
   <int value="0" label="Neither"/>
   <int value="1" label="Only cookies"/>
@@ -11964,57 +11846,6 @@
   <int value="3" label="Both"/>
 </enum>
 
-<enum name="CookiePrefix">
-  <int value="0" label="No special prefix"/>
-  <int value="1" label="Secure prefix"/>
-  <int value="2" label="Host prefix"/>
-</enum>
-
-<enum name="CookieRequestScheme">
-  <int value="0" label="Unset Cookie Scheme"/>
-  <int value="1" label="Nonsecurely Set Cookie Nonsecure Request"/>
-  <int value="2" label="Securely Set Cookie Secure Request"/>
-  <int value="3" label="Nonsecurely Set Cookie Secure Request"/>
-  <int value="4" label="Securely Set Cookie Nonsecure Request"/>
-</enum>
-
-<enum name="CookiesAllowedForUrlsUsage">
-  <int value="0" label="Explicit Content Settings Only"/>
-  <int value="1" label="Wildcard Primary Content Settings Only"/>
-  <int value="2" label="Wildcard Secondary Content Settings Only"/>
-  <int value="3" label="Wildcard Primary and Explicit Content Settings"/>
-  <int value="4" label="Wildcard Secondary and Explicit Content Settings"/>
-  <int value="5" label="Only Wildcard Content Settings"/>
-  <int value="6" label="All Types of Content Settings Present"/>
-</enum>
-
-<enum name="CookieSameSite2">
-  <int value="0" label="No SameSite attribute is specified."/>
-  <int value="1" label="SameSite=None"/>
-  <int value="2" label="SameSite=Lax"/>
-  <int value="3" label="SameSite=Strict"/>
-</enum>
-
-<enum name="CookieSameSiteString">
-  <int value="0" label="No SameSite attribute is present."/>
-  <int value="1" label="SameSite attribute is specified but has no value."/>
-  <int value="2" label="SameSite attribute value is unrecognized."/>
-  <int value="3" label="Lax"/>
-  <int value="4" label="Strict"/>
-  <int value="5" label="None"/>
-  <int value="6" label="Extended"/>
-</enum>
-
-<enum name="CookieSentToSamePort">
-  <int value="0" label="Cookie source port was unspecified."/>
-  <int value="1" label="Cookie source port was invalid."/>
-  <int value="2" label="Source and destination ports do not match."/>
-  <int value="3"
-      label="Source and destination ports do not match but are the defaults
-             for their scheme."/>
-  <int value="4" label="Source and destination ports match."/>
-</enum>
-
 <enum name="CookiesInUseDialogActions">
   <summary>Actions done in &quot;Cookies in use&quot; dialog.</summary>
   <int value="0" label="Dialog opened"/>
@@ -12027,51 +11858,6 @@
   <int value="7" label="Site cleared on exit"/>
 </enum>
 
-<enum name="CookieSourceScheme">
-  <int value="0" label="Secure cookie, source scheme is cryptographic"/>
-  <int value="1" label="Secure cookie, source scheme is not cryptographic"/>
-  <int value="2" label="Not Secure cookie, source scheme is cryptographic"/>
-  <int value="3" label="Not Secure cookie, source scheme is not cryptographic"/>
-</enum>
-
-<enum name="CookieSourceSchemeName">
-  <int value="0" label="Other Scheme"/>
-  <int value="1" label="About Blank URL"/>
-  <int value="2" label="About Srcdoc URL"/>
-  <int value="3" label="About Blank Path"/>
-  <int value="4" label="About Srcdoc Path"/>
-  <int value="5" label="About Scheme"/>
-  <int value="6" label="Blob Scheme"/>
-  <int value="7" label="Content Scheme"/>
-  <int value="8" label="Content ID Scheme"/>
-  <int value="9" label="Data Scheme"/>
-  <int value="10" label="File Scheme"/>
-  <int value="11" label="File System Scheme"/>
-  <int value="12" label="Ftp Scheme"/>
-  <int value="13" label="Http Scheme"/>
-  <int value="14" label="Https Scheme"/>
-  <int value="15" label="JavaScript Scheme"/>
-  <int value="16" label="MailTo Scheme"/>
-  <int value="17" label="Quic Transport Scheme"/>
-  <int value="18" label="Tel Scheme"/>
-  <int value="19" label="Urn Scheme"/>
-  <int value="20" label="Ws Scheme"/>
-  <int value="21" label="Wss Scheme"/>
-  <int value="22" label="Chrome Extension Scheme"/>
-</enum>
-
-<enum name="CookieType">
-  <summary>The type of a cookie when its added to the cookie store.</summary>
-  <int value="0" label="Default"/>
-  <int value="1" label="First-Party-Only"/>
-  <int value="2" label="HttpOnly"/>
-  <int value="3" label="First-Party-Only, HttpOnly"/>
-  <int value="4" label="Secure"/>
-  <int value="5" label="First-Party-Only, Secure"/>
-  <int value="6" label="HttpOnly, Secure"/>
-  <int value="7" label="First-Party-Only, HttpOnly, Secure"/>
-</enum>
-
 <enum name="CopylessCacheHit">
   <int value="0" label="Cache hit with entity"/>
   <int value="1" label="Cache hit without entity"/>
@@ -25636,11 +25422,6 @@
   <int value="7" label="UNINSTALLED (not in webstore)"/>
 </enum>
 
-<enum name="ExternalLauncherOption">
-  <int value="0" label="Cancel"/>
-  <int value="1" label="Open"/>
-</enum>
-
 <enum name="FactoryPingEmbargoEndDateValidity">
   <summary>
     Defines the validity of the factory ping embargo end date in RW_VPD.
@@ -31807,25 +31588,6 @@
   <int value="12" label="Guest OS Files"/>
 </enum>
 
-<enum name="FileStorageRequestType">
-  <int value="0" label="Save Request"/>
-  <int value="1" label="Restore Request"/>
-  <int value="2" label="Delete Request"/>
-</enum>
-
-<enum name="FileSystemAccessPermissionRequestOutcome">
-  <int value="0" label="Blocked by Content Setting"/>
-  <int value="1" label="Blocked from invalid frame"/>
-  <int value="2" label="Blocked for lack of user activation"/>
-  <int value="3" label="Blocked from third-party context"/>
-  <int value="4" label="Granted"/>
-  <int value="5" label="Denied by user"/>
-  <int value="6" label="Dismissed by user"/>
-  <int value="7" label="Request was aborted"/>
-  <int value="8" label="Granted by Content Setting"/>
-  <int value="9" label="Granted by persisted permission"/>
-</enum>
-
 <enum name="FileSystemDatabaseInitResult">
   <int value="0" label="OK"/>
   <int value="1" label="Corruption"/>
@@ -32217,15 +31979,6 @@
   <int value="2" label="YV12 format with Native Pixmaps"/>
 </enum>
 
-<enum name="FoundBuyableProductAnnotation">
-  <summary>
-    Whether a buyable product annotation was found in the response from an
-    endpoint which provides page metadata in ShoppingPersistedTabData.
-  </summary>
-  <int value="0" label="NotFound"/>
-  <int value="1" label="Found"/>
-</enum>
-
 <enum name="FramebustInterventionOutcome">
   <int value="0" label="Accepted"/>
   <int value="1" label="Declined and navigated"/>
@@ -34005,12 +33758,6 @@
   <int value="2" label="Enabled on timer"/>
 </enum>
 
-<enum name="HistoricalSaverCloseType">
-  <int value="0" label="Tab"/>
-  <int value="1" label="Group"/>
-  <int value="2" label="Bulk"/>
-</enum>
-
 <enum name="HistoryClustersFinalState">
   <obsolete>
     Removed as of 05/2022.
@@ -34729,19 +34476,6 @@
   <int value="1074610802" label="MF_I_MANUAL_PROXY"/>
 </enum>
 
-<enum name="HttpMethods">
-  <int value="0" label="UNKNOWN"/>
-  <int value="1" label="GET"/>
-  <int value="2" label="HEAD"/>
-  <int value="3" label="POST"/>
-  <int value="4" label="PUT"/>
-  <int value="5" label="DELETE"/>
-  <int value="6" label="CONNECT"/>
-  <int value="7" label="OPTIONS"/>
-  <int value="8" label="TRACE"/>
-  <int value="9" label="PATCH"/>
-</enum>
-
 <enum name="HttpResponseCode">
   <int value="100" label="100: Continue"/>
   <int value="101" label="101: Switching Protocols"/>
@@ -35474,14 +35208,6 @@
   <int value="1" label="FLOATING_MODE"/>
 </enum>
 
-<enum name="ImportantSitesReason">
-  <int value="0" label="Engagement"/>
-  <int value="1" label="Durable"/>
-  <int value="2" label="Bookmarks"/>
-  <int value="3" label="Home Screen"/>
-  <int value="4" label="Notifications"/>
-</enum>
-
 <enum name="ImporterType">
   <int value="0" label="Unknown"/>
   <int value="1" label="IMPORTER_METRICS_IE">IE (Windows-only)</int>
@@ -36360,100 +36086,6 @@
   <int value="5" label="Initial state"/>
 </enum>
 
-<enum name="InterestingCookiePorts">
-  <int value="0" label="Other Port"/>
-  <int value="1" label="Port 80"/>
-  <int value="2" label="Port 81"/>
-  <int value="3" label="Port 82"/>
-  <int value="4" label="Port 83"/>
-  <int value="5" label="Port 84"/>
-  <int value="6" label="Port 85"/>
-  <int value="7" label="Port 443"/>
-  <int value="8" label="Port 444"/>
-  <int value="9" label="Port 445"/>
-  <int value="10" label="Port 446"/>
-  <int value="11" label="Port 447"/>
-  <int value="12" label="Port 448"/>
-  <int value="13" label="Port 3000"/>
-  <int value="14" label="Port 3001"/>
-  <int value="15" label="Port 3002"/>
-  <int value="16" label="Port 3003"/>
-  <int value="17" label="Port 3004"/>
-  <int value="18" label="Port 3005"/>
-  <int value="19" label="Port 4200"/>
-  <int value="20" label="Port 4201"/>
-  <int value="21" label="Port 4202"/>
-  <int value="22" label="Port 4203"/>
-  <int value="23" label="Port 4204"/>
-  <int value="24" label="Port 4205"/>
-  <int value="25" label="Port 5000"/>
-  <int value="26" label="Port 5001"/>
-  <int value="27" label="Port 5002"/>
-  <int value="28" label="Port 5003"/>
-  <int value="29" label="Port 5004"/>
-  <int value="30" label="Port 5005"/>
-  <int value="31" label="Port 7000"/>
-  <int value="32" label="Port 7001"/>
-  <int value="33" label="Port 7002"/>
-  <int value="34" label="Port 7003"/>
-  <int value="35" label="Port 7004"/>
-  <int value="36" label="Port 7005"/>
-  <int value="37" label="Port 8000"/>
-  <int value="38" label="Port 8001"/>
-  <int value="39" label="Port 8002"/>
-  <int value="40" label="Port 8003"/>
-  <int value="41" label="Port 8004"/>
-  <int value="42" label="Port 8005"/>
-  <int value="43" label="Port 8080"/>
-  <int value="44" label="Port 8081"/>
-  <int value="45" label="Port 8082"/>
-  <int value="46" label="Port 8083"/>
-  <int value="47" label="Port 8084"/>
-  <int value="48" label="Port 8085"/>
-  <int value="49" label="Port 8090"/>
-  <int value="50" label="Port 8091"/>
-  <int value="51" label="Port 8092"/>
-  <int value="52" label="Port 8093"/>
-  <int value="53" label="Port 8094"/>
-  <int value="54" label="Port 8095"/>
-  <int value="55" label="Port 8100"/>
-  <int value="56" label="Port 8101"/>
-  <int value="57" label="Port 8102"/>
-  <int value="58" label="Port 8103"/>
-  <int value="59" label="Port 8104"/>
-  <int value="60" label="Port 8105"/>
-  <int value="61" label="Port 8200"/>
-  <int value="62" label="Port 8201"/>
-  <int value="63" label="Port 8202"/>
-  <int value="64" label="Port 8203"/>
-  <int value="65" label="Port 8204"/>
-  <int value="66" label="Port 8205"/>
-  <int value="67" label="Port 8443"/>
-  <int value="68" label="Port 8444"/>
-  <int value="69" label="Port 8445"/>
-  <int value="70" label="Port 8446"/>
-  <int value="71" label="Port 8447"/>
-  <int value="72" label="Port 8448"/>
-  <int value="73" label="Port 8888"/>
-  <int value="74" label="Port 8889"/>
-  <int value="75" label="Port 8890"/>
-  <int value="76" label="Port 8891"/>
-  <int value="77" label="Port 8892"/>
-  <int value="78" label="Port 8893"/>
-  <int value="79" label="Port 9000"/>
-  <int value="80" label="Port 9001"/>
-  <int value="81" label="Port 9002"/>
-  <int value="82" label="Port 9003"/>
-  <int value="83" label="Port 9004"/>
-  <int value="84" label="Port 9005"/>
-  <int value="85" label="Port 9090"/>
-  <int value="86" label="Port 9091"/>
-  <int value="87" label="Port 9092"/>
-  <int value="88" label="Port 9093"/>
-  <int value="89" label="Port 9094"/>
-  <int value="90" label="Port 9095"/>
-</enum>
-
 <enum name="InternalErrorLoadEvent">
   <summary>Internal Errors in the page_load_metrics system</summary>
   <int value="0" label="Invalid timing IPC sent from renderer (deprecated)"/>
@@ -36562,15 +36194,6 @@
   <int value="11" label="Omnibox Most Visited Tile"/>
 </enum>
 
-<enum name="IOSAllOpenTabsAge">
-  <int value="0" label="Less than 1 day"/>
-  <int value="1" label="1-3 days"/>
-  <int value="2" label="3-7 days"/>
-  <int value="3" label="7-14 days"/>
-  <int value="4" label="14-30 days"/>
-  <int value="5" label="More than 30 days"/>
-</enum>
-
 <enum name="IOSAppState">
   <int value="0"
       label="Unknown UTE. This should not happen and presence of this value
@@ -52097,14 +51720,6 @@
   <int value="9" label="&quot;service-not-allowed&quot; error"/>
 </enum>
 
-<enum name="NewTabType">
-  <int value="0" label="New tab button"/>
-  <int value="1" label="Regular menu option"/>
-  <int value="2" label="Tab strip menu option"/>
-  <int value="3" label="New tab button in toolbar for touch"/>
-  <int value="4" label="New tab button in the WebUI tab strip"/>
-</enum>
-
 <enum name="NewTabURLState">
   <int value="0" label="Valid URL was used"/>
   <int value="1" label="Corrupt state"/>
@@ -59631,16 +59246,6 @@
   <int value="9" label="Timeout"/>
 </enum>
 
-<enum name="PullDownGestureAction">
-  <summary>The type of action performed on the overscroll UI.</summary>
-  <int value="0" label="NewTab">The user selected the new tab action.</int>
-  <int value="1" label="Refresh">The user selected the refresh action.</int>
-  <int value="2" label="CloseTab">The user selected the close tab action.</int>
-  <int value="3" label="Canceled">
-    The user canceled the action by scrolling back up.
-  </int>
-</enum>
-
 <enum name="PushEventStatus">
   <int value="0" label="Successful"/>
   <int value="1" label="Message was invalid"/>
@@ -60686,24 +60291,6 @@
   <int value="7" label="Unknown"/>
 </enum>
 
-<enum name="RequestStorageResult">
-  <int value="0" label="Approved due to existing storage access"/>
-  <int value="1" label="Approved with new grant"/>
-  <int value="2" label="Rejected with missing user gesture"/>
-  <int value="3" label="Rejected due to missing origin"/>
-  <int value="4" label="Rejected due to opaque origin"/>
-  <int value="5" label="Rejected due to existing denial"/>
-  <int value="6" label="Rejected due to missing sandbox token"/>
-  <int value="7" label="Rejected due to denied prompt"/>
-  <int value="8"
-      label="Rejected due to a call being made from an invalid context"/>
-  <int value="9" label="Rejected due to insecure context"/>
-  <int value="10" label="Approved due to being called from top-level frame"/>
-  <int value="11" label="Rejected due to credentialless iframe"/>
-  <int value="12" label="Approved with a new or existing grant"/>
-  <int value="13" label="Rejected due to fenced frame"/>
-</enum>
-
 <enum name="ResourceHasClient">
   <int value="0" label="No client"/>
   <int value="1" label="Has client"/>
@@ -60774,12 +60361,6 @@
   <int value="3" label="Promo was dismissed via swipe down"/>
 </enum>
 
-<enum name="RestoreTabStateException">
-  <int value="0" label="File not found"/>
-  <int value="1" label="Closed by interrupt exception"/>
-  <int value="2" label="IO exception"/>
-</enum>
-
 <enum name="ResumeBlockedRequestsTrigger">
   <int value="0" label="Cookie fresh notification"/>
   <int value="1"
@@ -60830,12 +60411,6 @@
   <int value="3" label="Invalid File"/>
 </enum>
 
-<enum name="SadTabEvent">
-  <int value="0" label="Displayed"/>
-  <int value="1" label="Button clicked"/>
-  <int value="2" label="Learn more clicked"/>
-</enum>
-
 <enum name="SafetyCheckExtensionsStatus">
   <int value="0" label="CHECKING"/>
   <int value="1" label="DEPRECATED_ERROR"/>
@@ -62776,36 +62351,6 @@
   <int value="18" label="WrappedGraphiteTextureBacking"/>
 </enum>
 
-<enum name="SharedStorageWorkletDestroyedStatus">
-  <int value="0" label="DidNotEnterKeepAlive"/>
-  <int value="1" label="KeepAliveEndedDueToOperationsFinished"/>
-  <int value="2" label="KeepAliveEndedDueToTimeout"/>
-  <int value="3" label="Other">
-    Keep alive entered but reason for termination not given.
-  </int>
-</enum>
-
-<enum name="SharedStorageWorkletErrorType">
-  <int value="0" label="AddModuleWebVisible">
-    Errors in `addModule()` visible to the document.
-  </int>
-  <int value="1" label="AddModuleNonWebVisible">
-    Errors in `addModule()` not visible to the document.
-  </int>
-  <int value="2" label="RunWebVisible">
-    Errors in `run()` visible to the document.
-  </int>
-  <int value="3" label="RunNonWebVisible">
-    Errors in `run()` not visible to the document.
-  </int>
-  <int value="4" label="kSelectURLWebVisible">
-    Errors in `selectURL()` visible to the document.
-  </int>
-  <int value="5" label="kSelectURLNonWebVisible">
-    Errors in `selectURL()` not visible to the document.
-  </int>
-</enum>
-
 <enum name="ShareOrigin">
   <int value="0" label="OVERFLOW_MENU"/>
   <int value="1" label="TOP_TOOLBAR"/>
@@ -64423,23 +63968,6 @@
   <int value="58" label="kIoCorruptFileSystem">SQLITE_IOERR_CORRUPTFS</int>
 </enum>
 
-<enum name="SqlRecoveryResult">
-  <summary>
-    Outcome of attempting to recover a database with sql::BuiltInRecovery. See
-    sql::BuiltInRecovery::Result for descriptions.
-  </summary>
-  <int value="0" label="kUnknown"/>
-  <int value="1" label="kSuccess"/>
-  <int value="2" label="kFailedRecoveryInit"/>
-  <int value="3" label="kFailedRecoveryRun"/>
-  <int value="4" label="kFailedToOpenRecoveredDatabase"/>
-  <int value="5" label="kFailedMetaTableDoesNotExist"/>
-  <int value="6" label="kFailedMetaTableInit"/>
-  <int value="7" label="kFailedMetaTableVersionWasInvalid"/>
-  <int value="8" label="kFailedBackupInit"/>
-  <int value="9" label="kFailedBackupRun"/>
-</enum>
-
 <enum name="SSLCipherSuite">
   <summary>SSL/TLS cipher suites from the IANA registry</summary>
   <int value="10" label="TLS_RSA_WITH_3DES_EDE_CBC_SHA"/>
@@ -64804,77 +64332,6 @@
   <int value="3" label="Unable to determine the startup temperature"/>
 </enum>
 
-<enum name="StorageAccessAPIRequestOutcome">
-  <summary>
-    Possible outcomes of a Storage Access API permission request.
-  </summary>
-  <int value="0" label="Granted by First-Party Set"/>
-  <int value="1" label="Granted by implicit grant allowance"/>
-  <int value="2" label="Granted by user"/>
-  <int value="3" label="Denied by First-Party Set"/>
-  <int value="4" label="Denied by user"/>
-  <int value="5"
-      label="Denied by missing prerequisite (user gesture, feature enabled)"/>
-  <int value="6" label="Dismissed by user"/>
-  <int value="7" label="Reused previous decision (made by user)"/>
-  <int value="8" label="Denied by top-level user interaction heuristic"/>
-  <int value="9" label="Access was allowed through cookie settings"/>
-  <int value="10" label="Reused implicit grant (e.g. from First-Party-Sets)"/>
-  <int value="11" label="Access was denied through cookie settings"/>
-  <int value="12"
-      label="Allowed by requesting origin and embedding origin being
-             same-site"/>
-  <int value="13"
-      label="Denied due to an abort by browser (e.g. RenderFrameHost was
-             deleted)"/>
-</enum>
-
-<enum name="StorageAccessInputState">
-  <int value="0" label="OptInWithMatchingGrant">
-    The frame-level opt-in bool was provided, and there is a matching permission
-    grant.
-  </int>
-  <int value="1" label="OptInWithoutGrant">
-    The frame-level opt-in bool was provided, but there is no matching
-    permission grant.
-  </int>
-  <int value="2" label="MatchingGrantWithoutOptIn">
-    There is a matching permission grant, but no frame-level opt-in bool was
-    provided.
-  </int>
-  <int value="3" label="NoOptInNoGrant">
-    No frame-level opt-in bool was provided, and there is no matching permission
-    grant.
-  </int>
-</enum>
-
-<enum name="StorageAccessResult">
-  <int value="0" label="Storage access blocked"/>
-  <int value="1" label="Storage access allowed"/>
-  <int value="2" label="Access grant used to allow access"/>
-  <int value="3"
-      label="Storage access allowed by force-allowed third-party-cookies
-             (deprecated)"/>
-  <int value="4"
-      label="Storage access allowed by the top-level version of the API"/>
-  <int value="5" label="Storage access allowed by 3PCD setting"/>
-  <int value="6"
-      label="Storage access allowed by 3PCD metadata grants content settings"/>
-  <int value="7" label="Temporary storage access allowed by 3PCD heuristics"/>
-</enum>
-
-<enum name="StorageBucketDurabilityParameter">
-  <int value="0" label="Not provided"/>
-  <int value="1" label="Relaxed"/>
-  <int value="2" label="Strict"/>
-</enum>
-
-<enum name="StorageBucketPersistedParameter">
-  <int value="0" label="Not provided"/>
-  <int value="1" label="Not persisted"/>
-  <int value="2" label="Persisted"/>
-</enum>
-
 <enum name="StoragePartitionRemoverTasks">
   <int value="1" label="kSynchronous"/>
   <int value="2" label="kCookies"/>
@@ -64891,12 +64348,6 @@
   <int value="13" label="kInterestGroups"/>
 </enum>
 
-<enum name="StoragePressureBubbleUserAction">
-  <int value="0" label="The bubble was shown."/>
-  <int value="1" label="The user ignored the bubble."/>
-  <int value="2" label="The user clicked on the positive button."/>
-</enum>
-
 <enum name="StreamEffectState">
   <int value="0" label="Force disable system effect"/>
   <int value="1" label="Force enable system effect"/>
@@ -65324,17 +64775,6 @@
   <int value="37" label="Unified password manager error"/>
 </enum>
 
-<enum name="TabActivationTypes">
-  <int value="0" label="Activate a tab"/>
-  <int value="1" label="Activate the context menu of a tab"/>
-</enum>
-
-<enum name="TabBackgroundLoadStatus">
-  <int value="0" label="Loaded on creation and shown"/>
-  <int value="1" label="Loaded on creation and lost"/>
-  <int value="2" label="Not loaded on creation"/>
-</enum>
-
 <enum name="TabbedPaintPreviewCompositorFailureReason">
   <int value="0" label="OK"/>
   <int value="1" label="URL Mismatch"/>
@@ -65374,105 +64814,18 @@
   <int value="3" label="Background (app in background)"/>
 </enum>
 
-<enum name="TabManagerTabRankerResult">
-  <int value="0" label="Success">Tab score was calculated successfully.</int>
-  <int value="1" label="Preprocessor initialization failed">
-    The ExamplePreprocessorConfig could not be loaded or parsed.
-  </int>
-  <int value="2" label="Preprocessor other error">
-    The ExamplePreprocessor returned an error other than, or in addition to, the
-    kNoFeatureIndexFound error.
-  </int>
-</enum>
-
-<enum name="TabRestoreMethod">
-  <int value="0" label="TabState"/>
-  <int value="1" label="CriticalPersistedTabData"/>
-  <int value="2" label="Create new Tab"/>
-  <int value="3" label="Failed to restore"/>
-  <int value="4" label="Skipped (NTP)"/>
-  <int value="5" label="Skipped (Empty URL)"/>
-</enum>
-
 <enum name="TabRevisitTracker.TabState">
   <int value="0" label="Active"/>
   <int value="1" label="Background"/>
   <int value="2" label="Closed"/>
 </enum>
 
-<enum name="TabScreenshotAction">
-  <int value="0" label="User took no actions">
-    User made a screenshot, but did not take any actions with it before
-    navigating away or closing the tab.
-  </int>
-  <int value="1" label="Opened sharing">
-    User took a screenshot then tried to share.
-  </int>
-  <int value="2" label="Download IPH provided">
-    User was provided in product help for Downloads after taking a screenshot.
-  </int>
-</enum>
-
-<enum name="TabSearchCloseActions">
-  <int value="0" label="User did not close via Tab Search UI">
-    The UI was dismissed via interactions outside what is offered by the Tab
-    Search UI.
-  </int>
-  <int value="1" label="User switched to an open tab">
-    The Tab Search UI was dismissed as a result of the user opting to switch to
-    a currently open tab.
-  </int>
-</enum>
-
-<enum name="TabSearchOpenActions">
-  <int value="0" label="User opened the Tab Search UI via a mouse click"/>
-  <int value="1" label="User opened the Tab Search UI via keyboard navigation"/>
-  <int value="2" label="User opened the Tab Search UI via keyboard shortcut"/>
-  <int value="3" label="User opened the Tab Search UI via touch gesture"/>
-</enum>
-
-<enum name="TabSearchRecentlyClosedItemOpenAction">
-  <int value="0"
-      label="Opened a recently closed item from an unfiltered item list"/>
-  <int value="1"
-      label="Opened a recently closed item from a filtered item list"/>
-</enum>
-
-<enum name="TabSearchRecentlyClosedToggleActions">
-  <int value="0" label="Expand the recently closed items section"/>
-  <int value="1" label="Collapse the recently closed items section"/>
-</enum>
-
-<enum name="TabSearchTabSwitchAction">
-  <int value="0" label="Switch to tab from unfiltered tab list"/>
-  <int value="1" label="Switch to tab from filtered search tab list"/>
-</enum>
-
 <enum name="TabSelectionEditorShareActionState">
   <int value="0" label="UNKNOWN_SHARE_STATE"/>
   <int value="1" label="SUCCESSFULLY_SHARED_TABS"/>
   <int value="2" label="FAILED_SHARE_ALL_TABS_FILTERED"/>
 </enum>
 
-<enum name="TabStatus">
-  <int value="0" label="Memory resident"/>
-  <int value="1" label="Evicted and reloaded"/>
-  <int value="2" label="Reloaded due to cold start"/>
-  <int value="3" label="Partially evicted"/>
-  <int value="4" label="Reloaded due to backgrounding"/>
-  <int value="5" label="Reloaded due to incognito"/>
-  <int value="6" label="Reloaded due to cold start (fg tab on start)"/>
-  <int value="7" label="Reloaded due to cold start (bg tab on switch)"/>
-  <int value="8" label="Lazy load for 'Open in new tab'"/>
-  <int value="9" label="Stopped due to page loading when backgrounding"/>
-  <int value="10" label="Evicted due to page loading when backgrounding"/>
-  <int value="11" label="Evicted due to OS terminating the renderer."/>
-</enum>
-
-<enum name="TabStripFailureContext">
-  <int value="0" label="New tab failed to open"/>
-</enum>
-
 <enum name="TabSwitchResult2">
   <int value="0" label="Success"/>
   <int value="1" label="Tab hidden before a frame is presented"/>
@@ -65654,21 +65007,6 @@
   <int value="18" label="RELOAD_BUTTON"/>
 </enum>
 
-<enum name="TopLevelStorageAccessRequestOutcome">
-  <summary>
-    Possible outcomes of a Top-Level Storage Access API permission request.
-  </summary>
-  <int value="0" label="Granted by First-Party Set"/>
-  <int value="1" label="(Obsolete) Granted by implicit grant allowance"/>
-  <int value="2" label="(Obsolete) Granted by user"/>
-  <int value="3" label="Denied by First-Party Set"/>
-  <int value="4" label="(Obsolete) Denied by user"/>
-  <int value="5"
-      label="Denied by missing prerequisite (user gesture, feature enabled)"/>
-  <int value="6" label="(Obsolete) Dismissed by user"/>
-  <int value="7" label="(Obsolete) Reused previous decision (made by user)"/>
-</enum>
-
 <enum name="TopToolbarAllowCaptureReason">
   <int value="0" label="UNKNOWN"/>
   <int value="1" label="FORCE_CAPTURE"/>
@@ -66177,13 +65515,6 @@
   <int value="13" label="FAILURE_DISPLAY_LOCK"/>
 </enum>
 
-<enum name="TruncatingCharacterInCookieStringType">
-  <int value="0" label="No truncating character"/>
-  <int value="1" label="NULL (\x0)"/>
-  <int value="2" label="New Line (\xD)"/>
-  <int value="3" label="Line Feed (\xA)"/>
-</enum>
-
 <enum name="TrustedWebActivityQualityEnforcementViolationType">
   <int value="0" label="Error 404"/>
   <int value="1" label="Error 5xx"/>
diff --git a/tools/metrics/histograms/histograms_index.txt b/tools/metrics/histograms/histograms_index.txt
index f6d8c03..618614d 100644
--- a/tools/metrics/histograms/histograms_index.txt
+++ b/tools/metrics/histograms/histograms_index.txt
@@ -39,6 +39,7 @@
 tools/metrics/histograms/metadata/content/enums.xml
 tools/metrics/histograms/metadata/content/histograms.xml
 tools/metrics/histograms/metadata/content_creation/histograms.xml
+tools/metrics/histograms/metadata/cookie/enums.xml
 tools/metrics/histograms/metadata/cookie/histograms.xml
 tools/metrics/histograms/metadata/cras/histograms.xml
 tools/metrics/histograms/metadata/cros/histograms.xml
@@ -157,12 +158,14 @@
 tools/metrics/histograms/metadata/stability/histograms.xml
 tools/metrics/histograms/metadata/start_surface/histograms.xml
 tools/metrics/histograms/metadata/startup/histograms.xml
+tools/metrics/histograms/metadata/storage/enums.xml
 tools/metrics/histograms/metadata/storage/histograms.xml
 tools/metrics/histograms/metadata/structured_metrics/histograms.xml
 tools/metrics/histograms/metadata/subresource/histograms.xml
 tools/metrics/histograms/metadata/sync/enums.xml
 tools/metrics/histograms/metadata/sync/histograms.xml
 tools/metrics/histograms/metadata/system/histograms.xml
+tools/metrics/histograms/metadata/tab/enums.xml
 tools/metrics/histograms/metadata/tab/histograms.xml
 tools/metrics/histograms/metadata/translate/histograms.xml
 tools/metrics/histograms/metadata/trusted_vault/histograms.xml
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index 0a3d1bbe..98551b78 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -3232,19 +3232,6 @@
   </summary>
 </histogram>
 
-<histogram base="true" name="ChromeOS.SystemTray.AnimationSmoothness" units="%"
-    expires_after="2024-05-05">
-<!-- Name completed by histogram suffixes
-     name="SystemTrayTransitionType" -->
-
-  <owner>amehfooz@chromium.org</owner>
-  <owner>cros-status-area@google.com</owner>
-  <summary>
-    Tracks the animation smoothness for the collapse / expand animation of the
-    system tray.
-  </summary>
-</histogram>
-
 <histogram name="ChromeOS.SystemTray.BlockedNotifiersAfterUpdate"
     units="notifiers" expires_after="2023-05-10">
   <owner>amehfooz@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/cookie/enums.xml b/tools/metrics/histograms/metadata/cookie/enums.xml
new file mode 100644
index 0000000..d416269
--- /dev/null
+++ b/tools/metrics/histograms/metadata/cookie/enums.xml
@@ -0,0 +1,276 @@
+<!--
+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.
+-->
+
+<!--
+
+This file describes the enumerations referenced by entries in histograms.xml for
+this directory. Some enums may instead be listed in the central enums.xml file
+at src/tools/metrics/histograms/enums.xml when multiple files use them.
+
+For best practices on writing enumerations descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Enum-Histograms
+
+Please follow the instructions in the OWNERS file in this directory to find a
+reviewer. If no OWNERS file exists, please consider signing up at
+go/reviewing-metrics (Googlers only), as all subdirectories are expected to
+have an OWNERS file. As a last resort you can send the CL to
+chromium-metrics-reviews@google.com.
+-->
+
+<histogram-configuration>
+
+<!-- Enum types -->
+
+<enums>
+
+<enum name="ContextRedirectTypeBug1221316">
+  <int value="0" label="Unset"/>
+  <int value="1" label="No Redirect"/>
+  <int value="2" label="Cross-Site Redirect"/>
+  <int value="3" label="Partially Same-Site Redirect"/>
+  <int value="4" label="Fully Same-Site Redirect"/>
+</enum>
+
+<enum name="CookieCommitProblem">
+  <int value="0" label="Entry encryption failed"/>
+  <int value="1" label="Adding cookie to DB failed."/>
+  <int value="2" label="Updating access time of cookie failed."/>
+  <int value="3" label="Deleting cookie failed."/>
+  <int value="4" label="Committing the transaction failed"/>
+</enum>
+
+<enum name="CookieEffectiveSameSite">
+  <int value="0" label="No restriction"/>
+  <int value="1" label="Lax mode"/>
+  <int value="2" label="Strict mode"/>
+  <int value="3" label="Lax mode allow unsafe"/>
+  <int value="4" label="Undefined"/>
+</enum>
+
+<enum name="CookieLoadProblem">
+  <int value="0" label="Entry decryption failed"/>
+  <int value="1"
+      label="(Obsolete) Entry decryption taking over a minute. Removed
+             03/2021. The problem rarely happens and can be reported through
+             other mechanisms."/>
+  <int value="2" label="Cookie canonical form check failed"/>
+  <int value="3" label="Could not open or initialize database"/>
+  <int value="4" label="Attempt to delete broken (and related) rows failed"/>
+</enum>
+
+<enum name="CookiePrefix">
+  <int value="0" label="No special prefix"/>
+  <int value="1" label="Secure prefix"/>
+  <int value="2" label="Host prefix"/>
+</enum>
+
+<enum name="CookieRequestScheme">
+  <int value="0" label="Unset Cookie Scheme"/>
+  <int value="1" label="Nonsecurely Set Cookie Nonsecure Request"/>
+  <int value="2" label="Securely Set Cookie Secure Request"/>
+  <int value="3" label="Nonsecurely Set Cookie Secure Request"/>
+  <int value="4" label="Securely Set Cookie Nonsecure Request"/>
+</enum>
+
+<enum name="CookiesAllowedForUrlsUsage">
+  <int value="0" label="Explicit Content Settings Only"/>
+  <int value="1" label="Wildcard Primary Content Settings Only"/>
+  <int value="2" label="Wildcard Secondary Content Settings Only"/>
+  <int value="3" label="Wildcard Primary and Explicit Content Settings"/>
+  <int value="4" label="Wildcard Secondary and Explicit Content Settings"/>
+  <int value="5" label="Only Wildcard Content Settings"/>
+  <int value="6" label="All Types of Content Settings Present"/>
+</enum>
+
+<enum name="CookieSameSite2">
+  <int value="0" label="No SameSite attribute is specified."/>
+  <int value="1" label="SameSite=None"/>
+  <int value="2" label="SameSite=Lax"/>
+  <int value="3" label="SameSite=Strict"/>
+</enum>
+
+<enum name="CookieSameSiteString">
+  <int value="0" label="No SameSite attribute is present."/>
+  <int value="1" label="SameSite attribute is specified but has no value."/>
+  <int value="2" label="SameSite attribute value is unrecognized."/>
+  <int value="3" label="Lax"/>
+  <int value="4" label="Strict"/>
+  <int value="5" label="None"/>
+  <int value="6" label="Extended"/>
+</enum>
+
+<enum name="CookieSentToSamePort">
+  <int value="0" label="Cookie source port was unspecified."/>
+  <int value="1" label="Cookie source port was invalid."/>
+  <int value="2" label="Source and destination ports do not match."/>
+  <int value="3"
+      label="Source and destination ports do not match but are the defaults
+             for their scheme."/>
+  <int value="4" label="Source and destination ports match."/>
+</enum>
+
+<enum name="CookieSourceScheme">
+  <int value="0" label="Secure cookie, source scheme is cryptographic"/>
+  <int value="1" label="Secure cookie, source scheme is not cryptographic"/>
+  <int value="2" label="Not Secure cookie, source scheme is cryptographic"/>
+  <int value="3" label="Not Secure cookie, source scheme is not cryptographic"/>
+</enum>
+
+<enum name="CookieSourceSchemeName">
+  <int value="0" label="Other Scheme"/>
+  <int value="1" label="About Blank URL"/>
+  <int value="2" label="About Srcdoc URL"/>
+  <int value="3" label="About Blank Path"/>
+  <int value="4" label="About Srcdoc Path"/>
+  <int value="5" label="About Scheme"/>
+  <int value="6" label="Blob Scheme"/>
+  <int value="7" label="Content Scheme"/>
+  <int value="8" label="Content ID Scheme"/>
+  <int value="9" label="Data Scheme"/>
+  <int value="10" label="File Scheme"/>
+  <int value="11" label="File System Scheme"/>
+  <int value="12" label="Ftp Scheme"/>
+  <int value="13" label="Http Scheme"/>
+  <int value="14" label="Https Scheme"/>
+  <int value="15" label="JavaScript Scheme"/>
+  <int value="16" label="MailTo Scheme"/>
+  <int value="17" label="Quic Transport Scheme"/>
+  <int value="18" label="Tel Scheme"/>
+  <int value="19" label="Urn Scheme"/>
+  <int value="20" label="Ws Scheme"/>
+  <int value="21" label="Wss Scheme"/>
+  <int value="22" label="Chrome Extension Scheme"/>
+</enum>
+
+<enum name="CookieType">
+  <summary>The type of a cookie when its added to the cookie store.</summary>
+  <int value="0" label="Default"/>
+  <int value="1" label="First-Party-Only"/>
+  <int value="2" label="HttpOnly"/>
+  <int value="3" label="First-Party-Only, HttpOnly"/>
+  <int value="4" label="Secure"/>
+  <int value="5" label="First-Party-Only, Secure"/>
+  <int value="6" label="HttpOnly, Secure"/>
+  <int value="7" label="First-Party-Only, HttpOnly, Secure"/>
+</enum>
+
+<enum name="HttpMethods">
+  <int value="0" label="UNKNOWN"/>
+  <int value="1" label="GET"/>
+  <int value="2" label="HEAD"/>
+  <int value="3" label="POST"/>
+  <int value="4" label="PUT"/>
+  <int value="5" label="DELETE"/>
+  <int value="6" label="CONNECT"/>
+  <int value="7" label="OPTIONS"/>
+  <int value="8" label="TRACE"/>
+  <int value="9" label="PATCH"/>
+</enum>
+
+<enum name="InterestingCookiePorts">
+  <int value="0" label="Other Port"/>
+  <int value="1" label="Port 80"/>
+  <int value="2" label="Port 81"/>
+  <int value="3" label="Port 82"/>
+  <int value="4" label="Port 83"/>
+  <int value="5" label="Port 84"/>
+  <int value="6" label="Port 85"/>
+  <int value="7" label="Port 443"/>
+  <int value="8" label="Port 444"/>
+  <int value="9" label="Port 445"/>
+  <int value="10" label="Port 446"/>
+  <int value="11" label="Port 447"/>
+  <int value="12" label="Port 448"/>
+  <int value="13" label="Port 3000"/>
+  <int value="14" label="Port 3001"/>
+  <int value="15" label="Port 3002"/>
+  <int value="16" label="Port 3003"/>
+  <int value="17" label="Port 3004"/>
+  <int value="18" label="Port 3005"/>
+  <int value="19" label="Port 4200"/>
+  <int value="20" label="Port 4201"/>
+  <int value="21" label="Port 4202"/>
+  <int value="22" label="Port 4203"/>
+  <int value="23" label="Port 4204"/>
+  <int value="24" label="Port 4205"/>
+  <int value="25" label="Port 5000"/>
+  <int value="26" label="Port 5001"/>
+  <int value="27" label="Port 5002"/>
+  <int value="28" label="Port 5003"/>
+  <int value="29" label="Port 5004"/>
+  <int value="30" label="Port 5005"/>
+  <int value="31" label="Port 7000"/>
+  <int value="32" label="Port 7001"/>
+  <int value="33" label="Port 7002"/>
+  <int value="34" label="Port 7003"/>
+  <int value="35" label="Port 7004"/>
+  <int value="36" label="Port 7005"/>
+  <int value="37" label="Port 8000"/>
+  <int value="38" label="Port 8001"/>
+  <int value="39" label="Port 8002"/>
+  <int value="40" label="Port 8003"/>
+  <int value="41" label="Port 8004"/>
+  <int value="42" label="Port 8005"/>
+  <int value="43" label="Port 8080"/>
+  <int value="44" label="Port 8081"/>
+  <int value="45" label="Port 8082"/>
+  <int value="46" label="Port 8083"/>
+  <int value="47" label="Port 8084"/>
+  <int value="48" label="Port 8085"/>
+  <int value="49" label="Port 8090"/>
+  <int value="50" label="Port 8091"/>
+  <int value="51" label="Port 8092"/>
+  <int value="52" label="Port 8093"/>
+  <int value="53" label="Port 8094"/>
+  <int value="54" label="Port 8095"/>
+  <int value="55" label="Port 8100"/>
+  <int value="56" label="Port 8101"/>
+  <int value="57" label="Port 8102"/>
+  <int value="58" label="Port 8103"/>
+  <int value="59" label="Port 8104"/>
+  <int value="60" label="Port 8105"/>
+  <int value="61" label="Port 8200"/>
+  <int value="62" label="Port 8201"/>
+  <int value="63" label="Port 8202"/>
+  <int value="64" label="Port 8203"/>
+  <int value="65" label="Port 8204"/>
+  <int value="66" label="Port 8205"/>
+  <int value="67" label="Port 8443"/>
+  <int value="68" label="Port 8444"/>
+  <int value="69" label="Port 8445"/>
+  <int value="70" label="Port 8446"/>
+  <int value="71" label="Port 8447"/>
+  <int value="72" label="Port 8448"/>
+  <int value="73" label="Port 8888"/>
+  <int value="74" label="Port 8889"/>
+  <int value="75" label="Port 8890"/>
+  <int value="76" label="Port 8891"/>
+  <int value="77" label="Port 8892"/>
+  <int value="78" label="Port 8893"/>
+  <int value="79" label="Port 9000"/>
+  <int value="80" label="Port 9001"/>
+  <int value="81" label="Port 9002"/>
+  <int value="82" label="Port 9003"/>
+  <int value="83" label="Port 9004"/>
+  <int value="84" label="Port 9005"/>
+  <int value="85" label="Port 9090"/>
+  <int value="86" label="Port 9091"/>
+  <int value="87" label="Port 9092"/>
+  <int value="88" label="Port 9093"/>
+  <int value="89" label="Port 9094"/>
+  <int value="90" label="Port 9095"/>
+</enum>
+
+<enum name="TruncatingCharacterInCookieStringType">
+  <int value="0" label="No truncating character"/>
+  <int value="1" label="NULL (\x0)"/>
+  <int value="2" label="New Line (\xD)"/>
+  <int value="3" label="Line Feed (\xA)"/>
+</enum>
+
+</enums>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
index 8ae3a88..0e4d270 100644
--- a/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/metadata/histogram_suffixes_list.xml
@@ -4913,14 +4913,6 @@
   <affected-histogram name="Mobile.SystemNotification.Dismiss.Age"/>
 </histogram_suffixes>
 
-<histogram_suffixes name="SystemTrayTransitionType" separator=".">
-  <suffix name="TransitionToCollapsed"
-      label="Transition to the collapsed system tray"/>
-  <suffix name="TransitionToExpanded"
-      label="Transition to the expanded system tray"/>
-  <affected-histogram name="ChromeOS.SystemTray.AnimationSmoothness"/>
-</histogram_suffixes>
-
 <histogram_suffixes name="TabletOrClamshellMode" separator=".">
   <suffix name="ClamshellMode" label="Clamshell Mode Enabled"/>
   <suffix name="TabletMode" label="Tablet Mode Enabled"/>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 0761ed21..5eaadfa 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -2106,17 +2106,6 @@
   <summary>The count of HTTP Response codes encountered.</summary>
 </histogram>
 
-<histogram name="Net.HttpServerProperties.CountOfServers" units="units"
-    expires_after="M85">
-  <owner>mmenke@chromium.org</owner>
-  <owner>src/net/OWNERS</owner>
-  <summary>
-    The total number of ServerInfo structs written to the network service's
-    prefs file, recorded each time the HttpServerProperties are serialized to
-    update the copy stored on disk.
-  </summary>
-</histogram>
-
 <histogram name="Net.HttpTimeToFirstByte" units="ms" expires_after="never">
 <!-- expires-never: Core network stack health metric -->
 
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index d7d148c4..6c8838b 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -11533,12 +11533,6 @@
   </summary>
 </histogram>
 
-<histogram name="WinJumplist.Action" enum="WinJumplistCategory"
-    expires_after="M77">
-  <owner>noms@chromium.org</owner>
-  <summary>The type of category clicked in the Windows Jumplist</summary>
-</histogram>
-
 <histogram name="WorkerThread.ExitCode" enum="WorkerThreadExitCode"
     expires_after="2021-10-31">
   <owner>nhiroki@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/enums.xml b/tools/metrics/histograms/metadata/safe_browsing/enums.xml
index feec7f4..f6819a9 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/enums.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/enums.xml
@@ -121,6 +121,11 @@
   <int value="1" label="Managed Pref"/>
 </enum>
 
+<enum name="BooleanSafeBrowsingApiUnknownException">
+  <int value="0" label="Known exception"/>
+  <int value="1" label="Unknown exception"/>
+</enum>
+
 <enum name="BooleanSentOrReceived">
   <int value="0" label="Sent"/>
   <int value="1" label="Received"/>
@@ -169,6 +174,26 @@
   <int value="2" label="NO_MATCH"/>
 </enum>
 
+<enum name="SafeBrowsingApiExceptionStatusCode">
+  <int value="-1" label="SUCCESS_CACHE"/>
+  <int value="0" label="SUCCESS"/>
+  <int value="4" label="SIGN_IN_REQUIRED"/>
+  <int value="5" label="INVALID_ACCOUNT"/>
+  <int value="6" label="RESOLUTION_REQUIRED"/>
+  <int value="7" label="NETWORK_ERROR"/>
+  <int value="8" label="INTERNAL_ERROR"/>
+  <int value="10" label="DEVELOPER_ERROR"/>
+  <int value="13" label="ERROR"/>
+  <int value="14" label="INTERRUPTED"/>
+  <int value="15" label="TIMEOUT"/>
+  <int value="16" label="CANCELED"/>
+  <int value="17" label="API_NOT_CONNECTED"/>
+  <int value="19" label="REMOTE_EXCEPTION"/>
+  <int value="20" label="CONNECTION_SUSPENDED_DURING_CALL"/>
+  <int value="21" label="RECONNECTION_TIMED_OUT_DURING_UPDATE"/>
+  <int value="22" label="RECONNECTION_TIMED_OUT"/>
+</enum>
+
 <enum name="SafeBrowsingApiJavaValidationResult">
   <int value="0" label="valid"/>
   <int value="1" label="valid, but has unrecognized response status"/>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index a54f40e..173c706 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -978,6 +978,17 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.GmsSafeBrowsingApi.ApiExceptionStatusCode"
+    enum="SafeBrowsingApiExceptionStatusCode" expires_after="2024-05-09">
+  <owner>xinghuilu@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records the status code from SafeBrowsing API. Only logged if the API throws
+    an ApiException. The code definition can be found in
+    https://developers.google.com/android/reference/com/google/android/gms/common/api/CommonStatusCodes.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.GmsSafeBrowsingApi.CheckDelta{Protocol}"
     units="microseconds" expires_after="2024-05-05">
   <owner>xinghuilu@chromium.org</owner>
@@ -996,6 +1007,16 @@
   <token key="Protocol" variants="GmsSafeBrowsingApiProtocol"/>
 </histogram>
 
+<histogram name="SafeBrowsing.GmsSafeBrowsingApi.HasUnknownExceptionType"
+    enum="BooleanSafeBrowsingApiUnknownException" expires_after="2024-05-09">
+  <owner>xinghuilu@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records whether the Safe Browsing API throws an unknown exception type. Only
+    logged if the API throws an exception.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.GmsSafeBrowsingApi.IsAvailable{Protocol}"
     enum="BooleanAvailable" expires_after="2024-05-05">
   <owner>xinghuilu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/storage/enums.xml b/tools/metrics/histograms/metadata/storage/enums.xml
new file mode 100644
index 0000000..f84307e
--- /dev/null
+++ b/tools/metrics/histograms/metadata/storage/enums.xml
@@ -0,0 +1,288 @@
+<!--
+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.
+-->
+
+<!--
+
+This file describes the enumerations referenced by entries in histograms.xml for
+this directory. Some enums may instead be listed in the central enums.xml file
+at src/tools/metrics/histograms/enums.xml when multiple files use them.
+
+For best practices on writing enumerations descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Enum-Histograms
+
+Please follow the instructions in the OWNERS file in this directory to find a
+reviewer. If no OWNERS file exists, please consider signing up at
+go/reviewing-metrics (Googlers only), as all subdirectories are expected to
+have an OWNERS file. As a last resort you can send the CL to
+chromium-metrics-reviews@google.com.
+-->
+
+<histogram-configuration>
+
+<!-- Enum types -->
+
+<enums>
+
+<enum name="BooleanInherited">
+  <int value="0" label="Not inherited"/>
+  <int value="1" label="Inherited"/>
+</enum>
+
+<enum name="ClearDataSiteBlacklistCrossedReason">
+  <int value="0" label="Durable"/>
+  <int value="1" label="Notifications"/>
+  <int value="2" label="Engagement"/>
+  <int value="3" label="Notifications and Engagement"/>
+  <int value="4" label="Durable and Engagement"/>
+  <int value="5" label="Notifications and Durable"/>
+  <int value="6" label="Notifications, Durable, and Engagement"/>
+  <int value="7" label="Unknown"/>
+</enum>
+
+<enum name="ClearSiteDataParameters">
+  <int value="0" label="No valid types"/>
+  <int value="1" label="Cookies"/>
+  <int value="2" label="Storage"/>
+  <int value="3" label="Cookies and Storage"/>
+  <int value="4" label="Cache"/>
+  <int value="5" label="Cookies and Cache"/>
+  <int value="6" label="Storage and Cache"/>
+  <int value="7" label="Cookies, Storage, and Cache"/>
+  <int value="8" label="Storage buckets"/>
+  <int value="9" label="Storage buckets and Cookies"/>
+  <int value="10" label="Storage buckets and Storage"/>
+  <int value="11" label="Storage buckets, Cookies, and Storage"/>
+  <int value="12" label="Storage buckets and Cache"/>
+  <int value="13" label="Storage buckets, Cookies, and Cache"/>
+  <int value="14" label="Storage buckets, Storage, and Cache"/>
+  <int value="15" label="Storage buckets, Cookies, Storage, and Cache"/>
+  <int value="16" label="Client Hints"/>
+  <int value="17" label="Client Hints and Cookies"/>
+  <int value="18" label="Client Hints and Storage"/>
+  <int value="19" label="Client Hints, Cookies, and Storage"/>
+  <int value="20" label="Client Hints and Cache"/>
+  <int value="21" label="Client Hints, Cookies, and Cache"/>
+  <int value="22" label="Client Hints, Storage, and Cache"/>
+  <int value="23" label="Client Hints, Cookies, Storage, and Cache"/>
+  <int value="24" label="Client Hints and Storage buckets"/>
+  <int value="25" label="Client Hints, Storage buckets, and Cookies"/>
+  <int value="26" label="Client Hints, Storage buckets, and Storage"/>
+  <int value="27" label="Client Hints, Storage buckets, Cookies, and Storage"/>
+  <int value="28" label="Client Hints, Storage buckets, and Cache"/>
+  <int value="29" label="Client Hints, Storage buckets, Cookies and Cache"/>
+  <int value="30" label="Client Hints, Storage buckets, Storage and Cache"/>
+  <int value="31"
+      label="Client Hints, Storage buckets, Cookies, Storage, and Cache"/>
+</enum>
+
+<enum name="ClipboardFormatRead">
+  <int value="0" label="Text"/>
+  <int value="1" label="Html"/>
+  <int value="2" label="Rtf"/>
+  <int value="3" label="Image"/>
+  <int value="4" label="Bookmark"/>
+  <int value="5" label="Data"/>
+  <int value="6" label="CustomData"/>
+  <int value="7" label="WebSmartPaste"/>
+  <int value="8" label="Svg"/>
+  <int value="9" label="Filenames"/>
+  <int value="10" label="Png"/>
+</enum>
+
+<enum name="ClipboardFormatWrite">
+  <int value="0" label="Text"/>
+  <int value="1" label="Html"/>
+  <int value="2" label="Rtf"/>
+  <int value="3" label="Image"/>
+  <int value="4" label="Bookmark"/>
+  <int value="5" label="Data"/>
+  <int value="6" label="CustomData"/>
+  <int value="7" label="WebSmartPaste"/>
+  <int value="8" label="Svg"/>
+  <int value="9" label="Filenames"/>
+</enum>
+
+<enum name="FileSystemAccessPermissionRequestOutcome">
+  <int value="0" label="Blocked by Content Setting"/>
+  <int value="1" label="Blocked from invalid frame"/>
+  <int value="2" label="Blocked for lack of user activation"/>
+  <int value="3" label="Blocked from third-party context"/>
+  <int value="4" label="Granted"/>
+  <int value="5" label="Denied by user"/>
+  <int value="6" label="Dismissed by user"/>
+  <int value="7" label="Request was aborted"/>
+  <int value="8" label="Granted by Content Setting"/>
+  <int value="9" label="Granted by persisted permission"/>
+</enum>
+
+<enum name="ImportantSitesReason">
+  <int value="0" label="Engagement"/>
+  <int value="1" label="Durable"/>
+  <int value="2" label="Bookmarks"/>
+  <int value="3" label="Home Screen"/>
+  <int value="4" label="Notifications"/>
+</enum>
+
+<enum name="RequestStorageResult">
+  <int value="0" label="Approved due to existing storage access"/>
+  <int value="1" label="Approved with new grant"/>
+  <int value="2" label="Rejected with missing user gesture"/>
+  <int value="3" label="Rejected due to missing origin"/>
+  <int value="4" label="Rejected due to opaque origin"/>
+  <int value="5" label="Rejected due to existing denial"/>
+  <int value="6" label="Rejected due to missing sandbox token"/>
+  <int value="7" label="Rejected due to denied prompt"/>
+  <int value="8"
+      label="Rejected due to a call being made from an invalid context"/>
+  <int value="9" label="Rejected due to insecure context"/>
+  <int value="10" label="Approved due to being called from top-level frame"/>
+  <int value="11" label="Rejected due to credentialless iframe"/>
+  <int value="12" label="Approved with a new or existing grant"/>
+  <int value="13" label="Rejected due to fenced frame"/>
+</enum>
+
+<enum name="SharedStorageWorkletDestroyedStatus">
+  <int value="0" label="DidNotEnterKeepAlive"/>
+  <int value="1" label="KeepAliveEndedDueToOperationsFinished"/>
+  <int value="2" label="KeepAliveEndedDueToTimeout"/>
+  <int value="3" label="Other">
+    Keep alive entered but reason for termination not given.
+  </int>
+</enum>
+
+<enum name="SharedStorageWorkletErrorType">
+  <int value="0" label="AddModuleWebVisible">
+    Errors in `addModule()` visible to the document.
+  </int>
+  <int value="1" label="AddModuleNonWebVisible">
+    Errors in `addModule()` not visible to the document.
+  </int>
+  <int value="2" label="RunWebVisible">
+    Errors in `run()` visible to the document.
+  </int>
+  <int value="3" label="RunNonWebVisible">
+    Errors in `run()` not visible to the document.
+  </int>
+  <int value="4" label="kSelectURLWebVisible">
+    Errors in `selectURL()` visible to the document.
+  </int>
+  <int value="5" label="kSelectURLNonWebVisible">
+    Errors in `selectURL()` not visible to the document.
+  </int>
+</enum>
+
+<enum name="SqlRecoveryResult">
+  <summary>
+    Outcome of attempting to recover a database with sql::BuiltInRecovery. See
+    sql::BuiltInRecovery::Result for descriptions.
+  </summary>
+  <int value="0" label="kUnknown"/>
+  <int value="1" label="kSuccess"/>
+  <int value="2" label="kFailedRecoveryInit"/>
+  <int value="3" label="kFailedRecoveryRun"/>
+  <int value="4" label="kFailedToOpenRecoveredDatabase"/>
+  <int value="5" label="kFailedMetaTableDoesNotExist"/>
+  <int value="6" label="kFailedMetaTableInit"/>
+  <int value="7" label="kFailedMetaTableVersionWasInvalid"/>
+  <int value="8" label="kFailedBackupInit"/>
+  <int value="9" label="kFailedBackupRun"/>
+</enum>
+
+<enum name="StorageAccessAPIRequestOutcome">
+  <summary>
+    Possible outcomes of a Storage Access API permission request.
+  </summary>
+  <int value="0" label="Granted by First-Party Set"/>
+  <int value="1" label="Granted by implicit grant allowance"/>
+  <int value="2" label="Granted by user"/>
+  <int value="3" label="Denied by First-Party Set"/>
+  <int value="4" label="Denied by user"/>
+  <int value="5"
+      label="Denied by missing prerequisite (user gesture, feature enabled)"/>
+  <int value="6" label="Dismissed by user"/>
+  <int value="7" label="Reused previous decision (made by user)"/>
+  <int value="8" label="Denied by top-level user interaction heuristic"/>
+  <int value="9" label="Access was allowed through cookie settings"/>
+  <int value="10" label="Reused implicit grant (e.g. from First-Party-Sets)"/>
+  <int value="11" label="Access was denied through cookie settings"/>
+  <int value="12"
+      label="Allowed by requesting origin and embedding origin being
+             same-site"/>
+  <int value="13"
+      label="Denied due to an abort by browser (e.g. RenderFrameHost was
+             deleted)"/>
+</enum>
+
+<enum name="StorageAccessInputState">
+  <int value="0" label="OptInWithMatchingGrant">
+    The frame-level opt-in bool was provided, and there is a matching permission
+    grant.
+  </int>
+  <int value="1" label="OptInWithoutGrant">
+    The frame-level opt-in bool was provided, but there is no matching
+    permission grant.
+  </int>
+  <int value="2" label="MatchingGrantWithoutOptIn">
+    There is a matching permission grant, but no frame-level opt-in bool was
+    provided.
+  </int>
+  <int value="3" label="NoOptInNoGrant">
+    No frame-level opt-in bool was provided, and there is no matching permission
+    grant.
+  </int>
+</enum>
+
+<enum name="StorageAccessResult">
+  <int value="0" label="Storage access blocked"/>
+  <int value="1" label="Storage access allowed"/>
+  <int value="2" label="Access grant used to allow access"/>
+  <int value="3"
+      label="Storage access allowed by force-allowed third-party-cookies
+             (deprecated)"/>
+  <int value="4"
+      label="Storage access allowed by the top-level version of the API"/>
+  <int value="5" label="Storage access allowed by 3PCD setting"/>
+  <int value="6"
+      label="Storage access allowed by 3PCD metadata grants content settings"/>
+  <int value="7" label="Temporary storage access allowed by 3PCD heuristics"/>
+</enum>
+
+<enum name="StorageBucketDurabilityParameter">
+  <int value="0" label="Not provided"/>
+  <int value="1" label="Relaxed"/>
+  <int value="2" label="Strict"/>
+</enum>
+
+<enum name="StorageBucketPersistedParameter">
+  <int value="0" label="Not provided"/>
+  <int value="1" label="Not persisted"/>
+  <int value="2" label="Persisted"/>
+</enum>
+
+<enum name="StoragePressureBubbleUserAction">
+  <int value="0" label="The bubble was shown."/>
+  <int value="1" label="The user ignored the bubble."/>
+  <int value="2" label="The user clicked on the positive button."/>
+</enum>
+
+<enum name="TopLevelStorageAccessRequestOutcome">
+  <summary>
+    Possible outcomes of a Top-Level Storage Access API permission request.
+  </summary>
+  <int value="0" label="Granted by First-Party Set"/>
+  <int value="1" label="(Obsolete) Granted by implicit grant allowance"/>
+  <int value="2" label="(Obsolete) Granted by user"/>
+  <int value="3" label="Denied by First-Party Set"/>
+  <int value="4" label="(Obsolete) Denied by user"/>
+  <int value="5"
+      label="Denied by missing prerequisite (user gesture, feature enabled)"/>
+  <int value="6" label="(Obsolete) Dismissed by user"/>
+  <int value="7" label="(Obsolete) Reused previous decision (made by user)"/>
+</enum>
+
+</enums>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/tab/enums.xml b/tools/metrics/histograms/metadata/tab/enums.xml
new file mode 100644
index 0000000..de235dd5
--- /dev/null
+++ b/tools/metrics/histograms/metadata/tab/enums.xml
@@ -0,0 +1,199 @@
+<!--
+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.
+-->
+
+<!--
+
+This file describes the enumerations referenced by entries in histograms.xml for
+this directory. Some enums may instead be listed in the central enums.xml file
+at src/tools/metrics/histograms/enums.xml when multiple files use them.
+
+For best practices on writing enumerations descriptions, see
+https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#Enum-Histograms
+
+Please follow the instructions in the OWNERS file in this directory to find a
+reviewer. If no OWNERS file exists, please consider signing up at
+go/reviewing-metrics (Googlers only), as all subdirectories are expected to
+have an OWNERS file. As a last resort you can send the CL to
+chromium-metrics-reviews@google.com.
+-->
+
+<histogram-configuration>
+
+<!-- Enum types -->
+
+<enums>
+
+<enum name="BooleanOverlap">
+  <int value="0" label="No overlap"/>
+  <int value="1" label="Overlapped"/>
+</enum>
+
+<enum name="ExternalLauncherOption">
+  <int value="0" label="Cancel"/>
+  <int value="1" label="Open"/>
+</enum>
+
+<enum name="FileStorageRequestType">
+  <int value="0" label="Save Request"/>
+  <int value="1" label="Restore Request"/>
+  <int value="2" label="Delete Request"/>
+</enum>
+
+<enum name="FoundBuyableProductAnnotation">
+  <summary>
+    Whether a buyable product annotation was found in the response from an
+    endpoint which provides page metadata in ShoppingPersistedTabData.
+  </summary>
+  <int value="0" label="NotFound"/>
+  <int value="1" label="Found"/>
+</enum>
+
+<enum name="HistoricalSaverCloseType">
+  <int value="0" label="Tab"/>
+  <int value="1" label="Group"/>
+  <int value="2" label="Bulk"/>
+</enum>
+
+<enum name="IOSAllOpenTabsAge">
+  <int value="0" label="Less than 1 day"/>
+  <int value="1" label="1-3 days"/>
+  <int value="2" label="3-7 days"/>
+  <int value="3" label="7-14 days"/>
+  <int value="4" label="14-30 days"/>
+  <int value="5" label="More than 30 days"/>
+</enum>
+
+<enum name="NewTabType">
+  <int value="0" label="New tab button"/>
+  <int value="1" label="Regular menu option"/>
+  <int value="2" label="Tab strip menu option"/>
+  <int value="3" label="New tab button in toolbar for touch"/>
+  <int value="4" label="New tab button in the WebUI tab strip"/>
+</enum>
+
+<enum name="PullDownGestureAction">
+  <summary>The type of action performed on the overscroll UI.</summary>
+  <int value="0" label="NewTab">The user selected the new tab action.</int>
+  <int value="1" label="Refresh">The user selected the refresh action.</int>
+  <int value="2" label="CloseTab">The user selected the close tab action.</int>
+  <int value="3" label="Canceled">
+    The user canceled the action by scrolling back up.
+  </int>
+</enum>
+
+<enum name="RestoreTabStateException">
+  <int value="0" label="File not found"/>
+  <int value="1" label="Closed by interrupt exception"/>
+  <int value="2" label="IO exception"/>
+</enum>
+
+<enum name="SadTabEvent">
+  <int value="0" label="Displayed"/>
+  <int value="1" label="Button clicked"/>
+  <int value="2" label="Learn more clicked"/>
+</enum>
+
+<enum name="TabActivationTypes">
+  <int value="0" label="Activate a tab"/>
+  <int value="1" label="Activate the context menu of a tab"/>
+</enum>
+
+<enum name="TabBackgroundLoadStatus">
+  <int value="0" label="Loaded on creation and shown"/>
+  <int value="1" label="Loaded on creation and lost"/>
+  <int value="2" label="Not loaded on creation"/>
+</enum>
+
+<enum name="TabManagerTabRankerResult">
+  <int value="0" label="Success">Tab score was calculated successfully.</int>
+  <int value="1" label="Preprocessor initialization failed">
+    The ExamplePreprocessorConfig could not be loaded or parsed.
+  </int>
+  <int value="2" label="Preprocessor other error">
+    The ExamplePreprocessor returned an error other than, or in addition to, the
+    kNoFeatureIndexFound error.
+  </int>
+</enum>
+
+<enum name="TabRestoreMethod">
+  <int value="0" label="TabState"/>
+  <int value="1" label="CriticalPersistedTabData"/>
+  <int value="2" label="Create new Tab"/>
+  <int value="3" label="Failed to restore"/>
+  <int value="4" label="Skipped (NTP)"/>
+  <int value="5" label="Skipped (Empty URL)"/>
+</enum>
+
+<enum name="TabScreenshotAction">
+  <int value="0" label="User took no actions">
+    User made a screenshot, but did not take any actions with it before
+    navigating away or closing the tab.
+  </int>
+  <int value="1" label="Opened sharing">
+    User took a screenshot then tried to share.
+  </int>
+  <int value="2" label="Download IPH provided">
+    User was provided in product help for Downloads after taking a screenshot.
+  </int>
+</enum>
+
+<enum name="TabSearchCloseActions">
+  <int value="0" label="User did not close via Tab Search UI">
+    The UI was dismissed via interactions outside what is offered by the Tab
+    Search UI.
+  </int>
+  <int value="1" label="User switched to an open tab">
+    The Tab Search UI was dismissed as a result of the user opting to switch to
+    a currently open tab.
+  </int>
+</enum>
+
+<enum name="TabSearchOpenActions">
+  <int value="0" label="User opened the Tab Search UI via a mouse click"/>
+  <int value="1" label="User opened the Tab Search UI via keyboard navigation"/>
+  <int value="2" label="User opened the Tab Search UI via keyboard shortcut"/>
+  <int value="3" label="User opened the Tab Search UI via touch gesture"/>
+</enum>
+
+<enum name="TabSearchRecentlyClosedItemOpenAction">
+  <int value="0"
+      label="Opened a recently closed item from an unfiltered item list"/>
+  <int value="1"
+      label="Opened a recently closed item from a filtered item list"/>
+</enum>
+
+<enum name="TabSearchRecentlyClosedToggleActions">
+  <int value="0" label="Expand the recently closed items section"/>
+  <int value="1" label="Collapse the recently closed items section"/>
+</enum>
+
+<enum name="TabSearchTabSwitchAction">
+  <int value="0" label="Switch to tab from unfiltered tab list"/>
+  <int value="1" label="Switch to tab from filtered search tab list"/>
+</enum>
+
+<enum name="TabStatus">
+  <int value="0" label="Memory resident"/>
+  <int value="1" label="Evicted and reloaded"/>
+  <int value="2" label="Reloaded due to cold start"/>
+  <int value="3" label="Partially evicted"/>
+  <int value="4" label="Reloaded due to backgrounding"/>
+  <int value="5" label="Reloaded due to incognito"/>
+  <int value="6" label="Reloaded due to cold start (fg tab on start)"/>
+  <int value="7" label="Reloaded due to cold start (bg tab on switch)"/>
+  <int value="8" label="Lazy load for 'Open in new tab'"/>
+  <int value="9" label="Stopped due to page loading when backgrounding"/>
+  <int value="10" label="Evicted due to page loading when backgrounding"/>
+  <int value="11" label="Evicted due to OS terminating the renderer."/>
+</enum>
+
+<enum name="TabStripFailureContext">
+  <int value="0" label="New tab failed to open"/>
+</enum>
+
+</enums>
+
+</histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/web_rtc/histograms.xml b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
index c847649..1b0845d 100644
--- a/tools/metrics/histograms/metadata/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
@@ -1848,15 +1848,6 @@
   </summary>
 </histogram>
 
-<histogram name="WebRTC.Video.CaptureToSendTimeMs" units="ms"
-    expires_after="2024-05-07">
-  <owner>handellm@google.com</owner>
-  <summary>
-    The time from when video frames are captured until first packets are put on
-    the wire.
-  </summary>
-</histogram>
-
 <histogram name="WebRTC.Video.CpuLimitedResolutionInPercent" units="%"
     expires_after="never">
 <!-- expires-never: WebRTC health metric. -->
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 7231f97..78c1845 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/3f522a981c45dc5d735a5e5d775c70188adff93c/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "ae07515338644edf3d78199240df7672f9cd1a61",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/8dc60d8a5484638134420511e0f54415853621c9/trace_processor_shell.exe"
+            "hash": "5244c4a1f5fa197b4379477b3305e31f836c351c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/11e588ed3a8fc568f13a4ae156fa848d00909937/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "a6d798c6ea35705f2ab9ace2b224c32e376eb0ce",
@@ -14,7 +14,7 @@
         },
         "mac": {
             "hash": "b72021acf93ec3bdcca6fc3da2fd6e290ee11263",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/9c4d5090f6a29c785d4e2a1a1f577bcc17eeb648/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/11e588ed3a8fc568f13a4ae156fa848d00909937/trace_processor_shell"
         },
         "mac_arm64": {
             "hash": "cc287491e9ff9fe2c4866e5574eaea04134895a0",
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "a0a5e35797a177ab001400797a487094caacf872",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/9c4d5090f6a29c785d4e2a1a1f577bcc17eeb648/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/11e588ed3a8fc568f13a4ae156fa848d00909937/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/aura/test/test_window_delegate.cc b/ui/aura/test/test_window_delegate.cc
index d7880590..cdea9e93 100644
--- a/ui/aura/test/test_window_delegate.cc
+++ b/ui/aura/test/test_window_delegate.cc
@@ -90,6 +90,14 @@
 void TestWindowDelegate::OnWindowTargetVisibilityChanged(bool visible) {
 }
 
+void TestWindowDelegate::OnWindowOcclusionChanged(
+    Window::OcclusionState old_occlusion_state,
+    Window::OcclusionState new_occlusion_state) {
+  if (on_occlusion_changed_) {
+    on_occlusion_changed_.Run();
+  }
+}
+
 bool TestWindowDelegate::HasHitTestMask() const {
   return false;
 }
diff --git a/ui/aura/test/test_window_delegate.h b/ui/aura/test/test_window_delegate.h
index 08579ea8..8f6d05e 100644
--- a/ui/aura/test/test_window_delegate.h
+++ b/ui/aura/test/test_window_delegate.h
@@ -44,6 +44,10 @@
   // Sets the return value for CanFocus(). Default is true.
   void set_can_focus(bool can_focus) { can_focus_ = can_focus; }
 
+  void set_on_occlusion_changed(base::RepeatingClosure callback) {
+    on_occlusion_changed_ = std::move(callback);
+  }
+
   // Overridden from WindowDelegate:
   gfx::Size GetMinimumSize() const override;
   gfx::Size GetMaximumSize() const override;
@@ -62,6 +66,9 @@
   void OnWindowDestroying(Window* window) override;
   void OnWindowDestroyed(Window* window) override;
   void OnWindowTargetVisibilityChanged(bool visible) override;
+  void OnWindowOcclusionChanged(
+      Window::OcclusionState old_occlusion_state,
+      Window::OcclusionState new_occlusion_state) override;
   bool HasHitTestMask() const override;
   void GetHitTestMask(SkPath* mask) const override;
 
@@ -71,6 +78,8 @@
   gfx::Size minimum_size_;
   gfx::Size maximum_size_;
   bool can_focus_;
+
+  base::RepeatingClosure on_occlusion_changed_;
 };
 
 // A simple WindowDelegate implementation for these tests. It owns itself
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
index 5b14302..847d72b 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -12,6 +12,7 @@
 #include "base/process/process.h"
 #include "base/task/current_thread.h"
 #include "base/task/single_thread_task_runner.h"
+#include "base/version.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gfx/geometry/rrect_f.h"
 #include "ui/gfx/linux/drm_util_linux.h"
@@ -114,6 +115,9 @@
   supports_out_of_window_clip_rect_ =
       supported_surface_augmentor_version >=
       AUGMENTED_SURFACE_SET_CLIP_RECT_SINCE_VERSION + 2;
+  // Exo transformation fix landed in https://crrev.com/c/4961473
+  has_transformation_fix_ = server_version.IsValid() &&
+                            server_version >= base::Version("121.0.6113.0");
 
   supports_single_pixel_buffer_ = supports_single_pixel_buffer;
   BindHostInterface(std::move(remote_host));
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
index 867eaee5..87553deb 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
@@ -177,6 +177,7 @@
   bool supports_out_of_window_clip_rect() const {
     return supports_out_of_window_clip_rect_;
   }
+  bool has_transformation_fix() const { return has_transformation_fix_; }
 
   void set_drm_modifiers_filter(
       std::unique_ptr<DrmModifiersFilter> drm_modifiers_filter) {
@@ -313,6 +314,10 @@
   // partially or fully outside of the window.
   bool supports_out_of_window_clip_rect_ = false;
 
+  // Whether wayland server has the fix that applies transformations in the
+  // correct order.
+  bool has_transformation_fix_ = false;
+
   // A DRM modifiers filter to ensure we don't allocate buffers with modifiers
   // not supported by Vulkan.
   std::unique_ptr<DrmModifiersFilter> drm_modifiers_filter_;
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 7865f84..71850fc 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -407,6 +407,8 @@
           buffer_manager_->supports_affine_transform();
       properties.supports_out_of_window_clip_rect =
           buffer_manager_->supports_out_of_window_clip_rect();
+      properties.has_transformation_fix =
+          buffer_manager_->has_transformation_fix();
     }
     return properties;
   }
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index f2b0691..78b46426 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -215,6 +215,10 @@
     // there is a bug. Set this flag to enabled in GPU process when the
     // remaining issues are resolved.
     bool supports_out_of_window_clip_rect = false;
+
+    // Whether wayland server has the fix that applies transformations in the
+    // correct order.
+    bool has_transformation_fix = false;
   };
 
   // Corresponds to chrome_browser_main_extra_parts.h.
diff --git a/ui/views/action_view_controller_unittest.cc b/ui/views/action_view_controller_unittest.cc
index a6fc75c..626f33ed 100644
--- a/ui/views/action_view_controller_unittest.cc
+++ b/ui/views/action_view_controller_unittest.cc
@@ -123,24 +123,21 @@
   action_item->SetEnabled(true);
 }
 
-// Test that action triggered callbacks get called.
-// TODO(crbug.com/1500125): Re-enable this test
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
-#define MAYBE_TriggerAction DISABLED_TriggerAction
-#else
-#define MAYBE_TriggerAction TriggerAction
-#endif
-TEST_F(ActionViewControllerTest, MAYBE_TriggerAction) {
+TEST_F(ActionViewControllerTest, TriggerAction) {
+  std::unique_ptr<Widget> test_widget = CreateTestWidget();
+  View* parent_view = test_widget->SetContentsView(std::make_unique<View>());
+  MdTextButton* action_view =
+      parent_view->AddChildView(std::make_unique<MdTextButton>());
+  test_widget->Show();
   std::unique_ptr<actions::ActionItem> action_item = CreateEnabledActionItem();
-  auto action_view = std::make_unique<MdTextButton>();
   auto action_view_controller =
       std::make_unique<ActionViewController<MdTextButton>>(
-          action_view.get(), action_item->GetAsWeakPtr());
+          action_view, action_item->GetAsWeakPtr());
   action_view_controller->SetActionItem(action_item->GetAsWeakPtr());
   EXPECT_EQ(0, action_item->GetInvokeCount());
   ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
                    ui::EventTimeForNow(), 0, 0);
-  views::test::ButtonTestApi test_api(action_view.get());
+  views::test::ButtonTestApi test_api(action_view);
   test_api.NotifyClick(e);
   EXPECT_EQ(1, action_item->GetInvokeCount());
 }
diff --git a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
index 4e6040d7..1e339b44 100644
--- a/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
+++ b/ui/webui/resources/cr_elements/chromeos/cros_color_overrides.css
@@ -184,6 +184,19 @@
   --cr-checkbox-unchecked-ripple-opacity: 1;
 }
 
+:host-context([cros][chrome-refresh-2023]) cr-checkbox {
+  --cr-checkbox-focus-outline: none;
+}
+
+:host-context([cros][chrome-refresh-2023]) cr-checkbox[disabled] {
+  opacity: var(--cros-disabled-opacity);
+}
+
+:host-context([cros][chrome-refresh-2023]):host-context(.focus-outline-visible)
+    cr-checkbox:focus {
+  --cr-checkbox-ripple-ring: 2px solid var(--cros-sys-focus_ring);
+}
+
 /* Dialog */
 :host-context(body.jelly-enabled) cr-dialog::part(dialog) {
   --cr-dialog-background-color: var(--cros-sys-base_elevated);
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
index c2167a5..6aae71f 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.html
@@ -120,7 +120,8 @@
       }
 
       :host-context([chrome-refresh-2023]) #checkbox:focus-visible {
-        outline: 2px solid var(--cr-focus-outline-color);
+        outline: var(--cr-checkbox-focus-outline,
+            2px solid var(--cr-focus-outline-color));
         outline-offset: 2px;
       }
 
diff --git a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.ts b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.ts
index 1133b10..8ba2f50b 100644
--- a/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.ts
+++ b/ui/webui/resources/cr_elements/cr_checkbox/cr_checkbox.ts
@@ -92,6 +92,17 @@
 
   override ready() {
     super.ready();
+
+    // <if expr="chromeos_ash">
+    // TODO(b/309689294) Remove this once CrOS UIs migrate to Jellybean
+    // components and no longer use cr-elements.
+    // Force stamp the ripple element to enable CrOS focus styles. Ripple
+    // visibility is controlled by the event listeners below.
+    if (document.documentElement.hasAttribute('chrome-refresh-2023')) {
+      this.getRipple();
+    }
+    // </if>
+
     this.removeAttribute('unresolved');
     this.addEventListener('click', this.onClick_.bind(this));
     this.addEventListener('pointerup', this.hideRipple_.bind(this));
diff --git a/v8 b/v8
index eb5ccbd..6fc23a3 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit eb5ccbd8cda19af0d91ad55fb750637a3ec0f0b8
+Subproject commit 6fc23a31385211f1e580eb2646f398576c5d3a80