diff --git a/DEPS b/DEPS
index 481eb8e..08627639 100644
--- a/DEPS
+++ b/DEPS
@@ -253,7 +253,7 @@
   'screen_ai_windows_386': 'version:127.16',
 
   # siso CIPD package version.
-  'siso_version': 'git_revision:c5366c123992b4762aa22d05c4345b609832db2d',
+  'siso_version': 'git_revision:4442c27a02f2b5de389f88be9639e4e638ff761a',
 
   # download libaom test data
   'download_libaom_testdata': False,
@@ -276,7 +276,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '89050d27a6a3b76131a68e53f1b595f111c45531',
+  'src_internal_revision': '7a7ac80a328065cac1905dae0f019dee50e2ca76',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -284,11 +284,11 @@
   # 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': 'fe2f760357d5ca88c93d9a85df65d8857dd6d065',
+  'v8_revision': 'd0992c4954f47858b3996d9cd34e0f948be36a1e',
   # 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': '8dda514cb833c0c8392a4521f6d15454a053ebf7',
+  'angle_revision': 'bc40362b76d834549424022530a5e11933ff4862',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -348,7 +348,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '756ddec12fabb51aa3743ce9878b3242244806fa',
+  'catapult_revision': 'cfa9c31cbefb033f14b4281483ab9abcf43e4dc6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
@@ -396,7 +396,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'cb2eb64cf9e272cf20b97b3b5a03558160ac1fa5',
+  'dawn_revision': '18f45d0373057395c9875b455035ef19ca6a655c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1123,7 +1123,7 @@
   },
 
   'src/chrome/release_scripts': {
-      'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + '83f9d6beab5f8470b28544b3d957c4940a94aa1c',
+      'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + 'e54cd72a13e2b29fc1ddeee1e1b0553bfd034594',
       'condition': 'checkout_chrome_release_scripts',
   },
 
@@ -1452,7 +1452,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'efaadf4d8163bfc2e9fb9a0e60a75f3867b6beb7',
+    '5ba3a0efeedf8dfa0fd609ba309bcd7206ba69e3',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1611,7 +1611,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'RNaRkEnpAXvGMazFRpx3bIq1mAr-qKw3OayAO4r644EC',
+          'version': '8Lvm0dWvOzqAyYTrr0ZeIV3XNDBWvOfZ0V9t-sQPAmkC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -1947,7 +1947,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0a3addbf632a21bd96c25107719749c76746b977',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e69b086c0f3764fe1211435b297f793e02ecd13e',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2449,7 +2449,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '4ce2473d1db95a2a4e9648941165a209feb94f39',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '26425fcdd18c91a10f59740765c53c5079cf61e3',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2811,13 +2811,13 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '450cceb587613ac1469c5a131fac15935c99e0e7',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'cdf2938973d2a4e7c33fdf24012565f3cbf961a2',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'fc2030c82b883131bf452ba2be36e41f95ae5475',
 
   'src/third_party/webpagereplay':
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '4f2c1b8f947a15efd795df758c11b06a4e94646b',
+    Var('webrtc_git') + '/src.git' + '@' + '4aaaa6848bc2521f0ab75a46c4bfeaf68b8b9ffb',
 
   # 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.
@@ -4357,7 +4357,7 @@
     'packages' : [
       {
         'package': 'chromeos_internal/inputs/orca',
-        'version': '0TLXAzimnpVDxwj9MXCZ5vDkb4NtHI-R0E6z5FkjePEC'
+        'version': 'qajSgJbVk3YxHIMjv7jgzkG-95cz9xCTj7Lv38U6sAAC'
       }
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4545,7 +4545,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '9518013ca7508d5b342ad76d9b3d91185c606312',
+        '6444bdf131517e4efff8b82981cdc601b2e92e47',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 6699f7e..66f653d 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -2463,7 +2463,6 @@
                      'michaelcheco+watch-accelerators@google.com'],
     'accessibility': ['abigailbklein+watch@google.com',
                       'akihiroota@chromium.org',
-                      'bicioglu+watch@google.com',
                       'dtseng+watch@chromium.org',
                       'je_julie.kim@chromium.org',
                       'josiahk+watch@chromium.org',
diff --git a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index d7c361b..cc41902b 100644
--- a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4397,6 +4397,9 @@
     setter selectedIndex
     setter size
     setter value
+interface HTMLSelectedContentElement : HTMLElement
+    attribute @@toStringTag
+    method constructor
 interface HTMLSlotElement : HTMLElement
     attribute @@toStringTag
     getter name
diff --git a/ash/accelerators/tablet_volume_controller.cc b/ash/accelerators/tablet_volume_controller.cc
index a7fce05..e07c76c 100644
--- a/ash/accelerators/tablet_volume_controller.cc
+++ b/ash/accelerators/tablet_volume_controller.cc
@@ -66,20 +66,19 @@
     return;
   }
 
-  std::optional<base::Value> parsed_json =
-      base::JSONReader::Read(location_info);
-  if (!parsed_json || !parsed_json->is_dict()) {
+  std::optional<base::Value::Dict> parsed_json =
+      base::JSONReader::ReadDict(location_info);
+  if (!parsed_json) {
     LOG(ERROR) << "JSONReader failed reading side volume button location info: "
                << location_info;
     return;
   }
 
-  const base::Value::Dict& info_in_dict = parsed_json->GetDict();
-  const std::string* region = info_in_dict.FindString(kVolumeButtonRegion);
+  const std::string* region = parsed_json->FindString(kVolumeButtonRegion);
   if (region)
     side_volume_button_location_.region = *region;
 
-  const std::string* side = info_in_dict.FindString(kVolumeButtonSide);
+  const std::string* side = parsed_json->FindString(kVolumeButtonSide);
   if (side)
     side_volume_button_location_.side = *side;
 }
diff --git a/ash/app_list/app_list_bubble_presenter.cc b/ash/app_list/app_list_bubble_presenter.cc
index 409b5805..90b6620 100644
--- a/ash/app_list/app_list_bubble_presenter.cc
+++ b/ash/app_list/app_list_bubble_presenter.cc
@@ -22,6 +22,7 @@
 #include "ash/public/cpp/app_list/app_list_types.h"
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/scanner/scanner_metrics.h"
 #include "ash/shelf/home_button.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shelf/shelf_navigation_widget.h"
@@ -280,8 +281,14 @@
 
   // Show the sunfish nudge after the widget is shown, so the anchor view is
   // visible.
-  controller_->MaybeShowSunfishLauncherNudge(
-      bubble_view_->search_box_view()->sunfish_button());
+  views::ImageButton* sunfish_button =
+      bubble_view_->search_box_view()->sunfish_button();
+  // `sunfish_button` is always initialised in `SearchBoxView`'s
+  // constructor.
+  CHECK(sunfish_button);
+  controller_->MaybeShowSunfishLauncherNudge(sunfish_button);
+  RecordSunfishSessionButtonVisibilityOnLauncherShown(
+      /*is_visible=*/sunfish_button->GetVisible());
 }
 
 ShelfAction AppListBubblePresenter::Toggle(int64_t display_id) {
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 65995464..0ed17a6 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -50,6 +50,7 @@
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller.h"
 #include "ash/root_window_controller.h"
+#include "ash/scanner/scanner_metrics.h"
 #include "ash/screen_util.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/home_button.h"
@@ -66,6 +67,7 @@
 #include "ash/wm/window_util.h"
 #include "base/barrier_closure.h"
 #include "base/callback_list.h"
+#include "base/check.h"
 #include "base/command_line.h"
 #include "base/containers/adapters.h"
 #include "base/containers/contains.h"
@@ -1703,11 +1705,16 @@
       app_list_view->OnAppListVisibilityChanged(real_visibility);
 
       // Only handle the tablet mode visibility changes, and let clamshell mode
-      // handle the nudge separately.
+      // handle the nudge and button visibility metrics separately.
       if (real_visibility && IsInTabletMode()) {
-        MaybeShowSunfishLauncherNudge(fullscreen_presenter_->GetView()
-                                          ->search_box_view()
-                                          ->sunfish_button());
+        views::ImageButton* sunfish_button =
+            app_list_view->search_box_view()->sunfish_button();
+        // `sunfish_button` is always initialised in `SearchBoxView`'s
+        // constructor.
+        CHECK(sunfish_button);
+        MaybeShowSunfishLauncherNudge(sunfish_button);
+        RecordSunfishSessionButtonVisibilityOnLauncherShown(
+            /*is_visible=*/sunfish_button->GetVisible());
       }
     }
 
diff --git a/ash/ash_prefs.cc b/ash/ash_prefs.cc
index 64d10e27..709fe58 100644
--- a/ash/ash_prefs.cc
+++ b/ash/ash_prefs.cc
@@ -37,6 +37,7 @@
 #include "ash/metrics/feature_discovery_duration_reporter_impl.h"
 #include "ash/projector/projector_controller_impl.h"
 #include "ash/public/cpp/holding_space/holding_space_prefs.h"
+#include "ash/public/cpp/lobster/lobster_enums.h"
 #include "ash/quick_insert/quick_insert_controller.h"
 #include "ash/quick_pair/feature_status_tracker/scanning_enabled_provider.h"
 #include "ash/quick_pair/keyed_service/quick_pair_mediator.h"
@@ -213,6 +214,10 @@
     registry->RegisterBooleanPref(prefs::kOrcaEnabled, true);
     registry->RegisterBooleanPref(prefs::kOrcaFeedbackEnabled, true);
     registry->RegisterBooleanPref(prefs::kLobsterEnabled, true);
+    registry->RegisterBooleanPref(
+        prefs::kLobsterEnterprisePolicySettings,
+        base::to_underlying(
+            ash::LobsterEnterprisePolicyValue::kAllowedWithModelImprovement));
     registry->RegisterBooleanPref(::prefs::kLiveCaptionEnabled, false);
     registry->RegisterListPref(
         chromeos::prefs::kKeepFullscreenWithoutNotificationUrlAllowList);
diff --git a/ash/capture_mode/capture_mode_api.cc b/ash/capture_mode/capture_mode_api.cc
index f81b5bb..e2c9d90 100644
--- a/ash/capture_mode/capture_mode_api.cc
+++ b/ash/capture_mode/capture_mode_api.cc
@@ -17,26 +17,14 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 
 namespace ash {
-namespace {
-
-bool ScannerCanShowUi() {
-  if (!Shell::HasInstance()) {
-    return false;
-  }
-
-  auto* scanner_controller = Shell::Get()->scanner_controller();
-  // This check checks if scanner is enabled (while ignoring consent status).
-  return scanner_controller && scanner_controller->CanShowUi();
-}
-
-}  // namespace
 
 void CaptureScreenshotsOfAllDisplays() {
   CaptureModeController::Get()->CaptureScreenshotsOfAllDisplays();
 }
 
 bool IsSunfishSessionAllowed() {
-  if (!features::IsSunfishFeatureEnabled() && !ScannerCanShowUi()) {
+  if (!features::IsSunfishFeatureEnabled() &&
+      !ScannerController::CanShowUiForShell()) {
     return false;
   }
 
diff --git a/ash/capture_mode/capture_mode_behavior.cc b/ash/capture_mode/capture_mode_behavior.cc
index 175efbf..b3c615e 100644
--- a/ash/capture_mode/capture_mode_behavior.cc
+++ b/ash/capture_mode/capture_mode_behavior.cc
@@ -115,8 +115,7 @@
   }
   bool ShouldEndSessionOnShowingSearchResults() const override { return true; }
   bool CanShowSmartActionsButton() const override {
-    auto* scanner_controller = Shell::Get()->scanner_controller();
-    return scanner_controller && scanner_controller->CanShowUi();
+    return ScannerController::CanShowUiForShell();
   }
   bool CanShowActionButtons() const override { return true; }
   void OnRegionSelectedOrAdjusted() override {
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 7ac4b88..3914c9f 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -2033,19 +2033,21 @@
 void CaptureModeController::OnTextDetectionComplete(
     base::WeakPtr<BaseCaptureModeSession> image_search_token,
     base::TimeTicks ocr_attempt_start_time,
-    std::string detected_text) {
+    std::optional<std::string> detected_text) {
   RecordOnDeviceOcrTimerCompleted(ocr_attempt_start_time);
-  if (!image_search_token || detected_text.empty()) {
+  if (!image_search_token || !detected_text.has_value() ||
+      detected_text->empty()) {
     return;
   }
 
-  AddCopyTextAndSmartActionsButtons(detected_text);
+  AddCopyTextAndSmartActionsButtons(*detected_text);
 }
 
 void CaptureModeController::OnLensTextDetectionComplete(
     base::WeakPtr<BaseCaptureModeSession> image_search_token,
-    std::string detected_text) {
-  if (!image_search_token || detected_text.empty()) {
+    std::optional<std::string> detected_text) {
+  if (!image_search_token || !detected_text.has_value() ||
+      detected_text->empty()) {
     return;
   }
 
@@ -2053,7 +2055,7 @@
   // we are in a sunfish session.
   if (capture_mode_session_->active_behavior()->behavior_type() ==
       BehaviorType::kSunfish) {
-    AddCopyTextAndSmartActionsButtons(detected_text);
+    AddCopyTextAndSmartActionsButtons(*detected_text);
   }
 }
 
@@ -2084,8 +2086,7 @@
   if (!startup_success ||
       // Below conditions imply scanner is disabled in some way.
       // Hence we should skip showing the disclaimer.
-      !Shell::Get()->scanner_controller() ||
-      !Shell::Get()->scanner_controller()->CanShowUi()) {
+      !ScannerController::CanShowUiForShell()) {
     if (!features::IsSunfishFeatureEnabled() && IsActive()) {
       // Should stop because if both scanner and sunfish are disabled, then
       // there is nothing you can do in the session.
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index 74bc822..be2bd69 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -550,7 +550,7 @@
   void OnTextDetectionComplete(
       base::WeakPtr<BaseCaptureModeSession> image_search_token,
       base::TimeTicks ocr_attempt_start_time,
-      std::string detected_text);
+      std::optional<std::string> detected_text);
 
   // Called back when Lens-based text detection is complete to show the copy
   // text button if needed. `image_search_token` is a weak pointer which is
@@ -560,7 +560,7 @@
   // used in Sunfish capture mode sessions when a region is selected.
   void OnLensTextDetectionComplete(
       base::WeakPtr<BaseCaptureModeSession> image_search_token,
-      std::string detected_text);
+      std::optional<std::string> detected_text);
 
   // Helper function that adds a Copy Text button and potentially a Smart
   // Actions button to the session. Called when both Lens-based and on-device
diff --git a/ash/capture_mode/sunfish_unittest.cc b/ash/capture_mode/sunfish_unittest.cc
index 708d6f5..34512f61 100644
--- a/ash/capture_mode/sunfish_unittest.cc
+++ b/ash/capture_mode/sunfish_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <string_view>
 #include <utility>
@@ -3211,6 +3212,27 @@
       ActionButtonViewID::kCopyTextButton));
 }
 
+// Tests that the copy text button is not shown in default capture mode if the
+// OCR request for the selected region fails.
+TEST_F(ScannerTest, NoCopyTextButtonIfOcrRequestFailed) {
+  auto* controller = CaptureModeController::Get();
+  StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kImage);
+  base::test::TestFuture<OnTextDetectionComplete> detect_text_future;
+  auto* test_delegate =
+      static_cast<TestCaptureModeDelegate*>(controller->delegate_for_testing());
+  EXPECT_CALL(*test_delegate, DetectTextInImage)
+      .WillOnce(WithArg<1>(InvokeFuture(detect_text_future)));
+
+  SelectCaptureModeRegion(GetEventGenerator(), gfx::Rect(0, 0, 50, 200),
+                          /*release_mouse=*/true, /*verify_region=*/true);
+  detect_text_future.Take().Run(std::nullopt);
+
+  const CaptureModeSessionTestApi session_test_api(
+      controller->capture_mode_session());
+  EXPECT_FALSE(session_test_api.GetActionButtonByViewId(
+      ActionButtonViewID::kCopyTextButton));
+}
+
 // Tests that the copy text button is not shown if the selected region changes
 // before text detection completes.
 TEST_F(ScannerTest, NoCopyTextButtonIfSelectedRegionChanges) {
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 771e012..cdf73b7 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -95,6 +95,16 @@
 // A boolean pref of whether Lobster is enabled.
 inline constexpr char kLobsterEnabled[] = "settings.lobster_enabled";
 
+// An integer pref that records whether Lobster is allowed by enterprise
+// policy.
+// This integer has three valid values:
+// - 0: Allowed with model improvement.
+// - 1: Allowed without model improvement.
+// - 2: Disallowed.
+// Any other value outside of the range should behave identically to 1.
+inline constexpr char kLobsterEnterprisePolicySettings[] =
+    "settings.lobster.enterprise_settings";
+
 // A boolean pref used by an admin policy to enable/disable particular
 // features on the physical keyboard. See the policy at
 // PhysicalKeyboardAutocorrect.yml.
diff --git a/ash/public/cpp/capture_mode/capture_mode_delegate.h b/ash/public/cpp/capture_mode/capture_mode_delegate.h
index 849b334..101614b 100644
--- a/ash/public/cpp/capture_mode/capture_mode_delegate.h
+++ b/ash/public/cpp/capture_mode/capture_mode_delegate.h
@@ -5,6 +5,7 @@
 #ifndef ASH_PUBLIC_CPP_CAPTURE_MODE_CAPTURE_MODE_DELEGATE_H_
 #define ASH_PUBLIC_CPP_CAPTURE_MODE_CAPTURE_MODE_DELEGATE_H_
 
+#include <optional>
 #include <string>
 
 #include "ash/public/cpp/ash_public_export.h"
@@ -58,10 +59,11 @@
     base::OnceCallback<void(int64_t free_remaining_bytes)>;
 
 // Defines the type of the callback that will be invoked when text detection has
-// been performed on an image. `detected_text` contains detected text, or is
-// empty if no text is detected.
+// been performed on an image. `detected_text` contains detected text, empty if
+// no text has been detected, or nullopt if text detection fails (such as the
+// OCR service being reset after the text detection request).
 using OnTextDetectionComplete =
-    base::OnceCallback<void(std::string detected_text)>;
+    base::OnceCallback<void(std::optional<std::string> detected_text)>;
 
 // Defines the type of the callback that will be invoked when the search backend
 // result is fetched. Repeating because the `LensOverlayUrlResponseCallback`
diff --git a/ash/public/cpp/lobster/lobster_enums.h b/ash/public/cpp/lobster/lobster_enums.h
index c9fa234..9fd93b5 100644
--- a/ash/public/cpp/lobster/lobster_enums.h
+++ b/ash/public/cpp/lobster/lobster_enums.h
@@ -32,6 +32,14 @@
   kBlocked,
 };
 
+enum class ASH_PUBLIC_EXPORT LobsterEnterprisePolicyValue : int {
+  // The policy allows the feature to run with model improvement.
+  kAllowedWithModelImprovement = 0,
+  // The policy allows the feature
+  kAllowedWithoutModelImprovement = 1,
+  kDisabled = 2,
+};
+
 enum class ASH_PUBLIC_EXPORT LobsterSystemCheck {
   kMinValue,
   kInvalidConsent,
diff --git a/ash/scanner/scanner_controller.cc b/ash/scanner/scanner_controller.cc
index 8975260..f18da612 100644
--- a/ash/scanner/scanner_controller.cc
+++ b/ash/scanner/scanner_controller.cc
@@ -398,6 +398,29 @@
       static_cast<int>(ScannerEnterprisePolicy::kAllowedWithModelImprovement));
 }
 
+// static
+bool ScannerController::CanShowUiForShell() {
+  if (!Shell::HasInstance()) {
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedFalseDueToNoShellInstance);
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedFalse);
+    return false;
+  }
+
+  ScannerController* controller = Shell::Get()->scanner_controller();
+  if (!controller) {
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::
+            kCanShowUiReturnedFalseDueToNoControllerOnShell);
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedFalse);
+    return false;
+  }
+
+  return controller->CanShowUi();
+}
+
 void ScannerController::OnActiveUserSessionChanged(
     const AccountId& account_id) {
   scanner_session_ = nullptr;
@@ -405,22 +428,6 @@
 }
 
 bool ScannerController::CanShowUi() {
-  ScannerProfileScopedDelegate* profile_scoped_delegate =
-      delegate_->GetProfileScopedDelegate();
-
-  if (profile_scoped_delegate == nullptr) {
-    return false;
-  }
-
-  specialized_features::FeatureAccessFailureSet checks =
-      profile_scoped_delegate->CheckFeatureAccess();
-
-  checks.Remove(
-      specialized_features::FeatureAccessFailure::kConsentNotAccepted);
-  if (!checks.empty()) {
-    return false;
-  }
-
   // Check enterprise policy.
   const AccountId& account_id = session_controller_->GetActiveAccountId();
   PrefService* prefs =
@@ -430,9 +437,100 @@
   if (prefs != nullptr &&
       prefs->GetInteger(prefs::kScannerEnterprisePolicyAllowed) ==
           static_cast<int>(ScannerEnterprisePolicy::kDisallowed)) {
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedFalseDueToEnterprisePolicy);
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedFalse);
     return false;
   }
 
+  ScannerProfileScopedDelegate* profile_scoped_delegate =
+      delegate_->GetProfileScopedDelegate();
+
+  if (profile_scoped_delegate == nullptr) {
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::
+            kCanShowUiReturnedFalseDueToNoProfileScopedDelegate);
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedFalse);
+    return false;
+  }
+
+  specialized_features::FeatureAccessFailureSet checks =
+      profile_scoped_delegate->CheckFeatureAccess();
+
+  bool consent_accepted = true;
+  bool show_ui = true;
+
+  for (specialized_features::FeatureAccessFailure failure : checks) {
+    switch (failure) {
+      case specialized_features::FeatureAccessFailure::kConsentNotAccepted:
+        consent_accepted = false;
+        break;
+
+      case specialized_features::FeatureAccessFailure::kDisabledInSettings:
+        RecordScannerFeatureUserState(
+            ScannerFeatureUserState::
+                kCanShowUiReturnedFalseDueToSettingsToggle);
+        show_ui = false;
+        break;
+
+      case specialized_features::FeatureAccessFailure::kFeatureFlagDisabled:
+        RecordScannerFeatureUserState(
+            ScannerFeatureUserState::kCanShowUiReturnedFalseDueToFeatureFlag);
+        show_ui = false;
+        break;
+
+      case specialized_features::FeatureAccessFailure::
+          kFeatureManagementCheckFailed:
+        RecordScannerFeatureUserState(
+            ScannerFeatureUserState::
+                kCanShowUiReturnedFalseDueToFeatureManagement);
+        show_ui = false;
+        break;
+
+      case specialized_features::FeatureAccessFailure::kSecretKeyCheckFailed:
+        RecordScannerFeatureUserState(
+            ScannerFeatureUserState::kCanShowUiReturnedFalseDueToSecretKey);
+        show_ui = false;
+        break;
+
+      case specialized_features::FeatureAccessFailure::
+          kAccountCapabilitiesCheckFailed:
+        RecordScannerFeatureUserState(
+            ScannerFeatureUserState::
+                kCanShowUiReturnedFalseDueToAccountCapabilities);
+        show_ui = false;
+        break;
+
+      case specialized_features::FeatureAccessFailure::kCountryCheckFailed:
+        RecordScannerFeatureUserState(
+            ScannerFeatureUserState::kCanShowUiReturnedFalseDueToCountry);
+        show_ui = false;
+        break;
+
+      case specialized_features::FeatureAccessFailure::
+          kDisabledInKioskModeCheckFailed:
+        RecordScannerFeatureUserState(
+            ScannerFeatureUserState::kCanShowUiReturnedFalseDueToKioskMode);
+        show_ui = false;
+        break;
+    }
+  }
+
+  if (!show_ui) {
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedFalse);
+    return false;
+  }
+
+  if (!consent_accepted) {
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedTrueWithoutConsent);
+  } else {
+    RecordScannerFeatureUserState(
+        ScannerFeatureUserState::kCanShowUiReturnedTrueWithConsent);
+  }
   return true;
 }
 
@@ -458,17 +556,6 @@
 }
 
 bool ScannerController::CanStartSession() {
-  ScannerProfileScopedDelegate* profile_scoped_delegate =
-      delegate_->GetProfileScopedDelegate();
-
-  if (profile_scoped_delegate == nullptr) {
-    return false;
-  }
-
-  if (!profile_scoped_delegate->CheckFeatureAccess().empty()) {
-    return false;
-  }
-
   // Check enterprise policy.
   const AccountId& account_id = session_controller_->GetActiveAccountId();
   PrefService* prefs =
@@ -481,6 +568,17 @@
     return false;
   }
 
+  ScannerProfileScopedDelegate* profile_scoped_delegate =
+      delegate_->GetProfileScopedDelegate();
+
+  if (profile_scoped_delegate == nullptr) {
+    return false;
+  }
+
+  if (!profile_scoped_delegate->CheckFeatureAccess().empty()) {
+    return false;
+  }
+
   return true;
 }
 
diff --git a/ash/scanner/scanner_controller.h b/ash/scanner/scanner_controller.h
index 1710452..63fe57a 100644
--- a/ash/scanner/scanner_controller.h
+++ b/ash/scanner/scanner_controller.h
@@ -46,6 +46,10 @@
 
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
+  // Returns `CanShowUi` for the `ScannerController` on the global singleton
+  // `Shell` instance. If it does not exist, returns false.
+  static bool CanShowUiForShell();
+
   // SessionObserver:
   void OnActiveUserSessionChanged(const AccountId& account_id) override;
 
diff --git a/ash/scanner/scanner_controller_unittest.cc b/ash/scanner/scanner_controller_unittest.cc
index 6cf030a..d118a54 100644
--- a/ash/scanner/scanner_controller_unittest.cc
+++ b/ash/scanner/scanner_controller_unittest.cc
@@ -24,6 +24,7 @@
 #include "ash/scanner/fake_scanner_profile_scoped_delegate.h"
 #include "ash/scanner/scanner_action_view_model.h"
 #include "ash/scanner/scanner_enterprise_policy.h"
+#include "ash/scanner/scanner_metrics.h"
 #include "ash/scanner/scanner_session.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -38,6 +39,7 @@
 #include "base/strings/string_split.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/gmock_expected_support.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
 #include "base/test/protobuf_matchers.h"
 #include "base/test/scoped_feature_list.h"
@@ -140,6 +142,20 @@
   MOCK_METHOD(void, Resume, (), (override));
 };
 
+class MockScannerDelegate : public ScannerDelegate {
+ public:
+  MOCK_METHOD(ScannerProfileScopedDelegate*,
+              GetProfileScopedDelegate,
+              (),
+              (override));
+  MOCK_METHOD(void,
+              OpenFeedbackDialog,
+              (const AccountId& account_id,
+               ScannerFeedbackInfo feedback_info,
+               SendFeedbackCallback send_feedback_callback),
+              (override));
+};
+
 class ScannerControllerTest : public AshTestBase {
  public:
   ScannerControllerTest() = default;
@@ -273,6 +289,7 @@
           specialized_features::FeatureAccessFailure::kConsentNotAccepted}));
 
   EXPECT_TRUE(scanner_controller->CanShowUi());
+  EXPECT_TRUE(ScannerController::CanShowUiForShell());
 }
 
 TEST_F(ScannerControllerTest,
@@ -287,6 +304,7 @@
       static_cast<int>(ScannerEnterprisePolicy::kAllowedWithModelImprovement));
 
   EXPECT_TRUE(scanner_controller->CanShowUi());
+  EXPECT_TRUE(ScannerController::CanShowUiForShell());
 }
 
 TEST_F(ScannerControllerTest,
@@ -302,6 +320,7 @@
           ScannerEnterprisePolicy::kAllowedWithoutModelImprovement));
 
   EXPECT_TRUE(scanner_controller->CanShowUi());
+  EXPECT_TRUE(ScannerController::CanShowUiForShell());
 }
 
 TEST_F(ScannerControllerTest, CanShowUiIfEnterprisePolicyIsInvalidValue) {
@@ -314,6 +333,7 @@
       prefs::kScannerEnterprisePolicyAllowed, 3);
 
   EXPECT_TRUE(scanner_controller->CanShowUi());
+  EXPECT_TRUE(ScannerController::CanShowUiForShell());
 }
 
 TEST_F(ScannerControllerTest, CannotShowUiIfDisallowedByEnterprisePolicy) {
@@ -327,6 +347,317 @@
       static_cast<int>(ScannerEnterprisePolicy::kDisallowed));
 
   EXPECT_FALSE(scanner_controller->CanShowUi());
+  EXPECT_FALSE(ScannerController::CanShowUiForShell());
+}
+
+TEST(ScannerControllerNoFixtureTest, CanShowUiForShellFalseWhenNoShell) {
+  ASSERT_FALSE(Shell::HasInstance());
+  EXPECT_FALSE(ScannerController::CanShowUiForShell());
+}
+
+class ScannerControllerDisabledTest : public AshTestBase {
+ public:
+  ScannerControllerDisabledTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{}, /*disabled_features=*/{
+            features::kScannerUpdate, features::kScannerDogfood});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(ScannerControllerDisabledTest, CanShowUiForShellFalseWhenNoController) {
+  ASSERT_FALSE(Shell::Get()->scanner_controller());
+  EXPECT_FALSE(ScannerController::CanShowUiForShell());
+}
+
+TEST(ScannerControllerNoFixtureTest, CanShowUiForShellFalseWhenNoShellMetrics) {
+  base::HistogramTester histogram_tester;
+
+  ASSERT_FALSE(Shell::HasInstance());
+  ASSERT_FALSE(ScannerController::CanShowUiForShell());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToNoShellInstance, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerDisabledTest,
+       CanShowUiForShellFalseWhenNoControllerMetrics) {
+  base::HistogramTester histogram_tester;
+
+  ASSERT_FALSE(Shell::Get()->scanner_controller());
+  ASSERT_FALSE(ScannerController::CanShowUiForShell());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToNoControllerOnShell,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST(ScannerControllerNoFixtureTest,
+     CanShowUiFalseWhenNoProfileScopedDelegateMetrics) {
+  base::HistogramTester histogram_tester;
+  SessionControllerImpl session_controller;
+  auto mock_delegate = std::make_unique<MockScannerDelegate>();
+  EXPECT_CALL(*mock_delegate, GetProfileScopedDelegate())
+      .WillRepeatedly(Return(nullptr));
+  ScannerController scanner_controller(std::move(mock_delegate),
+                                       session_controller);
+
+  ASSERT_FALSE(scanner_controller.CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::
+          kCanShowUiReturnedFalseDueToNoProfileScopedDelegate,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest,
+       CanShowUiFalseWhenEnterprisePolicyDisallowedMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{}));
+  Shell::Get()->session_controller()->GetActivePrefService()->SetInteger(
+      prefs::kScannerEnterprisePolicyAllowed,
+      static_cast<int>(ScannerEnterprisePolicy::kDisallowed));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToEnterprisePolicy, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest, CanShowUiFalseWhenSettingsToggleDisabledMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kDisabledInSettings}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToSettingsToggle, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest, CanShowUiFalseWhenFeatureFlagDisabledMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kFeatureFlagDisabled}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToFeatureFlag, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest,
+       CanShowUiFalseWhenFeatureManagementCheckFailedMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::
+              kFeatureManagementCheckFailed}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToFeatureManagement,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest, CanShowUiFalseWhenSecretKeyCheckFailedMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kSecretKeyCheckFailed}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToSecretKey, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest, CanShowUiFalseWhenCountryCheckFailedMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kCountryCheckFailed}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToCountry, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest, CanShowUiFalseWhenKioskModeMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::
+              kDisabledInKioskModeCheckFailed}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToKioskMode, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest,
+       CanShowUiFalseMultipleFeatureAccessCheckFailsWithoutConsentMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::
+              kFeatureManagementCheckFailed,
+          specialized_features::FeatureAccessFailure::
+              kAccountCapabilitiesCheckFailed,
+          specialized_features::FeatureAccessFailure::kCountryCheckFailed}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToFeatureManagement,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToAccountCapabilities,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToCountry, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest,
+       CanShowUiFalseMultipleFeatureAccessCheckFailsWithConsentMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kConsentNotAccepted,
+          specialized_features::FeatureAccessFailure::
+              kFeatureManagementCheckFailed,
+          specialized_features::FeatureAccessFailure::
+              kAccountCapabilitiesCheckFailed,
+          specialized_features::FeatureAccessFailure::kCountryCheckFailed}));
+
+  ASSERT_FALSE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToFeatureManagement,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToAccountCapabilities,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalseDueToCountry, 1);
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedFalse, 1);
+}
+
+TEST_F(ScannerControllerTest, CanShowUiTrueWithoutConsentMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kConsentNotAccepted}));
+
+  ASSERT_TRUE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedTrueWithoutConsent, 1);
+}
+
+TEST_F(ScannerControllerTest, CanShowUiTrueWithConsentMetrics) {
+  base::HistogramTester histogram_tester;
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  EXPECT_CALL(*GetFakeScannerProfileScopedDelegate(*scanner_controller),
+              CheckFeatureAccess)
+      .WillRepeatedly(Return(specialized_features::FeatureAccessFailureSet{}));
+
+  ASSERT_TRUE(scanner_controller->CanShowUi());
+
+  histogram_tester.ExpectBucketCount(
+      "Ash.ScannerFeature.UserState",
+      ScannerFeatureUserState::kCanShowUiReturnedTrueWithConsent, 1);
 }
 
 TEST_F(ScannerControllerTest,
diff --git a/ash/scanner/scanner_metrics.cc b/ash/scanner/scanner_metrics.cc
index 43523435..11c398e 100644
--- a/ash/scanner/scanner_metrics.cc
+++ b/ash/scanner/scanner_metrics.cc
@@ -26,4 +26,11 @@
       kOnDeviceTextDetection, base::TimeTicks::Now() - ocr_attempt_start_time);
 }
 
+void RecordSunfishSessionButtonVisibilityOnLauncherShown(bool is_visible) {
+  RecordScannerFeatureUserState(
+      is_visible
+          ? ScannerFeatureUserState::kLauncherShownWithSunfishSessionButton
+          : ScannerFeatureUserState::kLauncherShownWithoutSunfishSessionButton);
+}
+
 }  // namespace ash
diff --git a/ash/scanner/scanner_metrics.h b/ash/scanner/scanner_metrics.h
index 42c522c..ecaf48d 100644
--- a/ash/scanner/scanner_metrics.h
+++ b/ash/scanner/scanner_metrics.h
@@ -84,7 +84,27 @@
   kNewGoogleSheetPopulatedActionExecutionFailed,
   kNewGoogleDocPopulatedActionExecutionFailed,
   kCopyToClipboardPopulatedActionExecutionFailed,
-  kMaxValue = kCopyToClipboardPopulatedActionExecutionFailed,
+
+  kCanShowUiReturnedFalse = 27,
+  kCanShowUiReturnedTrueWithoutConsent = 28,
+  kCanShowUiReturnedTrueWithConsent = 29,
+
+  kCanShowUiReturnedFalseDueToNoShellInstance = 30,
+  kCanShowUiReturnedFalseDueToNoControllerOnShell = 31,
+  kCanShowUiReturnedFalseDueToEnterprisePolicy = 32,
+  kCanShowUiReturnedFalseDueToNoProfileScopedDelegate = 33,
+  kCanShowUiReturnedFalseDueToSettingsToggle = 34,
+  kCanShowUiReturnedFalseDueToFeatureFlag = 35,
+  kCanShowUiReturnedFalseDueToFeatureManagement = 36,
+  kCanShowUiReturnedFalseDueToSecretKey = 37,
+  kCanShowUiReturnedFalseDueToAccountCapabilities = 38,
+  kCanShowUiReturnedFalseDueToCountry = 39,
+  kCanShowUiReturnedFalseDueToKioskMode = 40,
+
+  kLauncherShownWithoutSunfishSessionButton = 41,
+  kLauncherShownWithSunfishSessionButton = 42,
+
+  kMaxValue = kLauncherShownWithSunfishSessionButton,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/ash/enums.xml:ScannerFeatureUserState)
 
@@ -93,6 +113,9 @@
 ASH_EXPORT void RecordOnDeviceOcrTimerCompleted(
     base::TimeTicks ocr_attempt_start_time);
 
+ASH_EXPORT void RecordSunfishSessionButtonVisibilityOnLauncherShown(
+    bool is_visible);
+
 }  // namespace ash
 
 #endif  // ASH_SCANNER_SCANNER_METRICS_H_
diff --git a/ash/scanner/scanner_metrics_unittest.cc b/ash/scanner/scanner_metrics_unittest.cc
index 171ef429..86d023b5 100644
--- a/ash/scanner/scanner_metrics_unittest.cc
+++ b/ash/scanner/scanner_metrics_unittest.cc
@@ -4,10 +4,29 @@
 
 #include "ash/scanner/scanner_metrics.h"
 
+#include "ash/app_list/app_list_model_provider.h"
+#include "ash/app_list/app_list_public_test_util.h"
+#include "ash/app_list/model/search/search_box_model.h"
+#include "ash/app_list/views/app_list_bubble_view.h"
+#include "ash/app_list/views/app_list_view.h"
+#include "ash/app_list/views/search_box_view.h"
+#include "ash/capture_mode/capture_mode_test_util.h"
+#include "ash/constants/ash_features.h"
+#include "ash/public/cpp/capture_mode/capture_mode_api.h"
+#include "ash/public/cpp/scanner/scanner_delegate.h"
+#include "ash/scanner/fake_scanner_profile_scoped_delegate.h"
+#include "ash/scanner/scanner_controller.h"
+#include "ash/shelf/home_button.h"
+#include "ash/shelf/shelf.h"
+#include "ash/shelf/shelf_navigation_widget.h"
+#include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "chromeos/ash/components/specialized_features/feature_access_checker.h"
 #include "scanner_metrics.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
@@ -15,14 +34,15 @@
 namespace {
 
 using enum ScannerFeatureUserState;
+using ::testing::Return;
 
-class ScannerMetricsTest
+class ScannerMetricsParameterisedTest
     : public AshTestBase,
       public testing::WithParamInterface<ScannerFeatureUserState> {};
 
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
-    ScannerMetricsTest,
+    ScannerMetricsParameterisedTest,
     testing::ValuesIn<ScannerFeatureUserState>({
         kConsentDisclaimerAccepted,
         kConsentDisclaimerRejected,
@@ -51,9 +71,25 @@
         kNewGoogleSheetPopulatedActionExecutionFailed,
         kNewGoogleDocPopulatedActionExecutionFailed,
         kCopyToClipboardPopulatedActionExecutionFailed,
+        kCanShowUiReturnedFalse,
+        kCanShowUiReturnedTrueWithoutConsent,
+        kCanShowUiReturnedTrueWithConsent,
+        kCanShowUiReturnedFalseDueToNoShellInstance,
+        kCanShowUiReturnedFalseDueToNoControllerOnShell,
+        kCanShowUiReturnedFalseDueToEnterprisePolicy,
+        kCanShowUiReturnedFalseDueToNoProfileScopedDelegate,
+        kCanShowUiReturnedFalseDueToSettingsToggle,
+        kCanShowUiReturnedFalseDueToFeatureFlag,
+        kCanShowUiReturnedFalseDueToFeatureManagement,
+        kCanShowUiReturnedFalseDueToSecretKey,
+        kCanShowUiReturnedFalseDueToAccountCapabilities,
+        kCanShowUiReturnedFalseDueToCountry,
+        kCanShowUiReturnedFalseDueToKioskMode,
+        kLauncherShownWithoutSunfishSessionButton,
+        kLauncherShownWithSunfishSessionButton,
     }));
 
-TEST_P(ScannerMetricsTest, Record) {
+TEST_P(ScannerMetricsParameterisedTest, Record) {
   base::HistogramTester histogram_tester;
 
   RecordScannerFeatureUserState(GetParam());
@@ -62,6 +98,158 @@
                                      1);
 }
 
+TEST(ScannerMetricsNoFixtureTest, LauncherShownWithoutSunfishSessionButton) {
+  base::HistogramTester histogram_tester;
+
+  RecordSunfishSessionButtonVisibilityOnLauncherShown(/*is_visible=*/false);
+
+  histogram_tester.ExpectBucketCount("Ash.ScannerFeature.UserState",
+                                     kLauncherShownWithoutSunfishSessionButton,
+                                     1);
+}
+
+TEST(ScannerMetricsNoFixtureTest, LauncherShownWithSunfishSessionButton) {
+  base::HistogramTester histogram_tester;
+
+  RecordSunfishSessionButtonVisibilityOnLauncherShown(/*is_visible=*/true);
+
+  histogram_tester.ExpectBucketCount("Ash.ScannerFeature.UserState",
+                                     kLauncherShownWithSunfishSessionButton, 1);
+}
+
+class ScannerMetricsTest : public AshTestBase {
+ public:
+  ScannerMetricsTest() {
+    // Disable Sunfish so we can control `IsSunfishAllowedAndEnabled`.
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kScannerUpdate},
+        /*disabled_features=*/{features::kSunfishFeature});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(ScannerMetricsTest, ClamshellLauncherShownWithoutSunfishSessionButton) {
+  base::HistogramTester histogram_tester;
+  // Explicitly make `IsSunfishSessionAllowed` return false, even though we are
+  // setting the search box model's `ShowSunfishButton` explicitly, as it can be
+  // recalculated at any time.
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  auto* delegate = static_cast<FakeScannerProfileScopedDelegate*>(
+      scanner_controller->delegate_for_testing()->GetProfileScopedDelegate());
+  ON_CALL(*delegate, CheckFeatureAccess)
+      .WillByDefault(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kDisabledInSettings}));
+  ASSERT_FALSE(IsSunfishSessionAllowed());
+  SearchBoxModel* search_box_model =
+      AppListModelProvider::Get()->search_model()->search_box();
+  search_box_model->SetShowSunfishButton(false);
+
+  // Open the app list by clicking on the home button.
+  LeftClickOn(GetPrimaryShelf()->navigation_widget()->GetHomeButton());
+  AppListBubbleView* bubble_view = GetAppListBubbleView();
+  ASSERT_TRUE(bubble_view);
+  views::ImageButton* sunfish_button =
+      bubble_view->search_box_view()->sunfish_button();
+  ASSERT_TRUE(sunfish_button);
+  ASSERT_FALSE(sunfish_button->GetVisible());
+
+  histogram_tester.ExpectBucketCount("Ash.ScannerFeature.UserState",
+                                     kLauncherShownWithoutSunfishSessionButton,
+                                     1);
+}
+
+TEST_F(ScannerMetricsTest, ClamshellLauncherShownWithSunfishSessionButton) {
+  base::HistogramTester histogram_tester;
+  // Explicitly make `IsSunfishSessionAllowed` return true, even though we are
+  // setting the search box model's `ShowSunfishButton` explicitly, as it can be
+  // recalculated at any time.
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  auto* delegate = static_cast<FakeScannerProfileScopedDelegate*>(
+      scanner_controller->delegate_for_testing()->GetProfileScopedDelegate());
+  ON_CALL(*delegate, CheckFeatureAccess)
+      .WillByDefault(Return(specialized_features::FeatureAccessFailureSet{}));
+  ASSERT_TRUE(IsSunfishSessionAllowed());
+  SearchBoxModel* search_box_model =
+      AppListModelProvider::Get()->search_model()->search_box();
+  search_box_model->SetShowSunfishButton(true);
+
+  // Open the app list by clicking on the home button.
+  LeftClickOn(GetPrimaryShelf()->navigation_widget()->GetHomeButton());
+  AppListBubbleView* bubble_view = GetAppListBubbleView();
+  ASSERT_TRUE(bubble_view);
+  views::ImageButton* sunfish_button =
+      bubble_view->search_box_view()->sunfish_button();
+  ASSERT_TRUE(sunfish_button);
+  ASSERT_TRUE(sunfish_button->GetVisible());
+
+  histogram_tester.ExpectBucketCount("Ash.ScannerFeature.UserState",
+                                     kLauncherShownWithSunfishSessionButton, 1);
+}
+
+TEST_F(ScannerMetricsTest, TabletLauncherShownWithoutSunfishSessionButton) {
+  base::HistogramTester histogram_tester;
+  // Explicitly make `IsSunfishSessionAllowed` return false, even though we are
+  // setting the search box model's `ShowSunfishButton` explicitly, as it can be
+  // recalculated at any time.
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  auto* delegate = static_cast<FakeScannerProfileScopedDelegate*>(
+      scanner_controller->delegate_for_testing()->GetProfileScopedDelegate());
+  ON_CALL(*delegate, CheckFeatureAccess)
+      .WillByDefault(Return(specialized_features::FeatureAccessFailureSet{
+          specialized_features::FeatureAccessFailure::kDisabledInSettings}));
+  ASSERT_FALSE(IsSunfishSessionAllowed());
+  SearchBoxModel* search_box_model =
+      AppListModelProvider::Get()->search_model()->search_box();
+  search_box_model->SetShowSunfishButton(false);
+
+  // The app list should be open by default when we enter tablet mode.
+  SwitchToTabletMode();
+  AppListView* app_list_view = GetAppListView();
+  ASSERT_TRUE(app_list_view);
+  views::ImageButton* sunfish_button =
+      app_list_view->search_box_view()->sunfish_button();
+  ASSERT_TRUE(sunfish_button);
+  ASSERT_FALSE(sunfish_button->GetVisible());
+
+  histogram_tester.ExpectBucketCount("Ash.ScannerFeature.UserState",
+                                     kLauncherShownWithoutSunfishSessionButton,
+                                     1);
+}
+
+TEST_F(ScannerMetricsTest, TabletLauncherShownWithSunfishSessionButton) {
+  base::HistogramTester histogram_tester;
+  // Explicitly make `IsSunfishSessionAllowed` return true, even though we are
+  // setting the search box model's `ShowSunfishButton` explicitly, as it can be
+  // recalculated at any time.
+  ScannerController* scanner_controller = Shell::Get()->scanner_controller();
+  ASSERT_TRUE(scanner_controller);
+  auto* delegate = static_cast<FakeScannerProfileScopedDelegate*>(
+      scanner_controller->delegate_for_testing()->GetProfileScopedDelegate());
+  ON_CALL(*delegate, CheckFeatureAccess)
+      .WillByDefault(Return(specialized_features::FeatureAccessFailureSet{}));
+  ASSERT_TRUE(IsSunfishSessionAllowed());
+  SearchBoxModel* search_box_model =
+      AppListModelProvider::Get()->search_model()->search_box();
+  search_box_model->SetShowSunfishButton(true);
+
+  // The app list should be open by default when we enter tablet mode.
+  SwitchToTabletMode();
+  AppListView* app_list_view = GetAppListView();
+  ASSERT_TRUE(app_list_view);
+  views::ImageButton* sunfish_button =
+      app_list_view->search_box_view()->sunfish_button();
+  ASSERT_TRUE(sunfish_button);
+  ASSERT_TRUE(sunfish_button->GetVisible());
+
+  histogram_tester.ExpectBucketCount("Ash.ScannerFeature.UserState",
+                                     kLauncherShownWithSunfishSessionButton, 1);
+}
+
 }  // namespace
 
 }  // namespace ash
diff --git a/ash/system/power/power_button_controller.cc b/ash/system/power/power_button_controller.cc
index 090b20d9..469d921a 100644
--- a/ash/system/power/power_button_controller.cc
+++ b/ash/system/power/power_button_controller.cc
@@ -572,16 +572,15 @@
     return;
   }
 
-  std::optional<base::Value> parsed_json = base::JSONReader::Read(
+  std::optional<base::Value::Dict> parsed_json = base::JSONReader::ReadDict(
       cl->GetSwitchValueASCII(switches::kAshPowerButtonPosition));
-  if (!parsed_json || !parsed_json->is_dict()) {
+  if (!parsed_json) {
     LOG(ERROR) << switches::kAshPowerButtonPosition << " flag has no value";
     return;
   }
 
-  const base::Value::Dict& position_info = parsed_json->GetDict();
-  const std::string* edge = position_info.FindString(kEdgeField);
-  std::optional<double> position = position_info.FindDouble(kPositionField);
+  const std::string* edge = parsed_json->FindString(kEdgeField);
+  std::optional<double> position = parsed_json->FindDouble(kPositionField);
 
   if (!edge || !position) {
     LOG(ERROR) << "Both " << kEdgeField << " field and " << kPositionField
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.html b/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.html
index 4b536b2b..f35a67b 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.html
+++ b/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.html
@@ -89,7 +89,7 @@
   </iron-a11y-keys>
   <div
       id="tabContainer"
-      hidden="[[isTabContainerHidden_(seaPenQuery_, thumbnailResponseStatusCode_)]]"
+      hidden="[[isTabContainerHidden_(seaPenQuery_, thumbnailResponseStatusCode_, thumbnails_)]]"
       role="tablist">
     <cr-button id="resultsTab"
         aria-selected="[[isResultsTabSelected_(freeformTab_)]]"
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.ts b/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.ts
index 7c25fcf8..7bebee5 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_freeform_element.ts
@@ -16,12 +16,12 @@
 
 import type {SeaPenSamplePrompt} from './constants.js';
 import {FreeformTab} from './constants.js';
-import type {MantaStatusCode, SeaPenQuery} from './sea_pen.mojom-webui.js';
+import type {MantaStatusCode, SeaPenQuery, SeaPenThumbnail} from './sea_pen.mojom-webui.js';
 import {getTemplate} from './sea_pen_freeform_element.html.js';
 import {logSamplePromptShuffleClicked, logSeaPenFreeformTabClicked} from './sea_pen_metrics_logger.js';
 import {WithSeaPenStore} from './sea_pen_store.js';
 import {SEA_PEN_SAMPLES} from './sea_pen_untranslated_constants.js';
-import {isArrayEqual, shuffle} from './sea_pen_utils.js';
+import {isArrayEqual, isNonEmptyArray, shuffle} from './sea_pen_utils.js';
 
 export interface SeaPenFreeformElement {
   $: {
@@ -61,6 +61,10 @@
         type: Object,
         observer: 'onThumbnailResponseStatusCodeChanged_',
       },
+
+      thumbnails_: {
+        type: Object,
+      },
     };
   }
 
@@ -68,6 +72,7 @@
   private freeformTab_: FreeformTab;
   private seaPenQuery_: SeaPenQuery|null;
   private thumbnailResponseStatusCode_: MantaStatusCode|null;
+  private thumbnails_: SeaPenThumbnail[]|null;
 
   override connectedCallback() {
     super.connectedCallback();
@@ -76,8 +81,18 @@
     this.watch<SeaPenFreeformElement['thumbnailResponseStatusCode_']>(
         'thumbnailResponseStatusCode_',
         state => state.thumbnailResponseStatusCode);
+    this.watch<SeaPenFreeformElement['thumbnails_']>(
+        'thumbnails_', state => state.thumbnails);
     this.updateFromStore();
     this.shuffleSamplePrompts_();
+    // The tab container is hidden when there are no results to show. In that
+    // case, show the sample prompts tab.
+    this.freeformTab_ =
+        this.isTabContainerHidden_(
+            this.seaPenQuery_, this.thumbnailResponseStatusCode_,
+            this.thumbnails_) ?
+        FreeformTab.SAMPLE_PROMPTS :
+        FreeformTab.RESULTS;
     this.$.tabKeys.target = this.$.tabContainer;
   }
 
@@ -128,9 +143,12 @@
   }
 
   private isTabContainerHidden_(
-      query: SeaPenQuery,
-      thumbnailResponseStatusCode: MantaStatusCode|null): boolean {
-    return !query?.textQuery && !thumbnailResponseStatusCode;
+      query: SeaPenQuery|null,
+      thumbnailResponseStatusCode: MantaStatusCode|null,
+      thumbnails: SeaPenThumbnail[]|null): boolean {
+    // The tab container should appear whenever the user has generated images.
+    return !query?.textQuery && !thumbnailResponseStatusCode &&
+        !isNonEmptyArray(thumbnails);
   }
 
   private isSamplePromptsTabSelected_(tab: FreeformTab): boolean {
diff --git a/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts b/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts
index 0065fec2..84a4c9c 100644
--- a/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts
+++ b/ash/webui/common/resources/sea_pen/sea_pen_images_element.ts
@@ -181,8 +181,7 @@
 
       showHistory_: {
         type: Boolean,
-        computed:
-            'computeShowHistory_(thumbnailsLoading_, seaPenQuery_, textQueryHistory_)',
+        computed: 'computeShowHistory_(thumbnailsLoading_, textQueryHistory_)',
       },
 
       seaPenQuery_: {
@@ -535,10 +534,9 @@
   }
 
   private computeShowHistory_(
-      thumbnailsLoading: boolean, seaPenQuery: SeaPenQuery|null,
+      thumbnailsLoading: boolean,
       textQueryHistory: TextQueryHistoryEntry[]): boolean {
-    return !thumbnailsLoading && !!seaPenQuery?.textQuery &&
-        isNonEmptyArray(textQueryHistory);
+    return !thumbnailsLoading && isNonEmptyArray(textQueryHistory);
   }
 
   private onHistoryPromptClicked_(e: Event&
diff --git a/base/BUILD.gn b/base/BUILD.gn
index fe8369f..29bb963 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -4272,6 +4272,7 @@
       "sequence_checker_nocompile.nc",
       "strings/cstring_view_nocompile.nc",
       "strings/span_printf_nocompile.nc",
+      "strings/string_split_nocompile.nc",
       "synchronization/lock_nocompile.nc",
       "task/bind_post_task_nocompile.nc",
       "task/task_traits_nocompile.nc",
diff --git a/base/json/string_escape.cc b/base/json/string_escape.cc
index 2aeb9fb..1e90304 100644
--- a/base/json/string_escape.cc
+++ b/base/json/string_escape.cc
@@ -131,12 +131,18 @@
 
 std::string GetQuotedJSONString(std::string_view str) {
   std::string dest;
+  // The output will always be at least str.size() + 2 bytes for the quote
+  // characters.
+  dest.reserve(str.size() + 2);
   EscapeJSONStringImpl(str, true, &dest);
   return dest;
 }
 
 std::string GetQuotedJSONString(std::u16string_view str) {
   std::string dest;
+  // The output will always be at least str.size() + 2 bytes for the quote
+  // characters.
+  dest.reserve(str.size() + 2);
   EscapeJSONStringImpl(str, true, &dest);
   return dest;
 }
diff --git a/base/metrics/sample_vector.h b/base/metrics/sample_vector.h
index a15338e4..0548f36 100644
--- a/base/metrics/sample_vector.h
+++ b/base/metrics/sample_vector.h
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 // SampleVector implements HistogramSamples interface. It is used by all
 // Histogram based classes to store samples.
 
@@ -102,7 +97,7 @@
     if (data == nullptr) {
       return std::nullopt;
     }
-    return span(data, counts_size_);
+    return UNSAFE_TODO(span(data, counts_size_));
   }
 
   std::optional<span<const HistogramBase::AtomicCount>> counts() const {
@@ -111,7 +106,7 @@
     if (data == nullptr) {
       return std::nullopt;
     }
-    return span(data, counts_size_);
+    return UNSAFE_TODO(span(data, counts_size_));
   }
 
   void set_counts(span<HistogramBase::AtomicCount> counts) const {
diff --git a/base/posix/safe_strerror.cc b/base/posix/safe_strerror.cc
index ad57e330f..9034415 100644
--- a/base/posix/safe_strerror.cc
+++ b/base/posix/safe_strerror.cc
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/posix/safe_strerror.h"
 
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
 
+#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 namespace base {
@@ -72,7 +68,7 @@
     // it indirectly implies that typically ERANGE will be returned, instead
     // of truncating the string. We play it safe by always terminating the
     // string explicitly.
-    buf[len - 1] = '\0';
+    UNSAFE_TODO(buf[len - 1]) = '\0';
   } else {
     // Error. POSIX is vague about whether the return value is itself a system
     // error code or something else. On Linux currently it is -1 and errno is
diff --git a/base/process/environment_internal.cc b/base/process/environment_internal.cc
index 0d9dd24..f712ed4 100644
--- a/base/process/environment_internal.cc
+++ b/base/process/environment_internal.cc
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/process/environment_internal.h"
 
 #include <stddef.h>
 
 #include <vector>
 
+#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
@@ -35,13 +31,13 @@
                     NativeEnvironmentString* key) {
   // Skip to the equals or end of the string, this is the key.
   size_t cur = 0;
-  while (input[cur] && input[cur] != '=') {
+  while (UNSAFE_TODO(input[cur] && input[cur] != '=')) {
     cur++;
   }
   *key = NativeEnvironmentString(&input[0], cur);
 
   // Now just skip to the end of the string.
-  while (input[cur]) {
+  while (UNSAFE_TODO(input[cur])) {
     cur++;
   }
   return cur + 1;
@@ -60,14 +56,14 @@
   // First build up all of the unchanged environment strings. These are
   // null-terminated of the form "key=value".
   std::string key;
-  for (size_t i = 0; env[i]; i++) {
-    size_t line_length = ParseEnvLine(env[i], &key);
+  for (size_t i = 0; UNSAFE_TODO(env[i]); i++) {
+    size_t line_length = ParseEnvLine(UNSAFE_TODO(env[i]), &key);
 
     // Keep only values not specified in the change vector.
     auto found_change = changes.find(key);
     if (found_change == changes.end()) {
       result_indices.push_back(value_storage.size());
-      value_storage.append(env[i], line_length);
+      value_storage.append(UNSAFE_TODO(env[i]), line_length);
     }
   }
 
@@ -95,7 +91,7 @@
 
     // Fill array of pointers at the beginning of the result.
     for (size_t i = 0; i < result_indices.size(); i++) {
-      result[i] = &storage_data[result_indices[i]];
+      result[i] = UNSAFE_TODO(&storage_data[result_indices[i]]);
     }
   }
   result[result_indices.size()] = 0;  // Null terminator.
@@ -119,7 +115,7 @@
     if (changes.find(key) == changes.end()) {
       result.append(ptr, line_length);
     }
-    ptr += line_length;
+    UNSAFE_TODO(ptr += line_length);
   }
 
   // Now append all modified and new values.
diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc
index bc29449..cc213da 100644
--- a/base/process/launch_posix.cc
+++ b/base/process/launch_posix.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/process/launch.h"
 
 #include <dirent.h>
@@ -764,7 +759,7 @@
     defined(ARCH_CPU_PPC64_FAMILY) || defined(ARCH_CPU_LOONGARCH_FAMILY) || \
     defined(ARCH_CPU_RISCV_FAMILY)
   // The stack grows downward.
-  void* stack = stack_buf + sizeof(stack_buf);
+  void* stack = UNSAFE_TODO(stack_buf + sizeof(stack_buf));
 #else
 #error "Unsupported architecture"
 #endif
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index c3b35024..196231f 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/process/process_metrics.h"
 
 #include <dirent.h>
@@ -24,6 +19,7 @@
 #include <string_view>
 #include <utility>
 
+#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/cpu.h"
 #include "base/files/dir_reader_posix.h"
@@ -308,7 +304,7 @@
     if (--num_spaces_remaining == 0) {
       int utime = 0;
       int stime = 0;
-      if (sscanf(&input.data()[i], "%d %d", &utime, &stime) != 2) {
+      if (UNSAFE_TODO(sscanf(&input.data()[i], "%d %d", &utime, &stime)) != 2) {
         return -1;
       }
 
@@ -1031,7 +1027,7 @@
   std::string line;
   while (std::getline(clients_stream, line)) {
     pid_t pid;
-    int num_res = sscanf(&line.c_str()[21], "%5d", &pid);
+    int num_res = sscanf(UNSAFE_TODO(&line.c_str()[21]), "%5d", &pid);
     if (num_res == 1) {
       GetFdInfoFromPid(pid, fdinfo_table);
     }
diff --git a/base/process/set_process_title_linux.cc b/base/process/set_process_title_linux.cc
index 039bda3..7737718e 100644
--- a/base/process/set_process_title_linux.cc
+++ b/base/process/set_process_title_linux.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 // This file implements BSD-style setproctitle() for Linux.
 // It is written such that it can easily be compiled outside Chromium.
 //
@@ -54,6 +49,7 @@
 #include <string>
 #include <vector>
 
+#include "base/compiler_specific.h"
 #include "base/files/file_util.h"
 #include "base/no_destructor.h"
 #include "base/numerics/safe_conversions.h"
@@ -96,7 +92,7 @@
     // "\0\0\0...\0\0\0.\0" (on Linux 4.18--5.2)
     // "\0"                 (on Linux 5.3--)
     memset(g_argv_start, 0, avail_size + 1);
-    g_argv_end[-1] = '.';
+    UNSAFE_TODO(g_argv_end[-1]) = '.';
 
     std::string cmdline;
     if (!base::ReadFileToString(base::FilePath("/proc/self/cmdline"),
@@ -112,13 +108,13 @@
   va_start(ap, fmt);
   if (fmt[0] == '-') {
     size = base::checked_cast<size_t>(
-        vsnprintf(g_argv_start, avail_size, &fmt[1], ap));
+        UNSAFE_TODO(vsnprintf(g_argv_start, avail_size, &fmt[1], ap)));
   } else {
     size = base::checked_cast<size_t>(
         snprintf(g_argv_start, avail_size, "%s ", g_orig_argv0));
     if (size < avail_size) {
-      size += base::checked_cast<size_t>(
-          vsnprintf(&g_argv_start[size], avail_size - size, fmt, ap));
+      size += base::checked_cast<size_t>(UNSAFE_TODO(
+          vsnprintf(&g_argv_start[size], avail_size - size, fmt, ap)));
     }
   }
   va_end(ap);
@@ -137,7 +133,7 @@
   const size_t argv_size =
       base::checked_cast<size_t>(g_argv_end - g_argv_start - 1);
   if (!buggy_kernel && size < argv_size) {
-    g_argv_end[-1] = '.';
+    UNSAFE_TODO(g_argv_end[-1]) = '.';
   }
 }
 
@@ -158,19 +154,19 @@
   char** argv = const_cast<char**>(main_argv);
   char* argv_start = argv[0];
   char* p = argv_start;
-  for (size_t i = 0; argv[i]; ++i) {
-    if (p != argv[i]) {
+  for (size_t i = 0; UNSAFE_TODO(argv[i]); ++i) {
+    if (p != UNSAFE_TODO(argv[i])) {
       return;
     }
-    p += strlen(p) + 1;
+    UNSAFE_TODO(p += strlen(p) + 1);
   }
   char* argv_end = p;
   size_t environ_size = 0;
-  for (size_t i = 0; environ[i]; ++i, ++environ_size) {
-    if (p != environ[i]) {
+  for (size_t i = 0; UNSAFE_TODO(environ[i]); ++i, ++environ_size) {
+    if (p != UNSAFE_TODO(environ[i])) {
       return;
     }
-    p += strlen(p) + 1;
+    UNSAFE_TODO(p += strlen(p) + 1);
   }
   char* envp_end = p;
 
@@ -181,9 +177,9 @@
   // incrementally.
   static base::NoDestructor<std::vector<std::string>> environ_copy(
       environ_size);
-  for (size_t i = 0; environ[i]; ++i) {
-    (*environ_copy)[i] = environ[i];
-    environ[i] = &(*environ_copy)[i][0];
+  for (size_t i = 0; UNSAFE_TODO(environ[i]); ++i) {
+    (*environ_copy)[i] = UNSAFE_TODO(environ[i]);
+    UNSAFE_TODO(environ[i]) = &(*environ_copy)[i][0];
   }
 
   if (!argv[0]) {
diff --git a/base/profiler/thread_delegate_posix.cc b/base/profiler/thread_delegate_posix.cc
index 96738b3..b3bd31d 100644
--- a/base/profiler/thread_delegate_posix.cc
+++ b/base/profiler/thread_delegate_posix.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/profiler/thread_delegate_posix.h"
 
 #include <inttypes.h>
@@ -15,6 +10,7 @@
 
 #include <optional>
 
+#include "base/compiler_specific.h"
 #include "base/memory/ptr_util.h"
 #include "base/process/process_handle.h"
 #include "build/build_config.h"
@@ -81,7 +77,8 @@
   // Standard section 5.1.1, plus the stack pointer.
   registers.push_back(reinterpret_cast<uintptr_t*>(&thread_context->sp));
   for (size_t i = 19; i <= 29; ++i) {
-    registers.push_back(reinterpret_cast<uintptr_t*>(&thread_context->regs[i]));
+    registers.push_back(
+        UNSAFE_TODO(reinterpret_cast<uintptr_t*>(&thread_context->regs[i])));
   }
   return registers;
 #elif defined(ARCH_CPU_X86_FAMILY) && defined(ARCH_CPU_32_BITS)
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index e6c55c08..8bbf941 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/sampling_heap_profiler/sampling_heap_profiler.h"
 
 #include <algorithm>
diff --git a/base/stack_canary_linux.cc b/base/stack_canary_linux.cc
index 644f32db..92c1cbe 100644
--- a/base/stack_canary_linux.cc
+++ b/base/stack_canary_linux.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/stack_canary_linux.h"
 
 #include <dlfcn.h>
diff --git a/base/strings/escape.cc b/base/strings/escape.cc
index 995cddc2..a4ba96d 100644
--- a/base/strings/escape.cc
+++ b/base/strings/escape.cc
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/strings/escape.h"
 
 #include <ostream>
 #include <string_view>
 
 #include "base/check_op.h"
+#include "base/compiler_specific.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversion_utils.h"
@@ -29,7 +25,7 @@
 // Does quick bit-flicking to lookup needed characters.
 struct Charmap {
   bool Contains(unsigned char c) const {
-    return ((map[c >> 5] & (1 << (c & 31))) != 0);
+    return UNSAFE_TODO((map[c >> 5] & (1 << (c & 31))) != 0);
   }
 
   uint32_t map[8];
@@ -229,8 +225,8 @@
     // UnescapeUnsignedByteAtIndex checks lengths.
     while (num_bytes < std::size(bytes) &&
            UnescapeUnsignedByteAtIndex(escaped_text, index + num_bytes * 3,
-                                       &bytes[num_bytes]) &&
-           CBU8_IS_TRAIL(bytes[num_bytes])) {
+                                       UNSAFE_TODO(&bytes[num_bytes])) &&
+           CBU8_IS_TRAIL(UNSAFE_TODO(bytes[num_bytes]))) {
       ++num_bytes;
     }
   }
@@ -256,7 +252,7 @@
                              base_icu::UChar32 code_point) {
   // If this is an ASCII character, use the lookup table.
   if (code_point >= 0 && code_point < 0x80) {
-    return kUrlUnescape[static_cast<size_t>(code_point)] ||
+    return UNSAFE_TODO(kUrlUnescape[static_cast<size_t>(code_point)]) ||
            // Allow some additional unescaping when flags are set.
            (code_point == ' ' && (rules & UnescapeRule::SPACES)) ||
            // Allow any of the prohibited but non-control characters when doing
@@ -655,13 +651,15 @@
       // Potential ampersand encode char.
       size_t index = static_cast<size_t>(iter - text.begin());
       for (size_t i = 0; i < std::size(kEscapeToChars); i++) {
-        if (ampersand_chars[i].empty()) {
-          ampersand_chars[i] = ASCIIToUTF16(kEscapeToChars[i].ampersand_code);
+        if (UNSAFE_TODO(ampersand_chars[i].empty())) {
+          UNSAFE_TODO(ampersand_chars[i] =
+                          ASCIIToUTF16(kEscapeToChars[i].ampersand_code));
         }
-        if (text.find(ampersand_chars[i], index) == index) {
-          text.replace(
-              iter, iter + static_cast<ptrdiff_t>(ampersand_chars[i].length()),
-              1, kEscapeToChars[i].replacement);
+        if (text.find(UNSAFE_TODO(ampersand_chars[i]), index) == index) {
+          text.replace(iter,
+                       iter + static_cast<ptrdiff_t>(
+                                  UNSAFE_TODO(ampersand_chars[i]).length()),
+                       1, UNSAFE_TODO(kEscapeToChars[i].replacement));
           break;
         }
       }
diff --git a/base/strings/latin1_string_conversions.cc b/base/strings/latin1_string_conversions.cc
index 0b9b12e..3098a1d 100644
--- a/base/strings/latin1_string_conversions.cc
+++ b/base/strings/latin1_string_conversions.cc
@@ -2,13 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/strings/latin1_string_conversions.h"
 
+#include "base/compiler_specific.h"
+
 namespace base {
 
 std::u16string Latin1OrUTF16ToUTF16(size_t length,
@@ -18,9 +15,9 @@
     return std::u16string();
   }
   if (latin1) {
-    return std::u16string(latin1, latin1 + length);
+    return UNSAFE_TODO(std::u16string(latin1, latin1 + length));
   }
-  return std::u16string(utf16, utf16 + length);
+  return UNSAFE_TODO(std::u16string(utf16, utf16 + length));
 }
 
 }  // namespace base
diff --git a/base/strings/safe_sprintf.cc b/base/strings/safe_sprintf.cc
index 8061e5b..53674c62 100644
--- a/base/strings/safe_sprintf.cc
+++ b/base/strings/safe_sprintf.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/strings/safe_sprintf.h"
 
 #include <errno.h>
@@ -15,6 +10,7 @@
 #include <algorithm>
 #include <limits>
 
+#include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 
@@ -338,11 +334,12 @@
         if (padding) {
           --padding;
         }
-        Out(*prefix++);
+        UNSAFE_TODO(Out(*prefix++));
       }
       prefix = nullptr;
     } else {
-      for (reverse_prefix = prefix; *reverse_prefix; ++reverse_prefix) {
+      for (reverse_prefix = prefix; *reverse_prefix;
+           UNSAFE_TODO(++reverse_prefix)) {
       }
     }
   } else {
@@ -367,8 +364,8 @@
         // them. This is essentially equivalent to:
         //   memmove(buffer_ + start, buffer_ + start + 1, size_ - start - 1)
         for (char *move = buffer_ + start, *end = buffer_ + size_ - 1;
-             move < end; ++move) {
-          *move = move[1];
+             move < end; UNSAFE_TODO(++move)) {
+          *move = UNSAFE_TODO(move[1]);
         }
         ++discarded;
         --count_;
@@ -390,14 +387,14 @@
     // integer always ends in 2, 4, 6, or 8.
     if (!num && started) {
       if (reverse_prefix > prefix) {
-        Out(*--reverse_prefix);
+        UNSAFE_TODO(Out(*--reverse_prefix));
       } else {
         Out(pad);
       }
     } else {
       started = true;
-      Out((upcase ? kUpCaseHexDigits
-                  : kDownCaseHexDigits)[num % base + minint]);
+      UNSAFE_TODO(Out((upcase ? kUpCaseHexDigits
+                              : kDownCaseHexDigits)[num % base + minint]));
     }
 
     minint = 0;
@@ -428,11 +425,13 @@
     // So, now, we reverse the string (except for the possible '-' sign).
     char* front = buffer_ + start;
     char* back = GetInsertionPoint();
-    while (--back > front) {
-      char ch = *back;
-      *back = *front;
-      *front++ = ch;
-    }
+    UNSAFE_TODO({
+      while (--back > front) {
+        char ch = *back;
+        *back = *front;
+        *front++ = ch;
+      }
+    });
   }
   IncrementCount(discarded);
   return !discarded;
@@ -462,10 +461,10 @@
   size_t padding;
   char pad;
   for (unsigned int cur_arg = 0; *fmt && !buffer.OutOfAddressableSpace();) {
-    if (*fmt++ == '%') {
+    if (UNSAFE_TODO(*fmt++) == '%') {
       padding = 0;
       pad = ' ';
-      char ch = *fmt++;
+      char ch = UNSAFE_TODO(*fmt++);
     format_character_found:
       switch (ch) {
         case '0':
@@ -496,7 +495,7 @@
               // handling.
             padding_overflow:
               padding = max_padding;
-              while ((ch = *fmt++) >= '0' && ch <= '9') {
+              while ((ch = UNSAFE_TODO(*fmt++)) >= '0' && ch <= '9') {
               }
               if (cur_arg < max_args) {
                 ++cur_arg;
@@ -512,7 +511,7 @@
               DEBUG_CHECK(padding <= max_padding);
               goto padding_overflow;
             }
-            ch = *fmt++;
+            ch = UNSAFE_TODO(*fmt++);
             if (ch < '0' || ch > '9') {
               // Reached the end of the width parameter. This is where the
               // format character is found.
@@ -527,7 +526,7 @@
           }
 
           // Check that the argument has the expected type.
-          const Arg& arg = args[cur_arg++];
+          const Arg& arg = UNSAFE_TODO(args[cur_arg++]);
           if (arg.type != Arg::INT && arg.type != Arg::UINT) {
             DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT);
             goto fail_to_expand;
@@ -555,7 +554,7 @@
             goto fail_to_expand;
           }
 
-          const Arg& arg = args[cur_arg++];
+          const Arg& arg = UNSAFE_TODO(args[cur_arg++]);
           int64_t i;
           const char* prefix = nullptr;
           if (ch != 'p') {
@@ -617,7 +616,7 @@
           }
 
           // Check that the argument has the expected type.
-          const Arg& arg = args[cur_arg++];
+          const Arg& arg = UNSAFE_TODO(args[cur_arg++]);
           const char* s;
           if (arg.type == Arg::STRING) {
             s = arg.str ? arg.str : "<NULL>";
@@ -634,7 +633,7 @@
           // length of the string that we are outputting.
           if (padding) {
             size_t len = 0;
-            for (const char* src = s; *src++;) {
+            for (const char* src = s; UNSAFE_TODO(*src++);) {
               ++len;
             }
             buffer.Pad(' ', padding, len);
@@ -644,7 +643,7 @@
           // output buffer and making sure we don't output more bytes than
           // available space; Out() takes care of doing that.
           for (const char* src = s; *src;) {
-            buffer.Out(*src++);
+            buffer.Out(UNSAFE_TODO(*src++));
           }
           break;
         }
@@ -675,7 +674,7 @@
       }
     } else {
     copy_verbatim:
-      buffer.Out(fmt[-1]);
+      buffer.Out(UNSAFE_TODO(fmt[-1]));
     }
   }
 end_of_format_string:
@@ -702,13 +701,15 @@
   // SafeSPrintf() function always degenerates to a version of strncpy() that
   // de-duplicates '%' characters.
   const char* src = fmt;
-  for (; *src; ++src) {
-    buffer.Out(*src);
-    DEBUG_CHECK(src[0] != '%' || src[1] == '%');
-    if (src[0] == '%' && src[1] == '%') {
-      ++src;
+  UNSAFE_TODO({
+    for (; *src; ++src) {
+      buffer.Out(*src);
+      DEBUG_CHECK(src[0] != '%' || src[1] == '%');
+      if (src[0] == '%' && src[1] == '%') {
+        ++src;
+      }
     }
-  }
+  });
   return buffer.GetCount();
 }
 
diff --git a/base/strings/strcat_internal.h b/base/strings/strcat_internal.h
index 4d614f8a..9ada2da 100644
--- a/base/strings/strcat_internal.h
+++ b/base/strings/strcat_internal.h
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef BASE_STRINGS_STRCAT_INTERNAL_H_
 #define BASE_STRINGS_STRCAT_INTERNAL_H_
 
 #include <concepts>
 #include <string>
 
+#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 
 namespace base {
@@ -66,7 +62,7 @@
   CharT* dest_char = &dest[initial_size];
   for (const auto& cur : pieces) {
     std::char_traits<CharT>::copy(dest_char, cur.data(), cur.size());
-    dest_char += cur.size();
+    UNSAFE_TODO(dest_char += cur.size());
   }
 }
 
diff --git a/base/strings/string_number_conversions.h b/base/strings/string_number_conversions.h
index 9da1dc8..3beaa935 100644
--- a/base/strings/string_number_conversions.h
+++ b/base/strings/string_number_conversions.h
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
 #define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
 
@@ -18,6 +13,7 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "build/build_config.h"
 
@@ -127,7 +123,8 @@
                                             '6', '7', '8', '9', 'a', 'b',
                                             'c', 'd', 'e', 'f'};
   const char* const hex_chars = uppercase ? kHexCharsUpper : kHexCharsLower;
-  output.append({hex_chars[byte >> 4], hex_chars[byte & 0xf]});
+  output.append(
+      {UNSAFE_TODO(hex_chars[byte >> 4]), UNSAFE_TODO(hex_chars[byte & 0xf])});
 }
 
 // Best effort conversion, see StringToInt above for restrictions.
diff --git a/base/strings/string_number_conversions_internal.h b/base/strings/string_number_conversions_internal.h
index 11b7c06..2de7823 100644
--- a/base/strings/string_number_conversions_internal.h
+++ b/base/strings/string_number_conversions_internal.h
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_
 #define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_
 
@@ -18,6 +13,7 @@
 #include <string_view>
 
 #include "base/check.h"
+#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/numerics/safe_math.h"
 #include "base/strings/string_util.h"
@@ -44,16 +40,16 @@
   std::make_unsigned_t<INT> res =
       CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie();
 
-  CHR* end = outbuf + kOutputBufSize;
+  CHR* end = UNSAFE_TODO(outbuf + kOutputBufSize);
   CHR* i = end;
   do {
-    --i;
+    UNSAFE_TODO(--i);
     DCHECK(i != outbuf);
     *i = static_cast<CHR>((res % 10) + '0');
     res /= 10;
   } while (res != 0);
   if (IsValueNegative(value)) {
-    --i;
+    UNSAFE_TODO(--i);
     DCHECK(i != outbuf);
     *i = static_cast<CHR>('-');
   }
@@ -224,9 +220,11 @@
   return StringT(data, size);
 }
 
+// TODO(tsepez): should be UNSAFE_BUFFER_USAGE.
 template <typename StringT, typename CharT>
 StringT ToString(const CharT* data, size_t size) {
-  return StringT(data, data + size);
+  // SAFETY: required from caller.
+  return StringT(data, UNSAFE_BUFFERS(data + size));
 }
 
 template <typename StringT>
diff --git a/base/strings/string_split.h b/base/strings/string_split.h
index 6e5a7eb..49129e20 100644
--- a/base/strings/string_split.h
+++ b/base/strings/string_split.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/compiler_specific.h"
 #include "build/build_config.h"
 
 namespace base {
@@ -21,24 +22,26 @@
 // the separator, and `second` is the (potentially empty) part that comes after.
 // If `separator` is not in `input`, returns `std::nullopt`.
 BASE_EXPORT std::optional<std::pair<std::string_view, std::string_view>>
-SplitStringOnce(std::string_view input, char separator);
+SplitStringOnce(std::string_view input LIFETIME_BOUND, char separator);
 
 // Similar to the above, but splits the string at the first instance of any
 // separator in `separators`.
 BASE_EXPORT std::optional<std::pair<std::string_view, std::string_view>>
-SplitStringOnce(std::string_view input, std::string_view separators);
+SplitStringOnce(std::string_view input LIFETIME_BOUND,
+                std::string_view separators);
 
 // Splits a string at the last instance of `separator`, returning a pair of
 // `std::string_view`: `first` is the (potentially empty) part that comes before
 // the separator, and `second` is the (potentially empty) part that comes after.
 // If `separator` is not in `input`, returns `std::nullopt`.
 BASE_EXPORT std::optional<std::pair<std::string_view, std::string_view>>
-RSplitStringOnce(std::string_view input, char separator);
+RSplitStringOnce(std::string_view input LIFETIME_BOUND, char separator);
 
 // Similar to the above, but splits the string at the last instance of any
 // separator in `separators`.
 BASE_EXPORT std::optional<std::pair<std::string_view, std::string_view>>
-RSplitStringOnce(std::string_view input, std::string_view separators);
+RSplitStringOnce(std::string_view input LIFETIME_BOUND,
+                 std::string_view separators);
 
 enum WhitespaceHandling {
   KEEP_WHITESPACE,
@@ -96,12 +99,12 @@
 //                               base::SPLIT_WANT_NONEMPTY)) {
 //     ...
 [[nodiscard]] BASE_EXPORT std::vector<std::string_view> SplitStringPiece(
-    std::string_view input,
+    std::string_view input LIFETIME_BOUND,
     std::string_view separators,
     WhitespaceHandling whitespace,
     SplitResult result_type);
 [[nodiscard]] BASE_EXPORT std::vector<std::u16string_view> SplitStringPiece(
-    std::u16string_view input,
+    std::u16string_view input LIFETIME_BOUND,
     std::u16string_view separators,
     WhitespaceHandling whitespace,
     SplitResult result_type);
@@ -151,12 +154,12 @@
 //                                     base::SPLIT_WANT_NONEMPTY)) {
 //     ...
 [[nodiscard]] BASE_EXPORT std::vector<std::u16string_view>
-SplitStringPieceUsingSubstr(std::u16string_view input,
+SplitStringPieceUsingSubstr(std::u16string_view input LIFETIME_BOUND,
                             std::u16string_view delimiter,
                             WhitespaceHandling whitespace,
                             SplitResult result_type);
 [[nodiscard]] BASE_EXPORT std::vector<std::string_view>
-SplitStringPieceUsingSubstr(std::string_view input,
+SplitStringPieceUsingSubstr(std::string_view input LIFETIME_BOUND,
                             std::string_view delimiter,
                             WhitespaceHandling whitespace,
                             SplitResult result_type);
diff --git a/base/strings/string_split_nocompile.nc b/base/strings/string_split_nocompile.nc
new file mode 100644
index 0000000..e8bf5551
--- /dev/null
+++ b/base/strings/string_split_nocompile.nc
@@ -0,0 +1,60 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/strings/string_split.h"
+
+#include <optional>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+
+namespace base {
+
+namespace {
+
+std::string MakeString() { return std::string(); }
+std::u16string MakeString16() { return std::u16string(); }
+#if BUILDFLAG(IS_WIN)
+std::wstring MakeWideString() { return std::wstring(); }
+#endif  // BUILDFLAG(IS_WIN)
+
+void DanglingSplitOnce() {
+  [[maybe_unused]] auto r1 = SplitStringOnce(MakeString(), '.');  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+  [[maybe_unused]] auto r2 = SplitStringOnce(MakeString(), ".,");  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+
+  [[maybe_unused]] auto r3 = RSplitStringOnce(MakeString(), '.');  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+  [[maybe_unused]] auto r4 = RSplitStringOnce(MakeString(), "&;");  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+}
+
+void DanglingSplitStringPiece() {
+  [[maybe_unused]] auto v1 = SplitStringPiece(
+      MakeString(), "&;", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+  [[maybe_unused]] auto v2 = SplitStringPiece(
+      MakeString16(), u"&;", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+#if BUILDFLAG(IS_WIN)
+  [[maybe_unused]] auto v3 = SplitStringPiece(
+      MakeWideString(), L"&;", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+#endif  // BUILDFLAG(IS_WIN)
+}
+
+void DanglinSplitStringPieceUsingSubstr() {
+  [[maybe_unused]] auto v1 = SplitStringPieceUsingSubstr(
+      MakeString(), " and ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+  [[maybe_unused]] auto v2 = SplitStringPieceUsingSubstr(
+      MakeString16(), u" and ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+#if BUILDFLAG(IS_WIN)
+  [[maybe_unused]] auto v3 = SplitStringPieceUsingSubstr(
+      MakeWideString(), L" and ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);  // expected-error {{object backing the pointer will be destroyed at the end of the full-expression}}
+#endif  // BUILDFLAG(IS_WIN)
+}
+
+}
+
+}  // namespace base
diff --git a/base/strings/string_split_win.h b/base/strings/string_split_win.h
index d71a287..3627dc6d 100644
--- a/base/strings/string_split_win.h
+++ b/base/strings/string_split_win.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/compiler_specific.h"
 #include "base/strings/string_split.h"
 
 namespace base {
@@ -23,7 +24,7 @@
     SplitResult result_type);
 
 [[nodiscard]] BASE_EXPORT std::vector<std::wstring_view> SplitStringPiece(
-    std::wstring_view input,
+    std::wstring_view input LIFETIME_BOUND,
     std::wstring_view separators,
     WhitespaceHandling whitespace,
     SplitResult result_type);
@@ -35,7 +36,7 @@
     SplitResult result_type);
 
 [[nodiscard]] BASE_EXPORT std::vector<std::wstring_view>
-SplitStringPieceUsingSubstr(std::wstring_view input,
+SplitStringPieceUsingSubstr(std::wstring_view input LIFETIME_BOUND,
                             std::wstring_view delimiter,
                             WhitespaceHandling whitespace,
                             SplitResult result_type);
diff --git a/base/strings/string_tokenizer.h b/base/strings/string_tokenizer.h
index 4100bf083..b37c80c 100644
--- a/base/strings/string_tokenizer.h
+++ b/base/strings/string_tokenizer.h
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef BASE_STRINGS_STRING_TOKENIZER_H_
 #define BASE_STRINGS_STRING_TOKENIZER_H_
 
@@ -16,6 +11,7 @@
 #include <string_view>
 
 #include "base/check.h"
+#include "base/compiler_specific.h"
 #include "base/strings/string_util.h"
 
 namespace base {
@@ -241,7 +237,7 @@
   // policy.
   void SkipWhitespace() {
     while (token_end_ != end_ && ShouldSkip(*token_end_)) {
-      ++token_end_;
+      UNSAFE_TODO(++token_end_);
     }
   }
 
@@ -256,7 +252,7 @@
         token_is_delim_ = true;
         return false;
       }
-      ++token_end_;
+      UNSAFE_TODO(++token_end_);
       if (delims_.find(*token_begin_) == str::npos &&
           !ShouldSkip(*token_begin_)) {
         break;
@@ -265,7 +261,7 @@
     }
     while (token_end_ != end_ && delims_.find(*token_end_) == str::npos &&
            !ShouldSkip(*token_end_)) {
-      ++token_end_;
+      UNSAFE_TODO(++token_end_);
     }
     return true;
   }
@@ -293,7 +289,7 @@
 
         // Slurp all non-delimiter characters into the token.
         while (token_end_ != end_ && AdvanceOne(&state, *token_end_)) {
-          ++token_end_;
+          UNSAFE_TODO(++token_end_);
         }
 
         // If it's non-empty, or empty tokens were requested, return the token.
@@ -325,7 +321,7 @@
       }
 
       // Look at the delimiter.
-      ++token_end_;
+      UNSAFE_TODO(++token_end_);
       if (options_ & RETURN_DELIMS) {
         return true;
       }
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index 2c3f358..280d5df7 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/strings/string_util.h"
 
 #include <errno.h>
@@ -27,6 +22,7 @@
 #include <vector>
 
 #include "base/check_op.h"
+#include "base/compiler_specific.h"
 #include "base/no_destructor.h"
 #include "base/strings/string_util_impl_helpers.h"
 #include "base/strings/string_util_internal.h"
@@ -38,13 +34,14 @@
 namespace base {
 
 bool IsWprintfFormatPortable(const wchar_t* format) {
-  for (const wchar_t* position = format; *position != '\0'; ++position) {
+  for (const wchar_t* position = format; *position != '\0';
+       UNSAFE_TODO(++position)) {
     if (*position == '%') {
       bool in_specification = true;
       bool modifier_l = false;
       while (in_specification) {
         // Eat up characters until reaching a known specifier.
-        if (*++position == '\0') {
+        if (UNSAFE_TODO(*++position) == '\0') {
           // The format string ended in the middle of a specification.  Call
           // it portable because no unportable specifications were found.  The
           // string is equally broken on all platforms.
@@ -174,8 +171,8 @@
   while (char_index >= 0) {
     int32_t prev = char_index;
     base_icu::UChar32 code_point = 0;
-    CBU8_NEXT(reinterpret_cast<const uint8_t*>(data), char_index,
-              truncation_length, code_point);
+    UNSAFE_TODO(CBU8_NEXT(reinterpret_cast<const uint8_t*>(data), char_index,
+                          truncation_length, code_point));
     if (!IsValidCharacter(code_point)) {
       char_index = prev - 1;
     } else {
@@ -310,10 +307,10 @@
   char buf[64];
   if (bytes != 0 && dimension > 0 && unit_amount < 100) {
     base::snprintf(buf, std::size(buf), "%.1lf%s", unit_amount,
-                   kByteStringsUnlocalized[dimension]);
+                   UNSAFE_TODO(kByteStringsUnlocalized[dimension]));
   } else {
     base::snprintf(buf, std::size(buf), "%.0lf%s", unit_amount,
-                   kByteStringsUnlocalized[dimension]);
+                   UNSAFE_TODO(kByteStringsUnlocalized[dimension]));
   }
 
   return ASCIIToUTF16(buf);
diff --git a/base/strings/string_util_impl_helpers.h b/base/strings/string_util_impl_helpers.h
index 001edb4..af55a9d 100644
--- a/base/strings/string_util_impl_helpers.h
+++ b/base/strings/string_util_impl_helpers.h
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #ifndef BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_
 #define BASE_STRINGS_STRING_UTIL_IMPL_HELPERS_H_
 
@@ -18,6 +13,7 @@
 
 #include "base/check.h"
 #include "base/check_op.h"
+#include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/notreached.h"
 #include "base/third_party/icu/icu_utf.h"
@@ -97,7 +93,7 @@
   }
 
   // Trim.
-  output->assign(input.data() + first_good_char,
+  output->assign(UNSAFE_TODO(input.data() + first_good_char),
                  last_good_char - first_good_char + 1);
 
   // Return where we trimmed from.
@@ -174,11 +170,11 @@
   constexpr MachineWord non_ascii_bit_mask = NonASCIIMasks[sizeof(Char)];
   static_assert(non_ascii_bit_mask, "Error: Invalid Mask");
   MachineWord all_char_bits = 0;
-  const Char* end = characters + length;
+  const Char* end = UNSAFE_TODO(characters + length);
 
   // Prologue: align the input.
   while (!IsMachineWordAligned(characters) && characters < end) {
-    all_char_bits |= static_cast<MachineWord>(*characters++);
+    all_char_bits |= UNSAFE_TODO(static_cast<MachineWord>(*characters++));
   }
   if (all_char_bits & non_ascii_bit_mask) {
     return false;
@@ -187,11 +183,11 @@
   // Compare the values of CPU word size.
   constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char);
   constexpr int batch_count = 16;
-  while (characters <= end - batch_count * chars_per_word) {
+  while (characters <= UNSAFE_TODO(end - batch_count * chars_per_word)) {
     all_char_bits = 0;
     for (int i = 0; i < batch_count; ++i) {
       all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
-      characters += chars_per_word;
+      UNSAFE_TODO(characters += chars_per_word);
     }
     if (all_char_bits & non_ascii_bit_mask) {
       return false;
@@ -200,14 +196,14 @@
 
   // Process the remaining words.
   all_char_bits = 0;
-  while (characters <= end - chars_per_word) {
+  while (characters <= UNSAFE_TODO(end - chars_per_word)) {
     all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
-    characters += chars_per_word;
+    UNSAFE_TODO(characters += chars_per_word);
   }
 
   // Process the remaining bytes.
   while (characters < end) {
-    all_char_bits |= static_cast<MachineWord>(*characters++);
+    all_char_bits |= UNSAFE_TODO(static_cast<MachineWord>(*characters++));
   }
 
   return !(all_char_bits & non_ascii_bit_mask);
@@ -221,7 +217,7 @@
 
   while (char_index < src_len) {
     base_icu::UChar32 code_point;
-    CBU8_NEXT(src, char_index, src_len, code_point);
+    UNSAFE_TODO(CBU8_NEXT(src, char_index, src_len, code_point));
     if (!Validator(code_point)) {
       return false;
     }
@@ -333,7 +329,8 @@
     auto* buffer = &((*str)[0]);
     for (size_t offset = first_match; offset != std::basic_string<CharT>::npos;
          offset = matcher.Find(*str, offset + replace_length)) {
-      CharTraits::copy(buffer + offset, replace_with.data(), replace_length);
+      CharTraits::copy(UNSAFE_TODO(buffer + offset), replace_with.data(),
+                       replace_length);
     }
     return true;
   }
@@ -425,7 +422,7 @@
   size_t read_offset = first_match + expansion;
   do {
     if (replace_length) {
-      CharTraits::copy(buffer + write_offset, replace_with.data(),
+      CharTraits::copy(UNSAFE_TODO(buffer + write_offset), replace_with.data(),
                        replace_length);
       write_offset += replace_length;
     }
@@ -437,7 +434,8 @@
 
     size_t length = match - read_offset;
     if (length) {
-      CharTraits::move(buffer + write_offset, buffer + read_offset, length);
+      CharTraits::move(UNSAFE_TODO(buffer + write_offset),
+                       UNSAFE_TODO(buffer + read_offset), length);
       write_offset += length;
       read_offset += length;
     }
@@ -497,9 +495,9 @@
   auto iter = parts.begin();
   CHECK(iter != parts.end(), base::NotFatalUntil::M125);
   result.append(*iter);
-  ++iter;
+  UNSAFE_TODO(++iter);
 
-  for (; iter != parts.end(); ++iter) {
+  for (; iter != parts.end(); UNSAFE_TODO(++iter)) {
     result.append(sep);
     result.append(*iter);
   }
diff --git a/base/strings/sys_string_conversions_posix.cc b/base/strings/sys_string_conversions_posix.cc
index 73506f4b..f77dc7cd 100644
--- a/base/strings/sys_string_conversions_posix.cc
+++ b/base/strings/sys_string_conversions_posix.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/strings/sys_string_conversions.h"
 
 #include <stddef.h>
@@ -15,6 +10,7 @@
 
 #include <string_view>
 
+#include "base/compiler_specific.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 
@@ -113,7 +109,7 @@
   size_t num_out_chars = 0;
   memset(&ps, 0, sizeof(ps));
   for (size_t i = 0; i < native_mb.size();) {
-    const char* src = native_mb.data() + i;
+    const char* src = UNSAFE_TODO(native_mb.data() + i);
     size_t res = mbrtowc(nullptr, src, native_mb.size() - i, &ps);
     switch (res) {
       // Handle any errors and return an empty string.
@@ -142,7 +138,7 @@
   // We walk the input string again, with |i| tracking the index of the
   // multi-byte input, and |j| tracking the wide output.
   for (size_t i = 0, j = 0; i < native_mb.size(); ++j) {
-    const char* src = native_mb.data() + i;
+    const char* src = UNSAFE_TODO(native_mb.data() + i);
     wchar_t* dst = &out[j];
     size_t res = mbrtowc(dst, src, native_mb.size() - i, &ps);
     switch (res) {
diff --git a/build/android/incremental_install/java/org/chromium/incrementalinstall/Reflect.java b/build/android/incremental_install/java/org/chromium/incrementalinstall/Reflect.java
index f1e570b..fa871eb1 100644
--- a/build/android/incremental_install/java/org/chromium/incrementalinstall/Reflect.java
+++ b/build/android/incremental_install/java/org/chromium/incrementalinstall/Reflect.java
@@ -6,7 +6,7 @@
 
 import android.os.Build;
 
-import org.lsposed.hiddenapibypass.HiddenApiBypass;
+import org.lsposed.hiddenapibypass.LSPass;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
@@ -93,8 +93,8 @@
             } else {
                 List<Field> fields =
                         isStatic
-                                ? HiddenApiBypass.getStaticFields(clazz)
-                                : HiddenApiBypass.getInstanceFields(clazz);
+                                ? LSPass.getStaticFields(clazz)
+                                : LSPass.getInstanceFields(clazz);
                 for (Field field : fields) {
                     if (field.getName().equals(name)) {
                         return field;
diff --git a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/BUILD.gn b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/BUILD.gn
index 86e1466..d14c7b9 100644
--- a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/BUILD.gn
+++ b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/BUILD.gn
@@ -11,14 +11,14 @@
 ]
 
 android_library("stub_java") {
-  sources = [ "stub/src/main/java/dalvik/system/VMRuntime.java" ]
+  sources = [ "stub/src/main/java/stub/dalvik/system/VMRuntime.java" ]
   jar_excluded_patterns = [ "*" ]
 }
 
 android_library("hidden_api_bypass_java") {
   sources = [
     "library/src/main/java/org/lsposed/hiddenapibypass/Helper.java",
-    "library/src/main/java/org/lsposed/hiddenapibypass/HiddenApiBypass.java",
+    "library/src/main/java/org/lsposed/hiddenapibypass/LSPass.java",
     "local_modifications/org/lsposed/hiddenapibypass/library/BuildConfig.java",
   ]
   deps = [
diff --git a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.chromium b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.chromium
index 810b6cd..597d4c5 100644
--- a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.chromium
+++ b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.chromium
@@ -1,7 +1,7 @@
 Name: AndroidHiddenApiBypass
 URL: https://github.com/LSPosed/AndroidHiddenApiBypass
-Version: b16cc3934a27e55e51f00f5504c7f49e7c8cfab7
-Revision: b16cc3934a27e55e51f00f5504c7f49e7c8cfab7
+Version: 71aaad4ce558530b4788da67d0e3d9bb3596e9d3
+Revision: 71aaad4ce558530b4788da67d0e3d9bb3596e9d3
 License: Apache-2.0
 License File: LICENSE
 Security Critical: no
diff --git a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.md b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.md
index c7e0681..b5079d8 100644
--- a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.md
+++ b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/README.md
@@ -1,16 +1,28 @@
-# AndroidHiddenApiBypass
+# Android Hidden Api Bypass
 
 [![Android CI status](https://github.com/LSPosed/AndroidHiddenApiBypass/actions/workflows/android.yml/badge.svg?branch=main)](https://github.com/LSPosed/AndroidHiddenApiBypass/actions/workflows/android.yml)
+![](https://img.shields.io/badge/Android-1.0%20--%2016-blue.svg)
+![](https://img.shields.io/maven-central/v/org.lsposed.hiddenapibypass/hiddenapibypass.svg)
 
 Bypass restrictions on non-SDK interfaces.
 
-## Why AndroidHiddenApiBypass?
+## Why HiddenApiBypass?
 
 - Pure Java: no native code used.
 - Reliable: does not rely on specific behaviors, so it will not be blocked like meta-reflection or `dexfile`.
-- Stable: `unsafe`, art structs and `setHiddenApiExemptions` are stable APIs.
+- Stable: does not rely on internal ART structures on Android 10+. `Unsafe` and `setHiddenApiExemptions` are stable APIs.
 
-[How it works (Chinese)](https://lovesykun.cn/archives/android-hidden-api-bypass.html)
+## And LSPass?
+
+- Fast: no I/O, initializing faster than HiddenApiBypass.
+- Safe: no `Unsafe`.
+- Unreliable: can be blocked as easily as meta-reflection.
+
+## How it works
+
+HiddenApiBypass: [Unsafe](https://lovesykun.cn/archives/android-hidden-api-bypass.html)
+
+LSPass: [Property.of()](https://github.com/michalbednarski/LeakValue?tab=readme-ov-file#putting-it-all-together)
 
 ## Integration
 
@@ -21,12 +33,16 @@
     mavenCentral()
 }
 dependencies {
-    implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'
+    implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:+'
 }
 ```
 
 ## Usage
 
+This library has two variants of bypassing, they have the same API.
+When initializing, LSPass is faster than HiddenApiBypass, but LSPass maybe blocked in future Android releases.
+Replace `HiddenApiBypass` with `LSPass` if you do not want to use `Unsafe`.
+
 1. Invoke a restricted method:
     ```java
     HiddenApiBypass.invoke(ApplicationInfo.class, new ApplicationInfo(), "usesNonSdkApi"/*, args*/)
@@ -48,7 +64,7 @@
 1. Get all static fields including restricted ones from a class:
     ```java
     var allStaticFields = HiddenApiBypass.getStaticFields(ApplicationInfo.class);
-    ((Method).stream(allInstanceFields).filter(e -> e.getName().equals("HIDDEN_API_ENFORCEMENT_DEFAULT")).findFirst().get()).get(null);
+    ((Method).stream(allStaticFields).filter(e -> e.getName().equals("HIDDEN_API_ENFORCEMENT_DEFAULT")).findFirst().get()).get(null);
     ```
 1. Get specific class method or class constructor
     ```java
@@ -69,7 +85,7 @@
     ```
 ## License
 
-    Copyright 2021 LSPosed
+    Copyright 2021-2025 LSPosed
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
diff --git a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/Helper.java b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/Helper.java
index 07d130d..1fb7fd2 100644
--- a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/Helper.java
+++ b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/Helper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 LSPosed
+ * Copyright (C) 2021-2025 LSPosed
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,12 +16,31 @@
 
 package org.lsposed.hiddenapibypass;
 
-import java.lang.invoke.MethodHandleInfo;
 import java.lang.invoke.MethodType;
-import java.lang.reflect.Member;
+import java.util.HashSet;
+import java.util.Set;
 
 @SuppressWarnings("unused")
 public class Helper {
+    static final Set<String> signaturePrefixes = new HashSet<>();
+
+    static boolean checkArgsForInvokeMethod(java.lang.Class<?>[] params, Object[] args) {
+        if (params.length != args.length) return false;
+        for (int i = 0; i < params.length; ++i) {
+            if (params[i].isPrimitive()) {
+                if (params[i] == int.class && !(args[i] instanceof Integer)) return false;
+                else if (params[i] == byte.class && !(args[i] instanceof Byte)) return false;
+                else if (params[i] == char.class && !(args[i] instanceof Character)) return false;
+                else if (params[i] == boolean.class && !(args[i] instanceof Boolean)) return false;
+                else if (params[i] == double.class && !(args[i] instanceof Double)) return false;
+                else if (params[i] == float.class && !(args[i] instanceof Float)) return false;
+                else if (params[i] == long.class && !(args[i] instanceof Long)) return false;
+                else if (params[i] == short.class && !(args[i] instanceof Short)) return false;
+            } else if (args[i] != null && !params[i].isInstance(args[i])) return false;
+        }
+        return true;
+    }
+
     static public class MethodHandle {
         private final MethodType type = null;
         private MethodType nominalType;
@@ -32,15 +51,6 @@
         protected final long artFieldOrMethod = 0;
     }
 
-    static final public class MethodHandleImpl extends MethodHandle {
-        private final MethodHandleInfo info = null;
-    }
-
-    static final public class HandleInfo {
-        private final Member member = null;
-        private final MethodHandle handle = null;
-    }
-
     static final public class Class {
         private transient ClassLoader classLoader;
         private transient java.lang.Class<?> componentType;
diff --git a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/HiddenApiBypass.java b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/HiddenApiBypass.java
deleted file mode 100644
index 2344acff..0000000
--- a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/HiddenApiBypass.java
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (C) 2021 LSPosed
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.lsposed.hiddenapibypass;
-
-import android.os.Build;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.annotation.VisibleForTesting;
-
-import org.lsposed.hiddenapibypass.library.BuildConfig;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandleInfo;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Executable;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import dalvik.system.VMRuntime;
-import sun.misc.Unsafe;
-
-@RequiresApi(Build.VERSION_CODES.P)
-public final class HiddenApiBypass {
-    private static final String TAG = "HiddenApiBypass";
-    private static final Unsafe unsafe;
-    private static final long methodOffset;
-    private static final long classOffset;
-    private static final long artOffset;
-    private static final long infoOffset;
-    private static final long methodsOffset;
-    private static final long iFieldOffset;
-    private static final long sFieldOffset;
-    private static final long memberOffset;
-    private static final long artMethodSize;
-    private static final long artMethodBias;
-    private static final long artFieldSize;
-    private static final long artFieldBias;
-    private static final Set<String> signaturePrefixes = new HashSet<>();
-
-    static {
-        try {
-            //noinspection JavaReflectionMemberAccess DiscouragedPrivateApi
-            unsafe = (Unsafe) Unsafe.class.getDeclaredMethod("getUnsafe").invoke(null);
-            assert unsafe != null;
-            methodOffset = unsafe.objectFieldOffset(Helper.Executable.class.getDeclaredField("artMethod"));
-            classOffset = unsafe.objectFieldOffset(Helper.Executable.class.getDeclaredField("declaringClass"));
-            artOffset = unsafe.objectFieldOffset(Helper.MethodHandle.class.getDeclaredField("artFieldOrMethod"));
-            infoOffset = unsafe.objectFieldOffset(Helper.MethodHandleImpl.class.getDeclaredField("info"));
-            methodsOffset = unsafe.objectFieldOffset(Helper.Class.class.getDeclaredField("methods"));
-            iFieldOffset = unsafe.objectFieldOffset(Helper.Class.class.getDeclaredField("iFields"));
-            sFieldOffset = unsafe.objectFieldOffset(Helper.Class.class.getDeclaredField("sFields"));
-            memberOffset = unsafe.objectFieldOffset(Helper.HandleInfo.class.getDeclaredField("member"));
-            Method mA = Helper.NeverCall.class.getDeclaredMethod("a");
-            Method mB = Helper.NeverCall.class.getDeclaredMethod("b");
-            mA.setAccessible(true);
-            mB.setAccessible(true);
-            MethodHandle mhA = MethodHandles.lookup().unreflect(mA);
-            MethodHandle mhB = MethodHandles.lookup().unreflect(mB);
-            long aAddr = unsafe.getLong(mhA, artOffset);
-            long bAddr = unsafe.getLong(mhB, artOffset);
-            long aMethods = unsafe.getLong(Helper.NeverCall.class, methodsOffset);
-            artMethodSize = bAddr - aAddr;
-            if (BuildConfig.DEBUG) Log.v(TAG, artMethodSize + " " +
-                    Long.toString(aAddr, 16) + ", " +
-                    Long.toString(bAddr, 16) + ", " +
-                    Long.toString(aMethods, 16));
-            artMethodBias = aAddr - aMethods - artMethodSize;
-            Field fI = Helper.NeverCall.class.getDeclaredField("i");
-            Field fJ = Helper.NeverCall.class.getDeclaredField("j");
-            fI.setAccessible(true);
-            fJ.setAccessible(true);
-            MethodHandle mhI = MethodHandles.lookup().unreflectGetter(fI);
-            MethodHandle mhJ = MethodHandles.lookup().unreflectGetter(fJ);
-            long iAddr = unsafe.getLong(mhI, artOffset);
-            long jAddr = unsafe.getLong(mhJ, artOffset);
-            long iFields = unsafe.getLong(Helper.NeverCall.class, iFieldOffset);
-            artFieldSize = jAddr - iAddr;
-            if (BuildConfig.DEBUG) Log.v(TAG, artFieldSize + " " +
-                    Long.toString(iAddr, 16) + ", " +
-                    Long.toString(jAddr, 16) + ", " +
-                    Long.toString(iFields, 16));
-            artFieldBias = iAddr - iFields;
-        } catch (ReflectiveOperationException e) {
-            Log.e(TAG, "Initialize error", e);
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
-    @VisibleForTesting
-    static boolean checkArgsForInvokeMethod(Class<?>[] params, Object[] args) {
-        if (params.length != args.length) return false;
-        for (int i = 0; i < params.length; ++i) {
-            if (params[i].isPrimitive()) {
-                if (params[i] == int.class && !(args[i] instanceof Integer)) return false;
-                else if (params[i] == byte.class && !(args[i] instanceof Byte)) return false;
-                else if (params[i] == char.class && !(args[i] instanceof Character)) return false;
-                else if (params[i] == boolean.class && !(args[i] instanceof Boolean)) return false;
-                else if (params[i] == double.class && !(args[i] instanceof Double)) return false;
-                else if (params[i] == float.class && !(args[i] instanceof Float)) return false;
-                else if (params[i] == long.class && !(args[i] instanceof Long)) return false;
-                else if (params[i] == short.class && !(args[i] instanceof Short)) return false;
-            } else if (args[i] != null && !params[i].isInstance(args[i])) return false;
-        }
-        return true;
-    }
-
-    /**
-     * create an instance of the given class {@code clazz} calling the restricted constructor with arguments {@code args}
-     *
-     * @param clazz    the class of the instance to new
-     * @param initargs arguments to call constructor
-     * @return the new instance
-     * @see Constructor#newInstance(Object...)
-     */
-    public static Object newInstance(@NonNull Class<?> clazz, Object... initargs) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
-        Method stub = Helper.InvokeStub.class.getDeclaredMethod("invoke", Object[].class);
-        Constructor<?> ctor = Helper.InvokeStub.class.getDeclaredConstructor(Object[].class);
-        ctor.setAccessible(true);
-        long methods = unsafe.getLong(clazz, methodsOffset);
-        if (methods == 0) throw new NoSuchMethodException("Cannot find matching constructor");
-        int numMethods = unsafe.getInt(methods);
-        if (BuildConfig.DEBUG) Log.d(TAG, clazz + " has " + numMethods + " methods");
-        for (int i = 0; i < numMethods; i++) {
-            long method = methods + i * artMethodSize + artMethodBias;
-            unsafe.putLong(stub, methodOffset, method);
-            if (BuildConfig.DEBUG) Log.v(TAG, "got " + clazz.getTypeName() + "." + stub.getName() +
-                    "(" + Arrays.stream(stub.getParameterTypes()).map(Type::getTypeName).collect(Collectors.joining()) + ")");
-            if ("<init>".equals(stub.getName())) {
-                unsafe.putLong(ctor, methodOffset, method);
-                unsafe.putObject(ctor, classOffset, clazz);
-                Class<?>[] params = ctor.getParameterTypes();
-                if (checkArgsForInvokeMethod(params, initargs))
-                    return ctor.newInstance(initargs);
-            }
-        }
-        throw new NoSuchMethodException("Cannot find matching constructor");
-    }
-
-    /**
-     * invoke a restrict method named {@code methodName} of the given class {@code clazz} with this object {@code thiz} and arguments {@code args}
-     *
-     * @param clazz      the class call the method on (this parameter is required because this method cannot call inherit method)
-     * @param thiz       this object, which can be {@code null} if the target method is static
-     * @param methodName the method name
-     * @param args       arguments to call the method with name {@code methodName}
-     * @return the return value of the method
-     * @see Method#invoke(Object, Object...)
-     */
-    public static Object invoke(@NonNull Class<?> clazz, @Nullable Object thiz, @NonNull String methodName, Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        if (thiz != null && !clazz.isInstance(thiz)) {
-            throw new IllegalArgumentException("this object is not an instance of the given class");
-        }
-        Method stub = Helper.InvokeStub.class.getDeclaredMethod("invoke", Object[].class);
-        stub.setAccessible(true);
-        long methods = unsafe.getLong(clazz, methodsOffset);
-        if (methods == 0) throw new NoSuchMethodException("Cannot find matching method");
-        int numMethods = unsafe.getInt(methods);
-        if (BuildConfig.DEBUG) Log.d(TAG, clazz + " has " + numMethods + " methods");
-        for (int i = 0; i < numMethods; i++) {
-            long method = methods + i * artMethodSize + artMethodBias;
-            unsafe.putLong(stub, methodOffset, method);
-            if (BuildConfig.DEBUG) Log.v(TAG, "got " + clazz.getTypeName() + "." + stub.getName() +
-                    "(" + Arrays.stream(stub.getParameterTypes()).map(Type::getTypeName).collect(Collectors.joining()) + ")");
-            if (methodName.equals(stub.getName())) {
-                Class<?>[] params = stub.getParameterTypes();
-                if (checkArgsForInvokeMethod(params, args))
-                    return stub.invoke(thiz, args);
-            }
-        }
-        throw new NoSuchMethodException("Cannot find matching method");
-    }
-
-    /**
-     * get declared methods of given class without hidden api restriction
-     *
-     * @param clazz the class to fetch declared methods (including constructors with name `&lt;init&gt;`)
-     * @return list of declared methods of {@code clazz}
-     */
-    @NonNull
-    public static List<Executable> getDeclaredMethods(@NonNull Class<?> clazz) {
-        ArrayList<Executable> list = new ArrayList<>();
-        if (clazz.isPrimitive() || clazz.isArray()) return list;
-        MethodHandle mh;
-        try {
-            Method mA = Helper.NeverCall.class.getDeclaredMethod("a");
-            mA.setAccessible(true);
-            mh = MethodHandles.lookup().unreflect(mA);
-        } catch (NoSuchMethodException | IllegalAccessException e) {
-            return list;
-        }
-        long methods = unsafe.getLong(clazz, methodsOffset);
-        if (methods == 0) return list;
-        int numMethods = unsafe.getInt(methods);
-        if (BuildConfig.DEBUG) Log.d(TAG, clazz + " has " + numMethods + " methods");
-        for (int i = 0; i < numMethods; i++) {
-            long method = methods + i * artMethodSize + artMethodBias;
-            unsafe.putLong(mh, artOffset, method);
-            unsafe.putObject(mh, infoOffset, null);
-            try {
-                MethodHandles.lookup().revealDirect(mh);
-            } catch (Throwable ignored) {
-            }
-            MethodHandleInfo info = (MethodHandleInfo) unsafe.getObject(mh, infoOffset);
-            Executable member = (Executable) unsafe.getObject(info, memberOffset);
-            if (BuildConfig.DEBUG)
-                Log.v(TAG, "got " + clazz.getTypeName() + "." + member.getName() +
-                        "(" + Arrays.stream(member.getParameterTypes()).map(Type::getTypeName).collect(Collectors.joining()) + ")");
-            list.add(member);
-        }
-        return list;
-    }
-
-    /**
-     * get a restrict method named {@code methodName} of the given class {@code clazz} with argument types {@code parameterTypes}
-     *
-     * @param clazz          the class where the expected method declares
-     * @param methodName     the expected method's name
-     * @param parameterTypes argument types of the expected method with name {@code methodName}
-     * @return the found method
-     * @throws NoSuchMethodException when no method matches the given parameters
-     * @see Class#getDeclaredMethod(String, Class[]) 
-     */
-    @NonNull
-    public static Method getDeclaredMethod(@NonNull Class<?> clazz, @NonNull String methodName, @NonNull Class<?>... parameterTypes) throws NoSuchMethodException {
-        List<Executable> methods = getDeclaredMethods(clazz);
-        allMethods:
-        for (Executable method : methods) {
-            if (!method.getName().equals(methodName)) continue;
-            if (!(method instanceof Method)) continue;
-            Class<?>[] expectedTypes = method.getParameterTypes();
-            if (expectedTypes.length != parameterTypes.length) continue;
-            for (int i = 0; i < parameterTypes.length; ++i) {
-                if (parameterTypes[i] != expectedTypes[i]) continue allMethods;
-            }
-            return (Method) method;
-        }
-        throw new NoSuchMethodException("Cannot find matching method");
-    }
-
-    /**
-     * get a restrict constructor of the given class {@code clazz} with argument types {@code parameterTypes}
-     *
-     * @param clazz          the class where the expected constructor declares
-     * @param parameterTypes argument types of the expected constructor
-     * @return the found constructor
-     * @throws NoSuchMethodException when no constructor matches the given parameters
-     * @see Class#getDeclaredConstructor(Class[])
-     */
-    @NonNull
-    public static Constructor<?> getDeclaredConstructor(@NonNull Class<?> clazz, @NonNull Class<?>... parameterTypes) throws NoSuchMethodException {
-        List<Executable> methods = getDeclaredMethods(clazz);
-        allMethods:
-        for (Executable method : methods) {
-            if (!(method instanceof Constructor)) continue;
-            Class<?>[] expectedTypes = method.getParameterTypes();
-            if (expectedTypes.length != parameterTypes.length) continue;
-            for (int i = 0; i < parameterTypes.length; ++i) {
-                if (parameterTypes[i] != expectedTypes[i]) continue allMethods;
-            }
-            return (Constructor<?>) method;
-        }
-        throw new NoSuchMethodException("Cannot find matching constructor");
-    }
-
-
-    /**
-     * get declared non-static fields of given class without hidden api restriction
-     *
-     * @param clazz the class to fetch declared methods
-     * @return list of declared non-static fields of {@code clazz}
-     */
-    @NonNull
-    public static List<Field> getInstanceFields(@NonNull Class<?> clazz) {
-        ArrayList<Field> list = new ArrayList<>();
-        if (clazz.isPrimitive() || clazz.isArray()) return list;
-        MethodHandle mh;
-        try {
-            Field fI = Helper.NeverCall.class.getDeclaredField("i");
-            fI.setAccessible(true);
-            mh = MethodHandles.lookup().unreflectGetter(fI);
-        } catch (IllegalAccessException | NoSuchFieldException e) {
-            return list;
-        }
-        long fields = unsafe.getLong(clazz, iFieldOffset);
-        if (fields == 0) return list;
-        int numFields = unsafe.getInt(fields);
-        if (BuildConfig.DEBUG) Log.d(TAG, clazz + " has " + numFields + " instance fields");
-        for (int i = 0; i < numFields; i++) {
-            long field = fields + i * artFieldSize + artFieldBias;
-            unsafe.putLong(mh, artOffset, field);
-            unsafe.putObject(mh, infoOffset, null);
-            try {
-                MethodHandles.lookup().revealDirect(mh);
-            } catch (Throwable ignored) {
-            }
-            MethodHandleInfo info = (MethodHandleInfo) unsafe.getObject(mh, infoOffset);
-            Field member = (Field) unsafe.getObject(info, memberOffset);
-            if (BuildConfig.DEBUG)
-                Log.v(TAG, "got " + member.getType() + " " + clazz.getTypeName() + "." + member.getName());
-            list.add(member);
-        }
-        return list;
-    }
-
-    /**
-     * get declared static fields of given class without hidden api restriction
-     *
-     * @param clazz the class to fetch declared methods
-     * @return list of declared static fields of {@code clazz}
-     */
-    @NonNull
-    public static List<Field> getStaticFields(@NonNull Class<?> clazz) {
-        ArrayList<Field> list = new ArrayList<>();
-        if (clazz.isPrimitive() || clazz.isArray()) return list;
-        MethodHandle mh;
-        try {
-            Field fS = Helper.NeverCall.class.getDeclaredField("s");
-            fS.setAccessible(true);
-            mh = MethodHandles.lookup().unreflectGetter(fS);
-        } catch (IllegalAccessException | NoSuchFieldException e) {
-            return list;
-        }
-        long fields = unsafe.getLong(clazz, sFieldOffset);
-        if (fields == 0) return list;
-        int numFields = unsafe.getInt(fields);
-        if (BuildConfig.DEBUG) Log.d(TAG, clazz + " has " + numFields + " static fields");
-        for (int i = 0; i < numFields; i++) {
-            long field = fields + i * artFieldSize + artFieldBias;
-            unsafe.putLong(mh, artOffset, field);
-            unsafe.putObject(mh, infoOffset, null);
-            try {
-                MethodHandles.lookup().revealDirect(mh);
-            } catch (Throwable ignored) {
-            }
-            MethodHandleInfo info = (MethodHandleInfo) unsafe.getObject(mh, infoOffset);
-            Field member = (Field) unsafe.getObject(info, memberOffset);
-            if (BuildConfig.DEBUG)
-                Log.v(TAG, "got " + member.getType() + " " + clazz.getTypeName() + "." + member.getName());
-            list.add(member);
-        }
-        return list;
-    }
-
-    /**
-     * Sets the list of exemptions from hidden API access enforcement.
-     *
-     * @param signaturePrefixes A list of class signature prefixes. Each item in the list is a prefix match on the type
-     *                          signature of a blacklisted API. All matching APIs are treated as if they were on
-     *                          the whitelist: access permitted, and no logging..
-     * @return whether the operation is successful
-     */
-    public static boolean setHiddenApiExemptions(@NonNull String... signaturePrefixes) {
-        try {
-            Object runtime = invoke(VMRuntime.class, null, "getRuntime");
-            invoke(VMRuntime.class, runtime, "setHiddenApiExemptions", (Object) signaturePrefixes);
-            return true;
-        } catch (Throwable e) {
-            Log.w(TAG, "setHiddenApiExemptions", e);
-            return false;
-        }
-    }
-
-    /**
-     * Adds the list of exemptions from hidden API access enforcement.
-     *
-     * @param signaturePrefixes A list of class signature prefixes. Each item in the list is a prefix match on the type
-     *                          signature of a blacklisted API. All matching APIs are treated as if they were on
-     *                          the whitelist: access permitted, and no logging..
-     * @return whether the operation is successful
-     */
-    public static boolean addHiddenApiExemptions(String... signaturePrefixes) {
-        HiddenApiBypass.signaturePrefixes.addAll(Arrays.asList(signaturePrefixes));
-        String[] strings = new String[HiddenApiBypass.signaturePrefixes.size()];
-        HiddenApiBypass.signaturePrefixes.toArray(strings);
-        return setHiddenApiExemptions(strings);
-    }
-
-    /**
-     * Clear the list of exemptions from hidden API access enforcement.
-     * Android runtime will cache access flags, so if a hidden API has been accessed unrestrictedly,
-     * running this method will not restore the restriction on it.
-     *
-     * @return whether the operation is successful
-     */
-    public static boolean clearHiddenApiExemptions() {
-        HiddenApiBypass.signaturePrefixes.clear();
-        return setHiddenApiExemptions();
-    }
-}
diff --git a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/LSPass.java b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/LSPass.java
new file mode 100644
index 0000000..383dd8d
--- /dev/null
+++ b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/library/src/main/java/org/lsposed/hiddenapibypass/LSPass.java
@@ -0,0 +1,234 @@
+package org.lsposed.hiddenapibypass;
+
+import android.os.Build;
+import android.util.Log;
+import android.util.Property;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import stub.dalvik.system.VMRuntime;
+
+@SuppressWarnings("rawtypes")
+@RequiresApi(Build.VERSION_CODES.P)
+public final class LSPass {
+    private static final String TAG = "LSPass";
+    private static final Property<Class, Method[]> methods;
+    private static final Property<Class, Constructor[]> constructors;
+    private static final Property<Class, Field[]> fields;
+
+    static {
+        methods = Property.of(Class.class, Method[].class, "DeclaredMethods");
+        constructors = Property.of(Class.class, Constructor[].class, "DeclaredConstructors");
+        fields = Property.of(Class.class, Field[].class, "DeclaredFields");
+    }
+
+    /**
+     * get declared methods of given class without hidden api restriction
+     *
+     * @param clazz the class to fetch declared methods
+     * @return list of declared methods of {@code clazz}
+     */
+    public static List<Method> getDeclaredMethods(@NonNull Class<?> clazz) {
+        return Arrays.asList(methods.get(clazz));
+    }
+
+    /**
+     * get declared constructors of given class without hidden api restriction
+     *
+     * @param clazz the class to fetch declared constructors
+     * @return list of declared constructors of {@code clazz}
+     */
+    public static List<Constructor<?>> getDeclaredConstructors(@NonNull Class<?> clazz) {
+        return Arrays.<Constructor<?>>asList(constructors.get(clazz));
+    }
+
+    /**
+     * get declared fields of given class without hidden api restriction
+     *
+     * @param clazz the class to fetch declared methods
+     * @return list of declared fields of {@code clazz}
+     */
+    public static List<Field> getDeclaredFields(@NonNull Class<?> clazz) {
+        return Arrays.asList(fields.get(clazz));
+    }
+
+    /**
+     * get declared non-static fields of given class without hidden api restriction
+     *
+     * @param clazz the class to fetch declared methods
+     * @return list of declared non-static fields of {@code clazz}
+     */
+    @NonNull
+    public static List<Field> getInstanceFields(@NonNull Class<?> clazz) {
+        var list = new ArrayList<Field>();
+        for (var member : getDeclaredFields(clazz)) {
+            if (!Modifier.isStatic(member.getModifiers()))
+                list.add(member);
+        }
+        return list;
+    }
+
+    /**
+     * get declared static fields of given class without hidden api restriction
+     *
+     * @param clazz the class to fetch declared methods
+     * @return list of declared static fields of {@code clazz}
+     */
+    @NonNull
+    public static List<Field> getStaticFields(@NonNull Class<?> clazz) {
+        var list = new ArrayList<Field>();
+        for (var member : getDeclaredFields(clazz)) {
+            if (Modifier.isStatic(member.getModifiers()))
+                list.add(member);
+        }
+        return list;
+    }
+
+    /**
+     * get a restrict method named {@code methodName} of the given class {@code clazz} with argument types {@code parameterTypes}
+     *
+     * @param clazz          the class where the expected method declares
+     * @param methodName     the expected method's name
+     * @param parameterTypes argument types of the expected method with name {@code methodName}
+     * @return the found method
+     * @throws NoSuchMethodException when no method matches the given parameters
+     * @see Class#getDeclaredMethod(String, Class[])
+     */
+    @NonNull
+    public static Method getDeclaredMethod(@NonNull Class<?> clazz, @NonNull String methodName, @NonNull Class<?>... parameterTypes) throws NoSuchMethodException {
+        var methods = getDeclaredMethods(clazz);
+        all:
+        for (var method : methods) {
+            if (!method.getName().equals(methodName)) continue;
+            var expectedTypes = method.getParameterTypes();
+            if (expectedTypes.length != parameterTypes.length) continue;
+            for (int i = 0; i < parameterTypes.length; ++i) {
+                if (parameterTypes[i] != expectedTypes[i]) continue all;
+            }
+            return method;
+        }
+        throw new NoSuchMethodException("Cannot find matching method");
+    }
+
+    /**
+     * get a restrict constructor of the given class {@code clazz} with argument types {@code parameterTypes}
+     *
+     * @param clazz          the class where the expected constructor declares
+     * @param parameterTypes argument types of the expected constructor
+     * @return the found constructor
+     * @throws NoSuchMethodException when no constructor matches the given parameters
+     * @see Class#getDeclaredConstructor(Class[])
+     */
+    @NonNull
+    public static Constructor<?> getDeclaredConstructor(@NonNull Class<?> clazz, @NonNull Class<?>... parameterTypes) throws NoSuchMethodException {
+        var constructors = getDeclaredConstructors(clazz);
+        all:
+        for (var constructor : constructors) {
+            var expectedTypes = constructor.getParameterTypes();
+            if (expectedTypes.length != parameterTypes.length) continue;
+            for (int i = 0; i < parameterTypes.length; ++i) {
+                if (parameterTypes[i] != expectedTypes[i]) continue all;
+            }
+            return constructor;
+        }
+        throw new NoSuchMethodException("Cannot find matching constructor");
+    }
+
+    /**
+     * create an instance of the given class {@code clazz} calling the restricted constructor with arguments {@code args}
+     *
+     * @param clazz    the class of the instance to new
+     * @param initargs arguments to call constructor
+     * @return the new instance
+     * @see Constructor#newInstance(Object...)
+     */
+    public static Object newInstance(@NonNull Class<?> clazz, Object... initargs) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
+        var constructors = getDeclaredConstructors(clazz);
+        for (var constructor : constructors) {
+            var params = constructor.getParameterTypes();
+            if (!Helper.checkArgsForInvokeMethod(params, initargs)) continue;
+            constructor.setAccessible(true);
+            return constructor.newInstance(initargs);
+        }
+        throw new NoSuchMethodException("Cannot find matching constructor");
+    }
+
+    /**
+     * invoke a restrict method named {@code methodName} of the given class {@code clazz} with this object {@code thiz} and arguments {@code args}
+     *
+     * @param clazz      the class call the method on (this parameter is required because this method cannot call inherit method)
+     * @param thiz       this object, which can be {@code null} if the target method is static
+     * @param methodName the method name
+     * @param args       arguments to call the method with name {@code methodName}
+     * @return the return value of the method
+     * @see Method#invoke(Object, Object...)
+     */
+    public static Object invoke(@NonNull Class<?> clazz, @Nullable Object thiz, @NonNull String methodName, Object... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+        var methods = getDeclaredMethods(clazz);
+        for (var method : methods) {
+            if (!method.getName().equals(methodName)) continue;
+            var params = method.getParameterTypes();
+            if (!Helper.checkArgsForInvokeMethod(params, args)) continue;
+            method.setAccessible(true);
+            return method.invoke(thiz, args);
+        }
+        throw new NoSuchMethodException("Cannot find matching method");
+    }
+
+    /**
+     * Sets the list of exemptions from hidden API access enforcement.
+     *
+     * @param signaturePrefixes A list of class signature prefixes. Each item in the list is a prefix match on the type
+     *                          signature of a blacklisted API. All matching APIs are treated as if they were on
+     *                          the whitelist: access permitted, and no logging..
+     * @return whether the operation is successful
+     */
+    public static boolean setHiddenApiExemptions(@NonNull String... signaturePrefixes) {
+        try {
+            var runtime = invoke(VMRuntime.class, null, "getRuntime");
+            invoke(VMRuntime.class, runtime, "setHiddenApiExemptions", (Object) signaturePrefixes);
+            return true;
+        } catch (ReflectiveOperationException e) {
+            Log.w(TAG, "setHiddenApiExemptions", e);
+            return false;
+        }
+    }
+
+    /**
+     * Adds the list of exemptions from hidden API access enforcement.
+     *
+     * @param signaturePrefixes A list of class signature prefixes. Each item in the list is a prefix match on the type
+     *                          signature of a blacklisted API. All matching APIs are treated as if they were on
+     *                          the whitelist: access permitted, and no logging..
+     * @return whether the operation is successful
+     */
+    public static boolean addHiddenApiExemptions(String... signaturePrefixes) {
+        Helper.signaturePrefixes.addAll(Arrays.asList(signaturePrefixes));
+        var strings = new String[Helper.signaturePrefixes.size()];
+        Helper.signaturePrefixes.toArray(strings);
+        return setHiddenApiExemptions(strings);
+    }
+
+    /**
+     * Clear the list of exemptions from hidden API access enforcement.
+     * Android runtime will cache access flags, so if a hidden API has been accessed unrestrictedly,
+     * running this method will not restore the restriction on it.
+     *
+     * @return whether the operation is successful
+     */
+    public static boolean clearHiddenApiExemptions() {
+        Helper.signaturePrefixes.clear();
+        return setHiddenApiExemptions();
+    }
+}
diff --git a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/stub/src/main/java/dalvik/system/VMRuntime.java b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/stub/src/main/java/stub/dalvik/system/VMRuntime.java
similarity index 89%
rename from build/android/incremental_install/third_party/AndroidHiddenApiBypass/stub/src/main/java/dalvik/system/VMRuntime.java
rename to build/android/incremental_install/third_party/AndroidHiddenApiBypass/stub/src/main/java/stub/dalvik/system/VMRuntime.java
index 87db1ec..8492a1a 100644
--- a/build/android/incremental_install/third_party/AndroidHiddenApiBypass/stub/src/main/java/dalvik/system/VMRuntime.java
+++ b/build/android/incremental_install/third_party/AndroidHiddenApiBypass/stub/src/main/java/stub/dalvik/system/VMRuntime.java
@@ -1,4 +1,4 @@
-package dalvik.system;
+package stub.dalvik.system;
 
 @SuppressWarnings("unused")
 public class VMRuntime {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
index 4a0531e..a1b98a5 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinator.java
@@ -38,7 +38,7 @@
      * @param collaborationService Used for checking the user is the owner of a group.
      */
     public TabGridDialogMenuCoordinator(
-            OnItemClickedCallback onItemClicked,
+            OnItemClickedCallback<Token> onItemClicked,
             Supplier<TabModel> tabModelSupplier,
             Supplier<Token> tabGroupIdSupplier,
             @Nullable TabGroupSyncService tabGroupSyncService,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinatorUnitTest.java
index 3e52cfb..28e8721 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMenuCoordinatorUnitTest.java
@@ -33,7 +33,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tasks.tab_management.TabGroupOverflowMenuCoordinator.OnItemClickedCallback;
+import org.chromium.chrome.browser.tasks.tab_management.TabOverflowMenuCoordinator.OnItemClickedCallback;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.collaboration.ServiceStatus;
 import org.chromium.components.data_sharing.member_role.MemberRole;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupOverflowMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupOverflowMenuCoordinator.java
index 08de99e..0d97bf8 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupOverflowMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupOverflowMenuCoordinator.java
@@ -4,204 +4,21 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
-import android.app.Activity;
-import android.content.ComponentCallbacks;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ListView;
-
-import androidx.annotation.DimenRes;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.IdRes;
-import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.StyleRes;
-import androidx.appcompat.content.res.AppCompatResources;
-import androidx.core.content.res.ResourcesCompat;
 
-import org.chromium.base.Callback;
-import org.chromium.base.LifetimeAssert;
 import org.chromium.base.Token;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.collaboration.CollaborationService;
-import org.chromium.components.data_sharing.member_role.MemberRole;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
-import org.chromium.ui.listmenu.BasicListMenu.ListMenuItemType;
-import org.chromium.ui.listmenu.ListMenuItemProperties;
-import org.chromium.ui.listmenu.ListMenuItemViewBinder;
-import org.chromium.ui.listmenu.ListSectionDividerViewBinder;
-import org.chromium.ui.modelutil.LayoutViewBuilder;
-import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
-import org.chromium.ui.modelutil.ModelListAdapter;
-import org.chromium.ui.widget.AnchoredPopupWindow;
-import org.chromium.ui.widget.AnchoredPopupWindow.HorizontalOrientation;
-import org.chromium.ui.widget.RectProvider;
-import org.chromium.ui.widget.ViewRectProvider;
 
 /**
  * A coordinator for the overflow menu in tab groups. This applies to both the TabGridDialog toolbar
  * and tab group cards on GTS. It is responsible for creating a list of menu items, setting up the
  * menu and displaying the menu.
  */
-public abstract class TabGroupOverflowMenuCoordinator {
-
-    /** Helper interface for handling menu item clicks for tab group related actions. */
-    @FunctionalInterface
-    public interface OnItemClickedCallback {
-        void onClick(
-                @IdRes int menuId, @Nullable Token tabGroupId, @Nullable String collaborationId);
-    }
-
-    private static class OverflowMenuHolder {
-        private static final int INVALID_ITEM_ID = -1;
-        private final Context mContext;
-        private final View mContentView;
-        private final ModelList mModelList = new ModelList();
-        private final ComponentCallbacks mComponentCallbacks;
-        private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
-        private AnchoredPopupWindow mMenuWindow;
-
-        OverflowMenuHolder(
-                RectProvider anchorViewRectProvider,
-                boolean horizontalOverlapAnchor,
-                boolean verticalOverlapAnchor,
-                @StyleRes int animStyle,
-                @HorizontalOrientation int horizontalOrientation,
-                @LayoutRes int menuLayout,
-                Drawable menuBackground,
-                OnItemClickedCallback onItemClickedCallback,
-                @Nullable Token tabGroupId,
-                @Nullable String collaborationId,
-                @DimenRes int popupWidthRes,
-                @Nullable Callback<OverflowMenuHolder> onDismiss,
-                Activity activity) {
-            mContext = activity;
-            mComponentCallbacks =
-                    new ComponentCallbacks() {
-                        @Override
-                        public void onConfigurationChanged(Configuration newConfig) {
-                            if (mMenuWindow == null || !mMenuWindow.isShowing()) return;
-                            mMenuWindow.dismiss();
-                        }
-
-                        @Override
-                        public void onLowMemory() {}
-                    };
-            mContext.registerComponentCallbacks(mComponentCallbacks);
-
-            mContentView = LayoutInflater.from(mContext).inflate(menuLayout, null);
-
-            ListView listView = mContentView.findViewById(R.id.tab_group_action_menu_list);
-            ModelListAdapter adapter =
-                    new ModelListAdapter(mModelList) {
-                        @Override
-                        public long getItemId(int position) {
-                            ListItem item = (ListItem) getItem(position);
-                            if (getItemViewType(position) == ListMenuItemType.MENU_ITEM) {
-                                return item.model.get(ListMenuItemProperties.MENU_ITEM_ID);
-                            } else {
-                                return INVALID_ITEM_ID;
-                            }
-                        }
-                    };
-            adapter.registerType(
-                    ListMenuItemType.MENU_ITEM,
-                    new LayoutViewBuilder(R.layout.list_menu_item),
-                    ListMenuItemViewBinder::binder);
-            adapter.registerType(
-                    ListMenuItemType.DIVIDER,
-                    new LayoutViewBuilder(R.layout.list_section_divider),
-                    ListSectionDividerViewBinder::bind);
-            listView.setAdapter(adapter);
-            listView.setOnItemClickListener(
-                    (p, v, pos, id) -> {
-                        onItemClickedCallback.onClick((int) id, tabGroupId, collaborationId);
-                        mMenuWindow.dismiss();
-                    });
-
-            View decorView = activity.getWindow().getDecorView();
-
-            mMenuWindow =
-                    new AnchoredPopupWindow(
-                            mContext,
-                            decorView,
-                            menuBackground,
-                            mContentView,
-                            anchorViewRectProvider);
-            mMenuWindow.setFocusable(true);
-            mMenuWindow.setHorizontalOverlapAnchor(horizontalOverlapAnchor);
-            mMenuWindow.setVerticalOverlapAnchor(verticalOverlapAnchor);
-            mMenuWindow.setPreferredHorizontalOrientation(horizontalOrientation);
-            // Override animation style or animate from anchor as default.
-            if (animStyle == ResourcesCompat.ID_NULL) {
-                mMenuWindow.setAnimationStyle(animStyle);
-            } else {
-                mMenuWindow.setAnimateFromAnchor(true);
-            }
-            int popupWidth = mContext.getResources().getDimensionPixelSize(popupWidthRes);
-            mMenuWindow.setMaxWidth(popupWidth);
-
-            // Resize if any new elements are added.
-            adapter.registerDataSetObserver(
-                    new DataSetObserver() {
-                        @Override
-                        public void onChanged() {
-                            resize();
-                        }
-                    });
-
-            // When the menu is dismissed, call destroy to unregister the orientation listener.
-            mMenuWindow.addOnDismissListener(
-                    () -> {
-                        if (onDismiss != null) {
-                            onDismiss.onResult(this);
-                        }
-                        destroy();
-                    });
-        }
-
-        ModelList getModelList() {
-            return mModelList;
-        }
-
-        View getContentView() {
-            return mContentView;
-        }
-
-        void show() {
-            mMenuWindow.show();
-        }
-
-        void resize() {
-            mMenuWindow.onRectChanged();
-        }
-
-        void dismiss() {
-            mMenuWindow.dismiss();
-        }
-
-        void destroy() {
-            mContext.unregisterComponentCallbacks(mComponentCallbacks);
-            // If mLifetimeAssert is GC'ed before this is called, it will throw an exception
-            // with a stack trace showing the stack during LifetimeAssert.create().
-            LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
-        }
-    }
-
-    protected final @NonNull CollaborationService mCollaborationService;
-
-    private final @LayoutRes int mMenuLayout;
-    private final OnItemClickedCallback mOnItemClickedCallback;
-    private final Supplier<TabModel> mTabModelSupplier;
-    private final @Nullable TabGroupSyncService mTabGroupSyncService;
-    private @Nullable OverflowMenuHolder mMenuHolder;
+public abstract class TabGroupOverflowMenuCoordinator extends TabOverflowMenuCoordinator<Token> {
 
     /**
      * @param menuLayout The menu layout to use.
@@ -211,167 +28,21 @@
      * @param collaborationService Used for checking the user is the owner of a group.
      */
     protected TabGroupOverflowMenuCoordinator(
-            @LayoutRes int menuLayout,
-            OnItemClickedCallback onItemClickedCallback,
+            int menuLayout,
+            OnItemClickedCallback<Token> onItemClickedCallback,
             Supplier<TabModel> tabModelSupplier,
             @Nullable TabGroupSyncService tabGroupSyncService,
             @NonNull CollaborationService collaborationService) {
-        mMenuLayout = menuLayout;
-        mOnItemClickedCallback = onItemClickedCallback;
-        mTabModelSupplier = tabModelSupplier;
-        mTabGroupSyncService = tabGroupSyncService;
-        assert collaborationService != null;
-        mCollaborationService = collaborationService;
+        super(
+                menuLayout,
+                onItemClickedCallback,
+                tabModelSupplier,
+                tabGroupSyncService,
+                collaborationService);
     }
 
-    /**
-     * Implemented in {@link TabGroupContextMenuCoordinator} to initialize the custom view for the
-     * tab group context menu. This method inflates necessary components, including the color picker
-     * and group title text.
-     *
-     * @param contentView The root view of the content where the custom view will be initialized.
-     * @param isIncognito Whether the current tab model is incognito or not.
-     */
-    protected void buildCustomView(View contentView, boolean isIncognito) {}
-
-    /**
-     * Concrete class required to define what the ModelList for the menu contains.
-     *
-     * @param itemList The {@link ModelList} to populate.
-     * @param isIncognito Whether the current tab model is incognito or not.
-     * @param isTabGroupSyncEnabled Whether to tab group sync is enabled.
-     * @param hasCollaborationData Whether the menu will call buildCollaborationMenuItems after.
-     */
-    protected abstract void buildMenuActionItems(
-            ModelList itemList,
-            boolean isIncognito,
-            boolean isTabGroupSyncEnabled,
-            boolean hasCollaborationData);
-
-    /**
-     * Concrete class required to define what to add for collaborations.
-     *
-     * @param itemList The {@link ModelList} to populate.
-     * @param memberRole The role of the current user in the group.
-     */
-    protected abstract void buildCollaborationMenuItems(
-            ModelList itemList, @MemberRole int memberRole);
-
-    /** Concrete class required to get a specific menu width for the menu pop up window. */
-    protected abstract @DimenRes int getMenuWidth();
-
-    /** Returns menu background drawable. */
-    public Drawable getMenuBackground(Context context, boolean isIncognito) {
-        final @DrawableRes int bgDrawableId =
-                isIncognito ? R.drawable.menu_bg_tinted_on_dark_bg : R.drawable.menu_bg_tinted;
-
-        return AppCompatResources.getDrawable(context, bgDrawableId);
-    }
-
-    // TODO(crbug.com/357878838): Pass the activity through constructor and setup test to test this
-    // method
-    /** See {@link #createAndShowMenu(RectProvider, Token, boolean, boolean, Integer, Activity)} */
-    protected void createAndShowMenu(
-            View anchorView, @Nullable Token tabGroupId, @NonNull Activity activity) {
-        createAndShowMenu(
-                new ViewRectProvider(anchorView),
-                tabGroupId,
-                /* horizontalOverlapAnchor= */ true,
-                /* verticalOverlapAnchor= */ true,
-                R.style.EndIconMenuAnim,
-                HorizontalOrientation.MAX_AVAILABLE_SPACE,
-                activity);
-    }
-
-    /**
-     * Creates a menu view and renders it within an {@link AnchoredPopupWindow}
-     *
-     * @param anchorViewRectProvider Rect provider for view to anchor the menu.
-     * @param tabGroupId ID of the tab group the menu needs to be shown for.
-     * @param horizontalOverlapAnchor If true, horizontally overlaps menu with the anchor view.
-     * @param verticalOverlapAnchor If true, vertically overlaps menu with the anchor view.
-     * @param animStyle Animation style to apply for menu show/hide.
-     * @param horizontalOrientation {@link HorizontalOrientation} to use for the menu position.
-     * @param activity Activity to get resources and decorView for menu.
-     */
-    protected void createAndShowMenu(
-            RectProvider anchorViewRectProvider,
-            @Nullable Token tabGroupId,
-            boolean horizontalOverlapAnchor,
-            boolean verticalOverlapAnchor,
-            @StyleRes int animStyle,
-            @HorizontalOrientation int horizontalOrientation,
-            @NonNull Activity activity) {
-        assert mMenuHolder == null;
-        boolean isIncognito = mTabModelSupplier.get().isIncognitoBranded();
-        @Nullable String collaborationId = getCollaborationIdOrNull(tabGroupId);
-        Drawable menuBackground = getMenuBackground(activity, isIncognito);
-        mMenuHolder =
-                new OverflowMenuHolder(
-                        anchorViewRectProvider,
-                        horizontalOverlapAnchor,
-                        verticalOverlapAnchor,
-                        animStyle,
-                        horizontalOrientation,
-                        mMenuLayout,
-                        menuBackground,
-                        mOnItemClickedCallback,
-                        tabGroupId,
-                        collaborationId,
-                        getMenuWidth(),
-                        this::onDismiss,
-                        activity);
-        buildCustomView(mMenuHolder.getContentView(), isIncognito);
-        configureMenuItems(mMenuHolder.getModelList(), isIncognito, collaborationId);
-        mMenuHolder.show();
-    }
-
-    /**
-     * Resizes the menu if the menu holder is available. This is used to adjust the menu size when
-     * adding collaboration items for {@link TabGroupContextMenuCoordinator}.
-     */
-    protected void resizeMenu() {
-        if (mMenuHolder != null) {
-            mMenuHolder.resize();
-        }
-    }
-
-    /**
-     * Dismisses the menu. No-op if the menu holder is {@code null}, and therefore the menu is not
-     * already showing.
-     */
-    public void dismiss() {
-        if (mMenuHolder != null) {
-            mMenuHolder.dismiss();
-        }
-    }
-
-    protected void onMenuDismissed() {}
-
-    protected @Nullable TabModel getTabModel() {
-        return mTabModelSupplier.get();
-    }
-
-    private void onDismiss(OverflowMenuHolder menuHolder) {
-        assert mMenuHolder == menuHolder;
-        mMenuHolder = null;
-        onMenuDismissed();
-    }
-
-    private void configureMenuItems(
-            ModelList modelList, boolean isIncognito, @Nullable String collaborationId) {
-        boolean hasCollaborationData =
-                TabShareUtils.isCollaborationIdValid(collaborationId)
-                        && mCollaborationService.getServiceStatus().isAllowedToJoin();
-        buildMenuActionItems(
-                modelList, isIncognito, mTabGroupSyncService != null, hasCollaborationData);
-        if (hasCollaborationData) {
-            buildCollaborationMenuItems(
-                    modelList, mCollaborationService.getCurrentUserRoleForGroup(collaborationId));
-        }
-    }
-
-    private @Nullable String getCollaborationIdOrNull(@Nullable Token tabGroupId) {
+    @Override
+    protected @Nullable String getCollaborationIdOrNull(Token tabGroupId) {
         return TabShareUtils.getCollaborationIdOrNull(tabGroupId, mTabGroupSyncService);
     }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
index 98e178b..60a8342 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinator.java
@@ -33,7 +33,7 @@
      * @param tabGroupSyncService Used to checking if a group is shared or synced.
      */
     public TabListGroupMenuCoordinator(
-            OnItemClickedCallback onItemClicked,
+            OnItemClickedCallback<Token> onItemClicked,
             Supplier<TabModel> tabModelSupplier,
             @Nullable TabGroupSyncService tabGroupSyncService,
             @NonNull CollaborationService collaborationService) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
index 2b6fcfc..8ce6b208 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListGroupMenuCoordinatorUnitTest.java
@@ -33,7 +33,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tabmodel.TabModel;
-import org.chromium.chrome.browser.tasks.tab_management.TabGroupOverflowMenuCoordinator.OnItemClickedCallback;
+import org.chromium.chrome.browser.tasks.tab_management.TabOverflowMenuCoordinator.OnItemClickedCallback;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.collaboration.ServiceStatus;
 import org.chromium.components.data_sharing.member_role.MemberRole;
@@ -66,7 +66,7 @@
     @Mock private TabGroupSyncService mTabGroupSyncService;
     @Mock private CollaborationService mCollaborationService;
     @Mock private ServiceStatus mServiceStatus;
-    @Mock private OnItemClickedCallback mOnItemClickedCallback;
+    @Mock private OnItemClickedCallback<Token> mOnItemClickedCallback;
 
     @Captor private ArgumentCaptor<ModelList> mModelListCaptor;
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
index 0807217b..c16db7c 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java
@@ -333,8 +333,8 @@
 
     private final ValueChangedCallback<TabGroupModelFilter> mOnTabGroupModelFilterChanged =
             new ValueChangedCallback<>(this::onTabGroupModelFilterChanged);
-    private final TabListGroupMenuCoordinator.OnItemClickedCallback mOnMenuItemClickedCallback =
-            this::onMenuItemClicked;
+    private final TabListGroupMenuCoordinator.OnItemClickedCallback<Token>
+            mOnMenuItemClickedCallback = this::onMenuItemClicked;
     private final Activity mActivity;
     private final TabListModel mModelList;
     private final @TabListMode int mMode;
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java
new file mode 100644
index 0000000..a7aa3fb8
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java
@@ -0,0 +1,384 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_management;
+
+import android.app.Activity;
+import android.content.ComponentCallbacks;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ListView;
+
+import androidx.annotation.DimenRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IdRes;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
+import androidx.appcompat.content.res.AppCompatResources;
+import androidx.core.content.res.ResourcesCompat;
+
+import org.chromium.base.Callback;
+import org.chromium.base.LifetimeAssert;
+import org.chromium.base.supplier.Supplier;
+import org.chromium.chrome.browser.compositor.overlays.strip.TabGroupContextMenuCoordinator;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.tab_ui.R;
+import org.chromium.components.collaboration.CollaborationService;
+import org.chromium.components.data_sharing.member_role.MemberRole;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
+import org.chromium.ui.listmenu.BasicListMenu.ListMenuItemType;
+import org.chromium.ui.listmenu.ListMenuItemProperties;
+import org.chromium.ui.listmenu.ListMenuItemViewBinder;
+import org.chromium.ui.listmenu.ListSectionDividerViewBinder;
+import org.chromium.ui.modelutil.LayoutViewBuilder;
+import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
+import org.chromium.ui.modelutil.ModelListAdapter;
+import org.chromium.ui.widget.AnchoredPopupWindow;
+import org.chromium.ui.widget.AnchoredPopupWindow.HorizontalOrientation;
+import org.chromium.ui.widget.RectProvider;
+import org.chromium.ui.widget.ViewRectProvider;
+
+/**
+ * A coordinator for the overflow menu for tabs and tab groups. This applies to both the
+ * TabGridDialog toolbar and tab group cards on GTS. It is responsible for creating a list of menu
+ * items, setting up the menu, and displaying the menu.
+ *
+ * @param <T> The type of the ID of the overflow menu's origin. For individual tabs, this is a tab
+ *     ID. For tab groups, it's the tab group ID.
+ */
+public abstract class TabOverflowMenuCoordinator<T> {
+
+    /**
+     * Helper interface for handling menu item clicks.
+     *
+     * @param <T> The type of the ID of the overflow menu's origin. For individual tabs, this is a
+     *     tab ID. For tab groups, it's the tab group ID.
+     */
+    @FunctionalInterface
+    public interface OnItemClickedCallback<T> {
+        void onClick(@IdRes int menuId, T id, @Nullable String collaborationId);
+    }
+
+    private static class OverflowMenuHolder<T> {
+        private static final int INVALID_ITEM_ID = -1;
+        private final Context mContext;
+        private final View mContentView;
+        private final ModelList mModelList = new ModelList();
+        private final ComponentCallbacks mComponentCallbacks;
+        private final LifetimeAssert mLifetimeAssert = LifetimeAssert.create(this);
+        private AnchoredPopupWindow mMenuWindow;
+
+        OverflowMenuHolder(
+                RectProvider anchorViewRectProvider,
+                boolean horizontalOverlapAnchor,
+                boolean verticalOverlapAnchor,
+                @StyleRes int animStyle,
+                @HorizontalOrientation int horizontalOrientation,
+                @LayoutRes int menuLayout,
+                Drawable menuBackground,
+                OnItemClickedCallback<T> onItemClickedCallback,
+                T id,
+                @Nullable String collaborationId,
+                @DimenRes int popupWidthRes,
+                @Nullable Callback<OverflowMenuHolder<T>> onDismiss,
+                Activity activity) {
+            mContext = activity;
+            mComponentCallbacks =
+                    new ComponentCallbacks() {
+                        @Override
+                        public void onConfigurationChanged(Configuration newConfig) {
+                            if (mMenuWindow == null || !mMenuWindow.isShowing()) return;
+                            mMenuWindow.dismiss();
+                        }
+
+                        @Override
+                        public void onLowMemory() {}
+                    };
+            mContext.registerComponentCallbacks(mComponentCallbacks);
+
+            mContentView = LayoutInflater.from(mContext).inflate(menuLayout, null);
+
+            ListView listView = mContentView.findViewById(R.id.tab_group_action_menu_list);
+            ModelListAdapter adapter =
+                    new ModelListAdapter(mModelList) {
+                        @Override
+                        public long getItemId(int position) {
+                            ListItem item = (ListItem) getItem(position);
+                            if (getItemViewType(position) == ListMenuItemType.MENU_ITEM) {
+                                return item.model.get(ListMenuItemProperties.MENU_ITEM_ID);
+                            } else {
+                                return INVALID_ITEM_ID;
+                            }
+                        }
+                    };
+            adapter.registerType(
+                    ListMenuItemType.MENU_ITEM,
+                    new LayoutViewBuilder(R.layout.list_menu_item),
+                    ListMenuItemViewBinder::binder);
+            adapter.registerType(
+                    ListMenuItemType.DIVIDER,
+                    new LayoutViewBuilder(R.layout.list_section_divider),
+                    ListSectionDividerViewBinder::bind);
+            listView.setAdapter(adapter);
+            listView.setOnItemClickListener(
+                    (p, v, pos, menuId) -> {
+                        onItemClickedCallback.onClick((int) menuId, id, collaborationId);
+                        mMenuWindow.dismiss();
+                    });
+
+            View decorView = activity.getWindow().getDecorView();
+
+            mMenuWindow =
+                    new AnchoredPopupWindow(
+                            mContext,
+                            decorView,
+                            menuBackground,
+                            mContentView,
+                            anchorViewRectProvider);
+            mMenuWindow.setFocusable(true);
+            mMenuWindow.setHorizontalOverlapAnchor(horizontalOverlapAnchor);
+            mMenuWindow.setVerticalOverlapAnchor(verticalOverlapAnchor);
+            mMenuWindow.setPreferredHorizontalOrientation(horizontalOrientation);
+            // Override animation style or animate from anchor as default.
+            if (animStyle == ResourcesCompat.ID_NULL) {
+                mMenuWindow.setAnimationStyle(animStyle);
+            } else {
+                mMenuWindow.setAnimateFromAnchor(true);
+            }
+            int popupWidth = mContext.getResources().getDimensionPixelSize(popupWidthRes);
+            mMenuWindow.setMaxWidth(popupWidth);
+
+            // Resize if any new elements are added.
+            adapter.registerDataSetObserver(
+                    new DataSetObserver() {
+                        @Override
+                        public void onChanged() {
+                            resize();
+                        }
+                    });
+
+            // When the menu is dismissed, call destroy to unregister the orientation listener.
+            mMenuWindow.addOnDismissListener(
+                    () -> {
+                        if (onDismiss != null) {
+                            onDismiss.onResult(this);
+                        }
+                        destroy();
+                    });
+        }
+
+        ModelList getModelList() {
+            return mModelList;
+        }
+
+        View getContentView() {
+            return mContentView;
+        }
+
+        void show() {
+            mMenuWindow.show();
+        }
+
+        void resize() {
+            mMenuWindow.onRectChanged();
+        }
+
+        void dismiss() {
+            mMenuWindow.dismiss();
+        }
+
+        void destroy() {
+            mContext.unregisterComponentCallbacks(mComponentCallbacks);
+            // If mLifetimeAssert is GC'ed before this is called, it will throw an exception
+            // with a stack trace showing the stack during LifetimeAssert.create().
+            LifetimeAssert.setSafeToGc(mLifetimeAssert, true);
+        }
+    }
+
+    protected final @NonNull CollaborationService mCollaborationService;
+
+    private final @LayoutRes int mMenuLayout;
+    private final OnItemClickedCallback<T> mOnItemClickedCallback;
+    private final Supplier<TabModel> mTabModelSupplier;
+    protected final @Nullable TabGroupSyncService mTabGroupSyncService;
+    private @Nullable OverflowMenuHolder<T> mMenuHolder;
+
+    /**
+     * @param menuLayout The menu layout to use.
+     * @param onItemClickedCallback A callback for listening to clicks.
+     * @param tabModelSupplier The supplier of the tab model.
+     * @param tabGroupSyncService Used to checking if a group is shared or synced.
+     * @param collaborationService Used for checking the user is the owner of a group.
+     */
+    protected TabOverflowMenuCoordinator(
+            @LayoutRes int menuLayout,
+            OnItemClickedCallback<T> onItemClickedCallback,
+            Supplier<TabModel> tabModelSupplier,
+            @Nullable TabGroupSyncService tabGroupSyncService,
+            @NonNull CollaborationService collaborationService) {
+        mMenuLayout = menuLayout;
+        mOnItemClickedCallback = onItemClickedCallback;
+        mTabModelSupplier = tabModelSupplier;
+        mTabGroupSyncService = tabGroupSyncService;
+        assert collaborationService != null;
+        mCollaborationService = collaborationService;
+    }
+
+    /**
+     * Implemented in {@link TabGroupContextMenuCoordinator} to initialize the custom view for the
+     * tab group context menu. This method inflates necessary components, including the color picker
+     * and group title text.
+     *
+     * @param contentView The root view of the content where the custom view will be initialized.
+     * @param isIncognito Whether the current tab model is incognito or not.
+     */
+    protected void buildCustomView(View contentView, boolean isIncognito) {}
+
+    /**
+     * Concrete class required to define what the ModelList for the menu contains.
+     *
+     * @param itemList The {@link ModelList} to populate.
+     * @param isIncognito Whether the current tab model is incognito or not.
+     * @param isTabGroupSyncEnabled Whether to tab group sync is enabled.
+     * @param hasCollaborationData Whether the menu will call buildCollaborationMenuItems after.
+     */
+    protected abstract void buildMenuActionItems(
+            ModelList itemList,
+            boolean isIncognito,
+            boolean isTabGroupSyncEnabled,
+            boolean hasCollaborationData);
+
+    /**
+     * Concrete class required to define what to add for collaborations.
+     *
+     * @param itemList The {@link ModelList} to populate.
+     * @param memberRole The role of the current user in the group.
+     */
+    protected abstract void buildCollaborationMenuItems(
+            ModelList itemList, @MemberRole int memberRole);
+
+    /** Concrete class required to get a specific menu width for the menu pop up window. */
+    protected abstract @DimenRes int getMenuWidth();
+
+    /** Returns the collaborationId relevant for the object with ID {@code id} */
+    protected abstract @Nullable String getCollaborationIdOrNull(T id);
+
+    /** Returns menu background drawable. */
+    public Drawable getMenuBackground(Context context, boolean isIncognito) {
+        final @DrawableRes int bgDrawableId =
+                isIncognito ? R.drawable.menu_bg_tinted_on_dark_bg : R.drawable.menu_bg_tinted;
+
+        return AppCompatResources.getDrawable(context, bgDrawableId);
+    }
+
+    // TODO(crbug.com/357878838): Pass the activity through constructor and setup test to test this
+    // method
+    /**
+     * See {@link #createAndShowMenu(RectProvider, Object, boolean, boolean, int, int, Activity)}}
+     */
+    protected void createAndShowMenu(View anchorView, T id, @NonNull Activity activity) {
+        createAndShowMenu(
+                new ViewRectProvider(anchorView),
+                id,
+                /* horizontalOverlapAnchor= */ true,
+                /* verticalOverlapAnchor= */ true,
+                R.style.EndIconMenuAnim,
+                HorizontalOrientation.MAX_AVAILABLE_SPACE,
+                activity);
+    }
+
+    /**
+     * Creates a menu view and renders it within an {@link AnchoredPopupWindow}
+     *
+     * @param anchorViewRectProvider Rect provider for view to anchor the menu.
+     * @param id ID of the object the menu needs to be shown for.
+     * @param horizontalOverlapAnchor If true, horizontally overlaps menu with the anchor view.
+     * @param verticalOverlapAnchor If true, vertically overlaps menu with the anchor view.
+     * @param animStyle Animation style to apply for menu show/hide.
+     * @param horizontalOrientation {@link HorizontalOrientation} to use for the menu position.
+     * @param activity Activity to get resources and decorView for menu.
+     */
+    protected void createAndShowMenu(
+            RectProvider anchorViewRectProvider,
+            T id,
+            boolean horizontalOverlapAnchor,
+            boolean verticalOverlapAnchor,
+            @StyleRes int animStyle,
+            @HorizontalOrientation int horizontalOrientation,
+            @NonNull Activity activity) {
+        assert mMenuHolder == null;
+        boolean isIncognito = mTabModelSupplier.get().isIncognitoBranded();
+        @Nullable String collaborationId = getCollaborationIdOrNull(id);
+        Drawable menuBackground = getMenuBackground(activity, isIncognito);
+        mMenuHolder =
+                new OverflowMenuHolder<>(
+                        anchorViewRectProvider,
+                        horizontalOverlapAnchor,
+                        verticalOverlapAnchor,
+                        animStyle,
+                        horizontalOrientation,
+                        mMenuLayout,
+                        menuBackground,
+                        mOnItemClickedCallback,
+                        id,
+                        collaborationId,
+                        getMenuWidth(),
+                        this::onDismiss,
+                        activity);
+        buildCustomView(mMenuHolder.getContentView(), isIncognito);
+        configureMenuItems(mMenuHolder.getModelList(), isIncognito, collaborationId);
+        mMenuHolder.show();
+    }
+
+    /**
+     * Resizes the menu if the menu holder is available. This is used to adjust the menu size when
+     * adding collaboration items for {@link TabGroupContextMenuCoordinator}.
+     */
+    protected void resizeMenu() {
+        if (mMenuHolder != null) {
+            mMenuHolder.resize();
+        }
+    }
+
+    /**
+     * Dismisses the menu. No-op if the menu holder is {@code null}, and therefore the menu is not
+     * already showing.
+     */
+    public void dismiss() {
+        if (mMenuHolder != null) {
+            mMenuHolder.dismiss();
+        }
+    }
+
+    protected void onMenuDismissed() {}
+
+    protected @Nullable TabModel getTabModel() {
+        return mTabModelSupplier.get();
+    }
+
+    private void onDismiss(OverflowMenuHolder<T> menuHolder) {
+        assert mMenuHolder == menuHolder;
+        mMenuHolder = null;
+        onMenuDismissed();
+    }
+
+    private void configureMenuItems(
+            ModelList modelList, boolean isIncognito, @Nullable String collaborationId) {
+        boolean hasCollaborationData =
+                TabShareUtils.isCollaborationIdValid(collaborationId)
+                        && mCollaborationService.getServiceStatus().isAllowedToJoin();
+        buildMenuActionItems(
+                modelList, isIncognito, mTabGroupSyncService != null, hasCollaborationData);
+        if (hasCollaborationData) {
+            buildCollaborationMenuItems(
+                    modelList, mCollaborationService.getCurrentUserRoleForGroup(collaborationId));
+        }
+    }
+}
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni
index e6dfbab..5b04e11 100644
--- a/chrome/android/features/tab_ui/tab_management_java_sources.gni
+++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -165,6 +165,7 @@
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabManagementDelegateProvider.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabObjectLabeller.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabObjectNotificationUpdater.java",
+  "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabOverflowMenuCoordinator.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabProperties.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabShareUtils.java",
   "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabStripSnapshotter.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 273b73c..c6e1dd9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -2039,7 +2039,7 @@
         RectProvider anchorRectProvider = new RectProvider();
         getAnchorRect(groupTitle, anchorRectProvider);
         StripLayoutUtils.performHapticFeedback(mToolbarContainerView);
-        mTabGroupContextMenuCoordinator.showMenu(anchorRectProvider, groupTitle.getRootId());
+        mTabGroupContextMenuCoordinator.showMenu(anchorRectProvider, groupTitle.getTabGroupId());
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
index 955fdfd..61db8ef 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -85,7 +85,7 @@
     private WindowAndroid mWindowAndroid;
     private boolean mIsMenuShowing;
     private KeyboardVisibilityDelegate.KeyboardVisibilityListener mKeyboardVisibilityListener;
-    private CollaborationService mCollaborationService;
+    protected CollaborationService mCollaborationService;
     private final TabGroupModelFilterObserver mTabGroupModelFilterObserver =
             new TabGroupModelFilterObserver() {
                 @Override
@@ -170,7 +170,7 @@
     }
 
     @VisibleForTesting
-    static OnItemClickedCallback getMenuItemClickedCallback(
+    static OnItemClickedCallback<Token> getMenuItemClickedCallback(
             Activity activity,
             TabGroupModelFilter tabGroupModelFilter,
             ActionConfirmationManager actionConfirmationManager,
@@ -254,11 +254,10 @@
      *
      * @param anchorViewRectProvider The context menu's anchor view rect provider. These are screen
      *     coordinates.
-     * @param rootId The root id of the interacting tab group.
+     * @param tabGroupId The tab group ID of the interacting tab group.
      */
-    protected void showMenu(RectProvider anchorViewRectProvider, int rootId) {
-        mGroupRootId = rootId;
-        Token tabGroupId = mTabGroupModelFilter.getStableIdFromRootId(rootId);
+    protected void showMenu(RectProvider anchorViewRectProvider, Token tabGroupId) {
+        mGroupRootId = mTabGroupModelFilter.getRootIdFromStableId(tabGroupId);
         createAndShowMenu(
                 anchorViewRectProvider,
                 tabGroupId,
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index 26d959f..00a6077 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -1914,7 +1914,7 @@
                 ArgumentCaptor.forClass(RectProvider.class);
         // Verify tab group context menu is showing.
         verify(mTabGroupContextMenuCoordinator)
-                .showMenu(rectProviderArgumentCaptor.capture(), anyInt());
+                .showMenu(rectProviderArgumentCaptor.capture(), any());
         // Verify anchorView coordinates.
         StripLayoutView view = mStripLayoutHelper.getViewAtPositionX(10f, true);
         assertTrue(view instanceof StripLayoutGroupTitle);
@@ -1971,7 +1971,7 @@
         // Long press on group title and verify drag with context menu does not start a scroll.
         // Long press on group title.
         mStripLayoutHelper.onLongPress(TIMESTAMP, 10f, 0f);
-        verify(mTabGroupContextMenuCoordinator).showMenu(any(), anyInt());
+        verify(mTabGroupContextMenuCoordinator).showMenu(any(), any());
         when(mTabGroupContextMenuCoordinator.isMenuShowing()).thenReturn(true);
         mStripLayoutHelper.drag(TIMESTAMP, /* x= */ 60f, /* y= */ 10f, /* deltaX= */ 50f);
         verify(mTabGroupContextMenuCoordinator).dismiss();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
index 3c0ff92..bafc226 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinatorUnitTest.java
@@ -49,7 +49,7 @@
 import org.chromium.chrome.browser.tabmodel.TabUngrouper;
 import org.chromium.chrome.browser.tasks.tab_management.ActionConfirmationManager;
 import org.chromium.chrome.browser.tasks.tab_management.ColorPickerCoordinator;
-import org.chromium.chrome.browser.tasks.tab_management.TabGroupOverflowMenuCoordinator.OnItemClickedCallback;
+import org.chromium.chrome.browser.tasks.tab_management.TabOverflowMenuCoordinator.OnItemClickedCallback;
 import org.chromium.chrome.test.util.browser.tabmodel.MockTabModel;
 import org.chromium.components.collaboration.CollaborationService;
 import org.chromium.components.collaboration.ServiceStatus;
@@ -82,7 +82,7 @@
             new ActivityScenarioRule<>(TestActivity.class);
 
     private TabGroupContextMenuCoordinator mTabGroupContextMenuCoordinator;
-    private OnItemClickedCallback mOnItemClickedCallback;
+    private OnItemClickedCallback<Token> mOnItemClickedCallback;
     private MockTabModel mTabModel;
     @Mock private TabRemover mTabRemover;
     @Mock private TabUngrouper mTabUngrouper;
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f2b09c7..d8ece39 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -11688,6 +11688,15 @@
                                     "MaliciousApkDownloadCheck")},
 #endif  // BUILDFLAG(IS_ANDROID)
 
+#if BUILDFLAG(IS_ANDROID)
+    {"disable-facilitated-payments-merchant-allowlist",
+     flag_descriptions::kDisableFacilitatedPaymentsMerchantAllowlistName,
+     flag_descriptions::kDisableFacilitatedPaymentsMerchantAllowlistDescription,
+     kOsAndroid,
+     FEATURE_VALUE_TYPE(
+         payments::facilitated::kDisableFacilitatedPaymentsMerchantAllowlist)},
+#endif  // BUILDFLAF(IS_ANDROID)
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
index fcc96601..5814a89 100644
--- a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
+++ b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
@@ -257,9 +257,9 @@
     extension_service_->OnExtensionInstalled(
         extension, syncer::StringOrdinal::CreateInitialOrdinal(),
         extensions::kInstallFlagInstallImmediately);
-    auto installer = extensions::CrxInstaller::CreateSilent(extension_service_);
     extensions::InstallTracker::Get(browser_context_)
-        ->OnFinishCrxInstall(*installer, extension->id(), true);
+        ->OnFinishCrxInstall(base::FilePath(), extension->id(), extension,
+                             true);
     return true;
   }
 
@@ -276,9 +276,8 @@
 
     pending_crx_files_.erase(extension_id);
     pending_update_urls_.erase(extension_id);
-    auto installer = extensions::CrxInstaller::CreateSilent(extension_service_);
     extensions::InstallTracker::Get(browser_context_)
-        ->OnFinishCrxInstall(*installer, extension_id, false);
+        ->OnFinishCrxInstall(base::FilePath(), extension_id, nullptr, false);
     extension_service_->pending_extension_manager()->Remove(extension_id);
     return true;
   }
@@ -301,9 +300,8 @@
     }
 
     pending_crx_files_.insert(info.extension_id);
-    auto installer = extensions::CrxInstaller::CreateSilent(extension_service_);
     extensions::InstallTracker::Get(browser_context_)
-        ->OnBeginCrxInstall(*installer, info.extension_id);
+        ->OnBeginCrxInstall(info.extension_id);
     return true;
   }
   bool OnExternalExtensionUpdateUrlFound(
@@ -323,9 +321,8 @@
     }
 
     pending_update_urls_.insert(info.extension_id);
-    auto installer = extensions::CrxInstaller::CreateSilent(extension_service_);
     extensions::InstallTracker::Get(browser_context_)
-        ->OnBeginCrxInstall(*installer, info.extension_id);
+        ->OnBeginCrxInstall(info.extension_id);
     return true;
   }
   void OnExternalProviderReady(
diff --git a/chrome/browser/ash/app_mode/test/required_platform_version_browsertest.cc b/chrome/browser/ash/app_mode/test/required_platform_version_browsertest.cc
index f808ec2..4f77b19 100644
--- a/chrome/browser/ash/app_mode/test/required_platform_version_browsertest.cc
+++ b/chrome/browser/ash/app_mode/test/required_platform_version_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ash/app_mode/test/kiosk_mixin.h"
 #include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_web_app_install_util.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
diff --git a/chrome/browser/ash/arc/extensions/arc_support_message_host.cc b/chrome/browser/ash/arc/extensions/arc_support_message_host.cc
index 03205b3..f73c59f 100644
--- a/chrome/browser/ash/arc/extensions/arc_support_message_host.cc
+++ b/chrome/browser/ash/arc/extensions/arc_support_message_host.cc
@@ -69,12 +69,13 @@
   // which on Chrome OS runs in the browser process.
   // Therefore this use of JSONReader does not violate
   // https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/rule-of-2.md.
-  std::optional<base::Value> message = base::JSONReader::Read(message_string);
-  if (!message || !message->is_dict()) {
+  std::optional<base::Value::Dict> message =
+      base::JSONReader::ReadDict(message_string);
+  if (!message) {
     NOTREACHED();
   }
 
-  observer_->OnMessage(message->GetDict());
+  observer_->OnMessage(*message);
 }
 
 scoped_refptr<base::SingleThreadTaskRunner> ArcSupportMessageHost::task_runner()
diff --git a/chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.cc b/chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.cc
index 46c8277b..18296ac 100644
--- a/chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.cc
+++ b/chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.cc
@@ -618,11 +618,11 @@
 
 bool ArcTracingGraphicsModel::LoadFromJson(const std::string& json_data) {
   Reset();
-  std::optional<base::Value> root = base::JSONReader::Read(json_data);
-  if (!root || !root->is_dict()) {
+  std::optional<base::Value::Dict> root = base::JSONReader::ReadDict(json_data);
+  if (!root) {
     return false;
   }
-  return LoadFromValue(root->GetDict());
+  return LoadFromValue(*root);
 }
 
 bool ArcTracingGraphicsModel::LoadFromValue(const base::Value::Dict& root) {
diff --git a/chrome/browser/ash/extensions/external_cache_impl.cc b/chrome/browser/ash/extensions/external_cache_impl.cc
index 5fe9c5d..73b2aae 100644
--- a/chrome/browser/ash/extensions/external_cache_impl.cc
+++ b/chrome/browser/ash/extensions/external_cache_impl.cc
@@ -79,8 +79,9 @@
 
   // extensions::InstallObserver:
   void OnFinishCrxInstall(content::BrowserContext* context,
-                          const extensions::CrxInstaller& installer,
+                          const base::FilePath& source_file,
                           const std::string& extension_id,
+                          const extensions::Extension* extension,
                           bool success) override;
 
   bool IsAnyObservedProfileUsingTracker(
@@ -157,11 +158,12 @@
 
 void ExternalCacheImpl::AnyInstallFailureObserver::OnFinishCrxInstall(
     content::BrowserContext* context,
-    const extensions::CrxInstaller& installer,
+    const base::FilePath& source_file,
     const std::string& extension_id,
+    const extensions::Extension* extension,
     bool success) {
   if (!success) {
-    owner_->OnCrxInstallFailure(context, installer);
+    owner_->OnCrxInstallFailure(context, source_file);
   }
 }
 
@@ -307,10 +309,9 @@
   }
 }
 
-void ExternalCacheImpl::OnCrxInstallFailure(
-    content::BrowserContext* context,
-    const extensions::CrxInstaller& installer) {
-  OnDamagedFileDetected(installer.source_file());
+void ExternalCacheImpl::OnCrxInstallFailure(content::BrowserContext* context,
+                                            const base::FilePath& source_file) {
+  OnDamagedFileDetected(source_file);
 }
 
 void ExternalCacheImpl::OnExtensionDownloadFailed(
diff --git a/chrome/browser/ash/extensions/external_cache_impl.h b/chrome/browser/ash/extensions/external_cache_impl.h
index 2d1743d18b..d36566e 100644
--- a/chrome/browser/ash/extensions/external_cache_impl.h
+++ b/chrome/browser/ash/extensions/external_cache_impl.h
@@ -28,7 +28,6 @@
 }
 
 namespace extensions {
-class CrxInstaller;
 class ExtensionDownloader;
 }
 
@@ -142,7 +141,7 @@
   // by a call to UpdateExtensionLoader().
   void RemoveCachedExtension(const extensions::ExtensionId& id);
   void OnCrxInstallFailure(content::BrowserContext* context,
-                           const extensions::CrxInstaller& installer);
+                           const base::FilePath& source_file);
 
   std::unique_ptr<AnyInstallFailureObserver> any_install_failure_observer_;
 
diff --git a/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc b/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc
index f0645070..f37e9b0f 100644
--- a/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc
+++ b/chrome/browser/ash/lobster/lobster_bubble_coordinator.cc
@@ -27,7 +27,8 @@
 void LobsterBubbleCoordinator::LoadUI(Profile* profile,
                                       std::optional<std::string_view> query,
                                       LobsterMode mode,
-                                      const gfx::Rect& caret_bounds) {
+                                      const gfx::Rect& caret_bounds,
+                                      bool should_show_feedback_ui) {
   if (IsShowingUI()) {
     contents_wrapper_->CloseUI();
   }
@@ -46,8 +47,7 @@
 
   url = net::AppendOrReplaceQueryParameter(
       url, kLobsterFeedbackEnabledParamKey,
-      base::FeatureList::IsEnabled(ash::features::kLobsterFeedback) ? "true"
-                                                                    : "false");
+      should_show_feedback_ui ? "true" : "false");
 
   contents_wrapper_ = std::make_unique<WebUIContentsWrapperT<MakoUntrustedUI>>(
       url, profile, IDS_ACCNAME_ORCA,
diff --git a/chrome/browser/ash/lobster/lobster_bubble_coordinator.h b/chrome/browser/ash/lobster/lobster_bubble_coordinator.h
index 3bf3ef8..62326f7 100644
--- a/chrome/browser/ash/lobster/lobster_bubble_coordinator.h
+++ b/chrome/browser/ash/lobster/lobster_bubble_coordinator.h
@@ -29,7 +29,8 @@
   void LoadUI(Profile* profile,
               std::optional<std::string_view> query,
               LobsterMode mode,
-              const gfx::Rect& caret_bounds);
+              const gfx::Rect& caret_bounds,
+              bool should_show_feedback_ui);
   void ShowUI();
   void CloseUI();
 
diff --git a/chrome/browser/ash/lobster/lobster_service.cc b/chrome/browser/ash/lobster/lobster_service.cc
index 05e7530..979bc35 100644
--- a/chrome/browser/ash/lobster/lobster_service.cc
+++ b/chrome/browser/ash/lobster/lobster_service.cc
@@ -8,8 +8,10 @@
 #include <string>
 #include <utility>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
+#include "ash/public/cpp/lobster/lobster_enums.h"
 #include "ash/public/cpp/lobster/lobster_session.h"
 #include "base/check_deref.h"
 #include "base/command_line.h"
@@ -113,7 +115,14 @@
 void LobsterService::LoadUI(std::optional<std::string> query,
                             ash::LobsterMode mode,
                             const gfx::Rect& caret_bounds) {
-  bubble_coordinator_.LoadUI(profile_, query, mode, caret_bounds);
+  bubble_coordinator_.LoadUI(
+      profile_, query, mode, caret_bounds,
+      /*should_show_feedback=*/
+      profile_->GetPrefs()->GetInteger(
+          ash::prefs::kLobsterEnterprisePolicySettings) ==
+              base::to_underlying(ash::LobsterEnterprisePolicyValue::
+                                      kAllowedWithModelImprovement) &&
+          base::FeatureList::IsEnabled(ash::features::kLobsterFeedback));
 }
 
 void LobsterService::ShowUI() {
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc b/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
index 414bfe0a..b55ce90 100644
--- a/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
+++ b/chrome/browser/ash/login/app_mode/test/kiosk_base_test.cc
@@ -87,18 +87,18 @@
                                       int current_width) {
   std::string message;
   while (message_queue->WaitForMessage(&message)) {
-    std::optional<base::Value> message_value = base::JSONReader::Read(message);
-    if (!message_value || !message_value->is_dict()) {
+    std::optional<base::Value::Dict> message_value =
+        base::JSONReader::ReadDict(message);
+    if (!message_value) {
       continue;
     }
 
-    const base::Value::Dict& message_dict = message_value->GetDict();
-    const std::string* name = message_dict.FindString("name");
+    const std::string* name = message_value->FindString("name");
     if (!name || *name != kSizeChangedMessage) {
       continue;
     }
 
-    const std::optional<int> data = message_dict.FindInt("data");
+    const std::optional<int> data = message_value->FindInt("data");
     if (!data || data == current_width) {
       continue;
     }
diff --git a/chrome/browser/ash/login/login_browsertest.cc b/chrome/browser/ash/login/login_browsertest.cc
index f00b241..8e9ace32 100644
--- a/chrome/browser/ash/login/login_browsertest.cc
+++ b/chrome/browser/ash/login/login_browsertest.cc
@@ -55,6 +55,7 @@
 #include "components/user_manager/user_names.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_utils.h"
+#include "extensions/browser/extension_system.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_id.h"
 #include "net/dns/mock_host_resolver.h"
diff --git a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc
index ee1e5a8..57c1f32a 100644
--- a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc
@@ -344,19 +344,18 @@
             return;
           }
 
-          std::optional<base::Value> request_body =
-              base::JSONReader::Read(request.request_body->elements()
-                                         ->at(0)
-                                         .As<network::DataElementBytes>()
-                                         .AsStringPiece());
-          if (!request_body || !request_body->is_dict()) {
+          std::optional<base::Value::Dict> request_body =
+              base::JSONReader::ReadDict(request.request_body->elements()
+                                             ->at(0)
+                                             .As<network::DataElementBytes>()
+                                             .AsStringPiece());
+          if (!request_body) {
             SimulateBadRequest(kGetChallengeDataUrl);
             return;
           }
 
-          const base::Value::Dict& request_dict = request_body->GetDict();
           const std::string* target_device_type =
-              request_dict.FindString(kTargetDeviceType);
+              request_body->FindString(kTargetDeviceType);
           if (!target_device_type || *target_device_type != kChromeOS) {
             SimulateBadRequest(kGetChallengeDataUrl);
             return;
@@ -1012,19 +1011,18 @@
           return;
         }
 
-        std::optional<base::Value> request_body =
-            base::JSONReader::Read(request.request_body->elements()
-                                       ->at(0)
-                                       .As<network::DataElementBytes>()
-                                       .AsStringPiece());
-        if (!request_body || !request_body->is_dict()) {
+        std::optional<base::Value::Dict> request_body =
+            base::JSONReader::ReadDict(request.request_body->elements()
+                                           ->at(0)
+                                           .As<network::DataElementBytes>()
+                                           .AsStringPiece());
+        if (!request_body) {
           SimulateBadRequest(kStartSessionUrl);
           return;
         }
 
-        const base::Value::Dict& request_dict = request_body->GetDict();
         const base::Value::Dict* target_device_info =
-            request_dict.FindDict(kTargetDeviceInfoKey);
+            request_body->FindDict(kTargetDeviceInfoKey);
         if (!target_device_info) {
           SimulateBadRequest(kStartSessionUrl);
           return;
diff --git a/chrome/browser/ash/login/saml/fake_saml_idp_mixin.cc b/chrome/browser/ash/login/saml/fake_saml_idp_mixin.cc
index 261cc9e..04f21dd0 100644
--- a/chrome/browser/ash/login/saml/fake_saml_idp_mixin.cc
+++ b/chrome/browser/ash/login/saml/fake_saml_idp_mixin.cc
@@ -450,18 +450,18 @@
 
 void FakeSamlIdpMixin::SaveChallengeResponse(const std::string& response) {
   EXPECT_EQ(challenge_response_, std::nullopt);
-  auto parsed_value = base::JSONReader::Read(
+  auto parsed_value = base::JSONReader::ReadDict(
       response, base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
 
-  if (!parsed_value || !parsed_value->is_dict()) {
+  if (!parsed_value) {
     // Most likely given a V1, no need to try parsing the values out.
     challenge_response_ = response;
     return;
   }
 
   const std::string* challenge_response_string =
-      parsed_value->GetDict().FindString("challengeResponse");
-  const std::string* error_string = parsed_value->GetDict().FindString("error");
+      parsed_value->FindString("challengeResponse");
+  const std::string* error_string = parsed_value->FindString("error");
 
   // Only one of those values should be set.
   EXPECT_NE(!!challenge_response_string, !!error_string);
diff --git a/chrome/browser/ash/login/screens/locale_switch_screen.cc b/chrome/browser/ash/login/screens/locale_switch_screen.cc
index 37bd07d..0de5751 100644
--- a/chrome/browser/ash/login/screens/locale_switch_screen.cc
+++ b/chrome/browser/ash/login/screens/locale_switch_screen.cc
@@ -74,13 +74,14 @@
       response_body = std::move(*body);
     }
 
-    std::optional<base::Value> value = base::JSONReader::Read(response_body);
-    if (!value || !value->is_dict()) {
+    std::optional<base::Value::Dict> value =
+        base::JSONReader::ReadDict(response_body);
+    if (!value) {
       LOG(ERROR) << __func__ << " Bad response format";
       std::move(failure_callback_).Run();
       return;
     }
-    base::Value::List* locales_list = value->GetDict().FindList("locales");
+    base::Value::List* locales_list = value->FindList("locales");
     if (!locales_list) {
       LOG(ERROR) << __func__ << " No locales available";
       std::move(failure_callback_).Run();
diff --git a/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc b/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc
index 1d5acb96..16b26462 100644
--- a/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc
+++ b/chrome/browser/ash/net/rollback_network_config/rollback_network_config.cc
@@ -366,18 +366,16 @@
 
 void RollbackNetworkConfig::Importer::Import(const std::string& network_config,
                                              ImportCallback callback) {
-  std::optional<base::Value> managed_onc_network_config =
-      base::JSONReader::Read(network_config);
+  std::optional<base::Value::Dict> managed_onc_network_config =
+      base::JSONReader::ReadDict(network_config);
 
-  if (!managed_onc_network_config.has_value() ||
-      !managed_onc_network_config->is_dict()) {
+  if (!managed_onc_network_config) {
     std::move(callback).Run(false);
     return;
   }
 
-  base::Value::List* network_list =
-      managed_onc_network_config->GetDict().FindList(
-          onc::toplevel_config::kNetworkConfigurations);
+  base::Value::List* network_list = managed_onc_network_config->FindList(
+      onc::toplevel_config::kNetworkConfigurations);
   if (!network_list) {
     std::move(callback).Run(false);
     return;
diff --git a/chrome/browser/ash/policy/login/signin_profile_extensions_policy_browsertest.cc b/chrome/browser/ash/policy/login/signin_profile_extensions_policy_browsertest.cc
index 1adf434..5d1197f 100644
--- a/chrome/browser/ash/policy/login/signin_profile_extensions_policy_browsertest.cc
+++ b/chrome/browser/ash/policy/login/signin_profile_extensions_policy_browsertest.cc
@@ -116,8 +116,9 @@
 
   // extensions::InstallObserver:
   void OnFinishCrxInstall(content::BrowserContext* context,
-                          const extensions::CrxInstaller& installer,
+                          const base::FilePath& source_file,
                           const std::string& extension_id,
+                          const extensions::Extension* extension,
                           bool success) override {
     if (extension_id == extension_id_) {
       run_loop_.Quit();
diff --git a/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc b/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc
index c5aedc6..017dc32 100644
--- a/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc
+++ b/chrome/browser/ash/policy/networking/network_policy_application_browsertest.cc
@@ -790,11 +790,12 @@
         properties->FindString(shill::kUIDataProperty);
     if (!ui_data_json)
       return {};
-    std::optional<base::Value> ui_data_value =
-        base::JSONReader::Read(*ui_data_json);
-    if (!ui_data_value || !ui_data_value->is_dict())
+    std::optional<base::Value::Dict> ui_data_value =
+        base::JSONReader::ReadDict(*ui_data_json);
+    if (!ui_data_value) {
       return {};
-    return std::move(*ui_data_value).TakeDict();
+    }
+    return std::move(*ui_data_value);
   }
 
   // Sets the shill UIData property of the service `service_path` to the
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc
index c12c1a2..717dba2 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc
@@ -17,8 +17,10 @@
 #include "chrome/browser/ash/policy/remote_commands/crd/crd_logging.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/ash/services/network_config/in_process_instance.h"
+#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "remoting/host/chromeos/features.h"
 #include "remoting/protocol/errors.h"
 #include "ui/base/user_activity/user_activity_detector.h"
diff --git a/chrome/browser/ash/policy/remote_commands/crd/device_command_start_crd_session_job.cc b/chrome/browser/ash/policy/remote_commands/crd/device_command_start_crd_session_job.cc
index cb6084d6..8da64ef5 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/device_command_start_crd_session_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/device_command_start_crd_session_job.cc
@@ -191,8 +191,9 @@
 
 bool DeviceCommandStartCrdSessionJob::ParseCommandPayload(
     const std::string& command_payload) {
-  std::optional<base::Value> root(base::JSONReader::Read(command_payload));
-  if (!root || !root->is_dict()) {
+  std::optional<base::Value::Dict> root =
+      base::JSONReader::ReadDict(command_payload);
+  if (!root) {
     LOG(WARNING) << "Rejecting remote command with invalid payload: "
                  << std::quoted(command_payload);
     return false;
@@ -200,22 +201,19 @@
   CRD_VLOG(1) << "Received remote command with payload "
               << std::quoted(command_payload);
 
-  const base::Value::Dict& root_dict = root->GetDict();
-
   idleness_cutoff_ =
-      base::Seconds(root_dict.FindInt(kIdlenessCutoffFieldName).value_or(0));
+      base::Seconds(root->FindInt(kIdlenessCutoffFieldName).value_or(0));
 
   acked_user_presence_ =
-      root_dict.FindBool(kAckedUserPresenceFieldName).value_or(false);
+      root->FindBool(kAckedUserPresenceFieldName).value_or(false);
 
   CrdSessionType crd_session_type =
-      ToCrdSessionTypeOrDefault(root_dict.FindInt(kCrdSessionTypeFieldName),
+      ToCrdSessionTypeOrDefault(root->FindInt(kCrdSessionTypeFieldName),
                                 CrdSessionType::REMOTE_SUPPORT_SESSION);
 
-  admin_email_ = FindString(root_dict, kAdminEmailFieldName);
+  admin_email_ = FindString(*root, kAdminEmailFieldName);
 
-  show_confirmation_dialog_ =
-      root_dict.FindBool(kShowConfirmationDialogFieldName);
+  show_confirmation_dialog_ = root->FindBool(kShowConfirmationDialogFieldName);
 
   curtain_local_user_session_ =
       (crd_session_type == CrdSessionType::REMOTE_ACCESS_SESSION);
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc
index 64f7155..d2aa8cb 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_fetch_support_packet_job.cc
@@ -242,14 +242,14 @@
 bool DeviceCommandFetchSupportPacketJob::ParseCommandPayloadImpl(
     const std::string& command_payload) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  std::optional<base::Value> value = base::JSONReader::Read(command_payload);
-  if (!value.has_value() || !value->is_dict()) {
+  std::optional<base::Value::Dict> value =
+      base::JSONReader::ReadDict(command_payload);
+  if (!value) {
     return false;
   }
 
-  const base::Value::Dict& dict = value->GetDict();
   const base::Value::Dict* details_dict =
-      dict.FindDict(kSupportPacketDetailsKey);
+      value->FindDict(kSupportPacketDetailsKey);
   if (!details_dict) {
     return false;
   }
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_get_routine_update_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_get_routine_update_job.cc
index 7ddd5b8..a919b0a8 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_get_routine_update_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_get_routine_update_job.cc
@@ -116,17 +116,14 @@
 
 bool DeviceCommandGetRoutineUpdateJob::ParseCommandPayload(
     const std::string& command_payload) {
-  std::optional<base::Value> root(base::JSONReader::Read(command_payload));
-  if (!root.has_value()) {
-    return false;
-  }
-  if (!root->is_dict()) {
+  std::optional<base::Value::Dict> root =
+      base::JSONReader::ReadDict(command_payload);
+  if (!root) {
     return false;
   }
 
-  const base::Value::Dict& dict = root->GetDict();
   // Make sure the command payload specified a valid integer for the routine ID.
-  std::optional<int> id = dict.FindInt(kIdFieldName);
+  std::optional<int> id = root->FindInt(kIdFieldName);
   if (!id.has_value()) {
     return false;
   }
@@ -134,7 +131,7 @@
 
   // Make sure the command payload specified a valid
   // DiagnosticRoutineCommandEnum.
-  std::optional<int> command_enum = dict.FindInt(kCommandFieldName);
+  std::optional<int> command_enum = root->FindInt(kCommandFieldName);
   if (!command_enum.has_value()) {
     return false;
   }
@@ -145,7 +142,7 @@
   }
 
   // Make sure the command payload specified a boolean for include_output.
-  std::optional<bool> include_output = dict.FindBool(kIncludeOutputFieldName);
+  std::optional<bool> include_output = root->FindBool(kIncludeOutputFieldName);
   if (!include_output.has_value()) {
     return false;
   }
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_reboot_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_reboot_job.cc
index 2a081fc..3c60c11 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_reboot_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_reboot_job.cc
@@ -63,14 +63,14 @@
 
 std::optional<base::TimeDelta> ExtractUserSessionDelayFromPayload(
     const std::string& command_payload) {
-  const std::optional<base::Value> root =
-      base::JSONReader::Read(command_payload);
-  if (!root || !root->is_dict()) {
+  const std::optional<base::Value::Dict> root =
+      base::JSONReader::ReadDict(command_payload);
+  if (!root) {
     return std::nullopt;
   }
 
   std::optional<int> delay_in_seconds =
-      root->GetDict().FindInt(kPayloadUserSessionRebootDelayField);
+      root->FindInt(kPayloadUserSessionRebootDelayField);
   if (!delay_in_seconds || delay_in_seconds.value() < 0) {
     return std::nullopt;
   }
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc
index a3e7f9d7..de9e518 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_run_routine_job.cc
@@ -90,17 +90,14 @@
 
 bool DeviceCommandRunRoutineJob::ParseCommandPayload(
     const std::string& command_payload) {
-  std::optional<base::Value> root(base::JSONReader::Read(command_payload));
-  if (!root.has_value()) {
-    return false;
-  }
-  if (!root->is_dict()) {
+  std::optional<base::Value::Dict> root =
+      base::JSONReader::ReadDict(command_payload);
+  if (!root) {
     return false;
   }
 
-  base::Value::Dict& dict = root->GetDict();
   // Make sure the command payload specified a valid DiagnosticRoutineEnum.
-  std::optional<int> routine_enum = dict.FindInt(kRoutineEnumFieldName);
+  std::optional<int> routine_enum = root->FindInt(kRoutineEnumFieldName);
   if (!routine_enum.has_value()) {
     return false;
   }
@@ -113,7 +110,7 @@
   // Make sure there's a dictionary with parameter values for the routine.
   // Validation of routine-specific parameters will be done before running the
   // routine, so here we just check that any dictionary was given to us.
-  auto* params_dict = dict.FindDict(kParamsFieldName);
+  auto* params_dict = root->FindDict(kParamsFieldName);
   if (!params_dict) {
     return false;
   }
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_screenshot_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_screenshot_job.cc
index 943fa44..83d77cd 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_screenshot_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_screenshot_job.cc
@@ -107,12 +107,12 @@
 
 bool DeviceCommandScreenshotJob::ParseCommandPayload(
     const std::string& command_payload) {
-  std::optional<base::Value> root(base::JSONReader::Read(command_payload));
-  if (!root || !root->is_dict()) {
+  std::optional<base::Value::Dict> root =
+      base::JSONReader::ReadDict(command_payload);
+  if (!root) {
     return false;
   }
-  const std::string* upload_url =
-      root->GetDict().FindString(kUploadUrlFieldName);
+  const std::string* upload_url = root->FindString(kUploadUrlFieldName);
   if (!upload_url) {
     return false;
   }
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc
index e5771c7..20c7726 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_set_volume_job.cc
@@ -45,12 +45,13 @@
 
 bool DeviceCommandSetVolumeJob::ParseCommandPayload(
     const std::string& command_payload) {
-  std::optional<base::Value> root(base::JSONReader::Read(command_payload));
-  if (!root || !root->is_dict()) {
+  std::optional<base::Value::Dict> root =
+      base::JSONReader::ReadDict(command_payload);
+  if (!root) {
     return false;
   }
   std::optional<int> maybe_volume;
-  maybe_volume = root->GetDict().FindInt(kVolumeFieldName);
+  maybe_volume = root->FindInt(kVolumeFieldName);
   if (!maybe_volume) {
     return false;
   }
diff --git a/chrome/browser/ash/preferences/preferences.cc b/chrome/browser/ash/preferences/preferences.cc
index 774c91e..b1464487 100644
--- a/chrome/browser/ash/preferences/preferences.cc
+++ b/chrome/browser/ash/preferences/preferences.cc
@@ -16,6 +16,7 @@
 #include "ash/public/ash_interfaces.h"
 #include "ash/public/cpp/ash_prefs.h"
 #include "ash/public/cpp/ime_controller.h"
+#include "ash/public/cpp/lobster/lobster_enums.h"
 #include "ash/shell.h"
 #include "ash/system/geolocation/geolocation_controller.h"
 #include "ash/system/privacy_hub/privacy_hub_controller.h"
@@ -288,6 +289,10 @@
   registry->RegisterBooleanPref(prefs::kManagedOrcaEnabled, true);
   registry->RegisterBooleanPref(prefs::kLobsterEnabled, true);
   registry->RegisterBooleanPref(
+      prefs::kLobsterEnterprisePolicySettings,
+      base::to_underlying(
+          ash::LobsterEnterprisePolicyValue::kAllowedWithModelImprovement));
+  registry->RegisterBooleanPref(
       prefs::kManagedPhysicalKeyboardAutocorrectAllowed, true);
   registry->RegisterBooleanPref(
       prefs::kManagedPhysicalKeyboardPredictiveWritingAllowed, true);
diff --git a/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc b/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc
index 2871cf7..c5a9f2c2 100644
--- a/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc
+++ b/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ash/shimless_rma/diagnostics_app_profile_helper.h"
 #include "chrome/browser/ash/shimless_rma/diagnostics_app_profile_helper_constants.h"
 #include "chrome/browser/extensions/extension_garbage_collector_factory.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.cc b/chrome/browser/autofill/android/personal_data_manager_android.cc
index afa83cb0..2d77996 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.cc
+++ b/chrome/browser/autofill/android/personal_data_manager_android.cc
@@ -41,6 +41,7 @@
 #include "components/autofill/core/browser/geo/autofill_country.h"
 #include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #include "components/autofill/core/browser/ui/autofill_resource_utils.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_constants.h"
@@ -344,7 +345,7 @@
 ScopedJavaLocalRef<jobjectArray>
 PersonalDataManagerAndroid::GetCreditCardGUIDsToSuggest(JNIEnv* env) {
   return GetCreditCardGUIDs(env,
-                            payments_data_manager().GetCreditCardsToSuggest());
+                            GetCreditCardsToSuggest(payments_data_manager()));
 }
 
 ScopedJavaLocalRef<jobject> PersonalDataManagerAndroid::GetCreditCardByGUID(
diff --git a/chrome/browser/autofill/android/personal_data_manager_android.h b/chrome/browser/autofill/android/personal_data_manager_android.h
index f2ed1a2e..209e2cf1 100644
--- a/chrome/browser/autofill/android/personal_data_manager_android.h
+++ b/chrome/browser/autofill/android/personal_data_manager_android.h
@@ -130,7 +130,8 @@
       JNIEnv* env);
 
   // Returns the GUIDs of the credit cards to suggest to the user. See
-  // PersonalDataManager::GetCreditCardsToSuggest for more details.
+  // GetCreditCardsToSuggest in payments_suggestion_generator.h for more
+  // details.
   base::android::ScopedJavaLocalRef<jobjectArray> GetCreditCardGUIDsToSuggest(
       JNIEnv* env);
 
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.cc b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.cc
index 14a5fb90..ffda402 100644
--- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.cc
+++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.cc
@@ -284,8 +284,9 @@
 
 void ChromeKioskAppInstaller::OnFinishCrxInstall(
     content::BrowserContext* context,
-    const extensions::CrxInstaller& installer,
+    const base::FilePath& source_file,
     const std::string& extension_id,
+    const extensions::Extension* extension,
     bool success) {
   DCHECK(!install_complete_);
 
@@ -300,7 +301,7 @@
   waiting_ids_.erase(extension_id);
 
   // Also wait for updates on any shared modules the extension imports.
-  if (auto* extension = installer.extension(); extension != nullptr) {
+  if (extension != nullptr) {
     InsertPendingSharedModules(profile_.get(), waiting_ids_, *extension);
   }
 
diff --git a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.h b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.h
index d6e5083..3bf3634 100644
--- a/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.h
+++ b/chrome/browser/chromeos/app_mode/chrome_kiosk_app_installer.h
@@ -50,8 +50,9 @@
 
   // extensions::InstallObserver overrides.
   void OnFinishCrxInstall(content::BrowserContext* context,
-                          const extensions::CrxInstaller& installer,
+                          const base::FilePath& source_file,
                           const std::string& extension_id,
+                          const extensions::Extension* extension,
                           bool success) override;
 
   // extensions::InstallStageTracker::Observer overrides.
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
index 77fe12b6..05edfb3 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
@@ -36,6 +36,8 @@
 import org.chromium.components.data_sharing.SharedTabGroupPreview;
 import org.chromium.components.data_sharing.configs.DataSharingCreateUiConfig;
 import org.chromium.components.data_sharing.configs.DataSharingJoinUiConfig;
+import org.chromium.components.signin.identitymanager.ConsentLevel;
+import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
@@ -205,7 +207,10 @@
 
         SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(profile);
 
-        if (!signinManager.isSigninAllowed()) {
+        IdentityManager identityManager = signinManager.getIdentityManager();
+
+        if (!identityManager.hasPrimaryAccount(ConsentLevel.SIGNIN)
+                && !signinManager.isSigninAllowed()) {
             // The signin option is disabled manually by the user in settings.
             openSigninSettingsModel(resultCallback);
             return;
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
index b964bd9..4a875f9 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
@@ -46,6 +46,7 @@
 import org.chromium.components.data_sharing.SharedTabGroupPreview;
 import org.chromium.components.data_sharing.configs.DataSharingCreateUiConfig;
 import org.chromium.components.data_sharing.configs.DataSharingJoinUiConfig;
+import org.chromium.components.signin.identitymanager.IdentityManager;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
@@ -81,6 +82,7 @@
     @Mock private SettingsNavigation mSettingsNavigation;
     @Mock private IdentityServicesProvider mIdentityServicesProvider;
     @Mock private Callback<Runnable> mSwitchToTabSwitcherCallback;
+    @Mock private IdentityManager mIdentityManager;
 
     @Mock
     private CollaborationControllerDelegateImpl.Natives
@@ -98,6 +100,8 @@
         IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
 
         doReturn(mSigninManager).when(mIdentityServicesProvider).getSigninManager(mProfile);
+        doReturn(mIdentityManager).when(mSigninManager).getIdentityManager();
+        doReturn(false).when(mIdentityManager).hasPrimaryAccount(anyInt());
         doReturn(true).when(mSigninManager).isSigninAllowed();
         doReturn((long) 0)
                 .when(mCollaborationControllerDelegateImplNativeMock)
diff --git a/chrome/browser/component_updater/pki_metadata_component_installer.cc b/chrome/browser/component_updater/pki_metadata_component_installer.cc
index b56e186..4507e6fb 100644
--- a/chrome/browser/component_updater/pki_metadata_component_installer.cc
+++ b/chrome/browser/component_updater/pki_metadata_component_installer.cc
@@ -64,7 +64,7 @@
 // if it is set). This should never be decreased since that will cause CT
 // enforcement to eventually stop. This should also only be increased if Chrome
 // is compatible with the version it is being incremented to.
-const uint64_t kMaxSupportedCTCompatibilityVersion = 2;
+const uint64_t kMaxSupportedCTCompatibilityVersion = 3;
 
 // This is the last version of key pins lists that this version of Chrome will
 // accept. If a list is delivered with a compatibility version higher than this,
diff --git a/chrome/browser/component_updater/pki_metadata_component_installer_unittest.cc b/chrome/browser/component_updater/pki_metadata_component_installer_unittest.cc
index 2246618..1796678 100644
--- a/chrome/browser/component_updater/pki_metadata_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/pki_metadata_component_installer_unittest.cc
@@ -84,7 +84,7 @@
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-constexpr uint64_t kMaxSupportedCTCompatibilityVersion = 2;
+constexpr uint64_t kMaxSupportedCTCompatibilityVersion = 3;
 constexpr uint64_t kMaxSupportedKPCompatibilityVersion = 1;
 
 }  // namespace
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 89510ebb..9cd27d46 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -27,6 +27,8 @@
   # TODO(https://crbug.com/356905053): When files support being built on
   # desktop android, move them here, outside the if (enable_extensions) block.
   sources = [
+    "active_install_data.cc",
+    "active_install_data.h",
     "activity_log/activity_action_constants.cc",
     "activity_log/activity_action_constants.h",
     "activity_log/activity_actions.cc",
@@ -74,12 +76,20 @@
     "forced_extensions/install_stage_tracker.h",
     "forced_extensions/install_stage_tracker_factory.cc",
     "forced_extensions/install_stage_tracker_factory.h",
+    "install_observer.cc",
+    "install_observer.h",
+    "install_tracker.cc",
+    "install_tracker.h",
+    "install_tracker_factory.cc",
+    "install_tracker_factory.h",
     "load_error_reporter.cc",
     "load_error_reporter.h",
     "permissions_url_constants.cc",
     "permissions_url_constants.h",
     "policy_handlers.cc",
     "policy_handlers.h",
+    "scoped_active_install.cc",
+    "scoped_active_install.h",
     "window_controller.cc",
     "window_controller.h",
     "window_controller_list.cc",
@@ -122,8 +132,6 @@
     sources += [
       "account_extension_tracker.cc",
       "account_extension_tracker.h",
-      "active_install_data.cc",
-      "active_install_data.h",
       "permissions/active_tab_permission_granter.cc",
       "permissions/active_tab_permission_granter.h",
 
@@ -561,16 +569,10 @@
       "forced_extensions/force_installed_tracker.cc",
       "forced_extensions/force_installed_tracker.h",
       "install_gate.h",
-      "install_observer.cc",
-      "install_observer.h",
       "install_prompt_permissions.cc",
       "install_prompt_permissions.h",
       "install_signer.cc",
       "install_signer.h",
-      "install_tracker.cc",
-      "install_tracker.h",
-      "install_tracker_factory.cc",
-      "install_tracker_factory.h",
       "install_verifier.cc",
       "install_verifier.h",
       "install_verifier_factory.cc",
@@ -616,8 +618,6 @@
       "profile_util.h",
       "safe_browsing_verdict_handler.cc",
       "safe_browsing_verdict_handler.h",
-      "scoped_active_install.cc",
-      "scoped_active_install.h",
       "settings_api_helpers.cc",
       "settings_api_helpers.h",
       "shared_module_service.cc",
diff --git a/chrome/browser/extensions/background_xhr_browsertest.cc b/chrome/browser/extensions/background_xhr_browsertest.cc
index d18dd48..64b82d4 100644
--- a/chrome/browser/extensions/background_xhr_browsertest.cc
+++ b/chrome/browser/extensions/background_xhr_browsertest.cc
@@ -416,8 +416,7 @@
 IN_PROC_BROWSER_TEST_P(BackgroundFetchWebstoreTest, FetchToWebstorePolicy) {
   {
     ExtensionManagementPolicyUpdater pref(&policy_provider_);
-    pref.AddPolicyAllowedHost(
-        "*", "*://" + extension_urls::GetWebstoreLaunchURL().host());
+    pref.AddPolicyAllowedHost("*", "*://" + GetParam().host());
   }
 
   const Extension* extension = LoadFetchExtension("<all_urls>");
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 39d3c362..3af419d 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -1075,7 +1075,7 @@
       profile_, ProfileKeepAliveOrigin::kCrxInstaller);
 
   InstallTrackerFactory::GetForBrowserContext(profile())->OnBeginCrxInstall(
-      *this, expected_id_);
+      expected_id_);
 }
 
 void CrxInstaller::NotifyCrxInstallComplete(
@@ -1118,7 +1118,8 @@
   }
 
   InstallTrackerFactory::GetForBrowserContext(profile())->OnFinishCrxInstall(
-      *this, success ? extension()->id() : expected_id_, success);
+      source_file_, success ? extension()->id() : expected_id_, extension(),
+      success);
 
   if (success)
     ConfirmReEnable();
diff --git a/chrome/browser/extensions/extension_garbage_collector.cc b/chrome/browser/extensions/extension_garbage_collector.cc
index dd804edf..00fd145 100644
--- a/chrome/browser/extensions/extension_garbage_collector.cc
+++ b/chrome/browser/extensions/extension_garbage_collector.cc
@@ -245,15 +245,15 @@
 
 void ExtensionGarbageCollector::OnBeginCrxInstall(
     content::BrowserContext* context,
-    const CrxInstaller& installer,
     const ExtensionId& extension_id) {
   crx_installs_in_progress_++;
 }
 
 void ExtensionGarbageCollector::OnFinishCrxInstall(
     content::BrowserContext* context,
-    const CrxInstaller& installer,
+    const base::FilePath& source_file,
     const ExtensionId& extension_id,
+    const Extension* extension,
     bool success) {
   crx_installs_in_progress_--;
   if (crx_installs_in_progress_ < 0) {
diff --git a/chrome/browser/extensions/extension_garbage_collector.h b/chrome/browser/extensions/extension_garbage_collector.h
index ea7566b..05a182a0 100644
--- a/chrome/browser/extensions/extension_garbage_collector.h
+++ b/chrome/browser/extensions/extension_garbage_collector.h
@@ -46,11 +46,11 @@
 
   // InstallObserver:
   void OnBeginCrxInstall(content::BrowserContext* context,
-                         const CrxInstaller& installer,
                          const ExtensionId& extension_id) override;
   void OnFinishCrxInstall(content::BrowserContext* context,
-                          const CrxInstaller& installer,
+                          const base::FilePath& source_file,
                           const ExtensionId& extension_id,
+                          const Extension* extension,
                           bool success) override;
 
  protected:
diff --git a/chrome/browser/extensions/extension_garbage_collector_unittest.cc b/chrome/browser/extensions/extension_garbage_collector_unittest.cc
index f4d5cbb..305f927 100644
--- a/chrome/browser/extensions/extension_garbage_collector_unittest.cc
+++ b/chrome/browser/extensions/extension_garbage_collector_unittest.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/extensions/extension_garbage_collector.h"
+
 #include <stddef.h>
 
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/values.h"
-#include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/extensions/extension_garbage_collector.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_service_test_base.h"
 #include "chrome/browser/extensions/install_tracker.h"
@@ -140,9 +140,7 @@
   service_->Init();
 
   // Simulate a CRX installation.
-  auto installer = CrxInstaller::CreateSilent(service_);
-  InstallTracker::Get(profile_.get())
-      ->OnBeginCrxInstall(*installer, kExtensionId);
+  InstallTracker::Get(profile_.get())->OnBeginCrxInstall(kExtensionId);
 
   GarbageCollectExtensions();
 
@@ -153,7 +151,7 @@
 
   // Finish CRX installation and re-run garbage collection.
   InstallTracker::Get(profile_.get())
-      ->OnFinishCrxInstall(*installer, kExtensionId, false);
+      ->OnFinishCrxInstall(base::FilePath(), kExtensionId, nullptr, false);
   GarbageCollectExtensions();
 
   // extension1 dir should be gone
diff --git a/chrome/browser/extensions/install_observer.h b/chrome/browser/extensions/install_observer.h
index 6abc6f6..1644fb90 100644
--- a/chrome/browser/extensions/install_observer.h
+++ b/chrome/browser/extensions/install_observer.h
@@ -11,13 +11,16 @@
 #include "extensions/common/extension_id.h"
 #include "ui/gfx/image/image_skia.h"
 
+namespace base {
+class FilePath;
+}
+
 namespace content {
 class BrowserContext;
 }
 
 namespace extensions {
 
-class CrxInstaller;
 class Extension;
 
 // An InstallObserver observes extension installation events coming from an InstallTracker.
@@ -64,14 +67,16 @@
   // Called when the necessary downloads have completed, and the crx
   // installation is due to start.
   virtual void OnBeginCrxInstall(content::BrowserContext* context,
-                                 const CrxInstaller& installer,
                                  const std::string& extension_id) {}
 
   // Called when installation of a crx has completed (either successfully or
-  // not).
+  // not). `source_file` is the source of the install. If the installation
+  // failed `extension` will be null but `extension_id` may be valid (it will
+  // have the ID we expected the extension to have).
   virtual void OnFinishCrxInstall(content::BrowserContext* context,
-                                  const CrxInstaller& installer,
+                                  const base::FilePath& source_file,
                                   const std::string& extension_id,
+                                  const Extension* extension,
                                   bool success) {}
 
   // Called when the app list is reordered. If |extension_id| is set, it
diff --git a/chrome/browser/extensions/install_tracker.cc b/chrome/browser/extensions/install_tracker.cc
index b13aed4..fbb3b382 100644
--- a/chrome/browser/extensions/install_tracker.cc
+++ b/chrome/browser/extensions/install_tracker.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/extensions/install_tracker.h"
+
 #include <memory>
 
 #include "base/functional/bind.h"
@@ -101,19 +102,19 @@
   }
 }
 
-void InstallTracker::OnBeginCrxInstall(const CrxInstaller& installer,
-                                       const std::string& extension_id) {
+void InstallTracker::OnBeginCrxInstall(const std::string& extension_id) {
   for (auto& observer : observers_) {
-    observer.OnBeginCrxInstall(browser_context_, installer, extension_id);
+    observer.OnBeginCrxInstall(browser_context_, extension_id);
   }
 }
 
-void InstallTracker::OnFinishCrxInstall(const CrxInstaller& installer,
+void InstallTracker::OnFinishCrxInstall(const base::FilePath& source_file,
                                         const std::string& extension_id,
+                                        const Extension* extension,
                                         bool success) {
   for (auto& observer : observers_) {
-    observer.OnFinishCrxInstall(browser_context_, installer, extension_id,
-                                success);
+    observer.OnFinishCrxInstall(browser_context_, source_file, extension_id,
+                                extension, success);
   }
 }
 
diff --git a/chrome/browser/extensions/install_tracker.h b/chrome/browser/extensions/install_tracker.h
index c47e2cc4..988494e 100644
--- a/chrome/browser/extensions/install_tracker.h
+++ b/chrome/browser/extensions/install_tracker.h
@@ -11,7 +11,6 @@
 #include "base/observer_list.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/extensions/active_install_data.h"
-#include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/install_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -19,12 +18,17 @@
 #include "extensions/browser/extension_registry_observer.h"
 #include "extensions/common/extension_id.h"
 
+namespace base {
+class FilePath;
+}
+
 namespace content {
 class BrowserContext;
 }
 
 namespace extensions {
 
+class Extension;
 class ExtensionPrefs;
 
 class InstallTracker : public KeyedService, public ExtensionRegistryObserver {
@@ -63,10 +67,10 @@
   void OnBeginExtensionDownload(const std::string& extension_id);
   void OnDownloadProgress(const std::string& extension_id,
                           int percent_downloaded);
-  void OnBeginCrxInstall(const CrxInstaller& installer,
-                         const std::string& extension_id);
-  void OnFinishCrxInstall(const CrxInstaller& installer,
+  void OnBeginCrxInstall(const std::string& extension_id);
+  void OnFinishCrxInstall(const base::FilePath& source_file,
                           const std::string& extension_id,
+                          const Extension* extension,
                           bool success);
   void OnInstallFailure(const std::string& extension_id);
 
diff --git a/chrome/browser/fast_checkout/fast_checkout_personal_data_helper_impl.cc b/chrome/browser/fast_checkout/fast_checkout_personal_data_helper_impl.cc
index d3f2a3a7..5747c17 100644
--- a/chrome/browser/fast_checkout/fast_checkout_personal_data_helper_impl.cc
+++ b/chrome/browser/fast_checkout/fast_checkout_personal_data_helper_impl.cc
@@ -12,6 +12,7 @@
 #include "components/autofill/core/browser/data_manager/addresses/address_data_manager.h"
 #include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 
 FastCheckoutPersonalDataHelperImpl::FastCheckoutPersonalDataHelperImpl(
     content::WebContents* web_contents)
@@ -36,9 +37,8 @@
 std::vector<const autofill::CreditCard*>
 FastCheckoutPersonalDataHelperImpl::GetCreditCardsToSuggest() const {
   std::vector<const autofill::CreditCard*> cards_to_suggest =
-      GetPersonalDataManager()
-          ->payments_data_manager()
-          .GetCreditCardsToSuggest();
+      autofill::GetCreditCardsToSuggest(
+          GetPersonalDataManager()->payments_data_manager());
   // Do not offer cards with empty number.
   std::erase_if(cards_to_suggest, [](const autofill::CreditCard* card) {
     return !card->HasRawInfo(autofill::CREDIT_CARD_NUMBER);
@@ -67,9 +67,8 @@
 std::vector<const autofill::CreditCard*>
 FastCheckoutPersonalDataHelperImpl::GetValidCreditCards() const {
   std::vector<const autofill::CreditCard*> cards =
-      GetPersonalDataManager()
-          ->payments_data_manager()
-          .GetCreditCardsToSuggest();
+      autofill::GetCreditCardsToSuggest(
+          GetPersonalDataManager()->payments_data_manager());
   std::erase_if(cards, std::not_fn(&autofill::CreditCard::IsCompleteValidCard));
   return cards;
 }
diff --git a/chrome/browser/feedback/BUILD.gn b/chrome/browser/feedback/BUILD.gn
index 5f99f13..284054d 100644
--- a/chrome/browser/feedback/BUILD.gn
+++ b/chrome/browser/feedback/BUILD.gn
@@ -148,4 +148,11 @@
   if (is_win || is_mac) {
     deps += [ "//chrome/browser/updater:browser_updater_client" ]
   }
+  if (is_linux) {
+    sources += [
+      "system_logs/log_sources/ozone_platform_state_dump_source.cc",
+      "system_logs/log_sources/ozone_platform_state_dump_source.h",
+    ]
+    deps += [ "//ui/ozone" ]
+  }
 }
diff --git a/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc b/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
index 0943634..a43f5bd2 100644
--- a/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
+++ b/chrome/browser/feedback/system_logs/about_system_logs_fetcher.cc
@@ -38,6 +38,10 @@
 #include "chrome/browser/ash/system_logs/ui_hierarchy_log_source.h"
 #endif
 
+#if BUILDFLAG(IS_LINUX)
+#include "chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.h"
+#endif
+
 namespace system_logs {
 
 SystemLogsFetcher* BuildAboutSystemLogsFetcher(content::WebUI* web_ui) {
@@ -81,6 +85,10 @@
   fetcher->AddSource(std::make_unique<KeyboardInfoLogSource>());
 #endif
 
+#if BUILDFLAG(IS_LINUX)
+  fetcher->AddSource(std::make_unique<OzonePlatformStateDumpSource>());
+#endif  // BUILDFLAG(IS_LINUX)
+
   return fetcher;
 }
 
diff --git a/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc b/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc
index f11fd637..df2803d 100644
--- a/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc
+++ b/chrome/browser/feedback/system_logs/chrome_system_logs_fetcher.cc
@@ -45,6 +45,10 @@
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #endif
 
+#if BUILDFLAG(IS_LINUX)
+#include "chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.h"
+#endif
+
 namespace system_logs {
 
 SystemLogsFetcher* BuildChromeSystemLogsFetcher(Profile* profile,
@@ -98,6 +102,10 @@
   fetcher->AddSource(std::make_unique<UiHierarchyLogSource>(scrub_data));
 #endif
 
+#if BUILDFLAG(IS_LINUX)
+  fetcher->AddSource(std::make_unique<OzonePlatformStateDumpSource>());
+#endif  // BUILDFLAG(IS_LINUX)
+
   return fetcher;
 }
 
diff --git a/chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.cc b/chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.cc
new file mode 100644
index 0000000..d8a26ad
--- /dev/null
+++ b/chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.cc
@@ -0,0 +1,26 @@
+// Copyright 2025 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/feedback/system_logs/log_sources/ozone_platform_state_dump_source.h"
+
+#include <memory>
+#include <sstream>
+
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace system_logs {
+
+OzonePlatformStateDumpSource::OzonePlatformStateDumpSource()
+    : SystemLogsSource("OzonePlatformStateDump") {}
+
+void OzonePlatformStateDumpSource::Fetch(SysLogsSourceCallback callback) {
+  std::ostringstream out;
+  CHECK(ui::OzonePlatform::GetInstance());
+  ui::OzonePlatform::GetInstance()->DumpState(out);
+  auto response = std::make_unique<SystemLogsResponse>();
+  response->emplace("ozone-platform-state", out.str());
+  std::move(callback).Run(std::move(response));
+}
+
+}  // namespace system_logs
diff --git a/chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.h b/chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.h
new file mode 100644
index 0000000..09f6aa3
--- /dev/null
+++ b/chrome/browser/feedback/system_logs/log_sources/ozone_platform_state_dump_source.h
@@ -0,0 +1,27 @@
+// Copyright 2025 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_FEEDBACK_SYSTEM_LOGS_LOG_SOURCES_OZONE_PLATFORM_STATE_DUMP_SOURCE_H_
+#define CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_LOG_SOURCES_OZONE_PLATFORM_STATE_DUMP_SOURCE_H_
+
+#include "components/feedback/system_logs/system_logs_source.h"
+
+namespace system_logs {
+
+// Fetches Ozone state dump.
+class OzonePlatformStateDumpSource : public SystemLogsSource {
+ public:
+  OzonePlatformStateDumpSource();
+  OzonePlatformStateDumpSource(const OzonePlatformStateDumpSource&) = delete;
+  OzonePlatformStateDumpSource& operator=(const OzonePlatformStateDumpSource&) =
+      delete;
+  ~OzonePlatformStateDumpSource() override = default;
+
+  // SystemLogsSource:
+  void Fetch(SysLogsSourceCallback request) override;
+};
+
+}  // namespace system_logs
+
+#endif  // CHROME_BROWSER_FEEDBACK_SYSTEM_LOGS_LOG_SOURCES_OZONE_PLATFORM_STATE_DUMP_SOURCE_H_
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6c25162..137fa57bc 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -2047,6 +2047,12 @@
     "expiry_milestone": -1
   },
   {
+    "name": "disable-facilitated-payments-merchant-allowlist",
+    "owners": [ "siashah@chromium.org", "chrome-payments-team@google.com", "payments-autofill-team@google.com" ],
+    // This flag is required for QA and dogfood testing.
+    "expiry_milestone": -1
+  },
+  {
     "name": "disable-idle-sockets-close-on-memory-pressure",
     "owners": [ "pmarko@chromium.org" ],
     "expiry_milestone": 112
diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json
index feae0808..b36a16c 100644
--- a/chrome/browser/flag-never-expire-list.json
+++ b/chrome/browser/flag-never-expire-list.json
@@ -35,6 +35,7 @@
   "disable-bruschetta-install-checks",
   "disable-buffer-bw-compression",
   "disable-explicit-dma-fences",
+  "disable-facilitated-payments-merchant-allowlist",
   "disable-javascript-harmony-shipping",
   "disallow-doc-written-script-loads",
   "enable-autofill-credit-card-upload",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index d8947ad..440851c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -314,6 +314,13 @@
 const char kDataSharingJoinOnlyDescription[] =
     "Enabled Data Sharing Joining flow related UI and features.";
 
+const char kDisableFacilitatedPaymentsMerchantAllowlistName[] =
+    "Disable the merchant allowlist check for facilitated payments";
+const char kDisableFacilitatedPaymentsMerchantAllowlistDescription[] =
+    "When enabled, disable the merchant allowlist check for facilitated "
+    "payments, so that merchants that are not on the allowlist can also be "
+    "tested for the supported features.";
+
 const char kHistorySyncAlternativeIllustrationName[] =
     "History Sync Alternative Illustration";
 const char kHistorySyncAlternativeIllustrationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index ee3dcf6..3cbf625fe 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -214,6 +214,9 @@
 extern const char kDataSharingJoinOnlyName[];
 extern const char kDataSharingJoinOnlyDescription[];
 
+extern const char kDisableFacilitatedPaymentsMerchantAllowlistName[];
+extern const char kDisableFacilitatedPaymentsMerchantAllowlistDescription[];
+
 extern const char kHistorySyncAlternativeIllustrationName[];
 extern const char kHistorySyncAlternativeIllustrationDescription[];
 
diff --git a/chrome/browser/glic/glic_enabling.h b/chrome/browser/glic/glic_enabling.h
index 4560738..0843999e9 100644
--- a/chrome/browser/glic/glic_enabling.h
+++ b/chrome/browser/glic/glic_enabling.h
@@ -47,9 +47,6 @@
   // off. This will never change for a given profile.
   static bool IsProfileEligible(const Profile* profile);
 
-  // Returns true if the given profile has Glic enabled. True implies that
-  // IsEnabledByFlags is on and IsProfileEligible(profile) is also true. This
-  // value can change at runtime.
   // This is a convenience method for code outside of //chrome/browser/glic.
   // Code inside should use instance method IsEnabled() instead.
   static bool IsEnabledForProfile(const Profile* profile);
@@ -67,9 +64,23 @@
   explicit GlicEnabling(Profile* profile);
   ~GlicEnabling();
 
-  // Returns true if the given profile has Glic enabled. True implies that
-  // IsEnabledByFlags is on and IsProfileEligible(profile) is also true. This
-  // value can change at runtime.
+  // TODO(crbug.com/390487066): This method is misnamed. It would be more
+  // accurate to call it `IsAllowed()`.
+  // Returns true if the given profile is allowed to use glic. This means that
+  // IsProfileEligible() returns true and:
+  //   * the profile is signed in
+  //   * can_use_model_execution is true
+  //   * glic is allowed by enterprise policy.
+  // This value can change at runtime.
+  //
+  // Once a profile is allowed to run glic, there are several more checks that
+  // are required to use glic although many callsites may not care about all of
+  // these:
+  //   * FRE has been passed. There is no way to permanently decline FRE, as
+  //     it's only invoked on user interaction with glic entry points.
+  //   * Entry point specific flags (e.g. kGlicPinnedToTabstrip).
+  //   * Profile is not paused.
+  // If all entry-points have been disabled, then glic is functionally disabled.
   bool IsEnabled();
 
   using EnableChangedCallback = base::RepeatingClosure;
diff --git a/chrome/browser/glic/glic_metrics_unittest.cc b/chrome/browser/glic/glic_metrics_unittest.cc
index 0a2dc788..8a766bd 100644
--- a/chrome/browser/glic/glic_metrics_unittest.cc
+++ b/chrome/browser/glic/glic_metrics_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/glic/glic_keyed_service.h"
 #include "chrome/browser/glic/glic_pref_names.h"
 #include "chrome/browser/glic/glic_tab_data.h"
+#include "chrome/browser/glic/glic_test_util.h"
 #include "chrome/browser/glic/glic_window_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
@@ -254,10 +255,7 @@
           features::kTabstripComboButton,
       },
       {});
-  profile_.GetPrefs()->SetBoolean(prefs::kGlicCompletedFre, true);
-  profile_.GetPrefs()->SetInteger(
-      prefs::kGlicSettingsPolicy,
-      static_cast<int>(glic::prefs::SettingsPolicyState::kEnabled));
+  ForceSigninAndModelExecutionCapability(&profile_);
 
   task_environment_.FastForwardBy(base::Minutes(16));
   histogram_tester_.ExpectTotalCount("Glic.EntryPoint.Impression", 1);
diff --git a/chrome/browser/glic/glic_test_environment.cc b/chrome/browser/glic/glic_test_environment.cc
index 4e9bb5d..3cabe2b2 100644
--- a/chrome/browser/glic/glic_test_environment.cc
+++ b/chrome/browser/glic/glic_test_environment.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/glic/glic_cookie_synchronizer.h"
 #include "chrome/browser/glic/glic_keyed_service.h"
 #include "chrome/browser/glic/glic_keyed_service_factory.h"
+#include "chrome/browser/glic/glic_test_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 
 namespace glic {
@@ -55,6 +56,7 @@
 GlicTestEnvironment::GlicTestEnvironment(Profile* profile) {
   cookie_synchronizer_ =
       internal::TestCookieSynchronizer::InjectForProfile(profile)->GetWeakPtr();
+  ForceSigninAndModelExecutionCapability(profile);
 }
 
 GlicTestEnvironment::~GlicTestEnvironment() = default;
diff --git a/chrome/browser/glic/glic_test_environment.h b/chrome/browser/glic/glic_test_environment.h
index 24ce4441..be3d0608 100644
--- a/chrome/browser/glic/glic_test_environment.h
+++ b/chrome/browser/glic/glic_test_environment.h
@@ -16,6 +16,8 @@
 // Overrides some glic functionality to allow tests that depend on glic to run.
 // This should be created on the main thread.
 // If possible, use InteractiveGlicTest instead of this directly!
+// This class is used by tests in browser_tests and interactive_ui_tests that
+// cannot use InteractiveGlicTest.
 //
 // Note: This constructs the GlicKeyedService, if it's not already created,
 // which will also construct dependencies like IdentityManager. You likely want
diff --git a/chrome/browser/glic/glic_test_util.h b/chrome/browser/glic/glic_test_util.h
index c8185e3..642f4ca 100644
--- a/chrome/browser/glic/glic_test_util.h
+++ b/chrome/browser/glic/glic_test_util.h
@@ -10,7 +10,8 @@
 namespace glic {
 
 // Signs in a primary account, accepts the FRE, and eables model execution
-// capability for that profile.
+// capability for that profile. browser_tests and interactive_ui_tests should
+// use GlicTestEnvironment. These methods are for unit_tests.
 void ForceSigninAndModelExecutionCapability(Profile* profile);
 void SigninWithPrimaryAccount(Profile* profile);
 void SetModelExecutionCapability(Profile* profile, bool enabled);
diff --git a/chrome/browser/glic/guest_util_browsertest.cc b/chrome/browser/glic/guest_util_browsertest.cc
index 03d85f1..b35d445 100644
--- a/chrome/browser/glic/guest_util_browsertest.cc
+++ b/chrome/browser/glic/guest_util_browsertest.cc
@@ -72,17 +72,6 @@
 
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
-
-    // A signed-in user is required for chrome://glic.
-    signin::IdentityManager* identity_manager =
-        IdentityManagerFactory::GetForProfile(browser()->profile());
-    CoreAccountInfo account_info = SetPrimaryAccount(
-        identity_manager, "foo@gmail.com", signin::ConsentLevel::kSync);
-    // TODO(cuianthony): Move this logic to glic_test_util.h after
-    // https://chromium-review.googlesource.com/c/chromium/src/+/6197534 lands.
-    PrefService* prefs = InProcessBrowserTest::browser()->profile()->GetPrefs();
-    prefs->SetBoolean(prefs::kGlicCompletedFre, true);
-
     glic_test_environment_ =
         std::make_unique<glic::GlicTestEnvironment>(browser()->profile());
   }
diff --git a/chrome/browser/glic/interactive_glic_test.h b/chrome/browser/glic/interactive_glic_test.h
index c031354..5067843 100644
--- a/chrome/browser/glic/interactive_glic_test.h
+++ b/chrome/browser/glic/interactive_glic_test.h
@@ -157,8 +157,6 @@
   void SetUpOnMainThread() override {
     T::SetUpOnMainThread();
 
-    ForceSigninAndModelExecutionCapability(T::browser()->profile());
-
     Test::embedded_test_server()->ServeFilesFromDirectory(
         base::PathService::CheckedGet(base::DIR_ASSETS)
             .AppendASCII("gen/chrome/test/data/webui/glic/"));
diff --git a/chrome/browser/keyboard_accessory/android/payment_method_accessory_controller_impl.cc b/chrome/browser/keyboard_accessory/android/payment_method_accessory_controller_impl.cc
index 784499b..34d3092 100644
--- a/chrome/browser/keyboard_accessory/android/payment_method_accessory_controller_impl.cc
+++ b/chrome/browser/keyboard_accessory/android/payment_method_accessory_controller_impl.cc
@@ -30,6 +30,7 @@
 #include "components/autofill/core/browser/payments/constants.h"
 #include "components/autofill/core/browser/payments/iban_access_manager.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/strings/grit/components_strings.h"
@@ -406,7 +407,7 @@
   }
 
   std::vector<CardOrVirtualCard> cards;
-  for (const CreditCard* card : paydm()->GetCreditCardsToSuggest()) {
+  for (const CreditCard* card : GetCreditCardsToSuggest(*paydm())) {
     // If any of cards is enrolled for virtual cards and the feature is active,
     // then insert a virtual card suggestion right before the actual card.
     if (ShouldCreateVirtualCard(card)) {
diff --git a/chrome/browser/on_device_translation/translator.cc b/chrome/browser/on_device_translation/translator.cc
index 81553eb..d03707d2 100644
--- a/chrome/browser/on_device_translation/translator.cc
+++ b/chrome/browser/on_device_translation/translator.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/on_device_translation/translator.h"
 
+#include <algorithm>
+
 #include "base/functional/bind.h"
 #include "chrome/browser/on_device_translation/pref_names.h"
 #include "chrome/browser/on_device_translation/service_controller.h"
@@ -13,9 +15,23 @@
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "third_party/blink/public/mojom/ai/model_streaming_responder.mojom.h"
 #include "third_party/blink/public/mojom/on_device_translation/translator.mojom.h"
+#include "url/origin.h"
 
 namespace on_device_translation {
 
+namespace {
+
+bool IsTranslatableCharacter(char character) {
+  return !base::IsAsciiWhitespace(character) &&
+         !base::IsAsciiControl(character);
+}
+
+bool ContainsTranslatableContent(const std::string& input) {
+  return std::any_of(input.begin(), input.end(), IsTranslatableCharacter);
+}
+
+}  // namespace
+
 Translator::Translator(
     base::WeakPtr<content::BrowserContext> browser_context,
     const std::string& source_lang,
@@ -46,6 +62,19 @@
   RecordTranslationAPICallForLanguagePair("Translate", source_lang_,
                                           target_lang_);
   RecordTranslationCharacterCount(source_lang_, target_lang_, input.size());
+
+  // https://github.com/webmachinelearning/translation-api/pull/38: "If |input|
+  // is the empty string, or otherwise consists of no translatable content
+  // (e.g., only contains whitespace, or control characters), then the resulting
+  // translation should be |input|. In such cases, |sourceLanguage| and
+  // |targetLanguage| should be ignored."
+  if (!ContainsTranslatableContent(input)) {
+    responder->OnStreaming(
+        input, blink::mojom::ModelStreamingResponderAction::kReplace);
+    responder->OnCompletion(/*context_info=*/nullptr);
+    return;
+  }
+
   if (translator_remote_.is_connected()) {
     translator_remote_->Translate(
         input,
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
index a9712e4..fcecae6 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/BUILD.gn
@@ -76,6 +76,7 @@
   "background/es6_loader.ts",
   "background/event_source.ts",
   "background/event/base_automation_handler.ts",
+  "background/event/desktop_automation_handler.ts",
   "background/event/desktop_automation_interface.ts",
   "background/event/focus_automation_handler.ts",
   "background/event/media_automation_handler.ts",
@@ -166,7 +167,6 @@
   "background/automation_object_constructor_installer.js",
   "background/composite_tts.js",
   "background/editing/typing_echo.js",
-  "background/event/desktop_automation_handler.js",
   "background/output/output_rules.js",
   "background/phonetic_data.js",
   "background/primary_tts.js",
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event/desktop_automation_handler.ts
similarity index 68%
rename from chrome/browser/resources/chromeos/accessibility/chromevox/background/event/desktop_automation_handler.js
rename to chrome/browser/resources/chromeos/accessibility/chromevox/background/event/desktop_automation_handler.ts
index e7d4007..8c69c1719 100644
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/event/desktop_automation_handler.js
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/event/desktop_automation_handler.ts
@@ -15,6 +15,7 @@
 import {TestImportManager} from '/common/testing/test_import_manager.js';
 
 import {Command} from '../../common/command.js';
+import type {ChromeVoxEvent} from '../../common/custom_automation_event.js';
 import {CustomAutomationEvent} from '../../common/custom_automation_event.js';
 import {EventSourceType} from '../../common/event_source_type.js';
 import {Msgs} from '../../common/msgs.js';
@@ -34,118 +35,133 @@
 import {DesktopAutomationInterface} from './desktop_automation_interface.js';
 
 const ActionType = chrome.automation.ActionType;
-const AutomationNode = chrome.automation.AutomationNode;
+type AutomationEvent = chrome.automation.AutomationEvent;
+type AutomationNode = chrome.automation.AutomationNode;
 const Dir = constants.Dir;
 const EventType = chrome.automation.EventType;
+const IntentCommandType = chrome.automation.IntentCommandType;
+const IntentTextBoundaryType = chrome.automation.IntentTextBoundaryType;
 const RoleType = chrome.automation.RoleType;
 const StateType = chrome.automation.StateType;
 
 export class DesktopAutomationHandler extends DesktopAutomationInterface {
   /**
-   * @param {!AutomationNode} node
-   * @private
+   * Time to wait until processing more value changed events.
    */
-  constructor(node) {
+  static MIN_VALUE_CHANGE_DELAY_MS = 50;
+
+  /**
+   * Time to wait until processing more alert events with the same text content.
+   */
+  static MIN_ALERT_DELAY_MS = 50;
+
+  /**
+   * URL for NTP (New tap page).
+   */
+  static NTP_URL = 'chrome://new-tab-page/';
+
+  /** The object that speaks changes to an editable text field. */
+  private textEditHandler_: TextEditHandler|null = null;
+
+  /** The last time we handled a value changed event. */
+  private lastValueChanged_: Date = new Date(0);
+
+  /** The last node that triggered a value changed event. */
+  private lastValueTarget_: AutomationNode|null = null;
+
+  /** The last time we handled an alert event. */
+  private lastAlertTime_: Date = new Date(0);
+
+  /** The last alert text we processed. */
+  private lastAlertText_ = '';
+
+  /** The last root URL encountered. */
+  private lastRootUrl_ = '';
+
+  /** Whether a submenu is currently showing. */
+  private isSubMenuShowing_ = false;
+
+  /** Whether document selection changes should be ignored. */
+  private shouldIgnoreDocumentSelectionFromAction_ = false;
+
+  /** The current page number (for pagination tracking). */
+  private currentPage_ = -1;
+
+  /** The total number of pages (for pagination tracking). */
+  private totalPages_ = -1;
+
+  private constructor(node: AutomationNode) {
     super(node);
-
-    /**
-     * The object that speaks changes to an editable text field.
-     * @type {TextEditHandler}
-     * @private
-     */
-    this.textEditHandler_ = null;
-
-    /**
-     * The last time we handled a value changed event.
-     * @type {!Date}
-     * @private
-     */
-    this.lastValueChanged_ = new Date(0);
-
-    /** @private {AutomationNode} */
-    this.lastValueTarget_ = null;
-
-    /**
-     * The last time we handled an alert event.
-     * @type {!Date}
-     * @private
-     */
-    this.lastAlertTime_ = new Date(0);
-
-    /** @private {string} */
-    this.lastAlertText_ = '';
-
-    /** @private {string} */
-    this.lastRootUrl_ = '';
-
-    /** @private {boolean} */
-    this.isSubMenuShowing_ = false;
-
-    /** @private {boolean} */
-    this.shouldIgnoreDocumentSelectionFromAction_ = false;
-
-    /** @private {number?} */
-    this.delayedAttributeOutputId_;
-
-    /** @private {number} */
-    this.currentPage_ = -1;
-    /** @private {number} */
-    this.totalPages_ = -1;
-
     this.init_(node);
   }
 
-  /**
-   * @param {!AutomationNode} node
-   * @private
-   */
-  async init_(node) {
-    this.addListener_(EventType.ALERT, event => this.onAlert_(event));
-    this.addListener_(EventType.BLUR, event => this.onBlur_(event));
+  private async init_(node: AutomationNode): Promise<void> {
+    this.addListener_(
+        EventType.ALERT, (event: AutomationEvent) => this.onAlert_(event));
+    this.addListener_(EventType.BLUR, () => this.onBlur_());
     this.addListener_(
         EventType.DOCUMENT_SELECTION_CHANGED,
-        event => this.onDocumentSelectionChanged_(event));
-    this.addListener_(EventType.FOCUS, event => this.onFocus_(event));
+        (event: AutomationEvent) => this.onDocumentSelectionChanged_(event));
+    this.addListener_(
+        EventType.FOCUS, (event: AutomationEvent) => this.onFocus_(event));
 
     // Note that live region changes from views are really announcement
     // events. Their target nodes contain no live region semantics and have no
     // relation to live regions which are supported in |LiveRegions|.
     this.addListener_(
         EventType.LIVE_REGION_CHANGED,
-        event => this.onLiveRegionChanged_(event));
+        (event: AutomationEvent) => this.onLiveRegionChanged_(event));
 
     this.addListener_(
-        EventType.LOAD_COMPLETE, event => this.onLoadComplete_(event));
+        EventType.LOAD_COMPLETE,
+        (event: AutomationEvent) => this.onLoadComplete_(event));
     this.addListener_(
-        EventType.FOCUS_AFTER_MENU_CLOSE, event => this.onMenuEnd_(event));
+        EventType.FOCUS_AFTER_MENU_CLOSE,
+        (event: AutomationEvent) => this.onMenuEnd_(event));
     this.addListener_(
-        EventType.MENU_POPUP_START, event => this.onMenuPopupStart_(event));
-    this.addListener_(EventType.MENU_START, event => this.onMenuStart_(event));
+        EventType.MENU_POPUP_START,
+        (event: AutomationEvent) => this.onMenuPopupStart_(event));
     this.addListener_(
-        EventType.RANGE_VALUE_CHANGED, event => this.onValueChanged_(event));
+        EventType.MENU_START,
+        (event: AutomationEvent) => this.onMenuStart_(event));
     this.addListener_(
-        EventType.SCROLL_POSITION_CHANGED, this.onScrollPositionChanged);
+        EventType.RANGE_VALUE_CHANGED,
+        (event: AutomationEvent) => this.onValueChanged_(event));
+    this.addListener_(
+        EventType.SCROLL_POSITION_CHANGED,
+        (event: AutomationEvent) => this.onScrollPositionChanged(event));
     this.addListener_(
         EventType.SCROLL_HORIZONTAL_POSITION_CHANGED,
-        this.onScrollPositionChanged);
+        (event: AutomationEvent) => this.onScrollPositionChanged(event));
     this.addListener_(
         EventType.SCROLL_VERTICAL_POSITION_CHANGED,
-        this.onScrollPositionChanged);
+        (event: AutomationEvent) => this.onScrollPositionChanged(event));
     // Called when a same-page link is followed or the url fragment changes.
-    this.addListener_(EventType.SCROLLED_TO_ANCHOR, this.onScrolledToAnchor);
-    this.addListener_(EventType.SELECTION, this.onSelection);
     this.addListener_(
-        EventType.TEXT_SELECTION_CHANGED, this.onEditableChanged_);
+        EventType.SCROLLED_TO_ANCHOR,
+        (event: AutomationEvent) => this.onScrolledToAnchor(event));
     this.addListener_(
-        EventType.VALUE_IN_TEXT_FIELD_CHANGED, this.onEditableChanged_);
+        EventType.SELECTION,
+        (event: AutomationEvent) => this.onSelection(event));
     this.addListener_(
-        EventType.VALUE_CHANGED, event => this.onValueChanged_(event));
+        EventType.TEXT_SELECTION_CHANGED,
+        (event: AutomationEvent) => this.onEditableChanged_(event));
+    this.addListener_(
+        EventType.VALUE_IN_TEXT_FIELD_CHANGED,
+        (event: AutomationEvent) => this.onEditableChanged_(event));
+    this.addListener_(
+        EventType.VALUE_CHANGED,
+        (event: AutomationEvent) => this.onValueChanged_(event));
     this.addListener_(
         EventType.AUTOFILL_AVAILABILITY_CHANGED,
         this.onAutofillAvailabilityChanged);
-    this.addListener_(EventType.ORIENTATION_CHANGED, this.onOrientationChanged);
+    this.addListener_(
+        EventType.ORIENTATION_CHANGED,
+        (event: AutomationEvent) => this.onOrientationChanged(event));
     // Called when a child MenuItem is collapsed.
-    this.addListener_(EventType.COLLAPSED, this.onMenuItemCollapsed);
+    this.addListener_(
+        EventType.COLLAPSED,
+        (event: AutomationEvent) => this.onMenuItemCollapsed(event));
 
     await AutomationObjectConstructorInstaller.init(node);
     const focus = await AsyncUtil.getFocus();
@@ -157,25 +173,19 @@
     }
   }
 
-  /** @type {TextEditHandler} */
-  get textEditHandler() {
-    return this.textEditHandler_;
-  }
-
-  /** @override */
-  willHandleEvent_(evt) {
-    return false;
+  override get textEditHandler(): TextEditHandler|undefined {
+    return this.textEditHandler_ ?? undefined;
   }
 
   /**
    * Handles the result of a hit test.
-   * @param {!AutomationNode} node The hit result.
    */
-  onHitTestResult(node) {
+  onHitTestResult(node: AutomationNode): void {
     // It's possible the |node| hit has lost focus (via its root).
-    const host = node.root.parent;
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    const host = node.root!.parent;
     if (node.parent && host && host.role === RoleType.WEB_VIEW &&
-        !host.state.focused) {
+        !host.state![StateType.FOCUSED]) {
       return;
     }
 
@@ -193,11 +203,10 @@
       }
     }
 
-    chrome.automation.getFocus(function(focus) {
+    chrome.automation.getFocus(function(focus: AutomationNode|undefined) {
       if (!focus && !node) {
         return;
       }
-
       focus = node || focus;
       const focusedRoot = AutomationUtil.getTopLevelRoot(focus);
       const output = new Output();
@@ -209,17 +218,17 @@
       // results should generate output.
       const range = CursorRange.fromNode(focus);
       ChromeVoxRange.set(range);
-      output.withRichSpeechAndBraille(range, null, OutputCustomEvent.NAVIGATE)
+      output
+          .withRichSpeechAndBraille(
+              range, undefined, OutputCustomEvent.NAVIGATE)
           .go();
     });
   }
 
   /**
    * Makes an announcement without changing focus.
-   * @param {!ChromeVoxEvent} evt
-   * @private
    */
-  onAlert_(evt) {
+  private onAlert_(evt: ChromeVoxEvent): void {
     const node = evt.target;
     const range = CursorRange.fromNode(node);
     const output = new Output();
@@ -230,9 +239,9 @@
       output.withInitialSpeechProperties(Personality.DICTATION_HINT);
     }
     output.withSpeechCategory(TtsCategory.LIVE)
-        .withSpeechAndBraille(range, null, evt.type);
+        .withSpeechAndBraille(range, undefined, evt.type);
 
-    const alertDelayMet = new Date() - this.lastAlertTime_ >
+    const alertDelayMet = new Date().getTime() - this.lastAlertTime_.getTime() >
         DesktopAutomationHandler.MIN_ALERT_DELAY_MS;
     if (!alertDelayMet && output.toString() === this.lastAlertText_) {
       return;
@@ -247,24 +256,16 @@
     }
   }
 
-  /**
-   * @param {!ChromeVoxEvent} evt
-   * @private
-   */
-  onBlur_(evt) {
+  private onBlur_(): void {
     // Nullify focus if it no longer exists.
-    chrome.automation.getFocus(function(focus) {
+    chrome.automation.getFocus(function(focus: AutomationNode|undefined) {
       if (!focus) {
         ChromeVoxRange.set(null);
       }
     });
   }
 
-  /**
-   * @param {!ChromeVoxEvent} evt
-   * @private
-   */
-  onDocumentSelectionChanged_(evt) {
+  private onDocumentSelectionChanged_(evt: ChromeVoxEvent): void {
     let selectionStart = evt.target.selectionStartObject;
 
     // No selection.
@@ -279,13 +280,14 @@
     }
 
     // Editable selection.
-    if (selectionStart.state[StateType.EDITABLE]) {
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    if (selectionStart.state![StateType.EDITABLE]) {
       selectionStart =
           AutomationUtil.getEditableRoot(selectionStart) ?? selectionStart;
       this.onEditableChanged_(
           new CustomAutomationEvent(evt.type, selectionStart, {
             eventFrom: evt.eventFrom,
-            eventFromAction: evt.eventFromAction,
+            eventFromAction: (evt as CustomAutomationEvent).eventFromAction,
             intents: evt.intents,
           }));
     }
@@ -295,17 +297,16 @@
 
   /**
    * Provides all feedback once a focus event fires.
-   * @param {!ChromeVoxEvent} evt
-   * @private
    */
-  onFocus_(evt) {
-    let node = evt.target;
+  private onFocus_(evt: ChromeVoxEvent): void {
+    let node: AutomationNode|null = evt.target;
     const isRootWebArea = node.role === RoleType.ROOT_WEB_AREA;
     const isFrame = isRootWebArea && node.parent && node.parent.root &&
         node.parent.root.role === RoleType.ROOT_WEB_AREA;
     if (isRootWebArea && !isFrame && evt.eventFrom !== 'action') {
       chrome.automation.getFocus(
-          focus => this.maybeRecoverFocusAndOutput_(evt, focus));
+          (focus: AutomationNode|undefined) =>
+              this.maybeRecoverFocusAndOutput_(evt, focus));
       return;
     }
 
@@ -351,7 +352,7 @@
 
     const event = new CustomAutomationEvent(EventType.FOCUS, node, {
       eventFrom: evt.eventFrom,
-      eventFromAction: evt.eventFromAction,
+      eventFromAction: (evt as CustomAutomationEvent).eventFromAction,
       intents: evt.intents,
     });
     this.onEventDefault(event);
@@ -364,20 +365,14 @@
     this.isSubMenuShowing_ = false;
   }
 
-  /**
-   * @param {!ChromeVoxEvent} evt
-   * @private
-   */
-  onLiveRegionChanged_(evt) {
+  private onLiveRegionChanged_(evt: ChromeVoxEvent): void {
     LiveRegions.announceDesktopLiveRegionChanged(evt.target);
   }
 
   /**
    * Provides all feedback once a load complete event fires.
-   * @param {!ChromeVoxEvent} evt
-   * @private
    */
-  onLoadComplete_(evt) {
+  onLoadComplete_(evt: ChromeVoxEvent): void {
     // We are only interested in load completes on valid top level roots.
     const top = AutomationUtil.getTopLevelRoot(evt.target);
     if (!top || top !== evt.target.root || !top.docUrl) {
@@ -385,15 +380,18 @@
     }
 
     this.lastRootUrl_ = '';
-    chrome.automation.getFocus(focus => {
+    chrome.automation.getFocus((focus: AutomationNode|undefined) => {
       // In some situations, ancestor windows get focused before a descendant
       // webView/rootWebArea. In particular, a window that gets opened but no
       // inner focus gets set. We catch this generically by re-targetting focus
       // if focus is the ancestor of the load complete target (below).
+      if (!focus) {
+        return;
+      }
       const focusIsAncestor = AutomationUtil.isDescendantOf(evt.target, focus);
       const focusIsDescendant =
           AutomationUtil.isDescendantOf(focus, evt.target);
-      if (!focus || (!focusIsAncestor && !focusIsDescendant)) {
+      if (!focusIsAncestor && !focusIsDescendant) {
         return;
       }
 
@@ -422,54 +420,50 @@
 
   /**
    * Sets whether document selections from actions should be ignored.
-   * @param {boolean} val
-   * @override
    */
-  ignoreDocumentSelectionFromAction(val) {
+  override ignoreDocumentSelectionFromAction(val: boolean): void {
     this.shouldIgnoreDocumentSelectionFromAction_ = val;
   }
 
-  /** @override */
-  onNativeNextOrPreviousCharacter() {
+  override onNativeNextOrPreviousCharacter(): void {
     if (this.textEditHandler) {
       this.textEditHandler.injectInferredIntents([{
-        command: chrome.automation.IntentCommandType.MOVE_SELECTION,
-        textBoundary: chrome.automation.IntentTextBoundaryType.CHARACTER,
+        command: IntentCommandType.MOVE_SELECTION,
+        textBoundary: IntentTextBoundaryType.CHARACTER,
       }]);
     }
   }
 
-  /** @override */
-  onNativeNextOrPreviousWord(isNext) {
+  override onNativeNextOrPreviousWord(isNext: boolean): void {
     if (this.textEditHandler) {
       this.textEditHandler.injectInferredIntents([{
-        command: chrome.automation.IntentCommandType.MOVE_SELECTION,
-        textBoundary: isNext ?
-            chrome.automation.IntentTextBoundaryType.WORD_END :
-            chrome.automation.IntentTextBoundaryType.WORD_START,
+        command: IntentCommandType.MOVE_SELECTION,
+        textBoundary: isNext ? IntentTextBoundaryType.WORD_END :
+                               IntentTextBoundaryType.WORD_START,
       }]);
     }
   }
 
   /**
    * Provides all feedback once a change event in a text field fires.
-   * @param {!ChromeVoxEvent} evt
-   * @private
    */
-  onEditableChanged_(evt) {
-    if (!evt.target.state.editable) {
+  private onEditableChanged_(evt: ChromeVoxEvent): void {
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    if (!evt.target.state![StateType.EDITABLE]) {
       return;
     }
 
     // Skip all unfocused text fields.
-    if (!evt.target.state[StateType.FOCUSED] &&
-        evt.target.state[StateType.EDITABLE]) {
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    if (!evt.target.state![StateType.FOCUSED] &&
+        evt.target.state![StateType.EDITABLE]) {
       return;
     }
 
     const isInput = evt.target.htmlTag === 'input';
     const isTextArea = evt.target.htmlTag === 'textarea';
-    const isContentEditable = evt.target.state[StateType.RICHLY_EDITABLE];
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    const isContentEditable = evt.target.state![StateType.RICHLY_EDITABLE];
 
     switch (evt.type) {
       case EventType.DOCUMENT_SELECTION_CHANGED:
@@ -500,6 +494,7 @@
         if (isContentEditable || isInput || isTextArea) {
           return;
         }
+        break;
       default:
         return;
     }
@@ -514,10 +509,11 @@
     }
 
     // Sync the ChromeVox range to the editable, if a selection exists.
-    const selectionStartObject = evt.target.root.selectionStartObject;
-    const selectionStartOffset = evt.target.root.selectionStartOffset || 0;
-    const selectionEndObject = evt.target.root.selectionEndObject;
-    const selectionEndOffset = evt.target.root.selectionEndOffset || 0;
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    const selectionStartObject = evt.target.root!.selectionStartObject;
+    const selectionStartOffset = evt.target.root!.selectionStartOffset || 0;
+    const selectionEndObject = evt.target.root!.selectionEndObject;
+    const selectionEndOffset = evt.target.root!.selectionEndOffset || 0;
     if (selectionStartObject && selectionEndObject) {
       // Sync to the selection's deep equivalent especially in editables, where
       // selection is often on the root text field with a child offset.
@@ -528,20 +524,21 @@
               .deepEquivalent);
 
       // Sync ChromeVox range with selection.
-      if (!ChromeVoxState.instance.isReadingContinuously) {
+      // TODO(crbug.com/314203187): Not null asserted, check that this is
+      // correct.
+      if (!ChromeVoxState.instance!.isReadingContinuously) {
         ChromeVoxRange.set(selectedRange, true /* from editing */);
       }
     }
-    this.textEditHandler_.onEvent(evt);
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    this.textEditHandler_!.onEvent(evt);
   }
 
   /**
    * Provides all feedback once a rangeValueChanged or a valueInTextFieldChanged
    * event fires.
-   * @param {!ChromeVoxEvent} evt
-   * @private
    */
-  onValueChanged_(evt) {
+  private onValueChanged_(evt: ChromeVoxEvent): void {
     // Skip root web areas.
     if (evt.target.role === RoleType.ROOT_WEB_AREA) {
       return;
@@ -549,7 +546,8 @@
 
     // Delegate to the edit text handler if this is an editable, with the
     // exception of spin buttons.
-    if (evt.target.state[StateType.EDITABLE] &&
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    if (evt.target.state![StateType.EDITABLE] &&
         evt.target.role !== RoleType.SPIN_BUTTON) {
       // If a value changed event came from NTP Searchbox input, announce the
       // new value. This is a special behavior for NTP Searchbox to announce
@@ -560,10 +558,12 @@
       // TODO(crbug.com/328824322): Remove the special behavior and implement
       // the active-descendant-based approach in NTP Searchbox when
       // crbug.com/346835896 lands in the stable.
-      if (evt.target.root.url === DesktopAutomationHandler.NTP_URL &&
+      // TODO(crbug.com/314203187): Not null asserted, check that this is
+      // correct.
+      if (evt.target.root!.url === DesktopAutomationHandler.NTP_URL &&
           evt.target.htmlTag === 'input' && !evt.intents?.length) {
         new Output()
-            .withString(evt.target.value)
+            .withString(evt.target.value!)
             .withSpeechCategory(TtsCategory.NAV)
             .withQueueMode(QueueMode.CATEGORY_FLUSH)
             .withoutFocusRing()
@@ -575,9 +575,9 @@
     }
 
     const target = evt.target;
-    const fromDesktop = target.root.role === RoleType.DESKTOP;
+    const fromDesktop = target.root!.role === RoleType.DESKTOP;
     const onDesktop =
-        ChromeVoxRange.current?.start.node.root.role === RoleType.DESKTOP;
+        ChromeVoxRange.current?.start.node.root!.role === RoleType.DESKTOP;
     const isSlider = target.role === RoleType.SLIDER;
 
     // TODO(accessibility): get rid of callers who use value changes on list
@@ -590,13 +590,15 @@
       return;
     }
 
-    if (!target.state.focused && (!fromDesktop || (!isSlider && !isListBox)) &&
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    if (!target.state![StateType.FOCUSED] &&
+        (!fromDesktop || (!isSlider && !isListBox)) &&
         !AutomationUtil.isDescendantOf(
-            ChromeVoxRange.current.start.node, target)) {
+            ChromeVoxRange.current?.start.node!, target)) {
       return;
     }
 
-    if (new Date() - this.lastValueChanged_ <=
+    if (new Date().getTime() - this.lastValueChanged_.getTime() <=
         DesktopAutomationHandler.MIN_VALUE_CHANGE_DELAY_MS) {
       return;
     }
@@ -622,12 +624,11 @@
 
   /**
    * Handle updating the active indicator when the document scrolls.
-   * @param {!ChromeVoxEvent} evt
    */
-  onScrollPositionChanged(evt) {
+  onScrollPositionChanged(evt: ChromeVoxEvent): void {
     const currentRange = ChromeVoxRange.current;
     if (currentRange && currentRange.isValid()) {
-      new Output().withLocation(currentRange, null, evt.type).go();
+      new Output().withLocation(currentRange, undefined, evt.type).go();
 
       if (EventSource.get() !== EventSourceType.TOUCH_GESTURE) {
         return;
@@ -639,9 +640,11 @@
       }
 
       const currentPage = Math.ceil(root.scrollY / root.location.height) || 1;
+      // TODO(crbug.com/314203187): Not null asserted, check that this is
+      // correct.
       const totalPages =
           Math.ceil(
-              (root.scrollYMax - root.scrollYMin) / root.location.height) ||
+              (root.scrollYMax! - root.scrollYMin!) / root.location.height) ||
           1;
 
       // Ignore announcements if we've already announced something for this page
@@ -654,27 +657,28 @@
       this.currentPage_ = currentPage;
       this.totalPages_ = totalPages;
       ChromeVox.tts.speak(
-          Msgs.getMsg('describe_pos_by_page', [currentPage, totalPages]),
+          Msgs.getMsg(
+              'describe_pos_by_page',
+              [String(currentPage), String(totalPages)]),
           QueueMode.QUEUE);
     }
   }
 
-  /**
-   * @param {!ChromeVoxEvent} evt
-   */
-  onSelection(evt) {
+  onSelection(evt: ChromeVoxEvent): void {
     // Invalidate any previous editable text handler state since some nodes,
     // like menuitems, can receive selection while focus remains on an
     // editable leading to braille output routing to the editable.
     this.textEditHandler_ = null;
 
-    chrome.automation.getFocus(focus => {
+    chrome.automation.getFocus((focus: AutomationNode|undefined) => {
       const target = evt.target;
 
       // Desktop tabs get "selection" when there's a focused webview during
       // tab switching.
+      // TODO(crbug.com/314203187): Not null asserted, check that this is
+      // correct.
       if (target.role === RoleType.TAB &&
-          target.root.role === RoleType.DESKTOP) {
+          target.root!.role === RoleType.DESKTOP) {
         // Read it only if focus is on the
         // omnibox. We have to resort to this check to get tab switching read
         // out because on switching to a new tab, focus actually remains on the
@@ -695,8 +699,11 @@
       }
 
       let override = false;
+      // TODO(crbug.com/314203187): Not null asserted, check that this is
+      // correct.
       const isDesktop =
-          (target.root === focus.root && focus.root.role === RoleType.DESKTOP);
+          (focus && target.root === focus.root &&
+           focus.root!.role === RoleType.DESKTOP);
 
       // TableView fires selection events on rows/cells
       // and we want to ignore those because it also fires focus events.
@@ -710,7 +717,7 @@
       // This shouldn't move ChromeVoxRange to keep editing work.
       if (target.role === RoleType.IME_CANDIDATE) {
         const range = CursorRange.fromNode(target);
-        new Output().withRichSpeech(range, null, evt.type).go();
+        new Output().withRichSpeech(range, undefined, evt.type).go();
         return;
       }
 
@@ -721,7 +728,7 @@
 
       // Overview mode should allow selections.
       if (isDesktop) {
-        let walker = target;
+        let walker: AutomationNode|undefined = target;
         while (walker && walker.className !== 'OverviewDeskBarWidget' &&
                walker.className !== 'OverviewModeLabel' &&
                walker.className !== 'Desk_Container_A') {
@@ -751,12 +758,12 @@
 
       // The popup view associated with a datalist element does not descend
       // from the input with which it is associated.
-      if (focus.role === RoleType.TEXT_FIELD_WITH_COMBO_BOX &&
+      if (focus?.role === RoleType.TEXT_FIELD_WITH_COMBO_BOX &&
           target.role === RoleType.LIST_BOX_OPTION) {
         override = true;
       }
 
-      if (override || AutomationUtil.isDescendantOf(target, focus)) {
+      if (override || (focus && AutomationUtil.isDescendantOf(target, focus))) {
         this.onEventDefault(evt);
       }
     });
@@ -764,34 +771,30 @@
 
   /**
    * Provides all feedback once a menu end event fires.
-   * @param {!ChromeVoxEvent} evt
-   * @private
    */
-  onMenuEnd_(evt) {
+  private onMenuEnd_(_evt: ChromeVoxEvent): void {
     // This is a work around for Chrome context menus not firing a focus event
     // after you close them.
-    chrome.automation.getFocus(focus => {
-      if (focus) {
-        // Directly output the node here; do not go through |onFocus_| as it
-        // contains a lot of logic that can move the selection (if in an
-        // editable).
-        const range = CursorRange.fromNode(focus);
-        new Output()
-            .withRichSpeechAndBraille(range, null, OutputCustomEvent.NAVIGATE)
-            .go();
-        ChromeVoxRange.set(range);
+    chrome.automation.getFocus((focus: AutomationNode|undefined) => {
+      // Directly output the node here; do not go through |onFocus_| as it
+      // contains a lot of logic that can move the selection (if in an
+      // editable).
+      if (!focus) {
+        return;
       }
+      const range = CursorRange.fromNode(focus);
+      new Output()
+          .withRichSpeechAndBraille(
+              range, undefined, OutputCustomEvent.NAVIGATE)
+          .go();
+      ChromeVoxRange.set(range);
     });
 
     // Reset the state to stop handling a Collapsed event.
     this.isSubMenuShowing_ = false;
   }
 
-  /**
-   * @param {!ChromeVoxEvent} event
-   * @private
-   */
-  onMenuPopupStart_(event) {
+  onMenuPopupStart_(event: ChromeVoxEvent): void {
     // Handles a MenuPopupStart event only if it's from a menu node. This event
     // will be fired from a menu node, instead of a menu item node, when its
     // sub-menu gets expanded.
@@ -802,20 +805,15 @@
     this.isSubMenuShowing_ = true;
   }
 
-  /**
-   * @param {!ChromeVoxEvent} event
-   * @private
-   */
-  onMenuStart_(event) {
+  private onMenuStart_(event: ChromeVoxEvent): void {
     Output.forceModeForNextSpeechUtterance(QueueMode.CATEGORY_FLUSH);
     this.onEventDefault(event);
   }
 
   /**
    * Provides all feedback once a scrolled to anchor event fires.
-   * @param {!ChromeVoxEvent} evt
    */
-  onScrolledToAnchor(evt) {
+  onScrolledToAnchor(evt: ChromeVoxEvent): void {
     if (!evt.target) {
       return;
     }
@@ -836,11 +834,11 @@
 
   /**
    * Handles autofill availability changes.
-   * @param {!ChromeVoxEvent} evt
    */
-  onAutofillAvailabilityChanged(evt) {
+  onAutofillAvailabilityChanged(evt: ChromeVoxEvent): void {
     const node = evt.target;
-    const state = node.state;
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    const state = node.state!;
     const currentRange = ChromeVoxRange.current;
 
     // Notify the user about available autofill options on focused element.
@@ -848,7 +846,7 @@
         state[StateType.AUTOFILL_AVAILABLE]) {
       new Output()
           .withString(Msgs.getMsg('hint_autocomplete_list'))
-          .withLocation(currentRange, null, evt.type)
+          .withLocation(currentRange, undefined, evt.type)
           .withQueueMode(QueueMode.QUEUE)
           .go();
     }
@@ -856,26 +854,27 @@
 
   /**
    * Handles orientation changes on the desktop node.
-   * @param {!ChromeVoxEvent} evt
    */
-  onOrientationChanged(evt) {
+  onOrientationChanged(evt: ChromeVoxEvent): void {
     // Changes on display metrics result in the desktop node's
     // vertical/horizontal states changing.
     if (evt.target.role === RoleType.DESKTOP) {
-      const msg = evt.target.state[StateType.HORIZONTAL] ? 'device_landscape' :
-                                                           'device_portrait';
+      // TODO(crbug.com/314203187): Not null asserted, check that this is
+      // correct.
+      const msg = evt.target.state![StateType.HORIZONTAL] ? 'device_landscape' :
+                                                            'device_portrait';
       new Output().format('@' + msg).go();
     }
   }
 
   /**
    * Handles focus back to a parent MenuItem when its child is collapsed.
-   * @param {!ChromeVoxEvent} evt
    */
-  onMenuItemCollapsed(evt) {
+  onMenuItemCollapsed(evt: ChromeVoxEvent): void {
     const target = evt.target;
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
     if (!this.isSubMenuShowing_ || !AutomationPredicate.menuItem(target) ||
-        !target.state.collapsed || !target.selected) {
+        !target.state![StateType.COLLAPSED] || !target.selected) {
       return;
     }
 
@@ -884,13 +883,15 @@
 
   /**
    * Create an editable text handler for the given node if needed.
-   * @param {!AutomationNode} node
-   * @param {boolean=} opt_onFocus True if called within a focus event
+   * @param node
+   * @param opt_onFocus True if called within a focus event
    *     handler. False by default.
-   * @return {boolean} True if the handler exists (created/already present).
+   * @return True if the handler exists (created/already present).
    */
-  createTextEditHandlerIfNeeded_(node, opt_onFocus) {
-    if (!node.state.editable) {
+  createTextEditHandlerIfNeeded_(node: AutomationNode, opt_onFocus?: boolean):
+      boolean {
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    if (!node.state![StateType.EDITABLE]) {
       return false;
     }
 
@@ -900,27 +901,29 @@
     }
 
     const topRoot = AutomationUtil.getTopLevelRoot(node);
-    if (!node.state.focused ||
-        (topRoot && topRoot.parent && !topRoot.parent.state.focused)) {
+    if (!node.state![StateType.FOCUSED] ||
+        (topRoot && topRoot.parent &&
+         !topRoot.parent.state![StateType.FOCUSED])) {
       return false;
     }
 
     // Re-target the node to the root of the editable.
-    let target = node;
-    target = AutomationUtil.getEditableRoot(target);
+    const target: AutomationNode|undefined =
+        AutomationUtil.getEditableRoot(node);
     let voxTarget = ChromeVoxRange.current.start.node;
     voxTarget = AutomationUtil.getEditableRoot(voxTarget) || voxTarget;
 
     // It is possible that ChromeVox has range over some other node when a
     // text field is focused. Only allow this when focus is on a desktop node,
     // ChromeVox is over the keyboard, or during focus events.
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
     if (!target || !voxTarget ||
         (!opt_onFocus && target !== voxTarget &&
-         target.root.role !== RoleType.DESKTOP &&
-         voxTarget.root.role !== RoleType.DESKTOP &&
+         target.root!.role !== RoleType.DESKTOP &&
+         voxTarget.root!.role !== RoleType.DESKTOP &&
          !AutomationUtil.isDescendantOf(target, voxTarget) &&
-         !AutomationUtil.getAncestors(voxTarget.root)
-              .find(n => n.role === RoleType.KEYBOARD))) {
+         !AutomationUtil.getAncestors(voxTarget.root!)
+              .find((n: AutomationNode) => n.role === RoleType.KEYBOARD))) {
       return false;
     }
 
@@ -931,11 +934,11 @@
     return Boolean(this.textEditHandler_);
   }
 
-  /**
-   * @param {ChromeVoxEvent} evt
-   * @private
-   */
-  maybeRecoverFocusAndOutput_(evt, focus) {
+  private maybeRecoverFocusAndOutput_(
+      evt: ChromeVoxEvent, focus: AutomationNode|undefined): void {
+    if (!focus) {
+      return;
+    }
     const focusedRoot = AutomationUtil.getTopLevelRoot(focus);
     if (!focusedRoot) {
       return;
@@ -958,7 +961,8 @@
     this.lastRootUrl_ = focusedRoot.docUrl || '';
     const o = new Output();
     // Restore to previous position.
-    let url = focusedRoot.docUrl;
+    // TODO(crbug.com/314203187): Not null asserted, check that this is correct.
+    let url = focusedRoot.docUrl!;
     url = url.substring(0, url.indexOf('#')) || url;
     const pos = ChromeVoxState.position[url];
 
@@ -986,7 +990,8 @@
       return;
     }
 
-    o.withRichSpeechAndBraille(ChromeVoxRange.current, null, evt.type).go();
+    o.withRichSpeechAndBraille(ChromeVoxRange.current, undefined, evt.type)
+        .go();
 
     // Reset `isSubMenuShowing_` when a focus changes because focus
     // changes should automatically close any menus.
@@ -994,7 +999,7 @@
   }
 
   /** Initializes global state for DesktopAutomationHandler. */
-  static async init() {
+  static async init(): Promise<void> {
     if (DesktopAutomationInterface.instance) {
       throw new Error('DesktopAutomationInterface.instance already exists.');
     }
@@ -1004,22 +1009,4 @@
   }
 }
 
-/**
- * Time to wait until processing more value changed events.
- * @const {number}
- */
-DesktopAutomationHandler.MIN_VALUE_CHANGE_DELAY_MS = 50;
-
-/**
- * Time to wait until processing more alert events with the same text content.
- * @const {number}
- */
-DesktopAutomationHandler.MIN_ALERT_DELAY_MS = 50;
-
-/**
- * URL for NTP (New tap page).
- * @const {string}
- */
-DesktopAutomationHandler.NTP_URL = 'chrome://new-tab-page/';
-
 TestImportManager.exportForTesting(DesktopAutomationHandler);
diff --git a/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts b/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts
index 951a9bf0..49d0046 100644
--- a/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts
+++ b/chrome/browser/resources/chromeos/accessibility/definitions/automation.d.ts
@@ -632,7 +632,7 @@
     export interface AutomationIntent {
       command: IntentCommandType;
       textBoundary: IntentTextBoundaryType;
-      moveDirection: IntentMoveDirectionType;
+      moveDirection?: IntentMoveDirectionType;
     }
 
     export interface AutomationEvent {
diff --git a/chrome/browser/sync/BUILD.gn b/chrome/browser/sync/BUILD.gn
index 7c545fa2..b0373d2 100644
--- a/chrome/browser/sync/BUILD.gn
+++ b/chrome/browser/sync/BUILD.gn
@@ -2,7 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/chromeos/ui_mode.gni")
 import("//components/supervised_user/buildflags.gni")
 import("//extensions/buildflags/buildflags.gni")
 
@@ -181,7 +180,7 @@
       ]
     }
   }
-  if (is_chromeos_ash) {
+  if (is_chromeos) {
     sources += [
       "desk_sync_service_factory.cc",
       "desk_sync_service_factory.h",
diff --git a/chrome/browser/sync/chrome_sync_controller_builder.cc b/chrome/browser/sync/chrome_sync_controller_builder.cc
index 2185a23d..24c49e2 100644
--- a/chrome/browser/sync/chrome_sync_controller_builder.cc
+++ b/chrome/browser/sync/chrome_sync_controller_builder.cc
@@ -11,7 +11,6 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/security_events/security_event_recorder.h"
 #include "chrome/browser/spellchecker/spellcheck_service.h"
@@ -114,7 +113,7 @@
 }
 #endif  // BUILDFLAG(IS_ANDROID)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 void ChromeSyncControllerBuilder::SetAppListSyncableService(
     app_list::AppListSyncableService* app_list_syncable_service) {
   app_list_syncable_service_.Set(app_list_syncable_service);
@@ -162,7 +161,7 @@
         wifi_configuration_sync_service) {
   wifi_configuration_sync_service_.Set(wifi_configuration_sync_service);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 std::vector<std::unique_ptr<syncer::DataTypeController>>
 ChromeSyncControllerBuilder::Build(syncer::SyncService* sync_service) {
@@ -295,100 +294,102 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
 #endif  // BUILDFLAG(ENABLE_SPELLCHECK)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-  CHECK(os_pref_service_syncable_.value());
-  controllers.push_back(
-      std::make_unique<syncer::SyncableServiceBasedDataTypeController>(
-          syncer::OS_PREFERENCES, data_type_store_factory,
-          os_pref_service_syncable_.value()
-              ->GetSyncableService(syncer::OS_PREFERENCES)
-              ->AsWeakPtr(),
-          dump_stack,
-          syncer::SyncableServiceBasedDataTypeController::DelegateMode::
-              kTransportModeWithSingleModel));
-  controllers.push_back(
-      std::make_unique<syncer::SyncableServiceBasedDataTypeController>(
-          syncer::OS_PRIORITY_PREFERENCES, data_type_store_factory,
-          os_pref_service_syncable_.value()
-              ->GetSyncableService(syncer::OS_PRIORITY_PREFERENCES)
-              ->AsWeakPtr(),
-          dump_stack,
-          syncer::SyncableServiceBasedDataTypeController::DelegateMode::
-              kTransportModeWithSingleModel));
-
-  CHECK(synced_printer_manager_.value());
-  controllers.push_back(std::make_unique<syncer::DataTypeController>(
-      syncer::PRINTERS,
-      std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
-          synced_printer_manager_.value()
-              ->GetSyncBridge()
-              ->change_processor()
-              ->GetControllerDelegate()
-              .get()),
-      /*delegate_for_transport_mode=*/nullptr));
-
-  // Some profile types (e.g. sign-in screen) don't support app list.
-  // Temporarily Disable AppListSyncableService for tablet form factor devices.
-  // See crbug/1013732 for details.
-  if (app_list_syncable_service_.value() &&
-      !ash::switches::IsTabletFormFactor()) {
-    // Runs in sync transport-mode and full-sync mode.
+#if BUILDFLAG(IS_CHROMEOS)
+    CHECK(os_pref_service_syncable_.value());
     controllers.push_back(
         std::make_unique<syncer::SyncableServiceBasedDataTypeController>(
-            syncer::APP_LIST, data_type_store_factory,
-            app_list_syncable_service_.value()->AsWeakPtr(), dump_stack,
+            syncer::OS_PREFERENCES, data_type_store_factory,
+            os_pref_service_syncable_.value()
+                ->GetSyncableService(syncer::OS_PREFERENCES)
+                ->AsWeakPtr(),
+            dump_stack,
             syncer::SyncableServiceBasedDataTypeController::DelegateMode::
                 kTransportModeWithSingleModel));
-  }
+    controllers.push_back(
+        std::make_unique<syncer::SyncableServiceBasedDataTypeController>(
+            syncer::OS_PRIORITY_PREFERENCES, data_type_store_factory,
+            os_pref_service_syncable_.value()
+                ->GetSyncableService(syncer::OS_PRIORITY_PREFERENCES)
+                ->AsWeakPtr(),
+            dump_stack,
+            syncer::SyncableServiceBasedDataTypeController::DelegateMode::
+                kTransportModeWithSingleModel));
 
-  if (arc_package_syncable_service_.value()) {
-    controllers.push_back(std::make_unique<ArcPackageSyncDataTypeController>(
-        data_type_store_factory,
-        arc_package_syncable_service_.value()->AsWeakPtr(), dump_stack,
-        sync_service, arc_package_profile_.value()));
-  }
-
-  if (wifi_configuration_sync_service_.value()) {
-    syncer::DataTypeControllerDelegate* wifi_configurations_delegate =
-        wifi_configuration_sync_service_.value()->GetControllerDelegate().get();
+    CHECK(synced_printer_manager_.value());
     controllers.push_back(std::make_unique<syncer::DataTypeController>(
-        syncer::WIFI_CONFIGURATIONS,
+        syncer::PRINTERS,
         std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
-            wifi_configurations_delegate),
-        /*delegate_for_transport_mode=*/nullptr));
-  }
-
-  CHECK(desk_sync_service_.value());
-  controllers.push_back(std::make_unique<syncer::DataTypeController>(
-      syncer::WORKSPACE_DESK,
-      std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
-          desk_sync_service_.value()->GetControllerDelegate().get()),
-      /*delegate_for_transport_mode=*/nullptr));
-
-  if (authorization_zones_manager_.value()) {
-    syncer::DataTypeControllerDelegate*
-        printers_authorization_servers_delegate =
-            authorization_zones_manager_.value()
-                ->GetDataTypeSyncBridge()
+            synced_printer_manager_.value()
+                ->GetSyncBridge()
                 ->change_processor()
                 ->GetControllerDelegate()
-                .get();
-    controllers.push_back(std::make_unique<syncer::DataTypeController>(
-        syncer::PRINTERS_AUTHORIZATION_SERVERS,
-        std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
-            printers_authorization_servers_delegate),
+                .get()),
         /*delegate_for_transport_mode=*/nullptr));
-  }
 
-  if (floating_sso_service_.value()) {
-    controllers.push_back(
-        std::make_unique<ash::floating_sso::CookieSyncDataTypeController>(
-            /*delegate_for_full_sync_mode=*/
-            std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
-                floating_sso_service_.value()->GetControllerDelegate().get()),
-            sync_service, pref_service_.value()));
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+    // Some profile types (e.g. sign-in screen) don't support app list.
+    // Temporarily Disable AppListSyncableService for tablet form factor
+    // devices. See crbug/1013732 for details.
+    if (app_list_syncable_service_.value() &&
+        !ash::switches::IsTabletFormFactor()) {
+      // Runs in sync transport-mode and full-sync mode.
+      controllers.push_back(
+          std::make_unique<syncer::SyncableServiceBasedDataTypeController>(
+              syncer::APP_LIST, data_type_store_factory,
+              app_list_syncable_service_.value()->AsWeakPtr(), dump_stack,
+              syncer::SyncableServiceBasedDataTypeController::DelegateMode::
+                  kTransportModeWithSingleModel));
+    }
 
-  return controllers;
+    if (arc_package_syncable_service_.value()) {
+      controllers.push_back(std::make_unique<ArcPackageSyncDataTypeController>(
+          data_type_store_factory,
+          arc_package_syncable_service_.value()->AsWeakPtr(), dump_stack,
+          sync_service, arc_package_profile_.value()));
+    }
+
+    if (wifi_configuration_sync_service_.value()) {
+      syncer::DataTypeControllerDelegate* wifi_configurations_delegate =
+          wifi_configuration_sync_service_.value()
+              ->GetControllerDelegate()
+              .get();
+      controllers.push_back(std::make_unique<syncer::DataTypeController>(
+          syncer::WIFI_CONFIGURATIONS,
+          std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
+              wifi_configurations_delegate),
+          /*delegate_for_transport_mode=*/nullptr));
+    }
+
+    CHECK(desk_sync_service_.value());
+    controllers.push_back(std::make_unique<syncer::DataTypeController>(
+        syncer::WORKSPACE_DESK,
+        std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
+            desk_sync_service_.value()->GetControllerDelegate().get()),
+        /*delegate_for_transport_mode=*/nullptr));
+
+    if (authorization_zones_manager_.value()) {
+      syncer::DataTypeControllerDelegate*
+          printers_authorization_servers_delegate =
+              authorization_zones_manager_.value()
+                  ->GetDataTypeSyncBridge()
+                  ->change_processor()
+                  ->GetControllerDelegate()
+                  .get();
+      controllers.push_back(std::make_unique<syncer::DataTypeController>(
+          syncer::PRINTERS_AUTHORIZATION_SERVERS,
+          std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
+              printers_authorization_servers_delegate),
+          /*delegate_for_transport_mode=*/nullptr));
+    }
+
+    if (floating_sso_service_.value()) {
+      controllers.push_back(
+          std::make_unique<ash::floating_sso::CookieSyncDataTypeController>(
+              /*delegate_for_full_sync_mode=*/
+              std::make_unique<syncer::ForwardingDataTypeControllerDelegate>(
+                  floating_sso_service_.value()->GetControllerDelegate().get()),
+              sync_service, pref_service_.value()));
+    }
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
+    return controllers;
 }
diff --git a/chrome/browser/sync/chrome_sync_controller_builder.h b/chrome/browser/sync/chrome_sync_controller_builder.h
index c116772..6c4c1b3 100644
--- a/chrome/browser/sync/chrome_sync_controller_builder.h
+++ b/chrome/browser/sync/chrome_sync_controller_builder.h
@@ -14,7 +14,6 @@
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
-#include "build/chromeos_buildflags.h"
 #include "components/prefs/pref_service.h"
 #include "components/spellcheck/spellcheck_buildflags.h"
 #include "extensions/buildflags/buildflags.h"
diff --git a/chrome/browser/sync/device_info_sync_client_impl.cc b/chrome/browser/sync/device_info_sync_client_impl.cc
index 0d86cd0..b0e2fd4 100644
--- a/chrome/browser/sync/device_info_sync_client_impl.cc
+++ b/chrome/browser/sync/device_info_sync_client_impl.cc
@@ -10,7 +10,6 @@
 
 #include "base/feature_list.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/chrome_device_id_helper.h"
@@ -35,17 +34,12 @@
 std::string DeviceInfoSyncClientImpl::GetSigninScopedDeviceId() const {
 // Since the local sync backend is currently only supported on Windows, Mac and
 // Linux don't even check the pref on other os-es.
-// TODO(crbug.com/40118868): Reassess whether the next block needs to be
-// included in lacros-chrome once build flag switch of lacros-chrome is
-// complete.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
-    (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   syncer::SyncPrefs prefs(profile_->GetPrefs());
   if (prefs.IsLocalSyncEnabled()) {
     return "local_device";
   }
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) ||
-        // BUILDFLAG(IS_CHROMEOS_LACROS))
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
   return GetSigninScopedDeviceIdForProfile(profile_);
 }
@@ -96,7 +90,7 @@
 }
 
 bool DeviceInfoSyncClientImpl::IsUmaEnabledOnCrOSDevice() const {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   return ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
 #else
   return false;
diff --git a/chrome/browser/sync/device_info_sync_service_factory.cc b/chrome/browser/sync/device_info_sync_service_factory.cc
index 5531413..2598343 100644
--- a/chrome/browser/sync/device_info_sync_service_factory.cc
+++ b/chrome/browser/sync/device_info_sync_service_factory.cc
@@ -13,7 +13,6 @@
 #include "base/no_destructor.h"
 #include "base/time/default_clock.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
index 5d0e4cc..7ea76f0 100644
--- a/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
+++ b/chrome/browser/sync/prefs/chrome_syncable_prefs_database.cc
@@ -7,6 +7,7 @@
 #include <string_view>
 
 #include "base/containers/fixed_flat_map.h"
+#include "build/build_config.h"
 #include "chrome/browser/promos/promos_pref_names.h"
 #include "chrome/browser/ui/toolbar/toolbar_pref_names.h"
 #include "chrome/browser/ui/webui/side_panel/read_anything/read_anything_prefs.h"
@@ -21,7 +22,8 @@
 #include "components/sync/base/data_type.h"
 #include "components/sync_preferences/syncable_prefs_database.h"
 #include "components/translate/core/browser/translate_prefs.h"
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/shelf_prefs.h"
 #include "chrome/browser/ash/guest_os/guest_os_pref_names.h"
@@ -33,9 +35,11 @@
 #include "components/variations/service/google_groups_manager_prefs.h"
 #include "ui/events/ash/pref_names.h"
 #endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 #include "extensions/browser/pref_names.h"  // nogncheck
 #endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "components/supervised_user/core/common/pref_names.h"
 #endif
@@ -568,7 +572,7 @@
       sync_preferences::PrefSensitivity::kNone,
       sync_preferences::MergeBehavior::kNone}},
 #endif
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     {ash::prefs::kRestoreAppsAndPagesPrefName,
      {syncable_prefs_ids::kRestoreAppsAndPagesPrefName, syncer::OS_PREFERENCES,
       sync_preferences::PrefSensitivity::kNone,
@@ -1274,7 +1278,7 @@
      {syncable_prefs_ids::kKeyboardHasSplitModifierKeyboard,
       syncer::OS_PREFERENCES, sync_preferences::PrefSensitivity::kNone,
       sync_preferences::MergeBehavior::kNone}},
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
     {performance_manager::user_tuning::prefs::kTabDiscardingExceptions,
      {syncable_prefs_ids::kTabDiscardingExceptions, syncer::PREFERENCES,
       sync_preferences::PrefSensitivity::kSensitiveRequiresHistory,
diff --git a/chrome/browser/sync/sync_service_factory.cc b/chrome/browser/sync/sync_service_factory.cc
index 65e434a..6378be2 100644
--- a/chrome/browser/sync/sync_service_factory.cc
+++ b/chrome/browser/sync/sync_service_factory.cc
@@ -12,7 +12,6 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/no_destructor.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/android/webapk/webapk_sync_service_factory.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
diff --git a/chrome/browser/sync/sync_service_factory_unittest.cc b/chrome/browser/sync/sync_service_factory_unittest.cc
index 97121db..0c22982e 100644
--- a/chrome/browser/sync/sync_service_factory_unittest.cc
+++ b/chrome/browser/sync/sync_service_factory_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/test/bind.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/favicon/favicon_service_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -36,7 +35,7 @@
 #include "chrome/browser/spellchecker/spellcheck_service.h"
 #endif  // BUILDFLAG(ENABLE_SPELLCHECK)
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
 #include "chrome/browser/ash/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ash/arc/arc_util.h"
@@ -51,14 +50,10 @@
 class SyncServiceFactoryTest : public testing::Test {
  public:
   void SetUp() override {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     app_list::AppListSyncableServiceFactory::SetUseInTesting(true);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
     TestingProfile::Builder builder;
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-    // Only the main profile enables syncer::WEB_APPS.
-    builder.SetIsMainProfile(true);
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
     builder.AddTestingFactory(FaviconServiceFactory::GetInstance(),
                               FaviconServiceFactory::GetDefaultFactory());
     builder.AddTestingFactory(HistoryServiceFactory::GetInstance(),
@@ -88,9 +83,9 @@
   }
 
   void TearDown() override {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     app_list::AppListSyncableServiceFactory::SetUseInTesting(false);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
     // There may tasks in flight referencing fields owned by the test fixture.
     // Make sure they are flushed now to prevent memory safety errors, e.g.
     // use-after-destruction errors.
@@ -146,7 +141,7 @@
     datatypes.Put(syncer::DICTIONARY);
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     datatypes.Put(syncer::APP_LIST);
     if (arc::IsArcAllowedForProfile(profile())) {
       datatypes.Put(syncer::ARC_PACKAGE);
@@ -162,7 +157,7 @@
     }
     datatypes.Put(syncer::WIFI_CONFIGURATIONS);
     datatypes.Put(syncer::WORKSPACE_DESK);
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
     // Common types. This excludes PASSWORDS,
     // INCOMING_PASSWORD_SHARING_INVITATION and
@@ -221,7 +216,7 @@
   content::BrowserTaskEnvironment task_environment_;
   std::unique_ptr<TestingProfile> profile_;
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // Fake network stack is required for WIFI_CONFIGURATIONS datatype. It's also
   // used by `network_config_helper_`
   ash::NetworkHandlerTestHelper network_handler_test_helper_;
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc
index e43bedfb..9fdf55c8 100644
--- a/chrome/browser/sync/sync_ui_util.cc
+++ b/chrome/browser/sync/sync_ui_util.cc
@@ -9,7 +9,6 @@
 #include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
diff --git a/chrome/browser/sync/sync_ui_util_unittest.cc b/chrome/browser/sync/sync_ui_util_unittest.cc
index 3ebc6187..78f83f0 100644
--- a/chrome/browser/sync/sync_ui_util_unittest.cc
+++ b/chrome/browser/sync/sync_ui_util_unittest.cc
@@ -9,7 +9,7 @@
 #include <utility>
 
 #include "base/test/task_environment.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
diff --git a/chrome/browser/sync/test/integration/apps_sync_test_base.h b/chrome/browser/sync/test/integration/apps_sync_test_base.h
index 74bf50da..342d5fab 100644
--- a/chrome/browser/sync/test/integration/apps_sync_test_base.h
+++ b/chrome/browser/sync/test/integration/apps_sync_test_base.h
@@ -5,13 +5,9 @@
 #ifndef CHROME_BROWSER_SYNC_TEST_INTEGRATION_APPS_SYNC_TEST_BASE_H_
 #define CHROME_BROWSER_SYNC_TEST_INTEGRATION_APPS_SYNC_TEST_BASE_H_
 
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/web_applications/test/web_app_test_utils.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-
 #if BUILDFLAG(IS_WIN)
 #include "base/base_paths_win.h"
 #include "base/test/scoped_path_override.h"
@@ -23,9 +19,6 @@
   ~AppsSyncTestBase() override;
 
  private:
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  web_app::test::ScopedSkipMainProfileCheck skip_main_profile_check_;
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 #if BUILDFLAG(IS_WIN)
   // This stops extension installation from creating a shortcut in the real
   // desktop startup dir. This prevents Chrome launching with the extension
diff --git a/chrome/browser/sync/test/integration/enable_disable_test.cc b/chrome/browser/sync/test/integration/enable_disable_test.cc
index 0d50c7d..e4fca04 100644
--- a/chrome/browser/sync/test/integration/enable_disable_test.cc
+++ b/chrome/browser/sync/test/integration/enable_disable_test.cc
@@ -471,7 +471,7 @@
 // the following tests.
 //
 // ChromeOS does not support signing out of a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, RedownloadsAfterSignout) {
   ASSERT_TRUE(SetupClients());
   ASSERT_FALSE(bookmarks_helper::GetBookmarkModel(0)->IsBookmarked(
@@ -515,7 +515,7 @@
       GURL(kSyncedBookmarkURL)));
   EXPECT_EQ(GetNumUpdatesDownloadedInLastCycle(), initial_updates_downloaded);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
                        DoesNotRedownloadAfterSyncUnpaused) {
diff --git a/chrome/browser/sync/test/integration/local_sync_test.cc b/chrome/browser/sync/test/integration/local_sync_test.cc
index b1712fb2..773feae 100644
--- a/chrome/browser/sync/test/integration/local_sync_test.cc
+++ b/chrome/browser/sync/test/integration/local_sync_test.cc
@@ -9,7 +9,6 @@
 #include "base/files/scoped_temp_dir.h"
 #include "build/branding_buildflags.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/sync/sync_service_factory.h"
@@ -74,10 +73,8 @@
   base::ScopedTempDir local_sync_backend_dir_;
 };
 
-// The local sync backend is currently only supported on Windows, Mac, Linux,
-// and Lacros.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
-    BUILDFLAG(IS_CHROMEOS_LACROS)
+// The local sync backend is currently only supported on Windows, Mac, Linux.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 IN_PROC_BROWSER_TEST_F(LocalSyncTest, ShouldStart) {
   SyncServiceImpl* service =
       SyncServiceFactory::GetAsSyncServiceImplForProfileForTesting(
@@ -128,21 +125,8 @@
     expected_active_data_types.Put(syncer::PRODUCT_COMPARISON);
   }
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // Apps sync is controlled by a dedicated preference on Lacros,
-  // corresponding to the Apps toggle in OS Sync settings. we remove
-  // data types related to the Apps sync toggle.
-  if (base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing)) {
-    expected_active_data_types.RemoveAll(
-        {syncer::APPS, syncer::APP_SETTINGS, syncer::WEB_APPS});
-  }
-#endif
-
-  // The dictionary is currently only synced on Windows, Linux, and Lacros.
-  // TODO(crbug.com/40118868): Reassess whether the following block needs to be
-  // included in lacros-chrome once build flag switch of lacros-chrome is
-  // complete.
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
+  // The dictionary is currently only synced on Windows and Linux.
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
   expected_active_data_types.Put(syncer::DICTIONARY);
 #endif
   EXPECT_EQ(service->GetActiveDataTypes(), expected_active_data_types);
@@ -155,22 +139,7 @@
   EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::SHARING_MESSAGE));
   EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::SEND_TAB_TO_SELF));
   EXPECT_FALSE(service->GetActiveDataTypes().Has(syncer::HISTORY));
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // Apps sync is controlled by a dedicated preference on Lacros,
-  // corresponding to the Apps toggle in OS Sync settings.
-  if (base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing)) {
-    // Enable the Apps Toggle from OS level
-    service->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-    // Wait until Sync has reconfigured itself and becomes active again.
-    ASSERT_TRUE(SyncTransportActiveChecker(service).Wait());
-    expected_active_data_types.PutAll(
-        {syncer::APPS, syncer::APP_SETTINGS, syncer::WEB_APPS});
-    EXPECT_EQ(service->GetActiveDataTypes(), expected_active_data_types);
-  }
-#endif
 }
-#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) ||
-        // BUILDFLAG(IS_CHROMEOS_LACROS))
+#endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/password_manager_sync_test.cc b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
index 6673dd60..e49a89bc 100644
--- a/chrome/browser/sync/test/integration/password_manager_sync_test.cc
+++ b/chrome/browser/sync/test/integration/password_manager_sync_test.cc
@@ -16,7 +16,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/with_feature_override.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/affiliations/affiliation_service_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
@@ -150,11 +149,11 @@
 
 // Note: This helper applies to ChromeOS too, but is currently unused there. So
 // define it out to prevent a compile error due to the unused function.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 content::WebContents* GetNewTab(Browser* browser) {
   return PasswordManagerBrowserTestBase::GetNewTab(browser);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // This test fixture is similar to SingleClientPasswordsSyncTest, but it also
 // sets up all the necessary test hooks etc for PasswordManager code (like
@@ -425,7 +424,7 @@
   AccountInfo signed_in_account_;
 };
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest, UpdateInProfileStore) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
 
@@ -794,8 +793,6 @@
   bubble_observer.WaitForAutomaticUpdatePrompt();
 }
 
-// Signing out on Lacros is not possible,
-#if !BUILDFLAG(IS_CHROMEOS_LACROS)
 IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest,
                        SignOutWithUnsyncedPasswordsOpensBubble) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -821,7 +818,6 @@
   SignOut();
   bubble_observer.WaitForSaveUnsyncedCredentialsPrompt();
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 
@@ -1023,7 +1019,7 @@
               ElementsAre(MatchesLogin("localuser", "localpass")));
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(PasswordManagerSyncTest, SyncUtilApis) {
   ASSERT_TRUE(SetupSync());
@@ -1065,7 +1061,7 @@
             SyncTest::kDefaultUserEmail);
 }
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 class PasswordManagerSyncTestWithPolicy : public PasswordManagerSyncTest {
  public:
   void SetUpInProcessBrowserTestFixture() override {
@@ -1130,6 +1126,6 @@
     waiter.Wait();
   }
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/secondary_account_helper.cc b/chrome/browser/sync/test/integration/secondary_account_helper.cc
index 68a5fd2..be40ea19 100644
--- a/chrome/browser/sync/test/integration/secondary_account_helper.cc
+++ b/chrome/browser/sync/test/integration/secondary_account_helper.cc
@@ -9,7 +9,7 @@
 
 #include "base/containers/flat_map.h"
 #include "base/functional/bind.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_test_util.h"
@@ -21,13 +21,13 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_id.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ash/net/network_portal_detector_test_impl.h"
 #include "chromeos/ash/components/network/network_handler.h"
 #include "chromeos/ash/components/network/network_state.h"
 #include "chromeos/ash/components/network/network_state_handler.h"
 #include "chromeos/ash/components/network/portal_detector/network_portal_detector.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 namespace secondary_account_helper {
 
@@ -81,7 +81,7 @@
           &OnWillCreateBrowserContextServices, test_url_loader_factory));
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 void InitNetwork() {
   auto* portal_detector = new ash::NetworkPortalDetectorTestImpl();
 
@@ -93,7 +93,7 @@
   // Takes ownership.
   ash::network_portal_detector::InitializeForTesting(portal_detector);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 AccountInfo SignInUnconsentedAccount(
     Profile* profile,
@@ -101,13 +101,13 @@
     const std::string& email) {
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile);
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   AccountInfo account_info = signin::MakePrimaryAccountAvailable(
       identity_manager, email, signin::ConsentLevel::kSignin);
 #else
   AccountInfo account_info =
       signin::MakeAccountAvailable(identity_manager, email);
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
   SetCookieForGaiaId(account_info.gaia, account_info.email,
                      /*signed_out=*/false, identity_manager,
                      test_url_loader_factory);
@@ -124,9 +124,9 @@
   AccountInfo account_info = signin::MakeAccountAvailable(
       identity_manager,
       builder
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
           .AsPrimary(signin::ConsentLevel::kSignin)
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
         // `kWebSignin` is not explicit signin.
           .WithAccessPoint(signin_metrics::AccessPoint::kWebSignin)
           .Build(email));
@@ -150,7 +150,7 @@
   signin::RemoveRefreshTokenForPrimaryAccount(identity_manager);
 }
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 void GrantSyncConsent(Profile* profile, const std::string& email) {
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile);
@@ -162,6 +162,6 @@
   primary_account_mutator->SetPrimaryAccount(account.account_id,
                                              signin::ConsentLevel::kSync);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace secondary_account_helper
diff --git a/chrome/browser/sync/test/integration/secondary_account_helper.h b/chrome/browser/sync/test/integration/secondary_account_helper.h
index e353aed..54786a0 100644
--- a/chrome/browser/sync/test/integration/secondary_account_helper.h
+++ b/chrome/browser/sync/test/integration/secondary_account_helper.h
@@ -9,7 +9,6 @@
 #include <string>
 
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/signin/public/identity_manager/account_info.h"
 
@@ -29,7 +28,7 @@
 base::CallbackListSubscription SetUpSigninClient(
     network::TestURLLoaderFactory* test_url_loader_factory);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 // Sets up necessary fakes for fake network responses to work. Meant to be
 // called from SetUpOnMainThread.
 // TODO(crbug.com/40593103): On ChromeOS, we need to set up a fake
@@ -38,7 +37,7 @@
 // the ListAccounts requests (i.e. getting cookie accounts) will never make it
 // far enough to even request our fake response.
 void InitNetwork();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 // Sets an account as primary with `signin::ConsentLevel::kSignin`. There is no
 // consent for Sync. The account is available with both a refresh token and
@@ -62,11 +61,11 @@
 void SignOut(Profile* profile,
              network::TestURLLoaderFactory* test_url_loader_factory);
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 // Grants sync consent to an account (`signin::ConsentLevel::kSync`). The
 // account must already be signed in (per SignInUnconsentedAccount).
 void GrantSyncConsent(Profile* profile, const std::string& email);
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace secondary_account_helper
 
diff --git a/chrome/browser/sync/test/integration/single_client_app_settings_sync_test.cc b/chrome/browser/sync/test/integration/single_client_app_settings_sync_test.cc
index 6c097219..b2e1451 100644
--- a/chrome/browser/sync/test/integration/single_client_app_settings_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_app_settings_sync_test.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 "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/apps_sync_test_base.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "components/sync/base/data_type.h"
@@ -13,10 +13,10 @@
 #include "content/public/test/browser_test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 using syncer::UserSelectableOsType;
 using syncer::UserSelectableOsTypeSet;
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 using syncer::UserSelectableType;
 using syncer::UserSelectableTypeSet;
 
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 15550f73..35fc248 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -18,7 +18,6 @@
 #include "base/uuid.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/account_bookmark_sync_service_factory.h"
 #include "chrome/browser/sync/device_info_sync_service_factory.h"
@@ -267,7 +266,7 @@
     // (with a shorter delay).
     ASSERT_TRUE(GetClient(0)->SetupSync(
         base::BindOnce([](syncer::SyncUserSettings* user_settings) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
           user_settings->SetSelectedOsTypes(false, {});
 #endif
           user_settings->SetSelectedTypes(
@@ -2318,7 +2317,7 @@
 
 // On ChromeOS, Sync-the-feature gets started automatically once a primary
 // account is signed in and the transport mode is not a thing.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 class SingleClientBookmarksWithAccountStorageSyncTest
     : public SingleClientBookmarksSyncTest {
  public:
@@ -2833,7 +2832,7 @@
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(
     SingleClientBookmarksSyncTestWithEnabledClientTagHashMigration,
diff --git a/chrome/browser/sync/test/integration/single_client_common_sync_test.cc b/chrome/browser/sync/test/integration/single_client_common_sync_test.cc
index 5b8bb9b..11a2c34 100644
--- a/chrome/browser/sync/test/integration/single_client_common_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_common_sync_test.cc
@@ -10,6 +10,7 @@
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
+#include "build/build_config.h"
 #include "chrome/browser/reading_list/reading_list_model_factory.h"
 #include "chrome/browser/sync/account_bookmark_sync_service_factory.h"
 #include "chrome/browser/sync/local_or_syncable_bookmark_sync_service_factory.h"
@@ -194,8 +195,8 @@
   }
 }
 
-// ChromeOS-Ash doesn't support primary account signout.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+// ChromeOS doesn't support primary account signout.
+#if !BUILDFLAG(IS_CHROMEOS)
 
 // Note: See also SyncErrorTest.ClientDataObsoleteTest, which ensures the cache
 // GUID does *not* get reused if the client's data needs to be reset.
@@ -277,7 +278,7 @@
   }
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Android doesn't currently support PRE_ tests, see crbug.com/1117345.
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/sync/test/integration/single_client_contact_info_sync_test.cc b/chrome/browser/sync/test/integration/single_client_contact_info_sync_test.cc
index 74f948a7..b8bb8a4 100644
--- a/chrome/browser/sync/test/integration/single_client_contact_info_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_contact_info_sync_test.cc
@@ -5,6 +5,7 @@
 #include <string>
 
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/test/integration/contact_info_helper.h"
 #include "chrome/browser/sync/test/integration/encryption_helper.h"
@@ -212,7 +213,7 @@
 }
 
 // ChromeOS does not support signing out of a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientContactInfoSyncTest, ClearOnSignout) {
   const AutofillProfile kProfile = BuildTestAccountProfile();
   AddSpecificsToServer(AsContactInfoSpecifics(kProfile), GetFakeServer());
@@ -226,7 +227,7 @@
                   &GetPersonalDataManager()->address_data_manager(), IsEmpty())
                   .Wait());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Specialized fixture to test the behavior for custom passphrase users with and
 // without kSyncEnableContactInfoDataTypeForCustomPassphraseUsers enabled.
@@ -287,12 +288,12 @@
                   UnorderedElementsAre(profile))
                   .Wait());
   // ChromeOS doesn't have the concept of sign-out.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   GetClient(0)->SignOutPrimaryAccount();
   EXPECT_TRUE(AddressDataManagerProfileChecker(
                   &GetPersonalDataManager()->address_data_manager(), IsEmpty())
                   .Wait());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 }
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -426,7 +427,7 @@
                    ->address_data_manager()
                    .IsAutofillSyncToggleAvailable());
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Sign out.
   GetClient(0)->SignOutPrimaryAccount();
 
diff --git a/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
index b7fbb54..97c87f5 100644
--- a/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
@@ -8,7 +8,6 @@
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "chrome/browser/sync/device_info_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
@@ -207,7 +206,7 @@
   base::test::ScopedFeatureList override_features_;
 };
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest,
                        UmaEnabledSetFullHardwareClass) {
   bool uma_enabled = true;
@@ -246,7 +245,7 @@
 
   ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting(nullptr);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest, CommitLocalDevice) {
   ASSERT_TRUE(SetupSync());
@@ -316,7 +315,7 @@
 
 // On ChromeOS, Sync-the-feature gets started automatically once a primary
 // account is signed in and transport mode is not a thing.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 
 // TODO(crbug.com/40756482): Flaky on Android.
 #if BUILDFLAG(IS_ANDROID)
@@ -371,7 +370,7 @@
                             HasCacheGuid(CacheGuidForSuffix(2))}));
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest,
                        ShouldSetTheOnlyClientFlag) {
diff --git a/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc b/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc
index c468df6..c6bbcf5 100644
--- a/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_extension_apps_sync_test.cc
@@ -4,6 +4,7 @@
 
 #include <vector>
 
+#include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
 #include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
@@ -50,9 +51,9 @@
   // are implicitly added to the expected set of ids.
   explicit FakeServerAppChecker(std::vector<std::string> expected_app_ids) {
     expected_app_ids.push_back(extensions::kWebStoreAppId);
-    #if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     expected_app_ids.push_back(app_constants::kChromeAppId);
-    #endif
+#endif
     expected_app_ids_ = base::MakeFlatSet<std::string>(expected_app_ids);
   }
 
@@ -83,24 +84,7 @@
   SingleClientExtensionAppsSyncTest() : SyncTest(SINGLE_CLIENT) {}
   ~SingleClientExtensionAppsSyncTest() override = default;
 
-  bool SetupClients() override {
-    if (!SyncTest::SetupClients()) {
-      return false;
-    }
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-    // Apps sync is controlled by a dedicated preference on Lacros,
-    // corresponding to the Apps toggle in OS Sync settings.
-    if (base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing)) {
-      GetSyncService(0)->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-    }
-#endif
-    return true;
-  }
-
  private:
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  web_app::test::ScopedSkipMainProfileCheck skip_main_profile_check;
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 #if BUILDFLAG(IS_WIN)
   // This stops extension installation from creating a shortcut in the real
   // desktop startup dir. This prevents Chrome launching with the extension
diff --git a/chrome/browser/sync/test/integration/single_client_history_sync_test.cc b/chrome/browser/sync/test/integration/single_client_history_sync_test.cc
index 4f1a483..989a461 100644
--- a/chrome/browser/sync/test/integration/single_client_history_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_history_sync_test.cc
@@ -882,8 +882,8 @@
       "Sync.NonReflectionUpdateFreshnessPossiblySkewed2.HISTORY", 1);
 }
 
-// Signing out or turning off Sync isn't possible in ChromeOS-Ash.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+// Signing out or turning off Sync isn't possible on ChromeOS.
+#if !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientHistorySyncTest,
                        ClearsForeignHistoryOnTurningSyncOff) {
@@ -1034,7 +1034,7 @@
   EXPECT_EQ(history_helper::GetVisitsFromClient(0, row.id()).size(), 1u);
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // On Android, switches::kSyncUserForTest isn't supported (the passed-in
 // username gets ignored in SyncSigninDelegateAndroid::SigninFake()), so it's
diff --git a/chrome/browser/sync/test/integration/single_client_incoming_password_sharing_invitation_test.cc b/chrome/browser/sync/test/integration/single_client_incoming_password_sharing_invitation_test.cc
index c063c72..8c0c461 100644
--- a/chrome/browser/sync/test/integration/single_client_incoming_password_sharing_invitation_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_incoming_password_sharing_invitation_test.cc
@@ -82,7 +82,7 @@
       syncer::CrossUserSharingPublicPrivateKeyPair::GenerateNewKeyPair());
 }
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 // Fill in fields in `password_data` required for computing password client tag.
 // This is useful for testing collisions. Note that the `invitation` must
 // contain only one password in a group.
@@ -103,7 +103,7 @@
       invitation_group_element_data.password_element());
   password_data->set_signon_realm(invitation_group_element_data.signon_realm());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Waits until all the incoming invitations are deleted from the fake server.
 class ServerPasswordInvitationChecker
@@ -312,7 +312,7 @@
 
 // ChromeOS does not support signing out of a primary account, which these test
 // relies on to initialize Nigori.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientIncomingPasswordSharingInvitationTest,
                        ShouldHandleIncomingInvitationsAtInitialSync) {
   // First, setup sync to initialize Nigori node with a public key to be able to
@@ -401,12 +401,12 @@
           kCredentialsExistWithDifferentPassword,
       /*expected_bucket_count=*/1);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // The unconsented primary account isn't supported on ChromeOS.
 // TODO(crbug.com/358053884): enable on Android once transport mode for
 // Passwords is supported.
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(SingleClientIncomingPasswordSharingInvitationTest,
                        ShouldStoreIncomingPasswordIntoAccountDB) {
   // First, setup sync (in transport mode) to initialize Nigori node with a
@@ -468,7 +468,7 @@
       IncomingPasswordSharingInvitationInactiveChecker(GetSyncService(0))
           .Wait());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
+#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 
 // This test verifies that Incoming Password Sharing Invitation data type is
 // stopped when the Password data type is encountered error.
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index 3e25e62f..70da737a 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -15,7 +15,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -79,7 +79,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "url/url_constants.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_switches.h"
 #include "chrome/browser/ash/sync/sync_error_notifier.h"
 #include "chrome/browser/ash/sync/sync_error_notifier_factory.h"
@@ -87,7 +87,7 @@
 #include "ui/views/test/widget_test.h"
 #include "ui/views/widget/any_widget_observer.h"
 #include "ui/views/widget/widget.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 namespace {
 
@@ -1199,12 +1199,12 @@
                   ->IsTrustedVaultKeyRequiredForPreferredDataTypes());
   ASSERT_FALSE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::PASSWORDS));
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Verify the profile-menu error string.
   ASSERT_THAT(
       GetAvatarSyncErrorType(GetProfile(0)),
       Eq(AvatarSyncErrorType::kTrustedVaultKeyMissingForPasswordsError));
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   // Verify the string that would be displayed in settings.
   ASSERT_THAT(GetSyncStatusLabels(GetProfile(0)),
@@ -1239,10 +1239,10 @@
                                 IDS_SYNC_ACCOUNT_SYNCING, IDS_SYNC_EMPTY_STRING,
                                 SyncStatusActionType::kNoAction));
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Verify the profile-menu error string is empty.
   EXPECT_FALSE(GetAvatarSyncErrorType(GetProfile(0)).has_value());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 }
 
 // Regression test for crbug.com/1479879: test verifies that client is able to
@@ -1307,7 +1307,7 @@
   EXPECT_FALSE(GetSecurityDomainsServer()->IsRecoverabilityDegraded());
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 class SingleClientNigoriWithWebApiAndDialogUIParamTest
     : public SingleClientNigoriWithWebApiTest {
  public:
@@ -1433,7 +1433,7 @@
                   .Wait());
 }
 
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientNigoriWithWebApiTest,
                        ShouldAcceptEncryptionKeysFromSubFrameIfSyncEnabled) {
@@ -1526,10 +1526,10 @@
                                 IDS_SYNC_ACCOUNT_SYNCING, IDS_SYNC_EMPTY_STRING,
                                 SyncStatusActionType::kNoAction));
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Verify the profile-menu error string is empty.
   EXPECT_FALSE(GetAvatarSyncErrorType(GetProfile(0)).has_value());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 }
 
 IN_PROC_BROWSER_TEST_F(
@@ -1734,11 +1734,11 @@
   // reproduces a more realistic case of the first-time turn-sync-on experience,
   // with a temporary stage where the user is signed in without sync-the-feature
   // being enabled. Except on Ash where the two steps happen at once.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   ASSERT_TRUE(SetupClients());
   ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount());
   ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
   // TODO(crbug.com/40914333): SetupSync(WAIT_FOR_COMMITS_TO_COMPLETE) (e.g.
   // with default argument) causes test flakiness here due to unrelated issue in
   // SharingService. From this test perspective it doesn't matter whether to use
@@ -1862,12 +1862,12 @@
                    ->GetUserSettings()
                    ->IsTrustedVaultKeyRequiredForPreferredDataTypes());
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Verify the profile-menu error string.
   EXPECT_THAT(GetAvatarSyncErrorType(GetProfile(0)),
               Eq(AvatarSyncErrorType::
                      kTrustedVaultRecoverabilityDegradedForPasswordsError));
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   // No messages expected in settings.
   EXPECT_THAT(GetSyncStatusLabels(GetProfile(0)),
@@ -1893,10 +1893,10 @@
                    ->IsTrustedVaultRecoverabilityDegraded());
   EXPECT_FALSE(GetSecurityDomainsServer()->IsRecoverabilityDegraded());
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Verify the profile-menu error string is empty.
   EXPECT_FALSE(GetAvatarSyncErrorType(GetProfile(0)).has_value());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   histogram_tester.ExpectUniqueSample(
       "Sync.TrustedVaultRecoverabilityDegradedOnStartup",
@@ -2250,7 +2250,7 @@
 }
 
 // ChromeOS doesn't have unconsented primary accounts.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientNigoriWithWebApiTest,
                        ShouldAcceptEncryptionKeysFromTheWebInTransportMode) {
   // Mimic the account using a trusted vault passphrase.
@@ -2349,6 +2349,6 @@
       /*sample=*/true, /*expected_bucket_count=*/1);
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_offer_sync_test.cc b/chrome/browser/sync/test/integration/single_client_offer_sync_test.cc
index 7a8d0fc2..88c5fb6 100644
--- a/chrome/browser/sync/test/integration/single_client_offer_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_offer_sync_test.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/sync/test/integration/autofill_helper.h"
 #include "chrome/browser/sync/test/integration/offer_helper.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
@@ -103,7 +102,7 @@
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 // ChromeOS does not sign out, so the test below does not apply.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 // Offer data should get cleared from the database when the user signs out.
 IN_PROC_BROWSER_TEST_F(SingleClientOfferSyncTest, ClearOnSignOut) {
   GetFakeServer()->SetOfferData({CreateDefaultSyncCardLinkedOffer()});
@@ -118,7 +117,7 @@
   WaitForNumberOfOffers(0, paydm);
   EXPECT_EQ(0uL, paydm->GetAutofillOffers().size());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Offer is not using incremental updates. Make sure existing data gets
 // replaced when synced down.
diff --git a/chrome/browser/sync/test/integration/single_client_outgoing_password_sharing_invitation_test.cc b/chrome/browser/sync/test/integration/single_client_outgoing_password_sharing_invitation_test.cc
index 25b8717..0794385 100644
--- a/chrome/browser/sync/test/integration/single_client_outgoing_password_sharing_invitation_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_outgoing_password_sharing_invitation_test.cc
@@ -206,7 +206,7 @@
 // The unconsented primary account isn't supported on ChromeOS.
 // TODO(crbug.com/358053884): enable on Android once transport mode for
 // Passwords is supported.
-#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
+#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(SingleClientOutgoingPasswordSharingInvitationTest,
                        ShouldCommitSentPasswordInTransportMode) {
   // First, setup sync (in transport mode) to initialize Nigori node with a
@@ -226,6 +226,6 @@
 
   EXPECT_TRUE(InvitationCommittedChecker(/*expected_entities_count=*/1).Wait());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_ANDROID)
+#endif  // !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
index e962bb5..314d215 100644
--- a/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_passwords_sync_test.cc
@@ -5,7 +5,6 @@
 #include "base/base64.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/test/integration/committed_all_nudged_changes_checker.h"
 #include "chrome/browser/sync/test/integration/encryption_helper.h"
@@ -53,10 +52,10 @@
 using testing::Field;
 using testing::SizeIs;
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 const syncer::SyncFirstSetupCompleteSource kSetSourceFromTest =
     syncer::SyncFirstSetupCompleteSource::BASIC_FLOW;
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 MATCHER_P3(HasPasswordValueAndUnsupportedFields,
            cryptographer,
@@ -285,9 +284,9 @@
   }
 
   void SetUpOnMainThread() override {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     secondary_account_helper::InitNetwork();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
     SyncTest::SetUpOnMainThread();
 
     fake_server::SetKeystoreNigoriInFakeServer(GetFakeServer());
@@ -336,7 +335,7 @@
 
 // On ChromeOS, Sync-the-feature gets started automatically once a primary
 // account is signed in and the transport mode is not a thing.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientPasswordsWithAccountStorageSyncTest,
                        StoresDataForNonSyncingPrimaryAccountInAccountDB) {
   AddTestPasswordToFakeServer();
@@ -361,10 +360,10 @@
       passwords_helper::GetAccountPasswordStoreInterface(0);
   EXPECT_EQ(passwords_helper::GetAllLogins(account_store).size(), 1u);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // The unconsented primary account isn't supported on ChromeOS.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientPasswordsWithAccountStorageSyncTest,
                        StoresDataForSecondaryAccountInAccountDB) {
   AddTestPasswordToFakeServer();
@@ -389,10 +388,10 @@
       passwords_helper::GetAccountPasswordStoreInterface(0);
   EXPECT_EQ(passwords_helper::GetAllLogins(account_store).size(), 1u);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // ChromeOS does not support signing out of a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 
 // Sanity check: The profile database should *not* get cleared on signout.
 IN_PROC_BROWSER_TEST_F(SingleClientPasswordsWithAccountStorageSyncTest,
@@ -415,11 +414,10 @@
   // Make sure the password is still in the store.
   ASSERT_EQ(passwords_helper::GetAllLogins(profile_store).size(), 1u);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // The unconsented primary account isn't supported on ChromeOS so Sync won't
 // start up for an unconsented account.
-// Signing out on Lacros is not possible.
 #if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientPasswordsWithAccountStorageSyncTest,
                        ClearsAccountDBOnSignout) {
@@ -445,11 +443,7 @@
   // Make sure the password is gone from the store.
   ASSERT_EQ(passwords_helper::GetAllLogins(account_store).size(), 0u);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS)
 
-// The unconsented primary account isn't supported on ChromeOS so Sync won't
-// start up for an unconsented account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
 IN_PROC_BROWSER_TEST_F(SingleClientPasswordsWithAccountStorageSyncTest,
                        SwitchesStoresOnMakingAccountPrimary) {
   AddTestPasswordToFakeServer();
@@ -639,7 +633,7 @@
                   .Wait());
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientPasswordsSyncTest,
                        PreservesUnsupportedFieldsDataOnCommits) {
diff --git a/chrome/browser/sync/test/integration/single_client_plus_address_setting_sync_test.cc b/chrome/browser/sync/test/integration/single_client_plus_address_setting_sync_test.cc
index 5aee6b42f..6db948e 100644
--- a/chrome/browser/sync/test/integration/single_client_plus_address_setting_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_plus_address_setting_sync_test.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/plus_addresses/plus_address_setting_service_factory.h"
 #include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
@@ -141,7 +142,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ,
     SingleClientPlusAddressSettingSyncTest,
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     // On ChromeOS, sync-the-feature gets started automatically once a primary
     // account is signed in and transport mode is not a thing. As such, only run
     // the tests in sync-the-feature mode.
@@ -210,7 +211,7 @@
 }
 
 // ChromeOS does not support signing out of the primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_P(SingleClientPlusAddressSettingSyncTest,
                        Signout_DataCleared) {
   InjectSpecificsToServer(
@@ -221,6 +222,6 @@
   // The enabled setting defaults to true if the state is unknown.
   EXPECT_TRUE(WaitForPlusAddressEnabledState(true));
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_plus_address_sync_test.cc b/chrome/browser/sync/test/integration/single_client_plus_address_sync_test.cc
index 85ff9e0..a5d0118 100644
--- a/chrome/browser/sync/test/integration/single_client_plus_address_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_plus_address_sync_test.cc
@@ -10,6 +10,7 @@
 #include "base/containers/span.h"
 #include "base/scoped_observation.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/plus_addresses/plus_address_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/test/integration/status_change_checker.h"
@@ -136,7 +137,7 @@
 INSTANTIATE_TEST_SUITE_P(
     ,
     SingleClientPlusAddressSyncTest,
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     // On ChromeOS, sync-the-feature gets started automatically once a primary
     // account is signed in and transport mode is not a thing. As such, only run
     // the tests in sync-the-feature mode.
@@ -198,7 +199,7 @@
 }
 
 // ChromeOS does not support signing out of the primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_P(SingleClientPlusAddressSyncTest, Signout_DataCleared) {
   InjectEntityToServer(
       EntityDataFromPlusProfile(CreatePlusProfile()).specifics);
@@ -210,7 +211,7 @@
   EXPECT_TRUE(
       PlusProfileChecker(GetPlusAddressService(), testing::IsEmpty()).Wait());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Overwrites the Sync test account with a non-gmail account to treat it as a
 // Dasher account.
diff --git a/chrome/browser/sync/test/integration/single_client_polling_sync_test.cc b/chrome/browser/sync/test/integration/single_client_polling_sync_test.cc
index 228a897..88f190e 100644
--- a/chrome/browser/sync/test/integration/single_client_polling_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_polling_sync_test.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/sync/test/integration/session_hierarchy_match_checker.h"
 #include "chrome/browser/sync/test/integration/sessions_helper.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
diff --git a/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc b/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc
index 04352c3..f7772892 100644
--- a/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_preferences_sync_test.cc
@@ -14,7 +14,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/values.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/prefs/chrome_pref_service_factory.h"
 #include "chrome/browser/sync/test/integration/preferences_helper.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
@@ -625,7 +625,7 @@
 
 // Regression test for crbug.com/1456872.
 // ChromeOS does not support signing out of a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientPreferencesWithAccountStorageSyncTest,
                        ShouldHandleWalletSideEffectsWhenSyncDisabled) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -657,7 +657,7 @@
   // Enabling sync again should work, without crashes.
   EXPECT_TRUE(SetupSync());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 #if !BUILDFLAG(IS_ANDROID)
 
@@ -1501,7 +1501,7 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 // Preference tracking is not required on android and chromeos.
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
 
 const char* kProtectedPrefName = prefs::kShowHomeButton;
 const char* kUnprotectedPrefName = prefs::kShowForwardButton;
@@ -1806,6 +1806,6 @@
       << "Incorrect key " << account_pref_name << " in " << *prefs;
 }
 
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_reading_list_sync_test.cc b/chrome/browser/sync/test/integration/single_client_reading_list_sync_test.cc
index bf49234..2dfb1ac 100644
--- a/chrome/browser/sync/test/integration/single_client_reading_list_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_reading_list_sync_test.cc
@@ -4,6 +4,7 @@
 
 #include "base/location.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/reading_list/reading_list_model_factory.h"
 #include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
@@ -312,7 +313,7 @@
 
 // ChromeOS doesn't have the concept of sign-out, so this only exists on other
 // platforms.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientReadingListSyncTest,
                        ShouldDeleteAccountDataUponSignout) {
@@ -724,6 +725,6 @@
               Eq("entry_title"));
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_saved_tab_groups_sync_test.cc b/chrome/browser/sync/test/integration/single_client_saved_tab_groups_sync_test.cc
index d1300229..e3bed2d4 100644
--- a/chrome/browser/sync/test/integration/single_client_saved_tab_groups_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_saved_tab_groups_sync_test.cc
@@ -5,6 +5,7 @@
 #include <algorithm>
 
 #include "base/uuid.h"
+#include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/saved_tab_groups_helper.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
@@ -551,7 +552,7 @@
 
 // On ChromeOS, Sync-the-feature gets started automatically once a primary
 // account is signed in and the transport mode is not a thing.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 
 // Subclass that enables an additional feature, namely
 // `syncer::kReplaceSyncPromosWithSignInPromos`.
@@ -637,7 +638,7 @@
                   .Wait());
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
 }  // namespace tab_groups
diff --git a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
index 03d72b64..271940e 100644
--- a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
@@ -7,7 +7,6 @@
 #include "base/path_service.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/secondary_account_helper.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
@@ -20,13 +19,13 @@
 
 namespace {
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 base::FilePath GetTestFilePathForCacheGuid() {
   base::FilePath user_data_path;
   base::PathService::Get(chrome::DIR_USER_DATA, &user_data_path);
   return user_data_path.AppendASCII("SyncTestTmpCacheGuid");
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 class SingleClientSecondaryAccountSyncTest : public SyncTest {
  public:
@@ -47,9 +46,9 @@
   }
 
   void SetUpOnMainThread() override {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     secondary_account_helper::InitNetwork();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
     SyncTest::SetUpOnMainThread();
   }
 
@@ -60,7 +59,7 @@
 };
 
 // The unconsented primary account isn't supported on ChromeOS.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientSecondaryAccountSyncTest,
                        StartsSyncTransportOnSignin) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -103,11 +102,11 @@
   EXPECT_EQ(syncer::SyncService::TransportState::DISABLED,
             GetSyncService(0)->GetTransportState());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // ChromeOS doesn't support changes to the primary account after startup, so
 // this test doesn't apply.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientSecondaryAccountSyncTest,
                        SwitchesFromTransportToFeature) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -141,14 +140,14 @@
       syncer::UserSelectableType::kBookmarks));
   EXPECT_TRUE(GetSyncService(0)->GetActiveDataTypes().Has(syncer::BOOKMARKS));
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Regression test for crbug.com/955989 that verifies the cache GUID is not
 // reset upon restart of the browser, in standalone transport mode with
 // unconsented accounts.
 //
 // The unconsented primary account isn't supported on ChromeOS.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientSecondaryAccountSyncTest,
                        PRE_ReusesSameCacheGuid) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -202,6 +201,6 @@
 
   EXPECT_EQ(old_cache_guid, prefs.GetCacheGuid());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
index 2dc5777..3deb5e2 100644
--- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -386,7 +386,7 @@
   EXPECT_TRUE(found_header);
 }
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 // Regression test for crbug.com/361256057.
 IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, UpdateSessionTag) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
@@ -427,7 +427,7 @@
                                        SessionHeader(second_cache_guid)))
                   .Wait());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, NavigateInTab) {
   ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
diff --git a/chrome/browser/sync/test/integration/single_client_sharing_message_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sharing_message_sync_test.cc
index a602b62..9530fcf3f 100644
--- a/chrome/browser/sync/test/integration/single_client_sharing_message_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sharing_message_sync_test.cc
@@ -8,7 +8,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/test/mock_callback.h"
 #include "base/time/time.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/sharing/sharing_message_bridge_factory.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
@@ -204,7 +204,7 @@
 
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply, at least in the current form.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientSharingMessageSyncTest,
                        ShouldSubmitInTransportMode) {
   // We avoid calling SetupSync(), because we don't want to turn on full sync,
@@ -234,7 +234,7 @@
   EXPECT_TRUE(WaitForSharingMessage({specifics}));
   EXPECT_TRUE(callback_checker.Wait());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientSharingMessageSyncTest,
                        ShouldPropagateCommitFailure) {
@@ -256,7 +256,7 @@
 }
 
 // ChromeOS does not support signing out of a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientSharingMessageSyncTest,
                        ShouldCleanPendingMessagesUponSignout) {
   ASSERT_TRUE(SetupSync());
@@ -279,7 +279,7 @@
                   ->GetSyncEntitiesByDataType(syncer::SHARING_MESSAGE)
                   .empty());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(
     SingleClientSharingMessageSyncTest,
diff --git a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
index 13f3bd78..f8dac11 100644
--- a/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_standalone_transport_sync_test.cc
@@ -8,7 +8,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/encryption_helper.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
@@ -26,7 +25,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_launcher.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
 #endif
 
@@ -40,7 +39,7 @@
 }
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 class SyncDisabledViaDashboardChecker : public SingleClientStatusChangeChecker {
  public:
   explicit SyncDisabledViaDashboardChecker(syncer::SyncServiceImpl* service)
@@ -62,7 +61,7 @@
     return !service()->HasSyncConsent();
   }
 };
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 class SingleClientStandaloneTransportSyncTest : public SyncTest {
  public:
@@ -70,7 +69,7 @@
 };
 
 // On Chrome OS sync auto-starts on sign-in.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientStandaloneTransportSyncTest,
                        StartsSyncTransportOnSignin) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
@@ -104,7 +103,7 @@
                        AllowedTypesInStandaloneTransportMode());
   EXPECT_TRUE(bad_types.empty()) << syncer::DataTypeSetToDebugString(bad_types);
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 #if !BUILDFLAG(IS_ANDROID)
 IN_PROC_BROWSER_TEST_F(SingleClientStandaloneTransportSyncTest,
@@ -160,7 +159,7 @@
   // involves clearing the server data so that the birthday gets incremented.
   GetFakeServer()->ClearServerData();
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // On Ash, the primary account should remain, and Sync should start up
   // again in standalone transport mode, but report this specific case via
   // IsSyncFeatureDisabledViaDashboard().
@@ -184,7 +183,7 @@
   // up again in standalone transport mode. However, since we haven't set up
   // cookies in this test, the account is *not* considered primary anymore
   // (not even "unconsented").
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
 // TODO(crbug.com/40200835): Android currently doesn't support PRE_ tests.
@@ -203,12 +202,12 @@
   // On platforms where Sync starts automatically (in practice, Android and
   // ChromeOS), IsInitialSyncFeatureSetupComplete gets set automatically, and so
   // the full Sync feature will start upon sign-in to a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   ASSERT_FALSE(GetSyncService(0)
                    ->GetUserSettings()
                    ->IsInitialSyncFeatureSetupComplete());
   ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureEnabled());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   syncer::SyncTransportDataPrefs transport_data_prefs(
       GetProfile(0)->GetPrefs(),
@@ -235,12 +234,12 @@
   // On platforms where Sync starts automatically (in practice, Android and
   // ChromeOS), IsInitialSyncFeatureSetupComplete gets set automatically, and so
   // the full Sync feature will start upon sign-in to a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   ASSERT_FALSE(GetSyncService(0)
                    ->GetUserSettings()
                    ->IsInitialSyncFeatureSetupComplete());
   ASSERT_FALSE(GetSyncService(0)->IsSyncFeatureEnabled());
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   syncer::SyncTransportDataPrefs transport_data_prefs(
       GetProfile(0)->GetPrefs(),
diff --git a/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc b/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc
index dd4df5fd..dc5e351 100644
--- a/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc
@@ -4,6 +4,7 @@
 
 #include "base/memory/raw_ptr.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/sync/device_info_sync_service_factory.h"
 #include "chrome/browser/sync/sync_invalidations_service_factory.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
@@ -639,7 +640,7 @@
 }
 
 // ChromeOS doesn't have the concept of sign-out.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 
 // TODO(crbug.com/40833316): Enable test on Android once signout is supported.
 #if BUILDFLAG(IS_ANDROID)
@@ -684,6 +685,6 @@
       ServerDeviceInfoMatchChecker(Contains(HasInstanceIdToken(new_token)))
           .Wait());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc b/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
index 99f75d6..c3a8b71 100644
--- a/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_user_consents_sync_test.cc
@@ -5,7 +5,7 @@
 #include <stdint.h>
 
 #include "base/memory/raw_ptr.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/status_change_checker.h"
@@ -118,7 +118,7 @@
 }
 
 // ChromeOS does not support signing out of a primary account.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(
     SingleClientUserConsentsSyncTest,
     ShouldPreserveConsentsOnSignoutAndResubmitWhenReenabled) {
@@ -142,7 +142,7 @@
 
   EXPECT_TRUE(ExpectUserConsents({specifics}));
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientUserConsentsSyncTest,
                        ShouldPreserveConsentsLoggedBeforeSyncSetup) {
@@ -181,7 +181,7 @@
 
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply, at least in the current form.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientUserConsentsSyncTest,
                        ShouldSubmitIfSignedInAlthoughFullSyncNotEnabled) {
   // We avoid calling SetupSync(), because we don't want to turn on full sync,
@@ -213,6 +213,6 @@
   specifics.set_account_id(GetAccountId().ToString());
   EXPECT_TRUE(ExpectUserConsents({specifics}));
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_credential_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_credential_sync_test.cc
index 275aa509..94ab360 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_credential_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_credential_sync_test.cc
@@ -4,6 +4,7 @@
 
 #include "base/notreached.h"
 #include "base/test/test_future.h"
+#include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
@@ -61,7 +62,7 @@
              .size() == expected_count_;
 }
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 std::vector<std::unique_ptr<autofill::CreditCard>> GetServerCards(
     scoped_refptr<autofill::AutofillWebDataService> service) {
   base::test::TestFuture<WebDataServiceBase::Handle,
@@ -73,7 +74,7 @@
              *future.Get<1>())
       .GetValue();
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
 
@@ -163,7 +164,7 @@
 
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 // Verify card and CVC data is synced when the user signs in.
 IN_PROC_BROWSER_TEST_F(SingleClientWalletCredentialSyncTest,
                        DownloadCardCredential) {
@@ -416,7 +417,7 @@
   EXPECT_EQ(0uL, paydm->GetCreditCards().size());
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Verify that card and CVC data should get cleared from the database when the
 // sync for Payments is disabled.
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index ff2fbf0..89bdbde 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -9,7 +9,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_future.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/test/integration/autofill_helper.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
@@ -87,7 +86,7 @@
 const char kLocalGuidA[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
 const char kDifferentBillingAddressId[] = "another address entity ID";
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 std::vector<std::unique_ptr<CreditCard>> GetServerCards(
     scoped_refptr<autofill::AutofillWebDataService> service) {
   base::test::TestFuture<WebDataServiceBase::Handle,
@@ -124,7 +123,7 @@
              *future.Get<1>())
       .GetValue();
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Waits until local changes are committed or an auth error is encountered.
 class TestForAuthError : public UpdatedProgressMarkerChecker {
@@ -223,7 +222,7 @@
 
 // ChromeOS does not support late signin after profile creation, so the test
 // below does not apply, at least in the current form.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_P(SingleClientWalletWithImprovedSigninUISyncTest,
                        DownloadAccountStorage_Card) {
   ASSERT_TRUE(SetupClients());
@@ -442,7 +441,7 @@
   EXPECT_EQ("data-2",
             paydm->GetCreditCardCloudTokenData()[0]->instrument_token);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, EnabledByDefault) {
   ASSERT_TRUE(SetupSync());
@@ -456,7 +455,7 @@
 }
 
 // ChromeOS does not sign out, so the test below does not apply.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientWalletSyncTest, ClearOnSignOut) {
   GetFakeServer()->SetWalletData({CreateDefaultSyncWalletCard(),
                                   CreateDefaultSyncPaymentsCustomerData(),
@@ -481,7 +480,7 @@
   EXPECT_EQ(0uL, paydm->GetCreditCardCloudTokenData().size());
   EXPECT_EQ(0U, GetServerCardsMetadata(0).size());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 // Wallet data should get cleared from the database when the user enters the
 // sync paused state (e.g. persistent auth error).
@@ -1014,9 +1013,9 @@
   }
 
   void SetUpOnMainThread() override {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     secondary_account_helper::InitNetwork();
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
     SyncTest::SetUpOnMainThread();
   }
 
@@ -1028,7 +1027,7 @@
 
 // ChromeOS doesn't support changes to the primary account after startup, so
 // these tests don't apply.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(SingleClientWalletSecondaryAccountSyncTest,
                        SwitchesFromAccountToProfileStorageOnSyncOptIn) {
   ASSERT_TRUE(SetupClients());
@@ -1074,10 +1073,10 @@
   // complete.
   secondary_account_helper::GrantSyncConsent(profile(), "user@email.com");
   GetSyncService(0)->SetSyncFeatureRequested();
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   GetSyncService(0)->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
       syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   // Wait for Sync to get reconfigured into feature mode.
   ASSERT_TRUE(GetClient(0)->AwaitSyncSetupCompletion());
@@ -1159,10 +1158,10 @@
   ASSERT_EQ(syncer::SyncService::TransportState::CONFIGURING,
             GetSyncService(0)->GetTransportState());
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   GetSyncService(0)->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
       syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   // Wait for Sync to get reconfigured into feature mode.
   ASSERT_TRUE(GetClient(0)->AwaitSyncSetupCompletion());
@@ -1185,4 +1184,4 @@
   EXPECT_EQ(0U, GetCreditCardCloudTokenData(account_data).size());
   EXPECT_EQ(1U, GetCreditCardCloudTokenData(profile_data).size());
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/sync/test/integration/single_client_web_apps_sync_test.cc b/chrome/browser/sync/test/integration/single_client_web_apps_sync_test.cc
index 0a18bf6..5d37079 100644
--- a/chrome/browser/sync/test/integration/single_client_web_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_web_apps_sync_test.cc
@@ -10,7 +10,7 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
 #include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
diff --git a/chrome/browser/sync/test/integration/single_client_webauthn_credentials_sync_test.cc b/chrome/browser/sync/test/integration/single_client_webauthn_credentials_sync_test.cc
index 7eff28f2..c167ca3 100644
--- a/chrome/browser/sync/test/integration/single_client_webauthn_credentials_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_webauthn_credentials_sync_test.cc
@@ -1017,7 +1017,7 @@
 }
 
 // The unconsented primary account isn't supported on ChromeOS.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 
 class SingleClientWebAuthnCredentialsSyncTestExplicitParamTest
     : public SingleClientWebAuthnCredentialsSyncTest,
@@ -1074,6 +1074,6 @@
     [](const testing::TestParamInfo<bool>& info) {
       return info.param ? "Explicit" : "Implicit";
     });
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/sync_auth_test.cc b/chrome/browser/sync/test/integration/sync_auth_test.cc
index 723fdf57..cec849cf 100644
--- a/chrome/browser/sync/test/integration/sync_auth_test.cc
+++ b/chrome/browser/sync/test/integration/sync_auth_test.cc
@@ -8,7 +8,6 @@
 #include "base/threading/platform_thread.h"
 #include "base/time/time.h"
 #include "build/buildflag.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
@@ -314,13 +313,7 @@
 }
 
 // Verify that SyncServiceImpl fetches a new token when an old token expires.
-// TODO(crbug.com/40788468): Flaky on Lacros.
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#define MAYBE_TokenExpiry DISABLED_TokenExpiry
-#else
-#define MAYBE_TokenExpiry TokenExpiry
-#endif
-IN_PROC_BROWSER_TEST_F(SyncAuthTestOAuthTokens, MAYBE_TokenExpiry) {
+IN_PROC_BROWSER_TEST_F(SyncAuthTestOAuthTokens, TokenExpiry) {
   // Initial sync succeeds with a short lived OAuth2 Token.
   ASSERT_TRUE(SetupClients());
   GetFakeServer()->ClearHttpError();
diff --git a/chrome/browser/sync/test/integration/sync_errors_test.cc b/chrome/browser/sync/test/integration/sync_errors_test.cc
index 2c71c5cb..1630caa 100644
--- a/chrome/browser/sync/test/integration/sync_errors_test.cc
+++ b/chrome/browser/sync/test/integration/sync_errors_test.cc
@@ -6,7 +6,6 @@
 #include "base/test/bind.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
 #include "chrome/browser/sync/test/integration/fake_server_match_status_checker.h"
 #include "chrome/browser/sync/test/integration/preferences_helper.h"
@@ -224,7 +223,7 @@
 // This test verifies that sync keeps retrying if it encounters error during
 // setup.
 // crbug.com/689662
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #define MAYBE_ErrorWhileSettingUp DISABLED_ErrorWhileSettingUp
 #else
 #define MAYBE_ErrorWhileSettingUp ErrorWhileSettingUp
@@ -232,7 +231,7 @@
 IN_PROC_BROWSER_TEST_F(SyncErrorTest, MAYBE_ErrorWhileSettingUp) {
   ASSERT_TRUE(SetupClients());
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // On non auto start enabled environments if the setup sync fails then
   // the setup would fail. So setup sync normally.
   // In contrast on auto start enabled platforms like chrome os we should be
@@ -245,7 +244,7 @@
   GetFakeServer()->TriggerError(sync_pb::SyncEnums::TRANSIENT_ERROR);
   EXPECT_TRUE(GetFakeServer()->EnableAlternatingTriggeredErrors());
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // Now setup sync and it should succeed.
   ASSERT_TRUE(SetupSync());
 #else
diff --git a/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc b/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
index 73d1af01..42d44e7 100644
--- a/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
+++ b/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/functional/bind.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
 #include "chrome/browser/sync/test/integration/exponential_backoff_helper.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
@@ -37,13 +37,7 @@
   }
 };
 
-// TODO(crbug.com/40854025): Test fails on Lacros.
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#define MAYBE_OfflineToOnline DISABLED_OfflineToOnline
-#else
-#define MAYBE_OfflineToOnline OfflineToOnline
-#endif
-IN_PROC_BROWSER_TEST_F(SyncExponentialBackoffTest, MAYBE_OfflineToOnline) {
+IN_PROC_BROWSER_TEST_F(SyncExponentialBackoffTest, OfflineToOnline) {
   const std::u16string kFolderTitle1 = u"folder1";
   const std::u16string kFolderTitle2 = u"folder2";
 
diff --git a/chrome/browser/sync/test/integration/sync_integration_tests.gni b/chrome/browser/sync/test/integration/sync_integration_tests.gni
index 2ec3067..f16d05c 100644
--- a/chrome/browser/sync/test/integration/sync_integration_tests.gni
+++ b/chrome/browser/sync/test/integration/sync_integration_tests.gni
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/chromeos/ui_mode.gni")
-
 sync_integration_tests_sources = [
   "//chrome/browser/sync/test/integration/single_client_autofill_profile_sync_test.cc",
   "//chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc",
@@ -112,13 +110,11 @@
   sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_win.cc" ]
 }
 
-# https://crbug.com/1252812 The intent picker (launch icon) actions are not
-# working on Lacros.
-if (is_chromeos_ash) {
+if (is_chromeos) {
   sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/two_client_web_apps_integration_test_cros.cc" ]
 }
 
-if (!is_chromeos_ash && !is_android) {
+if (!is_chromeos && !is_android) {
   sync_integration_tests_sources += [ "//chrome/browser/sync/test/integration/select_type_and_migrate_local_data_items_when_active_sync_test.cc" ]
 }
 
diff --git a/chrome/browser/sync/test/integration/sync_service_impl_harness.cc b/chrome/browser/sync/test/integration/sync_service_impl_harness.cc
index 3ebbe89..66249c7 100644
--- a/chrome/browser/sync/test/integration/sync_service_impl_harness.cc
+++ b/chrome/browser/sync/test/integration/sync_service_impl_harness.cc
@@ -16,7 +16,6 @@
 #include "base/memory/raw_ptr.h"
 #include "base/test/bind.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
@@ -44,12 +43,6 @@
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "third_party/zlib/google/compression_utils.h"
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/signin/chrome_signin_client.h"
-#include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "components/signin/public/base/signin_client.h"
-#endif
-
 using syncer::SyncCycleSnapshot;
 using syncer::SyncServiceImpl;
 
@@ -193,15 +186,6 @@
       signin_type_(signin_type),
       profile_debug_name_(profile->GetDebugName()),
       signin_delegate_(CreateSyncSigninDelegate()) {
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // The Main profile already has a primary account that cannot be changed.
-  // Allow changing it for test purposes only.
-  if (profile_->IsMainProfile()) {
-    ChromeSigninClientFactory::GetForProfile(profile_)
-        ->set_is_clear_primary_account_allowed_for_testing(
-            SigninClient::SignoutDecision::ALLOW);
-  }
-#endif
 }
 
 SyncServiceImplHarness::~SyncServiceImplHarness() = default;
@@ -279,12 +263,12 @@
                username_, transport_data_prefs.GetBirthday());
 }
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 void SyncServiceImplHarness::SignOutPrimaryAccount() {
   DCHECK(!username_.empty());
   signin_delegate_->SignOutPrimaryAccount(profile_);
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
 #if !BUILDFLAG(IS_ANDROID)
 void SyncServiceImplHarness::EnterSyncPausedStateForPrimaryAccount() {
@@ -354,10 +338,10 @@
 
 void SyncServiceImplHarness::FinishSyncSetup() {
   sync_blocker_.reset();
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   service()->GetUserSettings()->SetInitialSyncFeatureSetupComplete(
       syncer::SyncFirstSetupCompleteSource::BASIC_FLOW);
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 }
 
 bool SyncServiceImplHarness::AwaitMutualSyncCycleCompletion(
diff --git a/chrome/browser/sync/test/integration/sync_service_impl_harness.h b/chrome/browser/sync/test/integration/sync_service_impl_harness.h
index dfc29977..42a536b 100644
--- a/chrome/browser/sync/test/integration/sync_service_impl_harness.h
+++ b/chrome/browser/sync/test/integration/sync_service_impl_harness.h
@@ -13,7 +13,6 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "components/sync/base/user_selectable_type.h"
 #include "components/sync/engine/cycle/sync_cycle_snapshot.h"
 #include "components/sync/service/sync_service_impl.h"
@@ -68,11 +67,11 @@
   // This is similar to click the reset button on chrome.google.com/sync.
   void ResetSyncForPrimaryAccount();
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
   // Signs out of the primary account. ChromeOS doesn't have the concept of
   // sign-out, so this only exists on other platforms.
   void SignOutPrimaryAccount();
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
 
   // The underlying implementation for mimic-ing persistent auth errors isn't
   // implemented on Android, see https://crbug.com/1373448.
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index df1f8d1..f47b5ac 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -30,7 +30,6 @@
 #include "base/task/thread_pool.h"
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
@@ -91,7 +90,7 @@
 #include "url/gurl.h"
 #include "url/url_constants.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "chrome/browser/ash/app_list/arc/arc_app_list_prefs_factory.h"
@@ -100,12 +99,7 @@
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "chromeos/ash/experiences/arc/test/arc_util_test_support.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "components/account_manager_core/chromeos/account_manager.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/sync/test/integration/sync_test_utils_android.h"
@@ -131,19 +125,13 @@
       ChromeSigninClientFactory::GetForProfile(profile));
   signin_client->SetURLLoaderFactoryForTest(url_loader_factory);
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   ash::AccountManagerFactory* factory =
       g_browser_process->platform_part()->GetAccountManagerFactory();
   account_manager::AccountManager* account_manager =
       factory->GetAccountManager(profile->GetPath().value());
   account_manager->SetUrlLoaderFactoryForTests(url_loader_factory);
-#elif BUILDFLAG(IS_CHROMEOS_LACROS)
-  account_manager::AccountManager* account_manager =
-      MaybeGetAshAccountManagerForTests();
-  if (account_manager) {
-    account_manager->SetUrlLoaderFactoryForTests(url_loader_factory);
-  }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 }
 
 }  // namespace
@@ -295,7 +283,7 @@
     cl->AppendSwitch(switches::kDisableSyncInvalidationOptimizations);
   }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   cl->AppendSwitch(ash::switches::kIgnoreUserProfileMappingForTests);
   cl->AppendSwitch(ash::switches::kDisableArcOptInVerification);
   arc::SetArcAvailableCommandLineForTesting(cl);
@@ -486,7 +474,7 @@
     cl->AppendSwitchASCII(syncer::kSyncDeferredStartupTimeoutSeconds, "0");
   }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // Sets Arc flags, need to be called before create test profiles.
   ArcAppListPrefsFactory::SetFactoryForSyncTest();
 
@@ -529,7 +517,7 @@
     WaitForDataModels(verifier());
   }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   if (ArcAppListPrefsFactory::IsFactorySetForSyncTest()) {
     // Init SyncArcPackageHelper to ensure that the arc services are initialized
     // for each Profile, only can be called after test profiles are created.
@@ -1017,7 +1005,7 @@
 }
 
 arc::SyncArcPackageHelper* SyncTest::sync_arc_helper() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   return arc::SyncArcPackageHelper::GetInstance();
 #else
   return nullptr;
@@ -1154,17 +1142,13 @@
   }
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  // On Lacros, Apps-related types may run in transport mode.
-  allowed_types.PutAll({syncer::APPS, syncer::APP_SETTINGS, syncer::WEB_APPS});
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // OS sync types run in transport mode.
   allowed_types.PutAll({syncer::APP_LIST, syncer::ARC_PACKAGE,
                         syncer::OS_PREFERENCES, syncer::OS_PRIORITY_PREFERENCES,
                         syncer::PRINTERS,
                         syncer::PRINTERS_AUTHORIZATION_SERVERS,
                         syncer::WIFI_CONFIGURATIONS, syncer::WORKSPACE_DESK});
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
   return allowed_types;
 }
diff --git a/chrome/browser/sync/test/integration/sync_test.h b/chrome/browser/sync/test/integration/sync_test.h
index a7d26f5c..c284646 100644
--- a/chrome/browser/sync/test/integration/sync_test.h
+++ b/chrome/browser/sync/test/integration/sync_test.h
@@ -16,7 +16,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/buildflag.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/sync/test/integration/invalidations/fake_server_sync_invalidation_sender.h"
@@ -29,9 +28,9 @@
 #include "net/http/http_status_code.h"
 #include "services/network/test/test_url_loader_factory.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ash/app_list/app_list_syncable_service.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_ANDROID)
 #include "components/gcm_driver/instance_id/scoped_use_fake_instance_id_android.h"
@@ -439,7 +438,7 @@
   extensions::ScopedInstallVerifierBypassForTest ignore_install_verification_;
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   // A factory-like callback to create a model updater for testing, which will
   // take the place of the real updater in AppListSyncableService for testing.
   std::unique_ptr<base::ScopedClosureRunner> model_updater_factory_scope_;
diff --git a/chrome/browser/sync/test/integration/two_client_app_settings_sync_test.cc b/chrome/browser/sync/test/integration/two_client_app_settings_sync_test.cc
index da68e2f..9cc79ab 100644
--- a/chrome/browser/sync/test/integration/two_client_app_settings_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_app_settings_sync_test.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/strings/stringprintf.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
 #include "chrome/browser/sync/test/integration/apps_sync_test_base.h"
@@ -15,7 +15,7 @@
 #include "components/sync/service/sync_service_impl.h"
 #include "content/public/test/browser_test.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
 #endif
 
@@ -76,22 +76,6 @@
     // TODO(crbug.com/40724949): rewrite tests to not use verifier.
     return true;
   }
-
-  bool SetupClients() override {
-    if (!SyncTest::SetupClients()) {
-      return false;
-    }
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-    // Apps sync is controlled by a dedicated preference on Lacros,
-    // corresponding to the Apps toggle in OS Sync settings.
-    // Enable the Apps toggle for both clients.
-    if (base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing)) {
-      GetSyncService(0)->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-      GetSyncService(1)->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-    }
-#endif
-    return true;
-  }
 };
 
 // For three independent extensions:
@@ -238,8 +222,8 @@
       InstallHostedAppForAllProfiles(1), InstallHostedAppForAllProfiles(2));
 }
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// Tests for ChromeOS-Ash, which uses a different DataTypeController for
+#if BUILDFLAG(IS_CHROMEOS)
+// Tests for ChromeOS, which uses a different DataTypeController for
 // syncer::APP_SETTINGS.
 class TwoClientAppSettingsOsSyncTest : public SyncTest {
  public:
@@ -267,6 +251,6 @@
       StartWithDifferentSettingsTest, InstallHostedAppForAllProfiles(0),
       InstallHostedAppForAllProfiles(1), InstallHostedAppForAllProfiles(2));
 }
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
index a53ccbf..8a56638 100644
--- a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
 #include "chrome/browser/sync/test/integration/encryption_helper.h"
 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
@@ -15,9 +14,9 @@
 #include "content/public/test/test_launcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
 
 namespace {
 
diff --git a/chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc
index bf0a70b..ba14fe6 100644
--- a/chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_extension_apps_sync_test.cc
@@ -66,20 +66,6 @@
       const TwoClientExtensionAppsSyncTest&) = delete;
 
   ~TwoClientExtensionAppsSyncTest() override = default;
-  bool SetupClients() override {
-    if (!SyncTest::SetupClients()) {
-      return false;
-    }
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-    // Apps sync is controlled by a dedicated preference on Lacros,
-    // corresponding to the Apps toggle in OS Sync settings.
-    if (base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing)) {
-      GetSyncService(0)->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-      GetSyncService(1)->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-    }
-#endif
-    return true;
-  }
 };
 
 IN_PROC_BROWSER_TEST_F(TwoClientExtensionAppsSyncTest,
diff --git a/chrome/browser/sync/test/integration/two_client_extension_settings_sync_test.cc b/chrome/browser/sync/test/integration/two_client_extension_settings_sync_test.cc
index fad7cba..127793ba 100644
--- a/chrome/browser/sync/test/integration/two_client_extension_settings_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_extension_settings_sync_test.cc
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/strings/stringprintf.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/browser/extensions/scoped_test_mv2_enabler.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/test/integration/extension_settings_helper.h"
@@ -14,7 +14,7 @@
 #include "components/sync/service/sync_service_impl.h"
 #include "content/public/test/browser_test.h"
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_features.h"
 #endif
 
diff --git a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
index f99ca46..75e30e9 100644
--- a/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_send_tab_to_self_sync_test.cc
@@ -5,7 +5,6 @@
 #include "base/callback_list.h"
 #include "base/run_loop.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
 #include "chrome/browser/sync/device_info_sync_service_factory.h"
@@ -256,7 +255,7 @@
 };
 
 // Non-primary accounts don't exist on ChromeOS.
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#if !BUILDFLAG(IS_CHROMEOS)
 
 IN_PROC_BROWSER_TEST_F(TwoClientSendTabToSelfWithTransportModeSyncTest,
                        SignedInClientCanReceive) {
@@ -289,4 +288,4 @@
   EXPECT_TRUE(device_infos[1]->send_tab_to_self_receiving_enabled());
 }
 
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // !BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
index cf35b388..796ca2c9 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_bmo_sync_test.cc
@@ -7,7 +7,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "build/build_config.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
 #include "chrome/browser/sync/test/integration/web_apps_sync_test_base.h"
@@ -76,17 +75,6 @@
     if (!result) {
       return result;
     }
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-    // Apps sync is controlled by a dedicated preference on Lacros,
-    // corresponding to the Apps toggle in OS Sync settings.
-    // Enable the Apps toggle for both clients.
-    if (base::FeatureList::IsEnabled(syncer::kSyncChromeOSAppsToggleSharing)) {
-      GetSyncService(0)->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-      GetSyncService(1)->GetUserSettings()->SetAppsSyncEnabledByOs(true);
-    }
-#endif
-
     for (Profile* profile : GetAllProfiles()) {
       web_app::test::WaitUntilWebAppProviderAndSubsystemsReady(
           WebAppProvider::GetForTest(profile));
@@ -513,8 +501,7 @@
 }
 
 // Flaky on Linux TSan (crbug.com/1108172).
-#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \
-    defined(THREAD_SANITIZER)
+#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)
 #define MAYBE_UninstallSynced DISABLED_UninstallSynced
 #else
 #define MAYBE_UninstallSynced UninstallSynced
@@ -646,7 +633,7 @@
     base::RunLoop loop;
     base::RepeatingCallback<void(const webapps::AppId&)> on_installed_closure;
     base::RepeatingCallback<void(const webapps::AppId&)> on_hooks_closure;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     on_installed_closure = base::DoNothing();
     on_hooks_closure = base::BindLambdaForTesting(
         [&](const webapps::AppId& installed_app_id) { loop.Quit(); });
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc
index 375f820b..f9d3907 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_integration_test_base.cc
@@ -48,34 +48,34 @@
 
 void TwoClientWebAppsIntegrationTestBase::SyncTurnOff() {
   for (SyncServiceImplHarness* client : GetSyncClients()) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     client->service()->GetUserSettings()->SetSelectedOsTypes(
         /*sync_everything=*/false, /*types=*/{});
-#else   // BUILDFLAG(IS_CHROMEOS_ASH)
+#else   // BUILDFLAG(IS_CHROMEOS)
     client->service()->GetUserSettings()->SetSelectedTypes(
         /*sync_everything=*/false, /*types=*/{});
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
   }
 }
 
 void TwoClientWebAppsIntegrationTestBase::SyncTurnOn() {
   for (SyncServiceImplHarness* client : GetSyncClients()) {
     ASSERT_TRUE(client->SetupSync());
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
     client->service()->GetUserSettings()->SetSelectedOsTypes(
         /*sync_everything=*/false,
         /*types=*/{syncer::UserSelectableOsType::kOsApps});
-#else   // BUILDFLAG(IS_CHROMEOS_ASH)
+#else   // BUILDFLAG(IS_CHROMEOS)
     client->service()->GetUserSettings()->SetSelectedTypes(
         /*sync_everything=*/false,
         /*types=*/{syncer::UserSelectableType::kApps});
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
   }
   AwaitWebAppQuiescence();
 }
 
 void TwoClientWebAppsIntegrationTestBase::SyncSignOut(Profile* profile) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   NOTREACHED();
 #else
   for (int i = 0; i < num_clients(); ++i) {
@@ -89,7 +89,7 @@
 }
 
 void TwoClientWebAppsIntegrationTestBase::SyncSignIn(Profile* profile) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   NOTREACHED();
 #else
   for (int i = 0; i < num_clients(); ++i) {
@@ -138,17 +138,17 @@
 
     ASSERT_TRUE(GetClient(i)->SetupSync(
         base::BindLambdaForTesting([](syncer::SyncUserSettings* user_settings) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
           user_settings->SetSelectedTypes(/*sync_everything=*/false,
                                           /*types=*/{});
           user_settings->SetSelectedOsTypes(
               /*sync_everything=*/false,
               /*types=*/{syncer::UserSelectableOsType::kOsApps});
-#else   // BUILDFLAG(IS_CHROMEOS_ASH)
+#else   // BUILDFLAG(IS_CHROMEOS)
           user_settings->SetSelectedTypes(
               /*sync_everything=*/false,
               /*types=*/{syncer::UserSelectableType::kApps});
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // BUILDFLAG(IS_CHROMEOS)
         })));
   }
 
diff --git a/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
index 64f0aed..e74fcec 100644
--- a/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_web_apps_sync_test.cc
@@ -8,7 +8,7 @@
 #include "base/scoped_observation.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
-#include "build/chromeos_buildflags.h"
+#include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/sync/test/integration/apps_helper.h"
 #include "chrome/browser/sync/test/integration/web_apps_sync_test_base.h"
diff --git a/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc b/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
index 5278a449..e335b50 100644
--- a/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
+++ b/chrome/browser/sync/test/integration/web_apps_sync_test_base.cc
@@ -5,20 +5,13 @@
 #include "chrome/browser/sync/test/integration/web_apps_sync_test_base.h"
 
 #include "base/containers/extend.h"
+#include "build/build_config.h"
 #include "chrome/common/chrome_features.h"
 #include "content/public/common/content_features.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/web_applications/link_capturing_features.h"
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/constants/ash_features.h"
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/sync/chrome_sync_client.h"
-#include "chrome/browser/web_applications/web_app_utils.h"
+#include "chrome/browser/web_applications/link_capturing_features.h"
 #endif
 
 namespace web_app {
diff --git a/chrome/browser/sync/test/integration/web_apps_sync_test_base.h b/chrome/browser/sync/test/integration/web_apps_sync_test_base.h
index 5b679302..f4e88719 100644
--- a/chrome/browser/sync/test/integration/web_apps_sync_test_base.h
+++ b/chrome/browser/sync/test/integration/web_apps_sync_test_base.h
@@ -6,13 +6,8 @@
 #define CHROME_BROWSER_SYNC_TEST_INTEGRATION_WEB_APPS_SYNC_TEST_BASE_H_
 
 #include "base/test/scoped_feature_list.h"
-#include "build/chromeos_buildflags.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-#include "chrome/browser/web_applications/test/web_app_test_utils.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
-
 namespace web_app {
 
 class WebAppsSyncTestBase : public SyncTest {
@@ -22,9 +17,6 @@
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-  web_app::test::ScopedSkipMainProfileCheck skip_main_profile_check_;
-#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 };
 
 }  // namespace web_app
diff --git a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
index 6c318139..6e6d9ea 100644
--- a/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
+++ b/chrome/browser/touch_to_fill/autofill/android/touch_to_fill_delegate_android_impl_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/foundations/test_autofill_driver.h"
 #include "components/autofill/core/browser/foundations/test_browser_autofill_manager.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/suggestions/suggestion_type.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
@@ -712,10 +713,8 @@
   autofill_client_.GetPersonalDataManager()
       .payments_data_manager()
       .AddCreditCard(expired_card);
-  std::vector<const CreditCard*> credit_cards =
-      autofill_client_.GetPersonalDataManager()
-          .payments_data_manager()
-          .GetCreditCardsToSuggest();
+  std::vector<const CreditCard*> credit_cards = GetCreditCardsToSuggest(
+      autofill_client_.GetPersonalDataManager().payments_data_manager());
 
   ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill());
   EXPECT_CALL(
@@ -868,10 +867,8 @@
   autofill_client_.GetPersonalDataManager()
       .payments_data_manager()
       .AddCreditCard(credit_card2);
-  std::vector<const CreditCard*> credit_cards =
-      autofill_client_.GetPersonalDataManager()
-          .payments_data_manager()
-          .GetCreditCardsToSuggest();
+  std::vector<const CreditCard*> credit_cards = GetCreditCardsToSuggest(
+      autofill_client_.GetPersonalDataManager().payments_data_manager());
 
   EXPECT_CALL(
       payments_autofill_client(),
diff --git a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc
index e3a30e7..e89a0cb0 100644
--- a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc
+++ b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h"
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -492,7 +493,7 @@
 
   Profile* profile = ProfileManager::GetActiveUserProfile();
   if (!profile) {
-    std::move(callback).Run("");
+    std::move(callback).Run(std::nullopt);
     return;
   }
 
@@ -509,10 +510,10 @@
 
   // Set a pending request to be fulfilled after the OCR service is ready. We
   // only need to fulfill the latest request when the OCR service becomes ready,
-  // so if there is a previous request then respond to it with an empty string
-  // and create a new request with the new `image` and `callback`.
+  // so if there is a previous request then respond to it with nullopt and
+  // create a new request with the new `image` and `callback`.
   if (!pending_ocr_request_callback_.is_null()) {
-    std::move(pending_ocr_request_callback_).Run("");
+    std::move(pending_ocr_request_callback_).Run(std::nullopt);
   }
   pending_ocr_request_image_ = image;
   pending_ocr_request_callback_ = std::move(callback);
@@ -713,7 +714,7 @@
   // before OCR finishes initialization or if the OCR service is disconnected.
   if (!optical_character_recognizer_ ||
       !optical_character_recognizer_->is_ready()) {
-    std::move(callback).Run("");
+    std::move(callback).Run(std::nullopt);
     ResetOcr();
     return;
   }
@@ -745,6 +746,6 @@
   optical_character_recognizer_ = nullptr;
   pending_ocr_request_image_.reset();
   if (!pending_ocr_request_callback_.is_null()) {
-    std::move(pending_ocr_request_callback_).Run("");
+    std::move(pending_ocr_request_callback_).Run(std::nullopt);
   }
 }
diff --git a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate_browsertest.cc b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate_browsertest.cc
index 5565ab83..111fadc 100644
--- a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate_browsertest.cc
+++ b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h"
 
+#include <optional>
 #include <string>
 #include <utility>
 
@@ -166,12 +167,12 @@
 // the real detected text.
 IN_PROC_BROWSER_TEST_F(ChromeCaptureModeDelegateBrowserTest,
                        EmptyDetectedTextWhenOCRNotSupported) {
-  base::test::TestFuture<std::string> detected_text_future;
+  base::test::TestFuture<std::optional<std::string>> detected_text_future;
 
   ChromeCaptureModeDelegate::Get()->DetectTextInImage(
       SkBitmap(), detected_text_future.GetCallback());
 
-  EXPECT_EQ(detected_text_future.Get(), "");
+  EXPECT_EQ(detected_text_future.Get(), std::nullopt);
 }
 
 // Simulates successful text detection using a fake OCR backend.
@@ -193,7 +194,7 @@
       std::move(visual_annotation));
   delegate->set_optical_character_recognizer_for_testing(
       std::move(optical_character_recognizer));
-  base::test::TestFuture<std::string> detected_text_future;
+  base::test::TestFuture<std::optional<std::string>> detected_text_future;
 
   delegate->DetectTextInImage(SkBitmap(), detected_text_future.GetCallback());
 
@@ -206,11 +207,11 @@
   delegate->set_optical_character_recognizer_for_testing(
       screen_ai::FakeOpticalCharacterRecognizer::Create(
           /*empty_ax_tree_update_result=*/false));
-  base::test::TestFuture<std::string> detected_text_future;
+  base::test::TestFuture<std::optional<std::string>> detected_text_future;
 
   // Close the session immediately after a text detection request.
   delegate->DetectTextInImage(SkBitmap(), detected_text_future.GetCallback());
   delegate->OnSessionStateChanged(/*started=*/false);
 
-  EXPECT_EQ(detected_text_future.Get(), "");
+  EXPECT_EQ(detected_text_future.Get(), std::nullopt);
 }
diff --git a/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc b/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
index 79cfe7ca..c69eda5 100644
--- a/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
+++ b/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
@@ -8,6 +8,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/public/cpp/capture_mode/capture_mode_api.h"
+#include "base/functional/callback_helpers.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h"
@@ -137,7 +138,7 @@
   ChromeCaptureModeDelegate* delegate = ChromeCaptureModeDelegate::Get();
   delegate->SendRegionSearch(SkBitmap(), gfx::Rect(),
                              base::BindRepeating([](GURL url) {}),
-                             base::BindRepeating([](std::string string) {}));
+                             base::DoNothing());
 
   // Send a multimodal search.
   delegate->SendMultimodalSearch(SkBitmap(), gfx::Rect(), "Search",
@@ -147,7 +148,7 @@
   // region.
   delegate->SendRegionSearch(SkBitmap(), gfx::Rect(10, 10, 400, 400),
                              base::BindRepeating([](GURL url) {}),
-                             base::BindRepeating([](std::string string) {}));
+                             base::DoNothing());
 
   // Simulate sending a multimodal search with the adjusted region.
   delegate->SendMultimodalSearch(SkBitmap(), gfx::Rect(10, 10, 400, 400),
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 63392d44..4a3f7edc 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -1171,6 +1171,11 @@
   RecordUkmAndTaskCompletionForLensOverlayInteraction(user_action);
 }
 
+void LensOverlayController::RecordSemanticEventForTesting(
+    lens::mojom::SemanticEvent event) {
+  RecordLensOverlaySemanticEvent(event);
+}
+
 void LensOverlayController::IssueSearchBoxRequestForTesting(
     const std::string& search_box_text,
     AutocompleteMatchType::Type match_type,
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.h b/chrome/browser/ui/lens/lens_overlay_controller.h
index c02b24b..50deeec 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.h
+++ b/chrome/browser/ui/lens/lens_overlay_controller.h
@@ -450,10 +450,13 @@
                                            int selection_end_index,
                                            bool is_translate = false);
 
-  // Testing function to issue a text request.
+  // Testing function to issue a task completion event for a user action.
   void RecordUkmAndTaskCompletionForLensOverlayInteractionForTesting(
       lens::mojom::UserAction user_action);
 
+  // Testing function to issue a semantic event.
+  void RecordSemanticEventForTesting(lens::mojom::SemanticEvent event);
+
   // Testing function to issue a translate request.
   void IssueTranslateSelectionRequestForTesting(
       const std::string& text_query,
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index 3d592df..880d38ac 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -15,6 +15,7 @@
 
 #include <memory>
 
+#include "base/base64url.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/memory/raw_ptr_exclusion.h"
@@ -22,6 +23,7 @@
 #include "base/strings/string_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
+#include "base/test/protobuf_matchers.h"
 #include "base/test/run_until.h"
 #include "base/test/with_feature_override.h"
 #include "base/threading/thread_restrictions.h"
@@ -155,6 +157,7 @@
 constexpr char kHelloWorldDataUri[] =
     "data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E";
 
+using ::base::test::EqualsProto;
 using ::testing::_;
 using State = LensOverlayController::State;
 using LensOverlayInvocationSource = lens::LensOverlayInvocationSource;
@@ -250,6 +253,16 @@
 
 constexpr char kResultsSearchBaseUrl[] = "https://www.google.com/search";
 
+std::string EncodeRequestId(const lens::LensOverlayRequestId& request_id) {
+  std::string serialized_request_id;
+  CHECK(request_id.SerializeToString(&serialized_request_id));
+  std::string encoded_request_id;
+  base::Base64UrlEncode(serialized_request_id,
+                        base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &encoded_request_id);
+  return encoded_request_id;
+}
+
 // Opens the given URL in the given browser and waits for the first paint to
 // complete.
 void WaitForPaintImpl(
@@ -507,11 +520,16 @@
       lens::LensOverlayInvocationSource invocation_source,
       bool use_dark_mode,
       lens::LensOverlayGen204Controller* gen204_controller) override {
+    url_callback_ = url_callback;
     auto fake_query_controller =
         std::make_unique<lens::TestLensOverlayQueryController>(
-            full_image_callback, url_callback, suggest_inputs_callback,
-            thumbnail_created_callback, variations_client, identity_manager,
-            profile, invocation_source, use_dark_mode, gen204_controller);
+            full_image_callback,
+            base::BindRepeating(
+                &LensOverlayControllerFake::RecordUrlResponseCallback,
+                base::Unretained(this)),
+            suggest_inputs_callback, thumbnail_created_callback,
+            variations_client, identity_manager, profile, invocation_source,
+            use_dark_mode, gen204_controller);
     // Set up the fake responses for the query controller.
     fake_query_controller->set_next_full_image_request_should_return_error(
         full_image_request_should_return_error_);
@@ -568,12 +586,22 @@
     is_side_panel_loading_set_to_false_ = 0;
   }
 
+  // A url response callback that records the url sent to the callback.
+  void RecordUrlResponseCallback(lens::proto::LensOverlayUrlResponse response) {
+    last_search_url_ = response.url();
+    if (!url_callback_.is_null()) {
+      url_callback_.Run(response);
+    }
+  }
+
   void FlushForTesting() { fake_overlay_page_receiver_.FlushForTesting(); }
 
   int is_side_panel_loading_set_to_true_ = 0;
   int is_side_panel_loading_set_to_false_ = 0;
+  std::string last_search_url_;
   std::vector<std::string> ocr_response_words_;
   LensOverlayPageFake fake_overlay_page_;
+  lens::LensOverlayUrlResponseCallback url_callback_;
   bool full_image_request_should_return_error_ = false;
   bool is_screenshot_possible_ = true;
   mojo::Receiver<lens::mojom::LensPage> fake_overlay_page_receiver_{
@@ -4504,13 +4532,16 @@
 }
 
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
-                       CorrectAnalyticsIdSentWithGen204s) {
+                       CorrectAnalyticsAndRequestIdSentWithGen204s) {
   WaitForPaint();
 
   // State should start in off.
   auto* controller = GetLensOverlayController();
   ASSERT_EQ(controller->state(), State::kOff);
 
+  auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller);
+  ASSERT_TRUE(fake_controller);
+
   // Showing UI should change the state to screenshot and eventually to overlay.
   // When the overlay is bound, it should start the query flow which returns a
   // response for the full image callback.
@@ -4533,9 +4564,16 @@
   EXPECT_FALSE(
       fake_query_controller->last_latency_gen204_analytics_id().has_value());
 
+  // Objects request latency gen204 should have the same vsrid as the actual
+  // request.
+  EXPECT_THAT(
+      fake_query_controller->sent_full_image_request_id(),
+      EqualsProto(
+          fake_query_controller->last_latency_gen204_request_id().value()));
+
   std::string encoded_sent_objects_analytics_id = base32::Base32Encode(
       base::as_byte_span(
-          fake_query_controller->sent_request_id().analytics_id()),
+          fake_query_controller->sent_full_image_request_id().analytics_id()),
       base32::Base32EncodePolicy::OMIT_PADDING);
 
   // Log a copy text user task completion event.
@@ -4543,7 +4581,7 @@
       lens::mojom::UserAction::kCopyText);
 
   // The objects request and the task completion gen204 should have the same
-  // analytics id.
+  // analytics id and request id.
   EXPECT_EQ(fake_query_controller->last_user_action(),
             lens::mojom::UserAction::kCopyText);
   EXPECT_TRUE(fake_query_controller->last_task_completion_gen204_analytics_id()
@@ -4551,6 +4589,9 @@
   EXPECT_EQ(
       fake_query_controller->last_task_completion_gen204_analytics_id().value(),
       encoded_sent_objects_analytics_id);
+  EXPECT_THAT(
+      fake_query_controller->last_task_completion_gen204_request_id().value(),
+      EqualsProto(fake_query_controller->sent_full_image_request_id()));
 
   // Issue a text selection request and record the task completion.
   controller->IssueTextSelectionRequestForTesting("oranges", 20, 200);
@@ -4563,7 +4604,7 @@
       controller->GetSidePanelWebContentsForTesting()));
 
   // The objects request and the task completion gen204 should have the same
-  // analytics id.
+  // analytics id and request id.
   EXPECT_EQ(fake_query_controller->last_user_action(),
             lens::mojom::UserAction::kTextSelection);
   EXPECT_TRUE(fake_query_controller->last_task_completion_gen204_analytics_id()
@@ -4585,33 +4626,64 @@
   // objects request.
   std::string encoded_sent_interaction_analytics_id = base32::Base32Encode(
       base::as_byte_span(
-          fake_query_controller->sent_request_id().analytics_id()),
+          fake_query_controller->sent_interaction_request_id().analytics_id()),
       base32::Base32EncodePolicy::OMIT_PADDING);
   EXPECT_NE(encoded_sent_interaction_analytics_id,
             encoded_sent_objects_analytics_id);
 
+  // Issue a delayed text gleams view start event. Normally, this would be sent
+  // as soon as the full image response is received, but it is possible that an
+  // interaction request and search page request is sent before the full image
+  // response is received, such as for the INJECTED_IMAGE case.
+  controller->RecordSemanticEventForTesting(
+      lens::mojom::SemanticEvent::kTextGleamsViewStart);
+
+  // There should be a semantic action for text view start, with a different
+  // request id than the objects or interaction request, as it instead
+  // corresponds to the search request.
+  EXPECT_EQ(fake_query_controller->last_semantic_event().value(),
+            lens::mojom::SemanticEvent::kTextGleamsViewStart);
+  EXPECT_THAT(
+      fake_query_controller->last_semantic_event_gen204_request_id().value(),
+      testing::Not(
+          EqualsProto(fake_query_controller->sent_full_image_request_id())));
+  EXPECT_THAT(
+      fake_query_controller->last_semantic_event_gen204_request_id().value(),
+      testing::Not(
+          EqualsProto(fake_query_controller->sent_interaction_request_id())));
+
+  std::string search_url_vsrid;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(
+      GURL(fake_controller->last_search_url_), kLensRequestQueryParameter,
+      &search_url_vsrid));
+  EXPECT_EQ(EncodeRequestId(
+                fake_query_controller->last_semantic_event_gen204_request_id()
+                    .value()),
+            search_url_vsrid);
+
   // The interaction request and latency gen204 should have the same analytics
-  // id.
+  // and request id.
   EXPECT_EQ(fake_query_controller->latency_gen_204_counter(
                 lens::LensOverlayGen204Controller::LatencyType::
                     kInteractionRequestFetchLatency),
             1);
   EXPECT_EQ(encoded_sent_interaction_analytics_id,
             fake_query_controller->last_latency_gen204_analytics_id().value());
+  EXPECT_THAT(
+      fake_query_controller->sent_interaction_request_id(),
+      EqualsProto(
+          fake_query_controller->last_latency_gen204_request_id().value()));
 
   // Log a copy text user task completion event.
   controller->RecordUkmAndTaskCompletionForLensOverlayInteractionForTesting(
       lens::mojom::UserAction::kCopyText);
 
-  // The interaction request and the task completion gen204 should have the same
-  // analytics id.
-  EXPECT_EQ(fake_query_controller->last_user_action(),
-            lens::mojom::UserAction::kCopyText);
-  EXPECT_TRUE(fake_query_controller->last_task_completion_gen204_analytics_id()
-                  .has_value());
-  EXPECT_EQ(
-      fake_query_controller->last_task_completion_gen204_analytics_id().value(),
-      encoded_sent_interaction_analytics_id);
+  // The encoded vsrid in the task completion gen204 should match that of
+  // the search request.
+  EXPECT_EQ(EncodeRequestId(
+                fake_query_controller->last_semantic_event_gen204_request_id()
+                    .value()),
+            search_url_vsrid);
 
   // Issue a text selection request and record the task completion.
   content::TestNavigationObserver text_selection_observer(
@@ -4624,8 +4696,8 @@
   // search query.
   text_selection_observer.Wait();
 
-  // The interaction request and the task completion gen204 should have the same
-  // analytics id.
+  // The encoded vsrid in the task completion gen204 should match that of
+  // the search request.
   EXPECT_EQ(fake_query_controller->last_user_action(),
             lens::mojom::UserAction::kTextSelection);
   EXPECT_TRUE(fake_query_controller->last_task_completion_gen204_analytics_id()
@@ -4633,6 +4705,15 @@
   EXPECT_EQ(
       fake_query_controller->last_task_completion_gen204_analytics_id().value(),
       encoded_sent_interaction_analytics_id);
+  EXPECT_THAT(
+      fake_query_controller->sent_interaction_request_id(),
+      testing::Not(EqualsProto(
+          fake_query_controller->last_task_completion_gen204_request_id()
+              .value())));
+  EXPECT_EQ(EncodeRequestId(
+                fake_query_controller->last_semantic_event_gen204_request_id()
+                    .value()),
+            search_url_vsrid);
 }
 
 class LensOverlayControllerBrowserStartQueryFlowOptimization
@@ -4686,7 +4767,8 @@
       fake_query_controller->sent_full_image_objects_request().has_payload());
   EXPECT_EQ(fake_query_controller->last_sent_underlying_content_type(),
             lens::MimeType::kPlainText);
-  EXPECT_EQ(fake_query_controller->sent_request_id().sequence_id(), 1);
+  EXPECT_EQ(fake_query_controller->sent_full_image_request_id().sequence_id(),
+            1);
   EXPECT_EQ(fake_query_controller->sent_page_content_request_id().sequence_id(),
             1);
   EXPECT_FALSE(
diff --git a/chrome/browser/ui/lens/lens_overlay_gen204_controller.cc b/chrome/browser/ui/lens/lens_overlay_gen204_controller.cc
index 0c22cc8..606bbcf 100644
--- a/chrome/browser/ui/lens/lens_overlay_gen204_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_gen204_controller.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/lens/lens_overlay_gen204_controller.h"
 
+#include "base/base64url.h"
 #include "base/containers/span.h"
 #include "base/format_macros.h"
 #include "base/rand_util.h"
@@ -42,6 +43,7 @@
 
 // Query parameter keys.
 constexpr char kEncodedAnalyticsIdParameter[] = "cad";
+constexpr char kEncodedRequestIdParameter[] = "vsrid";
 constexpr char kGen204IdentifierQueryParameter[] = "plla";
 constexpr char kLatencyRequestTypeQueryParameter[] = "rt";
 constexpr char kVisualInputTypeQueryParameter[] = "vit";
@@ -136,6 +138,16 @@
   }
 }
 
+std::string EncodeRequestId(const lens::LensOverlayRequestId& request_id) {
+  std::string serialized_request_id;
+  CHECK(request_id.SerializeToString(&serialized_request_id));
+  std::string encoded_request_id;
+  base::Base64UrlEncode(serialized_request_id,
+                        base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &encoded_request_id);
+  return encoded_request_id;
+}
+
 }  // namespace
 
 LensOverlayGen204Controller::LensOverlayGen204Controller() = default;
@@ -155,7 +167,8 @@
     base::TimeDelta latency_duration,
     std::string vit_query_param_value,
     std::optional<base::TimeDelta> cluster_info_latency,
-    std::optional<std::string> encoded_analytics_id) {
+    std::optional<std::string> encoded_analytics_id,
+    std::optional<lens::LensOverlayRequestId> request_id) {
   if (profile_ && lens::features::GetLensOverlaySendLatencyGen204()) {
     std::string cluster_info_latency_string =
         cluster_info_latency.has_value() &&
@@ -185,13 +198,19 @@
           fetch_url, kEncodedAnalyticsIdParameter,
           encoded_analytics_id.value());
     }
+    if (request_id.has_value()) {
+      fetch_url = net::AppendOrReplaceQueryParameter(
+          fetch_url, kEncodedRequestIdParameter,
+          EncodeRequestId(request_id.value()));
+    }
     CheckMetricsConsentAndIssueGen204NetworkRequest(fetch_url);
   }
 }
 
 void LensOverlayGen204Controller::SendTaskCompletionGen204IfEnabled(
     std::string encoded_analytics_id,
-    lens::mojom::UserAction user_action) {
+    lens::mojom::UserAction user_action,
+    lens::LensOverlayRequestId request_id) {
   if (profile_ && lens::features::GetLensOverlaySendTaskCompletionGen204()) {
     int task_id;
     switch (user_action) {
@@ -217,9 +236,10 @@
         return;
     }
     std::string query = base::StringPrintf(
-        "gen_204?uact=4&%s=%" PRIu64 "&%s=%d&%s=%s",
+        "gen_204?uact=4&%s=%" PRIu64 "&%s=%d&%s=%s&%s=%s",
         kGen204IdentifierQueryParameter, gen204_id_, kEventIdParameter, task_id,
-        kEncodedAnalyticsIdParameter, encoded_analytics_id.c_str());
+        kEncodedAnalyticsIdParameter, encoded_analytics_id.c_str(),
+        kEncodedRequestIdParameter, EncodeRequestId(request_id).c_str());
     auto fetch_url = GURL(TemplateURLServiceFactory::GetForProfile(profile_)
                               ->search_terms_data()
                               .GoogleBaseURLValue())
@@ -231,7 +251,8 @@
 }
 
 void LensOverlayGen204Controller::SendSemanticEventGen204IfEnabled(
-    lens::mojom::SemanticEvent event) {
+    lens::mojom::SemanticEvent event,
+    std::optional<lens::LensOverlayRequestId> request_id) {
   if (profile_ && lens::features::GetLensOverlaySendSemanticEventGen204()) {
     int event_id;
     switch (event) {
@@ -252,16 +273,21 @@
                          .Resolve(query);
     fetch_url =
         lens::AppendInvocationSourceParamToURL(fetch_url, invocation_source_);
+    if (request_id.has_value()) {
+      fetch_url = net::AppendOrReplaceQueryParameter(
+          fetch_url, kEncodedRequestIdParameter,
+          EncodeRequestId(request_id.value()));
+    }
     CheckMetricsConsentAndIssueGen204NetworkRequest(fetch_url);
   }
 }
 
-void LensOverlayGen204Controller::OnQueryFlowEnd(
-    std::string encoded_analytics_id) {
+void LensOverlayGen204Controller::OnQueryFlowEnd() {
   // Send a text gleams view end event because the event trigger from webui
   // will not fire when the overlay is closing. The server will dedupe
   // end events.
-  SendSemanticEventGen204IfEnabled(mojom::SemanticEvent::kTextGleamsViewEnd);
+  SendSemanticEventGen204IfEnabled(mojom::SemanticEvent::kTextGleamsViewEnd,
+                                   /*request_id=*/std::nullopt);
   profile_ = nullptr;
 }
 
diff --git a/chrome/browser/ui/lens/lens_overlay_gen204_controller.h b/chrome/browser/ui/lens/lens_overlay_gen204_controller.h
index 0939349d..53bfcab 100644
--- a/chrome/browser/ui/lens/lens_overlay_gen204_controller.h
+++ b/chrome/browser/ui/lens/lens_overlay_gen204_controller.h
@@ -8,6 +8,7 @@
 #include "chrome/browser/lens/core/mojom/lens.mojom.h"
 #include "components/lens/lens_overlay_invocation_source.h"
 #include "services/network/public/cpp/simple_url_loader.h"
+#include "third_party/lens_server_proto/lens_overlay_request_id.pb.h"
 
 class Profile;
 
@@ -69,29 +70,38 @@
                         Profile* profile,
                         uint64_t gen204_id);
 
-  // Sends a Lens objects request latency gen204 request.
+  // Sends a Lens latency gen204 request. The request id is optional because not
+  // all latency events have an associated request id, such as the cluster info
+  // fetch.
+  // TODO(crbug.com/394645019): Remove the encoded analytics id parameter when
+  // the analytics id param is no longer used on the server.
   void SendLatencyGen204IfEnabled(
       LatencyType latency_type,
       base::TimeDelta latency_duration,
       std::string vit_query_param_value,
       std::optional<base::TimeDelta> cluster_info_latency,
-      std::optional<std::string> encoded_analytics_id);
+      std::optional<std::string> encoded_analytics_id,
+      std::optional<lens::LensOverlayRequestId> request_id);
 
   // Sends a task completion gen204 request. The analytics id is the
   // latest Lens request analytics id from the query controller.
   // The user action is the action that triggered the task completion
   // event.
+  // TODO(crbug.com/394645019): Remove the encoded analytics id parameter when
+  // the analytics id param is no longer used on the server.
   void SendTaskCompletionGen204IfEnabled(std::string encoded_analytics_id,
-                                         lens::mojom::UserAction user_action);
+                                         lens::mojom::UserAction user_action,
+                                         lens::LensOverlayRequestId request_id);
 
-  // Sends a semantic event gen204 request.
-  void SendSemanticEventGen204IfEnabled(lens::mojom::SemanticEvent event);
+  // Sends a semantic event gen204 request. Some semantic events do not
+  // have an associated request id (e.g. text gleam view end).
+  void SendSemanticEventGen204IfEnabled(
+      lens::mojom::SemanticEvent event,
+      std::optional<lens::LensOverlayRequestId> request_id);
 
   // Sends any final gen204 requests and marks the end of the query flow.
   // Called when the Lens Overlay is closed.
-  // The analytics id is the latest Lens request analytics id from the
-  // query controller.
-  void OnQueryFlowEnd(std::string encoded_analytics_id);
+  void OnQueryFlowEnd();
 
   // Issues the gen204 network request and adds a loader to gen204_loaders_.
   // Checks that the user is opted into metrics logging.
diff --git a/chrome/browser/ui/lens/lens_overlay_gen204_controller_unittest.cc b/chrome/browser/ui/lens/lens_overlay_gen204_controller_unittest.cc
index 60e0ac98..380afc0d 100644
--- a/chrome/browser/ui/lens/lens_overlay_gen204_controller_unittest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_gen204_controller_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "lens_overlay_gen204_controller.h"
 
+#include "base/base64url.h"
 #include "base/containers/span.h"
 #include "base/strings/string_number_conversions.h"
 #include "chrome/browser/lens/core/mojom/lens.mojom-shared.h"
@@ -39,6 +40,7 @@
 
 // Query parameter keys.
 constexpr char kEncodedAnalyticsIdParameter[] = "cad";
+constexpr char kEncodedRequestIdParameter[] = "vsrid";
 constexpr char kGen204IdentifierQueryParameter[] = "plla";
 constexpr char kSemanticEventTimestampParameter[] = "zx";
 constexpr char kLatencyRequestTypeQueryParameter[] = "rt";
@@ -58,6 +60,24 @@
 constexpr int kTextGleamsViewStartSemanticEventID = 234181;
 constexpr int kTextGleamsViewEndSemanticEventID = 234180;
 
+// Creates a dummy request id for testing.
+lens::LensOverlayRequestId CreateRequestId() {
+  lens::LensOverlayRequestId request_id;
+  request_id.set_image_sequence_id(10);
+  request_id.set_sequence_id(15);
+  return request_id;
+}
+
+std::string EncodeRequestId(const lens::LensOverlayRequestId& request_id) {
+  std::string serialized_request_id;
+  CHECK(request_id.SerializeToString(&serialized_request_id));
+  std::string encoded_request_id;
+  base::Base64UrlEncode(serialized_request_id,
+                        base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &encoded_request_id);
+  return encoded_request_id;
+}
+
 class LensOverlayGen204ControllerMock : public LensOverlayGen204Controller {
  public:
   LensOverlayGen204ControllerMock() = default;
@@ -146,7 +166,8 @@
       LatencyType::kFullPageObjectsRequestFetchLatency, kRequestLatency,
       /*vit_query_param_value=*/"image",
       /*cluster_info_latency=*/std::nullopt,
-      /*encoded_analytics_id=*/std::nullopt);
+      /*encoded_analytics_id=*/std::nullopt,
+      /*request_id=*/std::nullopt);
 
   auto url = gen204_controller->last_url_sent_;
 
@@ -174,7 +195,8 @@
       LatencyType::kFullPageTranslateRequestFetchLatency, kRequestLatency,
       /*vit_query_param_value=*/"pdf",
       /*cluster_info_latency=*/std::nullopt,
-      /*encoded_analytics_id=*/std::nullopt);
+      /*encoded_analytics_id=*/std::nullopt,
+      /*request_id=*/std::nullopt);
 
   // Check that the new request type param is present and contains the latency.
   EXPECT_TRUE(net::GetValueForKeyInQuery(gen204_controller->last_url_sent_,
@@ -195,7 +217,8 @@
       LatencyType::kFullPageObjectsRequestFetchLatency, kRequestLatency,
       /*vit_query_param_value=*/"wp",
       std::make_optional<base::TimeDelta>(kClusterInfoLatency),
-      /*encoded_analytics_id=*/std::nullopt);
+      /*encoded_analytics_id=*/std::nullopt,
+      /*request_id=*/std::nullopt);
 
   // Check that the new request type param is present and contains the latency.
   EXPECT_TRUE(net::GetValueForKeyInQuery(gen204_controller->last_url_sent_,
@@ -216,7 +239,8 @@
       LatencyType::kFullPageObjectsRequestFetchLatency, kRequestLatency,
       /*vit_query_param_value=*/"wp",
       std::make_optional<base::TimeDelta>(kClusterInfoLatency),
-      std::make_optional<std::string>(kEncodedAnalyticsId));
+      std::make_optional<std::string>(kEncodedAnalyticsId),
+      /*request_id=*/std::nullopt);
 
   // Check that the encoded analytics id param is present.
   std::string encoded_analytics_id_param;
@@ -226,6 +250,23 @@
   ASSERT_EQ(encoded_analytics_id_param, kEncodedAnalyticsId);
 
   ASSERT_EQ(gen204_controller->num_gen204s_sent_, 4);
+
+  // Send an objects query with a request id.
+  gen204_controller->SendLatencyGen204IfEnabled(
+      LatencyType::kFullPageObjectsRequestFetchLatency, kRequestLatency,
+      /*vit_query_param_value=*/"wp",
+      std::make_optional<base::TimeDelta>(kClusterInfoLatency),
+      std::make_optional<std::string>(kEncodedAnalyticsId),
+      std::make_optional<lens::LensOverlayRequestId>(CreateRequestId()));
+
+  // Check that the encoded request id param is present.
+  std::string encoded_request_id_param;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(gen204_controller->last_url_sent_,
+                                         kEncodedRequestIdParameter,
+                                         &encoded_request_id_param));
+  ASSERT_EQ(encoded_request_id_param, EncodeRequestId(CreateRequestId()));
+
+  ASSERT_EQ(gen204_controller->num_gen204s_sent_, 5);
 }
 
 TEST_F(LensOverlayGen204ControllerTest,
@@ -233,7 +274,8 @@
   auto gen204_controller = std::make_unique<LensOverlayGen204ControllerMock>();
   gen204_controller->OnQueryFlowStart(kInvocationSource, profile(), kGen204Id);
   gen204_controller->SendTaskCompletionGen204IfEnabled(
-      kEncodedAnalyticsId, lens::mojom::UserAction::kCopyText);
+      kEncodedAnalyticsId, lens::mojom::UserAction::kCopyText,
+      CreateRequestId());
 
   auto url = gen204_controller->last_url_sent_;
   EXPECT_THAT(GetTaskCompletionIdFromUrl(url),
@@ -256,6 +298,12 @@
                                          &encoded_analytics_id_param));
   EXPECT_EQ(encoded_analytics_id_param, kEncodedAnalyticsId);
 
+  // Check that the encoded request id param is correct.
+  std::string encoded_request_id_param;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(url, kEncodedRequestIdParameter,
+                                         &encoded_request_id_param));
+  EXPECT_EQ(encoded_request_id_param, EncodeRequestId(CreateRequestId()));
+
   ASSERT_EQ(gen204_controller->num_gen204s_sent_, 1);
 }
 
@@ -265,29 +313,41 @@
   gen204_controller->OnQueryFlowStart(kInvocationSource, profile(), kGen204Id);
 
   gen204_controller->SendTaskCompletionGen204IfEnabled(
-      kEncodedAnalyticsId, lens::mojom::UserAction::kCopyText);
+      kEncodedAnalyticsId, lens::mojom::UserAction::kCopyText,
+      CreateRequestId());
   EXPECT_THAT(GetTaskCompletionIdFromUrl(gen204_controller->last_url_sent_),
               testing::Optional(lens::mojom::UserAction::kCopyText));
 
   gen204_controller->SendTaskCompletionGen204IfEnabled(
-      kEncodedAnalyticsId, lens::mojom::UserAction::kTranslateText);
+      kEncodedAnalyticsId, lens::mojom::UserAction::kTranslateText,
+      CreateRequestId());
   EXPECT_THAT(GetTaskCompletionIdFromUrl(gen204_controller->last_url_sent_),
               testing::Optional(lens::mojom::UserAction::kTranslateText));
 
   gen204_controller->SendTaskCompletionGen204IfEnabled(
-      kEncodedAnalyticsId, lens::mojom::UserAction::kCopyAsImage);
+      kEncodedAnalyticsId, lens::mojom::UserAction::kCopyAsImage,
+      CreateRequestId());
   EXPECT_THAT(GetTaskCompletionIdFromUrl(gen204_controller->last_url_sent_),
               testing::Optional(lens::mojom::UserAction::kCopyAsImage));
 
   gen204_controller->SendTaskCompletionGen204IfEnabled(
-      kEncodedAnalyticsId, lens::mojom::UserAction::kSaveAsImage);
+      kEncodedAnalyticsId, lens::mojom::UserAction::kSaveAsImage,
+      CreateRequestId());
   EXPECT_THAT(GetTaskCompletionIdFromUrl(gen204_controller->last_url_sent_),
               testing::Optional(lens::mojom::UserAction::kSaveAsImage));
 
   gen204_controller->SendTaskCompletionGen204IfEnabled(
-      kEncodedAnalyticsId, lens::mojom::UserAction::kTextSelection);
+      kEncodedAnalyticsId, lens::mojom::UserAction::kTextSelection,
+      CreateRequestId());
   EXPECT_THAT(GetTaskCompletionIdFromUrl(gen204_controller->last_url_sent_),
               testing::Optional(lens::mojom::UserAction::kTextSelection));
+
+  // Check that the encoded request id param is present and correct.
+  std::string encoded_request_id_param;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(gen204_controller->last_url_sent_,
+                                         kEncodedRequestIdParameter,
+                                         &encoded_request_id_param));
+  EXPECT_EQ(encoded_request_id_param, EncodeRequestId(CreateRequestId()));
 }
 
 TEST_F(LensOverlayGen204ControllerTest,
@@ -295,14 +355,15 @@
   auto gen204_controller = std::make_unique<LensOverlayGen204ControllerMock>();
   gen204_controller->OnQueryFlowStart(kInvocationSource, profile(), kGen204Id);
   gen204_controller->SendSemanticEventGen204IfEnabled(
-      lens::mojom::SemanticEvent::kTextGleamsViewStart);
+      lens::mojom::SemanticEvent::kTextGleamsViewStart,
+      /*request_id=*/std::nullopt);
 
   EXPECT_THAT(
       GetSemanticEventFromUrl(gen204_controller->last_url_sent_),
       testing::Optional(lens::mojom::SemanticEvent::kTextGleamsViewStart));
   ASSERT_EQ(gen204_controller->num_gen204s_sent_, 1);
 
-  gen204_controller->OnQueryFlowEnd(kEncodedAnalyticsId);
+  gen204_controller->OnQueryFlowEnd();
 
   // The query flow ending should cause another gen204 event to fire.
   EXPECT_THAT(
@@ -316,7 +377,7 @@
   auto gen204_controller = std::make_unique<LensOverlayGen204ControllerMock>();
   gen204_controller->OnQueryFlowStart(kInvocationSource, profile(), kGen204Id);
   gen204_controller->SendSemanticEventGen204IfEnabled(
-      lens::mojom::SemanticEvent::kTextGleamsViewStart);
+      lens::mojom::SemanticEvent::kTextGleamsViewStart, CreateRequestId());
 
   auto url = gen204_controller->last_url_sent_;
   EXPECT_THAT(
@@ -339,6 +400,12 @@
   EXPECT_TRUE(net::GetValueForKeyInQuery(url, kGen204IdentifierQueryParameter,
                                          &gen204_id_param));
 
+  // Check that the encoded request id param is present and correct.
+  std::string encoded_request_id_param;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(url, kEncodedRequestIdParameter,
+                                         &encoded_request_id_param));
+  EXPECT_EQ(encoded_request_id_param, EncodeRequestId(CreateRequestId()));
+
   ASSERT_EQ(gen204_controller->num_gen204s_sent_, 1);
 }
 
@@ -347,17 +414,33 @@
   auto gen204_controller = std::make_unique<LensOverlayGen204ControllerMock>();
   gen204_controller->OnQueryFlowStart(kInvocationSource, profile(), kGen204Id);
 
+  // Send a text gleams view start event with an encoded request id.
   gen204_controller->SendSemanticEventGen204IfEnabled(
-      lens::mojom::SemanticEvent::kTextGleamsViewStart);
+      lens::mojom::SemanticEvent::kTextGleamsViewStart,
+      std::make_optional<lens::LensOverlayRequestId>(CreateRequestId()));
   EXPECT_THAT(
       GetSemanticEventFromUrl(gen204_controller->last_url_sent_),
       testing::Optional(lens::mojom::SemanticEvent::kTextGleamsViewStart));
 
+  // Check that the encoded request id param is present and correct.
+  std::string encoded_request_id_param;
+  EXPECT_TRUE(net::GetValueForKeyInQuery(gen204_controller->last_url_sent_,
+                                         kEncodedRequestIdParameter,
+                                         &encoded_request_id_param));
+  EXPECT_EQ(encoded_request_id_param, EncodeRequestId(CreateRequestId()));
+
+  // Send a text gleams view end event without an encoded request id.
   gen204_controller->SendSemanticEventGen204IfEnabled(
-      lens::mojom::SemanticEvent::kTextGleamsViewEnd);
+      lens::mojom::SemanticEvent::kTextGleamsViewEnd,
+      /*request_id=*/std::nullopt);
   EXPECT_THAT(
       GetSemanticEventFromUrl(gen204_controller->last_url_sent_),
       testing::Optional(lens::mojom::SemanticEvent::kTextGleamsViewEnd));
+
+  // Check that the encoded request id param is not present.
+  EXPECT_FALSE(net::GetValueForKeyInQuery(gen204_controller->last_url_sent_,
+                                          kEncodedRequestIdParameter,
+                                          &encoded_request_id_param));
 }
 
 }  // namespace lens
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.cc b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
index 31141dca..af69f77 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
@@ -443,7 +443,7 @@
 
 void LensOverlayQueryController::EndQuery() {
   ResetPageContentData();
-  gen204_controller_->OnQueryFlowEnd(latest_encoded_analytics_id_);
+  gen204_controller_->OnQueryFlowEnd();
   full_image_endpoint_fetcher_.reset();
   interaction_endpoint_fetcher_.reset();
   pending_interaction_callback_.Reset();
@@ -616,12 +616,17 @@
 
 void LensOverlayQueryController::SendTaskCompletionGen204IfEnabled(
     lens::mojom::UserAction user_action) {
-  SendTaskCompletionGen204IfEnabled(latest_encoded_analytics_id_, user_action);
+  SendTaskCompletionGen204IfEnabled(latest_encoded_analytics_id_, user_action,
+                                    latest_request_id_);
 }
 
 void LensOverlayQueryController::SendSemanticEventGen204IfEnabled(
     lens::mojom::SemanticEvent event) {
-  gen204_controller_->SendSemanticEventGen204IfEnabled(event);
+  std::optional<lens::LensOverlayRequestId> request_id = std::nullopt;
+  if (event == lens::mojom::SemanticEvent::kTextGleamsViewStart) {
+    request_id = std::make_optional(latest_request_id_);
+  }
+  SendSemanticEventGen204IfEnabled(event, request_id);
 }
 
 void LensOverlayQueryController::ResetRequestClusterInfoStateForTesting() {
@@ -668,18 +673,27 @@
     base::TimeTicks start_time_ticks,
     std::string vit_query_param_value,
     std::optional<base::TimeDelta> cluster_info_latency,
-    std::optional<std::string> encoded_analytics_id) {
+    std::optional<std::string> encoded_analytics_id,
+    std::optional<lens::LensOverlayRequestId> request_id) {
   base::TimeDelta latency_duration = base::TimeTicks::Now() - start_time_ticks;
   gen204_controller_->SendLatencyGen204IfEnabled(
       latency_type, latency_duration, vit_query_param_value,
-      cluster_info_latency, encoded_analytics_id);
+      cluster_info_latency, encoded_analytics_id, request_id);
 }
 
 void LensOverlayQueryController::SendTaskCompletionGen204IfEnabled(
     std::string encoded_analytics_id,
-    lens::mojom::UserAction user_action) {
-  gen204_controller_->SendTaskCompletionGen204IfEnabled(encoded_analytics_id,
-                                                        user_action);
+    lens::mojom::UserAction user_action,
+    lens::LensOverlayRequestId request_id) {
+  gen204_controller_->SendTaskCompletionGen204IfEnabled(
+      encoded_analytics_id, user_action, request_id);
+}
+
+void LensOverlayQueryController::SendSemanticEventGen204IfEnabled(
+    lens::mojom::SemanticEvent event,
+    std::optional<lens::LensOverlayRequestId> request_id) {
+  gen204_controller_->SendSemanticEventGen204IfEnabled(event,
+                                                       request_id);
 }
 
 LensOverlayQueryController::LensServerFetchRequest::LensServerFetchRequest(
@@ -706,6 +720,7 @@
 LensOverlayQueryController::GetNextRequestId(RequestIdUpdateMode update_mode) {
   std::unique_ptr<lens::LensOverlayRequestId> request_id =
       request_id_generator_->GetNextRequestId(update_mode);
+  latest_request_id_ = *request_id.get();
   latest_encoded_analytics_id_ =
       request_id_generator_->GetBase32EncodedAnalyticsId();
   std::string serialized_request_id;
@@ -764,7 +779,8 @@
 
   SendInitialLatencyGen204IfNotAlreadySent(
       LatencyType::kInvocationToInitialClusterInfoRequestSent,
-      VitQueryParamValueForMimeType(underlying_content_type_));
+      VitQueryParamValueForMimeType(underlying_content_type_),
+      /*request_id=*/std::nullopt);
 }
 
 void LensOverlayQueryController::ClusterInfoFetchResponseHandler(
@@ -1008,7 +1024,8 @@
       base::Milliseconds(lens::features::GetLensOverlayServerRequestTimeout()),
       base::BindOnce(
           &LensOverlayQueryController::OnFullImageEndpointFetcherCreated,
-          weak_ptr_factory_.GetWeakPtr()),
+          weak_ptr_factory_.GetWeakPtr(),
+          *latest_full_image_request_data_->request_id_.get()),
       base::BindOnce(&LensOverlayQueryController::FullImageFetchResponseHandler,
                      weak_ptr_factory_.GetWeakPtr(),
                      latest_full_image_request_data_->sequence_id()));
@@ -1194,15 +1211,16 @@
           lens::features::GetLensOverlayPageContentRequestTimeoutMs()),
       base::BindOnce(
           &LensOverlayQueryController::OnPageContentEndpointFetcherCreated,
-          weak_ptr_factory_.GetWeakPtr()),
+          weak_ptr_factory_.GetWeakPtr(), request.objects_request().request_context().request_id()),
       base::BindOnce(&LensOverlayQueryController::PageContentResponseHandler,
-                     weak_ptr_factory_.GetWeakPtr()),
+                     weak_ptr_factory_.GetWeakPtr(), request.objects_request().request_context().request_id()),
       base::BindRepeating(
           &LensOverlayQueryController::PageContentUploadProgressHandler,
           weak_ptr_factory_.GetWeakPtr()));
 }
 
 void LensOverlayQueryController::PageContentResponseHandler(
+    lens::LensOverlayRequestId request_id,
     std::unique_ptr<EndpointResponse> response) {
   page_content_endpoint_fetcher_.reset();
 
@@ -1215,13 +1233,14 @@
       LatencyType::kPageContentUploadLatency, page_contents_request_start_time_,
       VitQueryParamValueForMimeType(underlying_content_type_),
       /*cluster_info_latency=*/std::nullopt,
-      /*encoded_analytics_id=*/std::nullopt);
+      /*encoded_analytics_id=*/std::nullopt,
+      std::make_optional<lens::LensOverlayRequestId>(request_id));
 }
 
 void LensOverlayQueryController::PageContentUploadProgressHandler(
     uint64_t position,
     uint64_t total) {
-  if(lens::features::ShouldHoldContextualQueriesUntilAck()) {
+  if (lens::features::ShouldHoldContextualQueriesUntilAck()) {
     return;
   }
   if (position == total) {
@@ -1305,13 +1324,14 @@
           lens::features::GetLensOverlayPageContentRequestTimeoutMs()),
       base::BindOnce(&LensOverlayQueryController::
                          OnPartialPageContentEndpointFetcherCreated,
-                     weak_ptr_factory_.GetWeakPtr()),
+                     weak_ptr_factory_.GetWeakPtr(), request.objects_request().request_context().request_id()),
       base::BindOnce(
           &LensOverlayQueryController::PartialPageContentResponseHandler,
-          weak_ptr_factory_.GetWeakPtr()));
+          weak_ptr_factory_.GetWeakPtr(), request.objects_request().request_context().request_id()));
 }
 
 void LensOverlayQueryController::PartialPageContentResponseHandler(
+    lens::LensOverlayRequestId request_id,
     std::unique_ptr<EndpointResponse> response) {
   partial_page_content_endpoint_fetcher_.reset();
 
@@ -1320,7 +1340,8 @@
       partial_page_contents_request_start_time_,
       VitQueryParamValueForMimeType(underlying_content_type_),
       /*cluster_info_latency=*/std::nullopt,
-      /*encoded_analytics_id=*/std::nullopt);
+      /*encoded_analytics_id=*/std::nullopt,
+      std::make_optional<lens::LensOverlayRequestId>(request_id));
 }
 
 void LensOverlayQueryController::SendInteraction(
@@ -1538,7 +1559,7 @@
       base::Milliseconds(lens::features::GetLensOverlayServerRequestTimeout()),
       base::BindOnce(
           &LensOverlayQueryController::OnInteractionEndpointFetcherCreated,
-          weak_ptr_factory_.GetWeakPtr()),
+          weak_ptr_factory_.GetWeakPtr(), *latest_interaction_request_data_->request_id_.get()),
       base::BindOnce(
           &LensOverlayQueryController::InteractionFetchResponseHandler,
           weak_ptr_factory_.GetWeakPtr(),
@@ -1625,7 +1646,8 @@
       latest_interaction_request_data_->query_start_time_,
       VitQueryParamValueForMimeType(underlying_content_type_),
       /*cluster_info_latency=*/std::nullopt,
-      std::make_optional(encoded_analytics_id));
+      std::make_optional(encoded_analytics_id),
+      *latest_interaction_request_data_->request_id_.get());
 
   if (!(lens::features::IsLensOverlayContextualSearchboxEnabled() &&
         !lens::features::GetLensOverlaySendImageSignalsForLensSuggest())) {
@@ -1654,21 +1676,23 @@
                                kFullPageObjectsRequestFetchLatency,
       start_time_ticks, vit_query_param_value,
       cluster_info_fetch_response_time_,
-      /*encoded_analytics_id=*/std::nullopt);
+      /*encoded_analytics_id=*/std::nullopt,
+      *latest_full_image_request_data_->request_id_.get());
   cluster_info_fetch_response_time_.reset();
 }
 
 void LensOverlayQueryController::SendInitialLatencyGen204IfNotAlreadySent(
     lens::LensOverlayGen204Controller::LatencyType latency_type,
-    std::string vit_query_param_value) {
+    std::string vit_query_param_value,
+    std::optional<lens::LensOverlayRequestId> request_id) {
   if (sent_initial_latency_request_events_.contains(latency_type)) {
     return;
   }
 
-  SendLatencyGen204IfEnabled(latency_type, invocation_time_,
-                             vit_query_param_value,
-                             /*cluster_info_latency=*/std::nullopt,
-                             /*encoded_analytics_id=*/std::nullopt);
+  SendLatencyGen204IfEnabled(
+      latency_type, invocation_time_, vit_query_param_value,
+      /*cluster_info_latency=*/std::nullopt,
+      /*encoded_analytics_id=*/std::nullopt, request_id);
   sent_initial_latency_request_events_.insert(latency_type);
 }
 
@@ -1932,34 +1956,42 @@
 }
 
 void LensOverlayQueryController::OnFullImageEndpointFetcherCreated(
+    lens::LensOverlayRequestId request_id,
     std::unique_ptr<EndpointFetcher> endpoint_fetcher) {
   SendInitialLatencyGen204IfNotAlreadySent(
       LatencyType::kInvocationToInitialFullPageObjectsRequestSent,
-      VitQueryParamValueForMimeType(underlying_content_type_));
+      VitQueryParamValueForMimeType(underlying_content_type_),
+      request_id);
   full_image_endpoint_fetcher_ = std::move(endpoint_fetcher);
 }
 
 void LensOverlayQueryController::OnPageContentEndpointFetcherCreated(
+    lens::LensOverlayRequestId request_id,
     std::unique_ptr<EndpointFetcher> endpoint_fetcher) {
   SendInitialLatencyGen204IfNotAlreadySent(
       LatencyType::kInvocationToInitialPageContentRequestSent,
-      VitQueryParamValueForMimeType(underlying_content_type_));
+      VitQueryParamValueForMimeType(underlying_content_type_),
+      request_id);
   page_content_endpoint_fetcher_ = std::move(endpoint_fetcher);
 }
 
 void LensOverlayQueryController::OnPartialPageContentEndpointFetcherCreated(
+    lens::LensOverlayRequestId request_id,
     std::unique_ptr<EndpointFetcher> endpoint_fetcher) {
   SendInitialLatencyGen204IfNotAlreadySent(
       LatencyType::kInvocationToInitialPartialPageContentRequestSent,
-      VitQueryParamValueForMimeType(underlying_content_type_));
+      VitQueryParamValueForMimeType(underlying_content_type_),
+      request_id);
   partial_page_content_endpoint_fetcher_ = std::move(endpoint_fetcher);
 }
 
 void LensOverlayQueryController::OnInteractionEndpointFetcherCreated(
+    lens::LensOverlayRequestId request_id,
     std::unique_ptr<EndpointFetcher> endpoint_fetcher) {
   SendInitialLatencyGen204IfNotAlreadySent(
       LatencyType::kInvocationToInitialInteractionRequestSent,
-      VitQueryParamValueForMimeType(underlying_content_type_));
+      VitQueryParamValueForMimeType(underlying_content_type_),
+      request_id);
   interaction_endpoint_fetcher_ = std::move(endpoint_fetcher);
 }
 
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.h b/chrome/browser/ui/lens/lens_overlay_query_controller.h
index 780f063..ee6ffbc6 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller.h
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller.h
@@ -194,20 +194,31 @@
       const UploadProgressCallback upload_progress_callback);
 
   // Sends a latency Gen204 ping if enabled, calculating the latency duration
-  // from the start time ticks and base::TimeTicks::Now().
+  // from the start time ticks and base::TimeTicks::Now(). The encoded request
+  // id is optional because not all latency events have an associated request
+  // id, such as the cluster info fetch.
   virtual void SendLatencyGen204IfEnabled(
       lens::LensOverlayGen204Controller::LatencyType latency_type,
       base::TimeTicks start_time_ticks,
       std::string vit_query_param_value,
       std::optional<base::TimeDelta> cluster_info_latency,
-      std::optional<std::string> encoded_analytics_id);
+      std::optional<std::string> encoded_analytics_id,
+      std::optional<lens::LensOverlayRequestId> request_id);
 
   // Sends a task completion Gen204 ping for certain user actions with
   // the given analytics id. Protected to allow overriding in tests to
-  // check the encoded analytics id.
+  // check the encoded analytics id and request id.
   virtual void SendTaskCompletionGen204IfEnabled(
       std::string encoded_analytics_id,
-      lens::mojom::UserAction user_action);
+      lens::mojom::UserAction user_action,
+      lens::LensOverlayRequestId request_id);
+
+  // Sends a semantic event Gen204 ping. Protected to allow overriding in tests
+  // to check the request id. The request id can be unset for events that
+  // do not have an associated request id (e.g. text gleam view end).
+  virtual void SendSemanticEventGen204IfEnabled(
+      lens::mojom::SemanticEvent event,
+      std::optional<lens::LensOverlayRequestId> request_id);
 
   // The callback for full image requests, including upon query flow start
   // and interaction retries.
@@ -367,7 +378,8 @@
                                  std::vector<std::string> headers);
 
   // Handles the endpoint fetch response for the page content request.
-  void PageContentResponseHandler(std::unique_ptr<EndpointResponse> response);
+  void PageContentResponseHandler(lens::LensOverlayRequestId request_id,
+                                  std::unique_ptr<EndpointResponse> response);
 
   // Handles the prgress of the page content upload request.
   void PageContentUploadProgressHandler(uint64_t position, uint64_t total);
@@ -387,6 +399,7 @@
 
   // Handles the endpoint fetch response for the partial page content request.
   void PartialPageContentResponseHandler(
+      lens::LensOverlayRequestId request_id,
       std::unique_ptr<EndpointResponse> response);
 
   // Sends the interaction data, triggering async image cropping and fetching
@@ -468,7 +481,8 @@
   // per query flow, if gen204 logging is enabled.
   void SendInitialLatencyGen204IfNotAlreadySent(
       lens::LensOverlayGen204Controller::LatencyType latency_type,
-      std::string vit_query_param_value);
+      std::string vit_query_param_value,
+      std::optional<lens::LensOverlayRequestId> request_id);
 
   // Creates an endpoint fetcher with the given request_headers to perform the
   // given request. Calls fetcher_created_callback when the EndpointFetcher is
@@ -518,18 +532,22 @@
 
   // Callback for when the full image endpoint fetcher is created.
   void OnFullImageEndpointFetcherCreated(
+      lens::LensOverlayRequestId request_id,
       std::unique_ptr<EndpointFetcher> endpoint_fetcher);
 
   // Callback for when the page content endpoint fetcher is created.
   void OnPageContentEndpointFetcherCreated(
+      lens::LensOverlayRequestId request_id,
       std::unique_ptr<EndpointFetcher> endpoint_fetcher);
 
   // Callback for when the partial page content endpoint fetcher is created.
   void OnPartialPageContentEndpointFetcherCreated(
+      lens::LensOverlayRequestId request_id,
       std::unique_ptr<EndpointFetcher> endpoint_fetcher);
 
   // Callback for when the interaction endpoint fetcher is created.
   void OnInteractionEndpointFetcherCreated(
+      lens::LensOverlayRequestId request_id,
       std::unique_ptr<EndpointFetcher> endpoint_fetcher);
 
   // Returns whether or not the contextual search query should be sent now or
@@ -734,11 +752,19 @@
   // The current gen204 id for logging, set on each overlay invocation.
   uint64_t gen204_id_ = 0;
 
+  // The latest request id. Updated on each full-image and interaction
+  // request. This may differ from the request id in the
+  // latest_interaction_request_data_ if the user makes a text-only query,
+  // because no interaction request is made in that case even though the request
+  // id is incremented.
+  lens::LensOverlayRequestId latest_request_id_;
+
   // The latest encoded analytics id. Updated on each full-image and
   // interaction request. This may differ from the analytics id in the
   // latest_interaction_request_data_ if the user makes a text-only query,
   // because no interaction request is made in that case even though the
-  // request id is incremented.
+  // request id is incremented. This is always equal to the analytics id in the
+  // latest_request_id_ field.
   std::string latest_encoded_analytics_id_;
 
   // The time it took from sending the cluster info request to receiving
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc b/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc
index 75e223d..304fbaa1 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller_unittest.cc
@@ -503,7 +503,7 @@
   ASSERT_FALSE(
       latest_suggest_inputs_.has_encoded_visual_search_interaction_log_data());
   ASSERT_EQ(latest_suggest_inputs_.search_session_id(), kTestSearchSessionId);
-  ASSERT_EQ(GetEncodedRequestId(query_controller.sent_request_id()),
+  ASSERT_EQ(GetEncodedRequestId(query_controller.sent_full_image_request_id()),
             latest_suggest_inputs_.encoded_request_id());
   ASSERT_EQ(query_controller.latency_gen_204_counter(
                 LatencyType::kInvocationToInitialClusterInfoRequestSent),
@@ -1911,7 +1911,7 @@
   }));
 
   // The full image and page content requests should have the same request id.
-  ASSERT_EQ(query_controller.sent_request_id().sequence_id(), 1);
+  ASSERT_EQ(query_controller.sent_full_image_request_id().sequence_id(), 1);
   ASSERT_EQ(query_controller.sent_page_content_objects_request()
                 .request_context()
                 .request_id()
@@ -2125,7 +2125,7 @@
 
   ASSERT_TRUE(full_image_response_future.IsReady());
   std::string first_analytics_id =
-      query_controller.sent_request_id().analytics_id();
+      query_controller.sent_full_image_request_id().analytics_id();
   query_controller.SendRegionSearch(std::move(region), lens::REGION_SEARCH,
                                     additional_search_query_params,
                                     std::nullopt);
@@ -2136,7 +2136,7 @@
   ASSERT_TRUE(url_response_future.IsReady());
   ASSERT_TRUE(latest_suggest_inputs_.has_encoded_image_signals());
   std::string second_analytics_id =
-      query_controller.sent_request_id().analytics_id();
+      query_controller.sent_interaction_request_id().analytics_id();
 
   ASSERT_NE(second_analytics_id, first_analytics_id);
   ASSERT_EQ(GetAnalyticsIdFromUrl(url_response_future.Get().url()),
diff --git a/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc b/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc
index 50df3c6..ed44f2e0 100644
--- a/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc
+++ b/chrome/browser/ui/lens/test_lens_overlay_query_controller.cc
@@ -219,7 +219,7 @@
       fake_server_response_code =
           google_apis::ApiErrorCode::HTTP_INTERNAL_SERVER_ERROR;
     }
-    sent_request_id_.CopyFrom(
+    sent_full_image_request_id_.CopyFrom(
         request->objects_request().request_context().request_id());
     disable_response = disable_next_objects_response_;
     disable_next_objects_response_ = false;
@@ -230,7 +230,7 @@
     fake_server_response.mutable_interaction_response()->CopyFrom(
         fake_interaction_response_);
     fake_server_response_string = fake_server_response.SerializeAsString();
-    sent_request_id_.CopyFrom(
+    sent_interaction_request_id_.CopyFrom(
         request->interaction_request().request_context().request_id());
     num_interaction_requests_sent_++;
   } else {
@@ -268,19 +268,30 @@
     base::TimeTicks start_time_ticks,
     std::string vit_query_param_value,
     std::optional<base::TimeDelta> cluster_info_latency,
-    std::optional<std::string> encoded_analytics_id) {
+    std::optional<std::string> encoded_analytics_id,
+    std::optional<lens::LensOverlayRequestId> request_id) {
   int counter = latency_gen_204_counter_.contains(latency_type)
                     ? latency_gen_204_counter_.at(latency_type)
                     : 0;
   latency_gen_204_counter_[latency_type] = counter + 1;
   last_latency_gen204_analytics_id_ = encoded_analytics_id;
+  last_latency_gen204_request_id_ = request_id;
 }
 
 void TestLensOverlayQueryController::SendTaskCompletionGen204IfEnabled(
     std::string encoded_analytics_id,
-    lens::mojom::UserAction user_action) {
+    lens::mojom::UserAction user_action,
+    lens::LensOverlayRequestId request_id) {
   last_user_action_ = user_action;
   last_task_completion_gen204_analytics_id_ = encoded_analytics_id;
+  last_task_completion_gen204_request_id_ =
+      std::make_optional<lens::LensOverlayRequestId>(request_id);
 }
 
+void TestLensOverlayQueryController::SendSemanticEventGen204IfEnabled(
+    lens::mojom::SemanticEvent event,
+    std::optional<lens::LensOverlayRequestId> request_id) {
+  last_semantic_event_ = event;
+  last_semantic_event_gen204_request_id_ = request_id;
+}
 }  // namespace lens
diff --git a/chrome/browser/ui/lens/test_lens_overlay_query_controller.h b/chrome/browser/ui/lens/test_lens_overlay_query_controller.h
index d4f9ea4b..df8a1b7 100644
--- a/chrome/browser/ui/lens/test_lens_overlay_query_controller.h
+++ b/chrome/browser/ui/lens/test_lens_overlay_query_controller.h
@@ -79,8 +79,12 @@
     return sent_client_logs_;
   }
 
-  const lens::LensOverlayRequestId& sent_request_id() const {
-    return sent_request_id_;
+  const lens::LensOverlayRequestId& sent_full_image_request_id() const {
+    return sent_full_image_request_id_;
+  }
+
+  const lens::LensOverlayRequestId& sent_interaction_request_id() const {
+    return sent_interaction_request_id_;
   }
 
   const lens::LensOverlayRequestId& sent_page_content_request_id() const {
@@ -143,6 +147,10 @@
     return last_user_action_;
   }
 
+  const std::optional<lens::mojom::SemanticEvent>& last_semantic_event() const {
+    return last_semantic_event_;
+  }
+
   const int& num_full_image_requests_sent() const {
     return num_full_image_requests_sent_;
   }
@@ -173,15 +181,30 @@
     return it == latency_gen_204_counter_.end() ? 0 : it->second;
   }
 
+  const std::optional<lens::LensOverlayRequestId>& last_latency_gen204_request_id()
+      const {
+    return last_latency_gen204_request_id_;
+  }
+
   const std::optional<std::string>& last_latency_gen204_analytics_id() const {
     return last_latency_gen204_analytics_id_;
   }
 
+  const std::optional<lens::LensOverlayRequestId>&
+  last_semantic_event_gen204_request_id() const {
+    return last_semantic_event_gen204_request_id_;
+  }
+
   const std::optional<std::string>& last_task_completion_gen204_analytics_id()
       const {
     return last_task_completion_gen204_analytics_id_;
   }
 
+  const std::optional<lens::LensOverlayRequestId>&
+  last_task_completion_gen204_request_id() const {
+    return last_task_completion_gen204_request_id_;
+  }
+
   void StartQueryFlow(
       const SkBitmap& screenshot,
       GURL page_url,
@@ -234,11 +257,17 @@
       base::TimeTicks start_time_ticks,
       std::string vit_query_param_value,
       std::optional<base::TimeDelta> cluster_info_latency,
-      std::optional<std::string> encoded_analytics_id) override;
+      std::optional<std::string> encoded_analytics_id,
+      std::optional<lens::LensOverlayRequestId> request_id) override;
 
   void SendTaskCompletionGen204IfEnabled(
       std::string encoded_analytics_id,
-      lens::mojom::UserAction user_action) override;
+      lens::mojom::UserAction user_action,
+      lens::LensOverlayRequestId request_id) override;
+
+  void SendSemanticEventGen204IfEnabled(
+      lens::mojom::SemanticEvent event,
+      std::optional<lens::LensOverlayRequestId> request_id) override;
 
   // The fake response to return for cluster info requests.
   lens::LensOverlayServerClusterInfoResponse fake_cluster_info_response_;
@@ -265,8 +294,12 @@
   // The last client logs sent by the query controller.
   lens::LensOverlayClientLogs sent_client_logs_;
 
-  // The last request id sent by the query controller.
-  lens::LensOverlayRequestId sent_request_id_;
+  // The last request id sent by the query controller for a full image request.
+  lens::LensOverlayRequestId sent_full_image_request_id_;
+
+  // The last request id sent by the query controller for an interaction
+  // request.
+  lens::LensOverlayRequestId sent_interaction_request_id_;
 
   // The last request id sent by the query controller for a page content upload
   // request.
@@ -319,6 +352,9 @@
   // The last user action sent by the query controller.
   std::optional<lens::mojom::UserAction> last_user_action_;
 
+  // The last semantic event sent by the query controller.
+  std::optional<lens::mojom::SemanticEvent> last_semantic_event_;
+
   // The number of full image objects requests sent by the query controller.
   int num_full_image_requests_sent_ = 0;
 
@@ -341,12 +377,23 @@
   // The number of partial page content requests sent by the query controller.
   int num_partial_page_content_requests_sent_ = 0;
 
+  // The last encoded request id attached to a latency gen204 ping.
+  std::optional<lens::LensOverlayRequestId> last_latency_gen204_request_id_;
+
   // The last analytics id attached to a latency gen204 ping.
   std::optional<std::string> last_latency_gen204_analytics_id_;
 
   // The last analytics id attached to a task completion gen204 ping.
   std::optional<std::string> last_task_completion_gen204_analytics_id_;
 
+  // The last encoded request id attached to a task completion gen204 ping.
+  std::optional<lens::LensOverlayRequestId>
+      last_task_completion_gen204_request_id_;
+
+  // The last encoded request id attached to a semantic event gen204 ping.
+  std::optional<lens::LensOverlayRequestId>
+      last_semantic_event_gen204_request_id_;
+
   // Tracker for the number of latency request events sent by the query
   // controller.
   base::flat_map<lens::LensOverlayGen204Controller::LatencyType, int>
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image.cc b/chrome/browser/ui/thumbnails/thumbnail_image.cc
index cc889be0..22bb7e2f 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_image.cc
@@ -230,7 +230,7 @@
     data = gfx::JPEGCodec::Encode(bitmap, kCompressionQuality);
   }
 
-  return data.value();
+  return data.value_or(std::vector<uint8_t>());
 }
 
 // static
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
index b9ff996b..b977aa2 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
@@ -285,8 +285,7 @@
                           base::Unretained(this)),
       l10n_util::GetStringUTF16(IDS_GLIC_PROMO_TITLE),
       kGlicNudgeButtonElementId, Edge::kNone,
-      glic::GlicVectorIconManager::GetVectorIcon(IDR_GLIC_BUTTON_VECTOR_ICON)
-  );
+      glic::GlicVectorIconManager::GetVectorIcon(IDR_GLIC_BUTTON_VECTOR_ICON));
 
   button->SetTooltipText(l10n_util::GetStringUTF16(IDS_GLIC_PROMO_TITLE));
   button->GetViewAccessibility().SetName(
@@ -359,7 +358,7 @@
           l10n_util::GetStringUTF16(IDS_GLIC_TAB_STRIP_BUTTON_TOOLTIP));
 
   glic_button->SetProperty(views::kCrossAxisAlignmentKey,
-                           views::LayoutAlignment::kEnd);
+                           views::LayoutAlignment::kCenter);
 
   return glic_button;
 }
@@ -534,8 +533,15 @@
     return;
   }
 
-  // If the tab strip already has a modal UI showing, exit early.
-  if (!tab_strip_controller_->CanShowModalUI()) {
+  // If the tab strip already has a modal UI showing, that is not for the same
+  // button being hidden, exit early. If the tab strip has modal UI for the same
+  // button being hidden, then continue to reset the animation and start a show
+  // animation.
+  if (!tab_strip_controller_->CanShowModalUI() &&
+      !(animation_session_ &&
+        animation_session_->session_type() ==
+            TabStripNudgeAnimationSession::AnimationSessionType::HIDE &&
+        animation_session_->button() == button)) {
     return;
   }
 
@@ -554,6 +560,7 @@
                        base::Unretained(this), button));
   }
 
+  scoped_tab_strip_modal_ui_.reset();
   scoped_tab_strip_modal_ui_ = tab_strip_controller_->ShowModalUI();
 
   animation_session_ = std::make_unique<TabStripNudgeAnimationSession>(
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc
index 121a072..b8c0c1b 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container_browsertest.cc
@@ -54,7 +54,6 @@
 #if BUILDFLAG(ENABLE_GLIC)
     glic_test_environment_ =
         std::make_unique<glic::GlicTestEnvironment>(browser()->profile());
-    glic::ForceSigninAndModelExecutionCapability(browser()->profile());
 #endif
   }
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc
index a41ef83..abb2214 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc
@@ -31,6 +31,10 @@
 #include "ui/views/test/views_test_utils.h"
 #include "ui/views/widget/widget.h"
 
+#if BUILDFLAG(ENABLE_GLIC)
+#include "chrome/browser/glic/glic_test_util.h"
+#endif  // BUILDFLAG(ENABLE_GLIC)
+
 class FakeGlicTabStripController : public FakeBaseTabStripController {
  public:
   Profile* GetProfile() const override {
@@ -83,6 +87,9 @@
         profile_.get(), nullptr);
     scoped_feature_list_.InitWithFeatures(
         {features::kGlic, features::kTabstripComboButton}, {});
+#if BUILDFLAG(ENABLE_GLIC)
+    glic::ForceSigninAndModelExecutionCapability(profile_.get());
+#endif  // BUILDFLAG(ENABLE_GLIC)
   }
 
   void TearDown() override {
diff --git a/chrome/browser/ui/webui/ash/network_ui/network_ui.cc b/chrome/browser/ui/webui/ash/network_ui/network_ui.cc
index 6568ea7..1d24c9e 100644
--- a/chrome/browser/ui/webui/ash/network_ui/network_ui.cc
+++ b/chrome/browser/ui/webui/ash/network_ui/network_ui.cc
@@ -642,9 +642,10 @@
     CHECK_EQ(2u, arg_list.size());
     std::string callback_id = arg_list[0].GetString();
     std::string tethering_config = arg_list[1].GetString();
-    std::optional<base::Value> value = base::JSONReader::Read(tethering_config);
+    std::optional<base::Value::Dict> value =
+        base::JSONReader::ReadDict(tethering_config);
 
-    if (!value || !value->is_dict()) {
+    if (!value) {
       NET_LOG(ERROR) << "Invalid tethering configuration: " << tethering_config;
       Respond(callback_id, base::Value("Invalid tethering configuration"));
       return;
@@ -652,14 +653,14 @@
     NET_LOG(USER) << "SetManagerProperty: " << shill::kTetheringConfigProperty
                   << ": " << *value;
     const std::string* ssid =
-        value->GetDict().FindString(shill::kTetheringConfSSIDProperty);
+        value->FindString(shill::kTetheringConfSSIDProperty);
     if (ssid) {
-      value->GetDict().Set(shill::kTetheringConfSSIDProperty,
-                           base::Value(base::HexEncode(*ssid)));
+      value->Set(shill::kTetheringConfSSIDProperty,
+                 base::Value(base::HexEncode(*ssid)));
     }
 
     ShillManagerClient::Get()->SetProperty(
-        shill::kTetheringConfigProperty, *value,
+        shill::kTetheringConfigProperty, base::Value(std::move(*value)),
         base::BindOnce(&HotspotConfigMessageHandler::RespondStringResult,
                        weak_ptr_factory_.GetWeakPtr(), callback_id, "success"),
         base::BindOnce(
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 820102d..bdce0d9d 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1739901582-337434d74e61c83dad01e19886ef02985b95fa12-0ef0f45f5b0cada6246a31ea73869fa63eb062ce.profdata
+chrome-android32-main-1739923150-7c209a8fa438433740cc8d7cb381d344782707a1-24399cf3548307a52b75efd2a923c5dd65c01f92.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 288654aa..f14bd49 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1739906306-7e03a07944783be7867e0ef012c836a4df4e6b83-4a724ef9a790a457b363b37120d5e6c101a7c756.profdata
+chrome-android64-main-1739922408-c38f46babe0c9d3e1d655bfa81da2026420fc579-727a56820bb82f73f5ed5693c7a502afe8663097.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 2006df541..a6696cc7 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1739901582-58e38537b58d8cffafa3c3338ebbe72acfcb2251-0ef0f45f5b0cada6246a31ea73869fa63eb062ce.profdata
+chrome-linux-main-1739923150-3b37f0864d11c65b478eb71fb6c4e765cea4bf7f-24399cf3548307a52b75efd2a923c5dd65c01f92.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index e01f64ad..ce752ec0 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1739901582-54c30de12e8066d06d2c2d36cda79865626a7cc6-0ef0f45f5b0cada6246a31ea73869fa63eb062ce.profdata
+chrome-mac-arm-main-1739894376-165d585fec36502f9ecd8985768541753abc7bff-b227335379d3cc9fcf8e32380012ad918d3df574.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 87fcaa7..40e1e57 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1739869112-a4b72a724ab20a80135b2386671cdc1d66c99f94-39e07ed02eaf1e0a4266858d845c9365f7d61dd2.profdata
+chrome-win32-main-1739901582-20f9503a592d2344204ad27fe45cabc9413bbe17-0ef0f45f5b0cada6246a31ea73869fa63eb062ce.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 542409c..7f75358e 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1739869112-9ed4d35322a0f030f5a75e9743b8592b70e6a173-39e07ed02eaf1e0a4266858d845c9365f7d61dd2.profdata
+chrome-win64-main-1739890722-5349423d56a2d0e8f0dc2364a119d220c2f98517-d938382bf5e8e17501bd8ad17f999e3bb32e77b4.profdata
diff --git a/chrome/enterprise_companion/installer_mac.mm b/chrome/enterprise_companion/installer_mac.mm
index c0c4493..40b37a4 100644
--- a/chrome/enterprise_companion/installer_mac.mm
+++ b/chrome/enterprise_companion/installer_mac.mm
@@ -80,7 +80,7 @@
     return false;
   }
 
-  base::FilePath source_app_bundle_path =
+  const base::FilePath source_app_bundle_path =
       base::apple::GetInnermostAppBundlePath(source_exe_path);
   if (source_app_bundle_path.empty()) {
     VLOG(1) << "Failed to determine the path to the app bundle containing "
@@ -89,9 +89,14 @@
     return false;
   }
 
-  if (!base::CopyDirectory(source_app_bundle_path,
-                           install_directory.AppendASCII(
-                               base::StrCat({PRODUCT_FULLNAME_STRING, ".app"})),
+  const base::FilePath dest_app_bundle_path = install_directory.AppendASCII(
+      base::StrCat({PRODUCT_FULLNAME_STRING, ".app"}));
+  if (!base::DeletePathRecursively(dest_app_bundle_path)) {
+    VLOG(1) << "Failed to delete " << dest_app_bundle_path;
+    return false;
+  }
+
+  if (!base::CopyDirectory(source_app_bundle_path, dest_app_bundle_path,
                            /*recursive=*/true)) {
     VLOG(1)
         << "Failed to copy the application bundle to the install directory.";
@@ -152,7 +157,10 @@
 
   if (!RegisterInstallation(*install_directory)) {
     if (base::PathExists(backup_path)) {
-      if (!base::Move(backup_path, app_path)) {
+      if (!base::DeletePathRecursively(app_path)) {
+        VLOG(1) << "Failed to delete " << app_path
+                << " while trying to restore backup.";
+      } else if (!base::Move(backup_path, app_path)) {
         VLOG(1) << "Failed to restore installation backup.";
       }
     } else {
diff --git a/chrome/release_scripts b/chrome/release_scripts
index 83f9d6b..e54cd72 160000
--- a/chrome/release_scripts
+++ b/chrome/release_scripts
@@ -1 +1 @@
-Subproject commit 83f9d6beab5f8470b28544b3d957c4940a94aa1c
+Subproject commit e54cd72a13e2b29fc1ddeee1e1b0553bfd034594
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 242bed7..eb28d1e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6935,6 +6935,7 @@
 
   if (enable_glic) {
     deps += [
+      "//chrome/browser/glic:test_support",
       "//chrome/browser/glic:unit_tests",
       "//chrome/browser/glic/launcher:unit_tests",
     ]
@@ -8715,6 +8716,7 @@
   # Unit tests that run on Win/Mac/Linux and Android desktop.
   if (enable_extensions_core) {
     sources += [
+      "../browser/extensions/install_tracker_unittest.cc",
       "../common/extensions/chrome_manifest_url_handlers_unittest.cc",
       "../common/extensions/manifest_handlers/automation_unittest.cc",
       "../common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc",
@@ -8912,7 +8914,6 @@
       "../browser/extensions/forced_extensions/force_installed_test_base.cc",
       "../browser/extensions/forced_extensions/force_installed_test_base.h",
       "../browser/extensions/forced_extensions/force_installed_tracker_unittest.cc",
-      "../browser/extensions/install_tracker_unittest.cc",
       "../browser/extensions/install_verifier_unittest.cc",
       "../browser/extensions/installed_loader_unittest.cc",
       "../browser/extensions/manifest_v2_experiment_manager_unittest.cc",
diff --git a/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_contact_visibility_test.js b/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_contact_visibility_test.js
index 216d4dd6..c83f662 100644
--- a/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_contact_visibility_test.js
+++ b/chrome/test/data/webui/chromeos/nearby_share/shared/nearby_contact_visibility_test.js
@@ -309,8 +309,9 @@
       });
 
   test(
-      'Save persists visibility setting and allowed contacts',
+      'Save persists visibility setting and allowed contacts LEGACY',
       async function() {
+        loadTimeData.overrideValues({'isQuickShareV2Enabled': false});
         fakeContactManager.setupContactRecords();
         fakeContactManager.setNumUnreachable(0);
         fakeContactManager.completeDownload();
@@ -347,15 +348,32 @@
         assertEquals(fakeContactManager.allowedContacts[0], '2');
       });
 
-  test('System Settings changed on Save only', async () => {
+  test(
+      'Save persists visibility setting and allowed contacts',
+      async function() {
+        fakeContactManager.setupContactRecords();
+        fakeContactManager.setNumUnreachable(0);
+        fakeContactManager.completeDownload();
+        visibilityElement.set('settings.visibility', Visibility.kAllContacts);
+        await waitAfterNextRender(visibilityElement);
+
+        // after save, ui state is persisted
+        visibilityElement.saveVisibilityAndAllowedContacts();
+        assertEquals(
+            visibilityElement.get('settings.visibility'),
+            Visibility.kAllContacts);
+        assertEquals(fakeContactManager.allowedContacts.length, 2);
+      });
+
+  test('System Settings changed on Save only LEGACY', async () => {
+    loadTimeData.overrideValues({'isQuickShareV2Enabled': false});
     fakeContactManager.setupContactRecords();
     fakeContactManager.setNumUnreachable(0);
     fakeContactManager.completeDownload();
     visibilityElement.set('settings.visibility', Visibility.kAllContacts);
     await waitAfterNextRender(visibilityElement);
 
-    // System visibility setting is not immediately updated to Selected
-    // Devices despite toggling Selected Devices in Dialog.
+    // visibility setting is not immediately updated
     visibilityElement.shadowRoot.querySelector('#AllContactsToggle').click();
     await waitAfterNextRender(visibilityElement);
     assertTrue(areContactCheckBoxesVisibleAndAllContactsToggledOff());
@@ -383,7 +401,8 @@
     assertEquals('2', fakeContactManager.allowedContacts[0]);
   });
 
-  test('Toggle some contacts from all contacts', async () => {
+  test('Toggle some contacts from all contacts LEGACY', async () => {
+    loadTimeData.overrideValues({'isQuickShareV2Enabled': false});
     fakeContactManager.setupContactRecords();
     fakeContactManager.setNumUnreachable(0);
     fakeContactManager.completeDownload();
@@ -397,7 +416,8 @@
     assertTrue(areContactCheckBoxesVisibleAndAllContactsToggledOff());
   });
 
-  test('Toggle all contacts from some contacts', async () => {
+  test('Toggle all contacts from some contacts LEGACY', async () => {
+    loadTimeData.overrideValues({'isQuickShareV2Enabled': false});
     fakeContactManager.setupContactRecords();
     fakeContactManager.setNumUnreachable(0);
     fakeContactManager.completeDownload();
@@ -444,8 +464,9 @@
   });
 
   test(
-      'Only contacts toggled are saved in allowed contact list when selected contacts toggled',
+      'Only contacts toggled are saved in allowed contact list when selected contacts toggled LEGACY',
       async () => {
+        loadTimeData.overrideValues({'isQuickShareV2Enabled': false});
         fakeContactManager.setupContactRecords();
         fakeContactManager.setNumUnreachable(0);
         fakeContactManager.completeDownload();
diff --git a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_freeform_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_freeform_element_test.ts
index 40069279..8102973 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_freeform_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_freeform_element_test.ts
@@ -88,6 +88,27 @@
         'sea-pen-images is not shown');
   });
 
+  test(
+      'shows freeform page with tab container if thumbnails exist',
+      async () => {
+        // Initialize |freeformElement|.
+        personalizationStore.data.wallpaper.seaPen.thumbnails =
+            seaPenProvider.thumbnails;
+
+        freeformElement = initElement(SeaPenFreeformElement);
+        await waitAfterNextRender(freeformElement);
+
+        const tabContainer =
+            freeformElement.shadowRoot!.querySelector<HTMLElement>(
+                '#tabContainer');
+        assertTrue(!!tabContainer);
+        assertFalse(!!tabContainer.hidden, 'tab container is shown');
+        assertTrue(
+            !!freeformElement.shadowRoot!.querySelector<HTMLElement>(
+                SeaPenImagesElement.is),
+            'sea-pen-images is shown');
+      });
+
   test('shows 6 sample prompts in freeform freeform page', async () => {
     // Initialize |freeformElement|.
     freeformElement = initElement(SeaPenFreeformElement);
diff --git a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_images_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_images_element_test.ts
index 4899bf2..1e12583 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/sea_pen_images_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/sea_pen_images_element_test.ts
@@ -196,9 +196,6 @@
     personalizationStore.data.wallpaper.seaPen.loading.thumbnails = false;
     personalizationStore.data.wallpaper.seaPen.thumbnails =
         seaPenProvider.thumbnails;
-    personalizationStore.data.wallpaper.seaPen.currentSeaPenQuery = {
-      textQuery: 'test freeform query',
-    };
     personalizationStore.data.wallpaper.seaPen.textQueryHistory = [
       {
         query: 'test freeform query',
diff --git a/chromeos/ash/components/network/onc/onc_normalizer_fuzzer.cc b/chromeos/ash/components/network/onc/onc_normalizer_fuzzer.cc
index be5439ed..f169d6ab 100644
--- a/chromeos/ash/components/network/onc/onc_normalizer_fuzzer.cc
+++ b/chromeos/ash/components/network/onc/onc_normalizer_fuzzer.cc
@@ -18,15 +18,16 @@
 
 // Fuzzer for methods of the `Normalizer` class.
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  std::optional<base::Value> parsed_json = base::JSONReader::Read(
+  std::optional<base::Value::Dict> parsed_json = base::JSONReader::ReadDict(
       std::string_view(reinterpret_cast<const char*>(data), size));
-  if (!parsed_json || !parsed_json->is_dict())
+  if (!parsed_json) {
     return 0;
+  }
 
   for (bool remove_recommended_fields : {false, true}) {
     Normalizer normalizer(remove_recommended_fields);
     normalizer.NormalizeObject(&chromeos::onc::kNetworkConfigurationSignature,
-                               parsed_json->GetDict());
+                               *parsed_json);
   }
 
   return 0;
diff --git a/chromeos/ash/components/proximity_auth/messenger_impl.cc b/chromeos/ash/components/proximity_auth/messenger_impl.cc
index a3c06a97..e6353e0 100644
--- a/chromeos/ash/components/proximity_auth/messenger_impl.cc
+++ b/chromeos/ash/components/proximity_auth/messenger_impl.cc
@@ -147,14 +147,14 @@
 
 void MessengerImpl::HandleMessage(const std::string& message) {
   // The decoded message should be a JSON string.
-  std::optional<base::Value> message_value = base::JSONReader::Read(message);
-  if (!message_value || !message_value->is_dict()) {
+  std::optional<base::Value::Dict> message_value =
+      base::JSONReader::ReadDict(message);
+  if (!message_value) {
     PA_LOG(ERROR) << "Unable to parse message as JSON:\n" << message;
     return;
   }
 
-  const base::Value::Dict& message_dictionary = message_value->GetDict();
-  const std::string* type = message_dictionary.FindString(kTypeKey);
+  const std::string* type = message_value->FindString(kTypeKey);
   if (!type) {
     PA_LOG(ERROR) << "Missing '" << kTypeKey << "' key in message:\n "
                   << message;
@@ -163,7 +163,7 @@
 
   // Remote status updates can be received out of the blue.
   if (*type == kMessageTypeRemoteStatusUpdate) {
-    HandleRemoteStatusUpdateMessage(message_dictionary);
+    HandleRemoteStatusUpdateMessage(*message_value);
     return;
   }
 
@@ -193,7 +193,7 @@
   }
 
   if (*type == kMessageTypeUnlockResponse) {
-    HandleUnlockResponseMessage(message_dictionary);
+    HandleUnlockResponseMessage(*message_value);
   } else {
     NOTREACHED();  // There are no other message types that expect a response.
   }
diff --git a/chromeos/ash/services/secure_channel/wire_message.cc b/chromeos/ash/services/secure_channel/wire_message.cc
index b53c904..a9bec407 100644
--- a/chromeos/ash/services/secure_channel/wire_message.cc
+++ b/chromeos/ash/services/secure_channel/wire_message.cc
@@ -64,15 +64,14 @@
 
 std::unique_ptr<WireMessage> DeserializeJsonMessageBody(
     const std::string& serialized_message_body) {
-  std::optional<base::Value> body_value =
-      base::JSONReader::Read(serialized_message_body);
-  if (!body_value || !body_value->is_dict()) {
+  std::optional<base::Value::Dict> body_value =
+      base::JSONReader::ReadDict(serialized_message_body);
+  if (!body_value) {
     PA_LOG(WARNING) << "Unable to parse message as JSON.";
     return nullptr;
   }
 
-  const base::Value::Dict& body = body_value->GetDict();
-  const std::string* payload_base64 = body.FindString(kPayloadKey);
+  const std::string* payload_base64 = body_value->FindString(kPayloadKey);
   if (!payload_base64) {
     // Legacy case: Message without a payload.
     return base::WrapUnique(new WireMessage(serialized_message_body));
@@ -91,7 +90,7 @@
     return nullptr;
   }
 
-  const std::string* feature = body.FindString(kFeatureKey);
+  const std::string* feature = body_value->FindString(kFeatureKey);
   if (!feature || feature->empty()) {
     return base::WrapUnique(new WireMessage(payload, kDefaultFeature));
   }
diff --git a/clank b/clank
index efaadf4..5ba3a0e 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit efaadf4d8163bfc2e9fb9a0e60a75f3867b6beb7
+Subproject commit 5ba3a0efeedf8dfa0fd609ba309bcd7206ba69e3
diff --git a/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc b/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc
index bcc56e2a..5b42ea4 100644
--- a/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc
+++ b/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc
@@ -117,27 +117,6 @@
   });
 }
 
-// TODO(crbug.com/326408802): Move to payments_suggestion_generator.
-std::vector<const CreditCard*> DeduplicatedCreditCardsForSuggestions(
-    base::span<const CreditCard* const> cards_to_suggest) {
-  std::vector<const CreditCard*> deduplicated_cards;
-  for (const CreditCard* card : cards_to_suggest) {
-    // Full server cards should never be suggestions, as they exist only as a
-    // cached state post-fill.
-    CHECK_NE(card->record_type(), CreditCard::RecordType::kFullServerCard);
-    // Masked server cards are preferred over their local duplicates.
-    if (!CreditCard::IsLocalCard(card) ||
-        std::ranges::none_of(
-            cards_to_suggest, [&card](const CreditCard* other_card) {
-              return card != other_card &&
-                     card->IsLocalOrServerDuplicateOf(*other_card);
-            })) {
-      deduplicated_cards.push_back(card);
-    }
-  }
-  return deduplicated_cards;
-}
-
 }  // namespace
 
 // Helper class to abstract the switching between account and profile storage
@@ -1296,31 +1275,6 @@
   return autofill_virtual_card_usage_data_;
 }
 
-std::vector<const CreditCard*> PaymentsDataManager::GetCreditCardsToSuggest(
-    bool should_use_legacy_algorithm) const {
-  if (!IsAutofillPaymentMethodsEnabled()) {
-    return {};
-  }
-  std::vector<const CreditCard*> cards_to_suggest =
-      DeduplicatedCreditCardsForSuggestions(ShouldSuggestServerPaymentMethods()
-                                                ? GetCreditCards()
-                                                : GetLocalCreditCards());
-  // Rank the cards by ranking score (see UsageHistoryInformation for details).
-  // All expired cards should be suggested last, also by ranking score.
-  std::ranges::sort(
-      cards_to_suggest,
-      [comparison_time = base::Time::Now(), should_use_legacy_algorithm](
-          const CreditCard* a, const CreditCard* b) {
-        if (const bool a_is_expired = a->IsExpired(comparison_time);
-            a_is_expired != b->IsExpired(comparison_time)) {
-          return !a_is_expired;
-        }
-        return a->HasGreaterRankingThan(*b, comparison_time,
-                                        should_use_legacy_algorithm);
-      });
-  return cards_to_suggest;
-}
-
 std::string PaymentsDataManager::AddAsLocalIban(Iban iban) {
   CHECK_EQ(iban.record_type(), Iban::kUnknown);
   // IBAN shares the same pref with payment methods enablement toggle.
diff --git a/components/autofill/core/browser/data_manager/payments/payments_data_manager.h b/components/autofill/core/browser/data_manager/payments/payments_data_manager.h
index d547d5f..1a10eaee 100644
--- a/components/autofill/core/browser/data_manager/payments/payments_data_manager.h
+++ b/components/autofill/core/browser/data_manager/payments/payments_data_manager.h
@@ -242,14 +242,6 @@
   // Returns all virtual card usage data linked to the credit card.
   base::span<const VirtualCardUsageData> GetVirtualCardUsageData() const;
 
-  // Returns the credit cards to suggest to the user. Those have been deduped
-  // and ordered by frecency with the expired cards put at the end of the
-  // vector. `should_use_legacy_algorithm` indicates if we should rank credit
-  // cards using the legacy ranking algorithm.
-  // TODO(crbug.com/326408802): Move to payments_suggestion_generator.
-  std::vector<const CreditCard*> GetCreditCardsToSuggest(
-      bool should_use_legacy_algorithm = false) const;
-
   // Returns the unlinked buy-now-pay-later issuers. This is a list of BNPL
   // issuers that are available to be used but have NOT been linked to the
   // payments account by the user.
@@ -510,13 +502,13 @@
   // Returns the value of the FacilitatedPaymentsEwallet user pref.
   bool IsFacilitatedPaymentsEwalletUserPrefEnabled() const;
 
- protected:
-  friend class PaymentsDataManagerTestApi;
-
   // Whether server cards or IBANs are enabled and should be suggested to the
   // user.
   virtual bool ShouldSuggestServerPaymentMethods() const;
 
+ protected:
+  friend class PaymentsDataManagerTestApi;
+
   // Loads the saved credit cards from the web database.
   virtual void LoadCreditCards();
 
diff --git a/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc b/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc
index c586adc..ea17a21 100644
--- a/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc
+++ b/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc
@@ -42,6 +42,7 @@
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
 #include "components/autofill/core/browser/metrics/payments/mandatory_reauth_metrics.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/browser/ui/autofill_image_fetcher_base.h"
@@ -1091,7 +1092,7 @@
   // Sublabel is card number when filling name (exact format depends on
   // the platform, but the last 4 digits should appear).
   std::vector<const CreditCard*> card_to_suggest =
-      payments_data_manager().GetCreditCardsToSuggest();
+      GetCreditCardsToSuggest(payments_data_manager());
   ASSERT_EQ(3U, card_to_suggest.size());
 
   // Ordered as expected.
@@ -1134,7 +1135,7 @@
   EXPECT_EQ(5U, payments_data_manager().GetCreditCards().size());
 
   std::vector<const CreditCard*> card_to_suggest =
-      payments_data_manager().GetCreditCardsToSuggest();
+      GetCreditCardsToSuggest(payments_data_manager());
   ASSERT_EQ(5U, card_to_suggest.size());
 
   // All cards should be ordered as expected.
@@ -1185,10 +1186,10 @@
   // Check that profiles were saved.
   EXPECT_EQ(5U, payments_data_manager().GetCreditCards().size());
   // Expect no autofilled values or suggestions.
-  EXPECT_EQ(0U, payments_data_manager().GetCreditCardsToSuggest().size());
+  EXPECT_EQ(0U, GetCreditCardsToSuggest(payments_data_manager()).size());
 
   std::vector<const CreditCard*> card_to_suggest =
-      payments_data_manager().GetCreditCardsToSuggest();
+      GetCreditCardsToSuggest(payments_data_manager());
   ASSERT_EQ(0U, card_to_suggest.size());
 }
 
@@ -1230,10 +1231,10 @@
   ResetPaymentsDataManager();
 
   // Expect no credit card values or suggestions were loaded.
-  EXPECT_EQ(0U, payments_data_manager().GetCreditCardsToSuggest().size());
+  EXPECT_EQ(0U, GetCreditCardsToSuggest(payments_data_manager()).size());
 
   std::vector<const CreditCard*> card_to_suggest =
-      payments_data_manager().GetCreditCardsToSuggest();
+      GetCreditCardsToSuggest(payments_data_manager());
   ASSERT_EQ(0U, card_to_suggest.size());
 }
 
@@ -1276,7 +1277,7 @@
   WaitForOnPaymentsDataChanged();
 
   std::vector<const CreditCard*> credit_cards =
-      payments_data_manager().GetCreditCardsToSuggest();
+      GetCreditCardsToSuggest(payments_data_manager());
   ASSERT_EQ(1U, credit_cards.size());
   EXPECT_EQ(0, credit_cards.front()->Compare(masked_card));
 }
@@ -1298,7 +1299,7 @@
   WaitForOnPaymentsDataChanged();
 
   std::vector<const CreditCard*> credit_cards =
-      payments_data_manager().GetCreditCardsToSuggest();
+      GetCreditCardsToSuggest(payments_data_manager());
   EXPECT_EQ(2U, credit_cards.size());
 }
 
@@ -1322,7 +1323,7 @@
   WaitForOnPaymentsDataChanged();
 
   std::vector<const CreditCard*> credit_cards =
-      payments_data_manager().GetCreditCardsToSuggest();
+      GetCreditCardsToSuggest(payments_data_manager());
   ASSERT_EQ(1U, credit_cards.size());
 
   // Verify `masked_card` is returned after deduping `credit_cards` list.
@@ -1481,7 +1482,7 @@
   SetUpTwoCardTypes();
 
   EXPECT_EQ(2U, payments_data_manager().GetCreditCards().size());
-  EXPECT_EQ(2U, payments_data_manager().GetCreditCardsToSuggest().size());
+  EXPECT_EQ(2U, GetCreditCardsToSuggest(payments_data_manager()).size());
   EXPECT_EQ(1U, payments_data_manager().GetLocalCreditCards().size());
   EXPECT_EQ(1U, payments_data_manager().GetServerCreditCards().size());
 }
@@ -1585,7 +1586,7 @@
 
   // Check that the server card is available for suggestion.
   EXPECT_EQ(2U, payments_data_manager().GetCreditCards().size());
-  EXPECT_EQ(2U, payments_data_manager().GetCreditCardsToSuggest().size());
+  EXPECT_EQ(2U, GetCreditCardsToSuggest(payments_data_manager()).size());
   EXPECT_EQ(1U, payments_data_manager().GetLocalCreditCards().size());
   EXPECT_EQ(1U, payments_data_manager().GetServerCreditCards().size());
 
@@ -1596,7 +1597,7 @@
 
   // Check that server cards are unavailable.
   EXPECT_EQ(1U, payments_data_manager().GetCreditCards().size());
-  EXPECT_EQ(1U, payments_data_manager().GetCreditCardsToSuggest().size());
+  EXPECT_EQ(1U, GetCreditCardsToSuggest(payments_data_manager()).size());
   EXPECT_EQ(1U, payments_data_manager().GetLocalCreditCards().size());
   EXPECT_EQ(0U, payments_data_manager().GetServerCreditCards().size());
 }
@@ -1614,7 +1615,7 @@
   // The server card should not be available at first. The user needs to
   // accept the opt-in offer.
   EXPECT_EQ(2U, payments_data_manager().GetCreditCards().size());
-  EXPECT_EQ(1U, payments_data_manager().GetCreditCardsToSuggest().size());
+  EXPECT_EQ(1U, GetCreditCardsToSuggest(payments_data_manager()).size());
   EXPECT_EQ(1U, payments_data_manager().GetLocalCreditCards().size());
   EXPECT_EQ(1U, payments_data_manager().GetServerCreditCards().size());
 
@@ -1623,7 +1624,7 @@
 
   // Check that the server card is available for suggestion.
   EXPECT_EQ(2U, payments_data_manager().GetCreditCards().size());
-  EXPECT_EQ(2U, payments_data_manager().GetCreditCardsToSuggest().size());
+  EXPECT_EQ(2U, GetCreditCardsToSuggest(payments_data_manager()).size());
   EXPECT_EQ(1U, payments_data_manager().GetLocalCreditCards().size());
   EXPECT_EQ(1U, payments_data_manager().GetServerCreditCards().size());
 }
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
index 7a26c341..7888806 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
+++ b/components/autofill/core/browser/metrics/autofill_metrics_unittest.cc
@@ -55,6 +55,7 @@
 #include "components/autofill/core/browser/payments/credit_card_access_manager.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/suggestions/suggestion_type.h"
 #include "components/autofill/core/browser/test_utils/autofill_form_test_utils.h"
@@ -5205,10 +5206,10 @@
                         /*include_masked_server_credit_card=*/false,
                         /*masked_card_is_enrolled_for_virtual_card=*/false);
 
-    credit_card_ = *autofill_client_->GetPersonalDataManager()
-                        .payments_data_manager()
-                        .GetCreditCardsToSuggest()
-                        .front();
+    credit_card_ =
+        *GetCreditCardsToSuggest(
+             autofill_client_->GetPersonalDataManager().payments_data_manager())
+             .front();
     credit_card_.set_cvc(u"123");
 
     url::Origin main_origin =
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 82325a44..0cb1fc6 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -41,6 +41,7 @@
 #include "components/autofill/core/browser/payments/payments_util.h"
 #include "components/autofill/core/browser/payments/payments_window_manager.h"
 #include "components/autofill/core/browser/payments/webauthn_callback_types.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/strings/grit/components_strings.h"
@@ -155,7 +156,7 @@
 void CreditCardAccessManager::PrepareToFetchCreditCard() {
 #if !BUILDFLAG(IS_IOS)
   // No need to fetch details if there are no server cards.
-  if (std::ranges::all_of(payments_data_manager().GetCreditCardsToSuggest(),
+  if (std::ranges::all_of(GetCreditCardsToSuggest(payments_data_manager()),
                           &CreditCard::IsLocalCard)) {
     return;
   }
diff --git a/components/autofill/core/browser/payments/payments_data_cleaner_unittest.cc b/components/autofill/core/browser/payments/payments_data_cleaner_unittest.cc
index 4705a83..1632f4a8 100644
--- a/components/autofill/core/browser/payments/payments_data_cleaner_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_data_cleaner_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/autofill/core/browser/data_manager/payments/payments_data_manager_test_base.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager_test_utils.h"
+#include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/credit_card_network_identifiers.h"
@@ -219,25 +220,21 @@
 
   // The first three profiles' origin should be cleared and the fourth one still
   // be the settings origin.
-  EXPECT_TRUE(personal_data()
-                  .payments_data_manager()
-                  .GetCreditCardsToSuggest()[0]
-                  ->origin()
-                  .empty());
-  EXPECT_TRUE(personal_data()
-                  .payments_data_manager()
-                  .GetCreditCardsToSuggest()[1]
-                  ->origin()
-                  .empty());
-  EXPECT_TRUE(personal_data()
-                  .payments_data_manager()
-                  .GetCreditCardsToSuggest()[2]
-                  ->origin()
-                  .empty());
-  EXPECT_EQ(kSettingsOrigin, personal_data()
-                                 .payments_data_manager()
-                                 .GetCreditCardsToSuggest()[3]
-                                 ->origin());
+  EXPECT_TRUE(
+      GetCreditCardsToSuggest(personal_data().payments_data_manager())[0]
+          ->origin()
+          .empty());
+  EXPECT_TRUE(
+      GetCreditCardsToSuggest(personal_data().payments_data_manager())[1]
+          ->origin()
+          .empty());
+  EXPECT_TRUE(
+      GetCreditCardsToSuggest(personal_data().payments_data_manager())[2]
+          ->origin()
+          .empty());
+  EXPECT_EQ(kSettingsOrigin,
+            GetCreditCardsToSuggest(personal_data().payments_data_manager())[3]
+                ->origin());
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
index cbd7aa1..b97689b 100644
--- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
@@ -98,6 +98,29 @@
   return suggestion;
 }
 
+// The priority ranking for deduplicating a duplicate card is:
+// 1. RecordType::kMaskedServerCard
+// 2. RecordType::kLocalCard
+std::vector<const CreditCard*> DeduplicateCreditCardsForSuggestions(
+    base::span<const CreditCard* const> cards_to_suggest) {
+  std::vector<const CreditCard*> deduplicated_cards;
+  for (const CreditCard* card : cards_to_suggest) {
+    // Full server cards should never be suggestions, as they exist only as a
+    // cached state post-fill.
+    CHECK_NE(card->record_type(), CreditCard::RecordType::kFullServerCard);
+    // Masked server cards are preferred over their local duplicates.
+    if (!CreditCard::IsLocalCard(card) ||
+        std::ranges::none_of(
+            cards_to_suggest, [&card](const CreditCard* other_card) {
+              return card != other_card &&
+                     card->IsLocalOrServerDuplicateOf(*other_card);
+            })) {
+      deduplicated_cards.push_back(card);
+    }
+  }
+  return deduplicated_cards;
+}
+
 // Returns the card-linked offers map with credit card guid as the key and the
 // pointer to the linked AutofillOfferData as the value.
 std::map<std::string, const AutofillOfferData*> GetCardLinkedOffers(
@@ -747,10 +770,9 @@
     bool require_non_empty_value_on_trigger_field,
     bool include_virtual_cards,
     bool use_legacy_algorithm = false) {
-  std::vector<const CreditCard*> available_cards =
-      client.GetPersonalDataManager()
-          .payments_data_manager()
-          .GetCreditCardsToSuggest(use_legacy_algorithm);
+  std::vector<const CreditCard*> available_cards = GetCreditCardsToSuggest(
+      client.GetPersonalDataManager().payments_data_manager(),
+      use_legacy_algorithm);
   // If a card has available card linked offers on the last committed url, rank
   // it to the top.
   if (std::map<std::string, const AutofillOfferData*> card_linked_offers_map =
@@ -982,6 +1004,34 @@
 
 BnplSuggestionUpdateResult::~BnplSuggestionUpdateResult() = default;
 
+std::vector<const CreditCard*> GetCreditCardsToSuggest(
+    const PaymentsDataManager& payments_data_manager,
+    bool should_use_legacy_algorithm) {
+  if (!payments_data_manager.IsAutofillPaymentMethodsEnabled()) {
+    return {};
+  }
+
+  std::vector<const CreditCard*> cards_to_suggest =
+      DeduplicateCreditCardsForSuggestions(
+          payments_data_manager.ShouldSuggestServerPaymentMethods()
+              ? payments_data_manager.GetCreditCards()
+              : payments_data_manager.GetLocalCreditCards());
+  // Rank the cards by ranking score (see UsageHistoryInformation for details).
+  // All expired cards should be suggested last, also by ranking score.
+  std::ranges::sort(
+      cards_to_suggest,
+      [comparison_time = base::Time::Now(), should_use_legacy_algorithm](
+          const CreditCard* a, const CreditCard* b) {
+        if (const bool a_is_expired = a->IsExpired(comparison_time);
+            a_is_expired != b->IsExpired(comparison_time)) {
+          return !a_is_expired;
+        }
+        return a->HasGreaterRankingThan(*b, comparison_time,
+                                        should_use_legacy_algorithm);
+      });
+  return cards_to_suggest;
+}
+
 std::vector<Suggestion> GetSuggestionsForCreditCards(
     const AutofillClient& client,
     const FormFieldData& trigger_field,
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h
index 63e03cc..7adde59f 100644
--- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h
+++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h
@@ -66,6 +66,14 @@
   bool is_bnpl_suggestion_added = false;
 };
 
+// Returns the credit cards to suggest to the user. Those have been deduped
+// and ordered by frecency with the expired cards put at the end of the
+// vector. `should_use_legacy_algorithm` indicates if we should rank credit
+// cards using the legacy ranking algorithm.
+std::vector<const CreditCard*> GetCreditCardsToSuggest(
+    const PaymentsDataManager& payments_data_manager,
+    bool should_use_legacy_algorithm = false);
+
 // Generates suggestions for all available credit cards based on the
 // `trigger_field_type`, `trigger_field` and `four_digit_combinations_in_dom`.
 // `summary` contains metadata about the returned suggestions.
diff --git a/components/facilitated_payments/core/browser/pix_manager.cc b/components/facilitated_payments/core/browser/pix_manager.cc
index 69aea0aa..4119220 100644
--- a/components/facilitated_payments/core/browser/pix_manager.cc
+++ b/components/facilitated_payments/core/browser/pix_manager.cc
@@ -89,6 +89,13 @@
 }
 
 bool PixManager::IsMerchantAllowlisted(const GURL& url) const {
+  if (base::FeatureList::IsEnabled(
+          kDisableFacilitatedPaymentsMerchantAllowlist)) {
+    // If the merchant allowlist check is disabled, simply return true. This is
+    // mainly used for manual testing of new domains before being added to the
+    // allowlist.
+    return true;
+  }
   // Since the optimization guide decider integration corresponding to PIX
   // merchant lists are allowlists for the question "Can this site be
   // optimized?", a match on the allowlist answers the question with "yes".
diff --git a/components/facilitated_payments/core/browser/pix_manager_unittest.cc b/components/facilitated_payments/core/browser/pix_manager_unittest.cc
index b863a1e..19e186a 100644
--- a/components/facilitated_payments/core/browser/pix_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/pix_manager_unittest.cc
@@ -407,6 +407,36 @@
   task_environment_.RunUntilIdle();
 }
 
+TEST_F(
+    PixManagerTest,
+    CopyTrigger_UrlNotInAllowlist_AllowlistCheckDisabled_PixValidationTriggered) {
+  base::test::ScopedFeatureList feature_list(
+      kDisableFacilitatedPaymentsMerchantAllowlist);
+  payments_data_manager_->AddMaskedBankAccountForTest(
+      CreatePixBankAccount(/*instrument_id=*/1));
+  GURL url("https://example.com/");
+
+  // Verify that the allowlist check never happens.
+  EXPECT_CALL(
+      *optimization_guide_decider_,
+      CanApplyOptimization(
+          testing::Eq(url),
+          testing::Eq(
+              optimization_guide::proto::PIX_MERCHANT_ORIGINS_ALLOWLIST),
+          testing::Matcher<optimization_guide::OptimizationMetadata*>(
+              testing::Eq(nullptr))))
+      .Times(0);
+  // If Pix validation is run, then IsAvailable should get called once.
+  EXPECT_CALL(GetApiClient(), IsAvailable(testing::_));
+
+  pix_manager_->OnPixCodeCopiedToClipboard(
+      url, "00020126370014br.gov.bcb.pix2515www.example.com6304EA3F",
+      ukm::UkmRecorder::GetNewSourceID());
+  // The DataDecoder (utility process) validates the Pix code string
+  // asynchronously.
+  task_environment_.RunUntilIdle();
+}
+
 TEST_F(PixManagerTest, TestPayFlowCanBeTriggeredOnlyOncePerPageLoad) {
   payments_data_manager_->AddMaskedBankAccountForTest(
       CreatePixBankAccount(/*instrument_id=*/1));
diff --git a/components/facilitated_payments/core/features/features.cc b/components/facilitated_payments/core/features/features.cc
index 7256cef7..dbb153e 100644
--- a/components/facilitated_payments/core/features/features.cc
+++ b/components/facilitated_payments/core/features/features.cc
@@ -17,7 +17,14 @@
 BASE_FEATURE(kEnablePixPaymentsInLandscapeMode,
              "EnablePixPaymentsInLandscapeMode",
              base::FEATURE_DISABLED_BY_DEFAULT);
+
 #if BUILDFLAG(IS_ANDROID)
+// When enabled, the check for matching the main frame domain with the
+// allowlisted domains will be disabled.
+BASE_FEATURE(kDisableFacilitatedPaymentsMerchantAllowlist,
+             "DisableFacilitatedPaymentsMerchantAllowlist",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // When enabled, Chrome will offer to pay with eWallet accounts if a payment
 // link is detected.
 BASE_FEATURE(kEwalletPayments,
diff --git a/components/facilitated_payments/core/features/features.h b/components/facilitated_payments/core/features/features.h
index bd29dc7..4caf5e0 100644
--- a/components/facilitated_payments/core/features/features.h
+++ b/components/facilitated_payments/core/features/features.h
@@ -13,6 +13,7 @@
 BASE_DECLARE_FEATURE(kEnablePixPayments);
 BASE_DECLARE_FEATURE(kEnablePixPaymentsInLandscapeMode);
 #if BUILDFLAG(IS_ANDROID)
+BASE_DECLARE_FEATURE(kDisableFacilitatedPaymentsMerchantAllowlist);
 BASE_DECLARE_FEATURE(kEwalletPayments);
 #endif  // BUILDFLAG(IS_ANDROID)
 BASE_DECLARE_FEATURE(kSupportMultipleServerRequestsForPixPayments);
diff --git a/components/feed/core/proto/BUILD.gn b/components/feed/core/proto/BUILD.gn
index 2cd2d85..337ed1f8 100644
--- a/components/feed/core/proto/BUILD.gn
+++ b/components/feed/core/proto/BUILD.gn
@@ -25,15 +25,18 @@
     "v2/wire/chrome_fulfillment_info.proto",
     "v2/wire/client_info.proto",
     "v2/wire/client_user_profiles.proto",
+    "v2/wire/code.proto",
     "v2/wire/column_types.proto",
     "v2/wire/consistency_token.proto",
     "v2/wire/content_id.proto",
     "v2/wire/content_lifetime.proto",
+    "v2/wire/cui_server_metadata.proto",
     "v2/wire/data_operation.proto",
     "v2/wire/device.proto",
     "v2/wire/diagnostic_info.proto",
     "v2/wire/display_info.proto",
     "v2/wire/duration.proto",
+    "v2/wire/error_details.proto",
     "v2/wire/eventid.proto",
     "v2/wire/expiration_info.proto",
     "v2/wire/feature.proto",
@@ -43,6 +46,7 @@
     "v2/wire/feed_query.proto",
     "v2/wire/feed_request.proto",
     "v2/wire/feed_response.proto",
+    "v2/wire/feed_streaming.proto",
     "v2/wire/info_card.proto",
     "v2/wire/language_preferences.proto",
     "v2/wire/next_page_token.proto",
@@ -56,6 +60,7 @@
     "v2/wire/stream_structure.proto",
     "v2/wire/table.proto",
     "v2/wire/there_and_back_again_data.proto",
+    "v2/wire/timestamp.proto",
     "v2/wire/token.proto",
     "v2/wire/upload_actions_request.proto",
     "v2/wire/upload_actions_response.proto",
diff --git a/components/feed/core/proto/v2/wire/client_user_profiles.proto b/components/feed/core/proto/v2/wire/client_user_profiles.proto
index 0f2cfbf7..55fbfe0 100644
--- a/components/feed/core/proto/v2/wire/client_user_profiles.proto
+++ b/components/feed/core/proto/v2/wire/client_user_profiles.proto
@@ -33,13 +33,7 @@
     optional uint64 mid = 2;
     repeated ActionCounts counts = 3;
   }
-  message CardCategoryXEntityActionCounts {
-    optional uint64 card_category = 1;
-    optional uint64 mid = 2;
-    repeated ActionCounts counts = 3;
-  }
   repeated ContentMediaXEntityActionCounts content_media_x_entity = 2;
-  repeated CardCategoryXEntityActionCounts card_category_x_entity = 3;
 }
 message ViewDemotionProfile {
   optional ViewDemotionProfileExtension view_demotion_profile = 1000;
diff --git a/components/feed/core/proto/v2/wire/code.proto b/components/feed/core/proto/v2/wire/code.proto
new file mode 100644
index 0000000..9008e56
--- /dev/null
+++ b/components/feed/core/proto/v2/wire/code.proto
@@ -0,0 +1,29 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto3";
+
+package feedwire;
+
+option optimize_for = LITE_RUNTIME;
+
+enum Code {
+  OK = 0;
+  CANCELLED = 1;
+  UNKNOWN = 2;
+  INVALID_ARGUMENT = 3;
+  DEADLINE_EXCEEDED = 4;
+  NOT_FOUND = 5;
+  ALREADY_EXISTS = 6;
+  PERMISSION_DENIED = 7;
+  UNAUTHENTICATED = 16;
+  RESOURCE_EXHAUSTED = 8;
+  FAILED_PRECONDITION = 9;
+  ABORTED = 10;
+  OUT_OF_RANGE = 11;
+  UNIMPLEMENTED = 12;
+  INTERNAL = 13;
+  UNAVAILABLE = 14;
+  DATA_LOSS = 15;
+}
diff --git a/components/feed/core/proto/v2/wire/cui_server_metadata.proto b/components/feed/core/proto/v2/wire/cui_server_metadata.proto
new file mode 100644
index 0000000..2c50664e
--- /dev/null
+++ b/components/feed/core/proto/v2/wire/cui_server_metadata.proto
@@ -0,0 +1,11 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto3";
+
+package feedwire;
+
+option optimize_for = LITE_RUNTIME;
+
+message CuiServerMetadata {}
diff --git a/components/feed/core/proto/v2/wire/error_details.proto b/components/feed/core/proto/v2/wire/error_details.proto
new file mode 100644
index 0000000..145f636e
--- /dev/null
+++ b/components/feed/core/proto/v2/wire/error_details.proto
@@ -0,0 +1,19 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto3";
+
+package feedwire;
+
+option optimize_for = LITE_RUNTIME;
+
+message ErrorInfo {
+  message MetadataEntry {
+    string key = 1;
+    string value = 2;
+  }
+  string reason = 1;
+  string domain = 2;
+  repeated MetadataEntry metadata = 3;
+}
diff --git a/components/feed/core/proto/v2/wire/feed_response.proto b/components/feed/core/proto/v2/wire/feed_response.proto
index afd2b8f..9b29aa3 100644
--- a/components/feed/core/proto/v2/wire/feed_response.proto
+++ b/components/feed/core/proto/v2/wire/feed_response.proto
@@ -9,6 +9,7 @@
 import "components/feed/core/proto/v2/wire/capabilities_debug_data.proto";
 import "components/feed/core/proto/v2/wire/chrome_feed_response_metadata.proto";
 import "components/feed/core/proto/v2/wire/content_lifetime.proto";
+import "components/feed/core/proto/v2/wire/cui_server_metadata.proto";
 import "components/feed/core/proto/v2/wire/data_operation.proto";
 import "components/feed/core/proto/v2/wire/eventid.proto";
 import "components/feed/core/proto/v2/wire/info_card.proto";
@@ -28,5 +29,7 @@
   optional ContentLifetime content_lifetime = 6;
   optional InfoCardServingInfo info_card_serving_info = 7;
   optional CapabilitiesDebugData capabilities_debug_data = 8;
+  optional int32 feed_end_status = 9;
+  optional CuiServerMetadata feed_launch_cui_server_metadata = 13;
   optional ChromeFeedResponseMetadata chrome_feed_response_metadata = 326233599;
 }
diff --git a/components/feed/core/proto/v2/wire/feed_streaming.proto b/components/feed/core/proto/v2/wire/feed_streaming.proto
new file mode 100644
index 0000000..5ccc11f
--- /dev/null
+++ b/components/feed/core/proto/v2/wire/feed_streaming.proto
@@ -0,0 +1,97 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto3";
+
+package feedwire;
+
+import "components/feed/core/proto/v2/wire/action_diagnostic_info.proto";
+import "components/feed/core/proto/v2/wire/action_surface.proto";
+import "components/feed/core/proto/v2/wire/code.proto";
+import "components/feed/core/proto/v2/wire/consistency_token.proto";
+import "components/feed/core/proto/v2/wire/data_operation.proto";
+import "components/feed/core/proto/v2/wire/error_details.proto";
+import "components/feed/core/proto/v2/wire/feed_action.proto";
+import "components/feed/core/proto/v2/wire/feed_request.proto";
+import "components/feed/core/proto/v2/wire/feed_response.proto";
+import "components/feed/core/proto/v2/wire/timestamp.proto";
+import "components/feed/core/proto/v2/wire/token.proto";
+
+option optimize_for = LITE_RUNTIME;
+
+enum VisibilitySignal {
+  VISIBILITY_SIGNAL_UNSPECIFIED = 0;
+  VISIBLE = 1;
+  HIDDEN = 2;
+}
+message FullRefreshRequest {}
+message InitialView {
+  int32 stream_index = 1;
+  FeedAction feed_actions = 2;
+  VisibilitySignal visibility_signal = 3;
+}
+message FeedVisibilityChange {
+  ActionSurface action_surface = 1;
+  VisibilitySignal visibility_signal = 2;
+  Timestamp event_time = 3;
+}
+message ClientAction {
+  oneof client_action_payload {
+    FullRefreshRequest full_refresh_request = 1;
+    InitialView initial_view = 2;
+    FeedVisibilityChange feed_visibility_change = 3;
+  }
+}
+message ActionsPayload {
+  repeated FeedAction feed_actions = 1;
+  repeated ClientAction client_actions = 3;
+  string id = 2;
+  ActionDiagnosticInfo action_diagnostic_info = 4;
+}
+message InitializationPayload {
+  enum SessionType {
+    SESSION_TYPE_UNSPECIFIED = 0;
+    BACKGROUND = 1;
+    INTERACTIVE = 2;
+  }
+  optional FeedRequest feed_request = 1;
+  optional Token token = 2;
+  optional SessionType session_type = 4;
+}
+message StreamingTokenUpdatePayload {
+  optional Token token = 1;
+}
+message InitializationAckPayload {}
+message DataOperationPayload {
+  repeated DataOperation data_operations = 1;
+}
+message ResponseStatus {
+  optional Code response_code = 1;
+  optional ErrorInfo response_error_info = 2;
+}
+message NewFeedPayload {
+  FeedResponse feed_response = 1;
+  optional ResponseStatus status = 2;
+}
+message ActionsPayloadAck {
+  string processed_id = 1;
+  optional ResponseStatus status = 3;
+}
+message ClientStreamingMessage {
+  oneof payload {
+    InitializationPayload initialization_payload = 3;
+    StreamingTokenUpdatePayload streaming_token_update_payload = 4;
+  }
+  optional ActionsPayload actions_payload = 1;
+}
+message ServerStreamingMessage {
+  oneof payload {
+    InitializationAckPayload initialization_ack_payload = 4;
+    DataOperationPayload data_operation_payload = 1;
+    NewFeedPayload new_feed_payload = 5;
+    ActionsPayloadAck actions_payload_ack = 3;
+  }
+  optional Token token = 6;
+  optional ConsistencyToken consistency_token = 7;
+}
diff --git a/components/feed/core/proto/v2/wire/timestamp.proto b/components/feed/core/proto/v2/wire/timestamp.proto
new file mode 100644
index 0000000..e479c2a8
--- /dev/null
+++ b/components/feed/core/proto/v2/wire/timestamp.proto
@@ -0,0 +1,14 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto3";
+
+package feedwire;
+
+option optimize_for = LITE_RUNTIME;
+
+message Timestamp {
+  int64 seconds = 1;
+  int32 nanos = 2;
+}
diff --git a/components/media_effects/media_effects_service.cc b/components/media_effects/media_effects_service.cc
index 6852ea4..39f4f6d0 100644
--- a/components/media_effects/media_effects_service.cc
+++ b/components/media_effects/media_effects_service.cc
@@ -86,7 +86,6 @@
                                                     std::move(gpu_receiver)));
   }
 
-  LOG(WARNING) << "Calling CreateEffectsProcessor";
   video_effects_service->CreateEffectsProcessor(
       device_id, std::move(gpu_remote),
       std::move(readonly_video_effects_manager),
diff --git a/components/optimization_guide/proto/features/model_prototyping.proto b/components/optimization_guide/proto/features/model_prototyping.proto
index 59b66fc..49ec845 100644
--- a/components/optimization_guide/proto/features/model_prototyping.proto
+++ b/components/optimization_guide/proto/features/model_prototyping.proto
@@ -293,8 +293,6 @@
   bool pdf_data = 6;
 
   // Whether to collect the last FormsPredictions request and response.
-  // Note: This flag is currently ignored while another AutofillAi iteration
-  // is under development.
   bool forms_prediction = 7;
 
   // Whether to collect the annotated page content.
diff --git a/components/viz/service/input/android_state_transfer_handler.cc b/components/viz/service/input/android_state_transfer_handler.cc
index 3a21477..59fc4ff9 100644
--- a/components/viz/service/input/android_state_transfer_handler.cc
+++ b/components/viz/service/input/android_state_transfer_handler.cc
@@ -189,13 +189,16 @@
 
 void AndroidStateTransferHandler::HandleTouchEvent(
     base::android::ScopedInputEvent input_event) {
-  CHECK(state_for_curr_sequence_.has_value() &&
+  CHECK(state_for_curr_sequence_.has_value());
+  const int action = AMotionEvent_getAction(input_event.a_input_event()) &
+                     AMOTION_EVENT_ACTION_MASK;
+  // Due to an Android platform bug b/395610162, we see some motion events have
+  // different down time than the rest of the sequence.
+  CHECK(action == AMOTION_EVENT_ACTION_MOVE ||
         GetEventDowntime(input_event) ==
             state_for_curr_sequence_->transfer_state->down_time_ms);
 
   if (!state_for_curr_sequence_->rir_support) {
-    const int action = AMotionEvent_getAction(input_event.a_input_event()) &
-                       AMOTION_EVENT_ACTION_MASK;
     if (action == AMOTION_EVENT_ACTION_CANCEL ||
         action == AMOTION_EVENT_ACTION_UP) {
       state_for_curr_sequence_.reset();
diff --git a/content/browser/web_contents/immediate_accessibility_mode_policy.cc b/content/browser/web_contents/immediate_accessibility_mode_policy.cc
index a47c627..959e377 100644
--- a/content/browser/web_contents/immediate_accessibility_mode_policy.cc
+++ b/content/browser/web_contents/immediate_accessibility_mode_policy.cc
@@ -5,7 +5,6 @@
 #include "content/browser/web_contents/immediate_accessibility_mode_policy.h"
 
 #include "base/functional/callback.h"
-#include "content/browser/web_contents/web_contents_impl.h"
 
 namespace content {
 
diff --git a/content/test/gpu/find_bad_machines.py b/content/test/gpu/find_bad_machines.py
index e8cf5ee..b16828f3 100755
--- a/content/test/gpu/find_bad_machines.py
+++ b/content/test/gpu/find_bad_machines.py
@@ -30,6 +30,7 @@
         'gpu_samsung_s23_stable',
         'gpu_samsung_s24_stable',
         'linux_amd_rx_5500_xt',
+        'linux_amd_rx_7600_stable',
         'linux_intel_uhd_630_experimental',
         'linux_intel_uhd_630_stable',
         'linux_intel_uhd_770_stable',
@@ -52,6 +53,7 @@
         'win10_intel_uhd_770_stable',
         'win10_nvidia_gtx_1660_experimental',
         'win10_nvidia_gtx_1660_stable',
+        'win11_amd_rx_7600_stable',
         'win11_nvidia_rtx_4070_super_stable',
         'win11_qualcomm_adreno_690_stable',
     ],
diff --git a/docs/updater/functional_spec.md b/docs/updater/functional_spec.md
index 7bbb852..9bb07aa 100644
--- a/docs/updater/functional_spec.md
+++ b/docs/updater/functional_spec.md
@@ -521,6 +521,20 @@
 The page is opened with a query string:
 `?product={AppId}&errorcode={ErrorCode}`.
 
+### Periodic detection of over-installed apps
+
+The updater periodically checks if application versions persisted to the system
+match its built-in `pv` value. If different, the updater sends an installation
+ping for the application indicating the actually installed version and updates
+the internal `pv` value to match.
+
+The application's version persisted to the system is determined via:
+
+* The plist file and key indicated by the `pv_path` and `pv_key` in the app's
+  updater registration on MacOS.
+* The `pv` registry key as described by the "App Registration" section on
+  Windows.
+
 ## Updates
 There is no limit for the number of retries to update an application if the
 update fails repeatedly.
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index 41ba51a..65cd77e3 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -728,7 +728,7 @@
     IntentTextBoundaryType textBoundary;
 
     // A move direction associated with this AutomationIntent.
-    IntentMoveDirectionType moveDirection;
+    IntentMoveDirectionType? moveDirection;
   };
 
   // An event in the Automation tree.
diff --git a/extensions/common/api/externs_checker.py b/extensions/common/api/externs_checker.py
index de9d1b6..17cce61 100644
--- a/extensions/common/api/externs_checker.py
+++ b/extensions/common/api/externs_checker.py
@@ -3,9 +3,10 @@
 # found in the LICENSE file.
 
 class ExternsChecker(object):
-  _UPDATE_MESSAGE = """To update the externs, run:
- src/ $ python3 tools/json_schema_compiler/compiler.py\
- %s --root=. --generator=externs > %s"""
+  _UPDATE_MESSAGE_HDR = 'To update the externs, run:\n'
+  _UPDATE_MESSAGE_CMD = (
+    ' src/ $ python3 tools/json_schema_compiler/compiler.py %s'
+    ' --root=. --generator=externs > %s')
 
   def __init__(self, input_api, output_api, api_pairs=None, api_root=None):
     if api_pairs is None:
@@ -54,9 +55,9 @@
         bad_files.append({'source': path, 'extern': pair})
     results = []
     if bad_files:
-      replacements = (('<source_file>', '<output_file>') if len(bad_files) > 1
-          else (bad_files[0]['source'], bad_files[0]['extern']))
-      long_text = self._UPDATE_MESSAGE % replacements
+      long_text = self._UPDATE_MESSAGE_HDR + '\n'.join(
+          self._UPDATE_MESSAGE_CMD % (f['source'], f['extern'])
+          for f in bad_files)
       results.append(self._output_api.PresubmitPromptWarning(
           str('Found updated extension api files without updated extern files. '
               'Please update the extern files.'),
diff --git a/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt b/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt
index d5c1c879..207f2ef 100644
--- a/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-gpu-infra@google.com.txt
@@ -98,6 +98,7 @@
 ci/Linux FYI Experimental Release (NVIDIA)
 ci/Linux FYI GPU TSAN Release
 ci/Linux FYI Release (AMD RX 5500 XT)
+ci/Linux FYI Release (AMD RX 7600)
 ci/Linux FYI Release (Intel UHD 630)
 ci/Linux FYI Release (Intel UHD 770)
 ci/Linux FYI Release (NVIDIA)
@@ -137,6 +138,7 @@
 ci/Win10 x64 Debug (NVIDIA)
 ci/Win10 x64 Release (NVIDIA)
 ci/Win11 FYI arm64 Release (Qualcomm Adreno 690)
+ci/Win11 FYI x64 Release (AMD RX 7600)
 ci/Win11 FYI x64 Release (NVIDIA RTX 4070 Super)
 ci/linux-swangle-chromium-x64
 ci/linux-swangle-chromium-x64-exp
@@ -192,6 +194,7 @@
 try/gpu-fyi-try-chromeos-amd64-generic
 try/gpu-fyi-try-chromeos-skylab-volteer
 try/gpu-fyi-try-linux-amd-rel
+try/gpu-fyi-try-linux-amd-rx-7600-rel
 try/gpu-fyi-try-linux-intel-exp
 try/gpu-fyi-try-linux-intel-rel
 try/gpu-fyi-try-linux-intel-uhd770-rel
@@ -226,6 +229,7 @@
 try/gpu-fyi-try-win10-nvidia-exp-64
 try/gpu-fyi-try-win10-nvidia-rel-32
 try/gpu-fyi-try-win10-nvidia-rel-64
+try/gpu-fyi-try-win11-amd-rel-64
 try/gpu-fyi-try-win11-nvidia-4070-rel-64
 try/gpu-fyi-try-win11-qualcomm-rel-64
 try/gpu-try-android-m-nexus-5x-64
diff --git a/infra/config/generated/builders/ci/GPU FYI Linux Builder/properties.json b/infra/config/generated/builders/ci/GPU FYI Linux Builder/properties.json
index ec323f1..e1531d6 100644
--- a/infra/config/generated/builders/ci/GPU FYI Linux Builder/properties.json
+++ b/infra/config/generated/builders/ci/GPU FYI Linux Builder/properties.json
@@ -119,6 +119,35 @@
           {
             "builder_id": {
               "bucket": "ci",
+              "builder": "Linux FYI Release (AMD RX 7600)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Linux Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
               "builder": "Linux FYI Release (Intel UHD 630)",
               "project": "chromium"
             },
@@ -230,6 +259,11 @@
         },
         {
           "bucket": "ci",
+          "builder": "Linux FYI Release (AMD RX 7600)",
+          "project": "chromium"
+        },
+        {
+          "bucket": "ci",
           "builder": "Linux FYI Release (Intel UHD 630)",
           "project": "chromium"
         },
@@ -250,6 +284,10 @@
           "group": "tryserver.chromium.linux"
         },
         {
+          "builder": "gpu-fyi-try-linux-amd-rx-7600-rel",
+          "group": "tryserver.chromium.linux"
+        },
+        {
           "builder": "gpu-fyi-try-linux-intel-exp",
           "group": "tryserver.chromium.linux"
         },
diff --git a/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json
index 5f80365..5d7302372f 100644
--- a/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Linux Builder/targets/chromium.gpu.fyi.json
@@ -1186,6 +1186,42 @@
       }
     ]
   },
+  "Linux FYI Release (AMD RX 7600)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-24.2.8",
+            "os": "Ubuntu-24.04",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      }
+    ]
+  },
   "Linux FYI Release (Intel UHD 630)": {
     "gtest_tests": [
       {
diff --git a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/properties.json b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/properties.json
index 609ee24..308c079 100644
--- a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/properties.json
+++ b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/properties.json
@@ -206,6 +206,35 @@
           {
             "builder_id": {
               "bucket": "ci",
+              "builder": "Win11 FYI x64 Release (AMD RX 7600)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Win x64 Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
               "builder": "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)",
               "project": "chromium"
             },
@@ -274,6 +303,11 @@
         },
         {
           "bucket": "ci",
+          "builder": "Win11 FYI x64 Release (AMD RX 7600)",
+          "project": "chromium"
+        },
+        {
+          "bucket": "ci",
           "builder": "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)",
           "project": "chromium"
         }
@@ -304,6 +338,10 @@
           "group": "tryserver.chromium.win"
         },
         {
+          "builder": "gpu-fyi-try-win11-amd-rel-64",
+          "group": "tryserver.chromium.win"
+        },
+        {
           "builder": "gpu-fyi-try-win11-nvidia-4070-rel-64",
           "group": "tryserver.chromium.win"
         }
diff --git a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
index f256c53..7f8e231 100644
--- a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
@@ -2943,6 +2943,42 @@
       }
     ]
   },
+  "Win11 FYI x64 Release (AMD RX 7600)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-31.0.24033.1003",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      }
+    ]
+  },
   "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)": {
     "gtest_tests": [
       {
diff --git "a/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 7600\051/properties.json" "b/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 7600\051/properties.json"
new file mode 100644
index 0000000..d853460
--- /dev/null
+++ "b/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 7600\051/properties.json"
@@ -0,0 +1,92 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Linux Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Linux FYI Release (AMD RX 7600)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Linux Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Linux FYI Release (AMD RX 7600)",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "gpu-fyi-try-linux-amd-rx-7600-rel",
+          "group": "tryserver.chromium.linux"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/ci/Linux FYI Release (AMD RX 7600)/targets"
+    }
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.gpu.fyi",
+  "gardener_rotations": [
+    "chromium.gpu"
+  ],
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "chromium.gpu"
+  ]
+}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json"
new file mode 100644
index 0000000..906d439
--- /dev/null
+++ "b/infra/config/generated/builders/ci/Linux FYI Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json"
@@ -0,0 +1,38 @@
+{
+  "Linux FYI Release (AMD RX 7600)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-24.2.8",
+            "os": "Ubuntu-24.04",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/properties.json" "b/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/properties.json"
new file mode 100644
index 0000000..306f987
--- /dev/null
+++ "b/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/properties.json"
@@ -0,0 +1,92 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Win x64 Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Win11 FYI x64 Release (AMD RX 7600)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Win x64 Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "Win11 FYI x64 Release (AMD RX 7600)",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "gpu-fyi-try-win11-amd-rel-64",
+          "group": "tryserver.chromium.win"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/ci/Win11 FYI x64 Release (AMD RX 7600)/targets"
+    }
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.gpu.fyi",
+  "gardener_rotations": [
+    "chromium.gpu"
+  ],
+  "perf_dashboard_machine_group": "ChromiumGPUFYI",
+  "recipe": "chromium",
+  "sheriff_rotations": [
+    "chromium.gpu"
+  ]
+}
\ No newline at end of file
diff --git "a/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json"
new file mode 100644
index 0000000..1a4f54f
--- /dev/null
+++ "b/infra/config/generated/builders/ci/Win11 FYI x64 Release \050AMD RX 7600\051/targets/chromium.gpu.fyi.json"
@@ -0,0 +1,38 @@
+{
+  "Win11 FYI x64 Release (AMD RX 7600)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-31.0.24033.1003",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index 9dd078c..616ff28 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -796,6 +796,7 @@
     "chromeos-js-coverage-rel": "try/chromeos-js-coverage-rel/gn-args.json",
     "compile-size": "try/compile-size/gn-args.json",
     "gpu-fyi-try-linux-amd-rel": "try/gpu-fyi-try-linux-amd-rel/gn-args.json",
+    "gpu-fyi-try-linux-amd-rx-7600-rel": "try/gpu-fyi-try-linux-amd-rx-7600-rel/gn-args.json",
     "gpu-fyi-try-linux-intel-exp": "try/gpu-fyi-try-linux-intel-exp/gn-args.json",
     "gpu-fyi-try-linux-intel-rel": "try/gpu-fyi-try-linux-intel-rel/gn-args.json",
     "gpu-fyi-try-linux-intel-uhd770-rel": "try/gpu-fyi-try-linux-intel-uhd770-rel/gn-args.json",
@@ -972,6 +973,7 @@
     "gpu-fyi-try-win10-nvidia-exp-64": "try/gpu-fyi-try-win10-nvidia-exp-64/gn-args.json",
     "gpu-fyi-try-win10-nvidia-rel-32": "try/gpu-fyi-try-win10-nvidia-rel-32/gn-args.json",
     "gpu-fyi-try-win10-nvidia-rel-64": "try/gpu-fyi-try-win10-nvidia-rel-64/gn-args.json",
+    "gpu-fyi-try-win11-amd-rel-64": "try/gpu-fyi-try-win11-amd-rel-64/gn-args.json",
     "gpu-fyi-try-win11-nvidia-4070-rel-64": "try/gpu-fyi-try-win11-nvidia-4070-rel-64/gn-args.json",
     "gpu-fyi-try-win11-qualcomm-rel-64": "try/gpu-fyi-try-win11-qualcomm-rel-64/gn-args.json",
     "gpu-try-win-nvidia-dbg": "try/gpu-try-win-nvidia-dbg/gn-args.json",
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/gn-args.json b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/gn-args.json
new file mode 100644
index 0000000..145d7af01
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/gn-args.json
@@ -0,0 +1,14 @@
+{
+  "gn_args": {
+    "dcheck_always_on": true,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "symbol_level": 1,
+    "target_cpu": "x64",
+    "target_os": "linux",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/properties.json b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/properties.json
new file mode 100644
index 0000000..1cf6aad
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/properties.json
@@ -0,0 +1,104 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Linux Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Linux FYI Release (AMD RX 7600)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Linux Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "GPU FYI Linux Builder",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "Linux FYI Release (AMD RX 7600)",
+          "project": "chromium"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/targets"
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "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",
+    "remote_jobs": 150
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.linux",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/targets/chromium.gpu.fyi.json
new file mode 100644
index 0000000..0337b5d
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel/targets/chromium.gpu.fyi.json
@@ -0,0 +1,39 @@
+{
+  "GPU FYI Linux Builder": {},
+  "Linux FYI Release (AMD RX 7600)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=release",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-24.2.8",
+            "os": "Ubuntu-24.04",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/gn-args.json b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/gn-args.json
new file mode 100644
index 0000000..63a8c2327
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/gn-args.json
@@ -0,0 +1,14 @@
+{
+  "gn_args": {
+    "dcheck_always_on": true,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "symbol_level": 1,
+    "target_cpu": "x64",
+    "target_os": "win",
+    "use_remoteexec": true,
+    "use_siso": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/properties.json b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/properties.json
new file mode 100644
index 0000000..f1453493
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/properties.json
@@ -0,0 +1,104 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Win x64 Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Win11 FYI x64 Release (AMD RX 7600)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Win x64 Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "GPU FYI Win x64 Builder",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "Win11 FYI x64 Release (AMD RX 7600)",
+          "project": "chromium"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets"
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-untrusted",
+    "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",
+    "remote_jobs": 150
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "tryserver.chromium.win",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets/chromium.gpu.fyi.json
new file mode 100644
index 0000000..f76810c
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win11-amd-rel-64/targets/chromium.gpu.fyi.json
@@ -0,0 +1,39 @@
+{
+  "GPU FYI Win x64 Builder": {},
+  "Win11 FYI x64 Release (AMD RX 7600)": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "noop_sleep",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "noop_sleep_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "1002:7480-31.0.24033.1003",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index acb75a6..79bc302f6 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -3573,6 +3573,27 @@
           }
         ]
       },
+      "Linux FYI Release (AMD RX 7600)": {
+        "contact_team_email": "chrome-gpu-infra@google.com",
+        "problem_specs": [
+          {
+            "name": "Unhealthy",
+            "period_days": 7,
+            "score": 5,
+            "thresholds": {
+              "_default": "_default"
+            }
+          },
+          {
+            "name": "Low Value",
+            "period_days": 90,
+            "score": 1,
+            "thresholds": {
+              "_default": "_default"
+            }
+          }
+        ]
+      },
       "Linux FYI Release (Intel UHD 630)": {
         "contact_team_email": "chrome-gpu-infra@google.com",
         "problem_specs": [
@@ -6268,6 +6289,27 @@
           }
         ]
       },
+      "Win11 FYI x64 Release (AMD RX 7600)": {
+        "contact_team_email": "chrome-gpu-infra@google.com",
+        "problem_specs": [
+          {
+            "name": "Unhealthy",
+            "period_days": 7,
+            "score": 5,
+            "thresholds": {
+              "_default": "_default"
+            }
+          },
+          {
+            "name": "Low Value",
+            "period_days": 90,
+            "score": 1,
+            "thresholds": {
+              "_default": "_default"
+            }
+          }
+        ]
+      },
       "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)": {
         "contact_team_email": "chrome-gpu-infra@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 98c9fdf3..31a0140 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -3874,6 +3874,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/gpu-fyi-try-linux-amd-rx-7600-rel"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/gpu-fyi-try-linux-intel-exp"
         includable_only: true
       }
@@ -4010,6 +4014,10 @@
         includable_only: true
       }
       builders {
+        name: "chromium/try/gpu-fyi-try-win11-amd-rel-64"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/gpu-fyi-try-win11-nvidia-4070-rel-64"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index b40f947..a7dc0eb 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -15804,7 +15804,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "Builds release Linux x64 binaries for GPU testing<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-amd-rel\">gpu-fyi-try-linux-amd-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-intel-exp\">gpu-fyi-try-linux-intel-exp</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-intel-rel\">gpu-fyi-try-linux-intel-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-intel-uhd770-rel\">gpu-fyi-try-linux-intel-uhd770-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-nvidia-exp\">gpu-fyi-try-linux-nvidia-exp</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-nvidia-rel\">gpu-fyi-try-linux-nvidia-rel</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
+      description_html: "Builds release Linux x64 binaries for GPU testing<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-amd-rel\">gpu-fyi-try-linux-amd-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel\">gpu-fyi-try-linux-amd-rx-7600-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-intel-exp\">gpu-fyi-try-linux-intel-exp</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-intel-rel\">gpu-fyi-try-linux-intel-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-intel-uhd770-rel\">gpu-fyi-try-linux-intel-uhd770-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-nvidia-exp\">gpu-fyi-try-linux-nvidia-exp</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-nvidia-rel\">gpu-fyi-try-linux-nvidia-rel</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -16849,7 +16849,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "Builds release Windows x64 binaries for GPU testing<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-amd-rel-64\">gpu-fyi-try-win10-amd-rel-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-intel-exp-64\">gpu-fyi-try-win10-intel-exp-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-intel-rel-64\">gpu-fyi-try-win10-intel-rel-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-intel-uhd770-rel\">gpu-fyi-try-win10-intel-uhd770-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-nvidia-exp-64\">gpu-fyi-try-win10-nvidia-exp-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-nvidia-rel-64\">gpu-fyi-try-win10-nvidia-rel-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win11-nvidia-4070-rel-64\">gpu-fyi-try-win11-nvidia-4070-rel-64</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
+      description_html: "Builds release Windows x64 binaries for GPU testing<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-amd-rel-64\">gpu-fyi-try-win10-amd-rel-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-intel-exp-64\">gpu-fyi-try-win10-intel-exp-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-intel-rel-64\">gpu-fyi-try-win10-intel-rel-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-intel-uhd770-rel\">gpu-fyi-try-win10-intel-uhd770-rel</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-nvidia-exp-64\">gpu-fyi-try-win10-nvidia-exp-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win10-nvidia-rel-64\">gpu-fyi-try-win10-nvidia-rel-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win11-amd-rel-64\">gpu-fyi-try-win11-amd-rel-64</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win11-nvidia-4070-rel-64\">gpu-fyi-try-win11-nvidia-4070-rel-64</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -21512,6 +21512,122 @@
       }
     }
     builders {
+      name: "Linux FYI Release (AMD RX 7600)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:2"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.gpu.ci"
+      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/ci/Linux FYI Release (AMD RX 7600)/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.gpu.fyi",'
+        '  "gardener_rotations": ['
+        '    "chromium.gpu"'
+        '  ],'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "chromium.gpu"'
+        '  ]'
+        '}'
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.use_per_builder_build_dir_name"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Runs release GPU tests on stable Linux/AMD RX 7600 configs<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-linux-amd-rx-7600-rel\">gpu-fyi-try-linux-amd-rx-7600-rel</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+        pool: "luci.chromium.try"
+        dimensions: "free_space:"
+        dimensions: "pool:luci.chromium.try"
+      }
+      contact_team_email: "chrome-gpu-infra@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+    }
+    builders {
       name: "Linux FYI Release (Intel UHD 630)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -35864,6 +35980,122 @@
       }
     }
     builders {
+      name: "Win11 FYI x64 Release (AMD RX 7600)"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:2"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.gpu.ci"
+      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/ci/Win11 FYI x64 Release (AMD RX 7600)/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.gpu.fyi",'
+        '  "gardener_rotations": ['
+        '    "chromium.gpu"'
+        '  ],'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium",'
+        '  "sheriff_rotations": ['
+        '    "chromium.gpu"'
+        '  ]'
+        '}'
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.use_per_builder_build_dir_name"
+        value: 100
+      }
+      experiments {
+        key: "luci.recipes.use_python3"
+        value: 100
+      }
+      resultdb {
+        enable: true
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "ci_test_results"
+          test_results {}
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "gpu_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
+            }
+          }
+        }
+        bq_exports {
+          project: "chrome-luci-data"
+          dataset: "chromium"
+          table: "blink_web_tests_ci_test_results"
+          test_results {
+            predicate {
+              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "Runs release GPU tests on stable Windows 11/AMD RX 7600 configs<br/>This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-win11-amd-rel-64\">gpu-fyi-try-win11-amd-rel-64</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+        pool: "luci.chromium.try"
+        dimensions: "free_space:"
+        dimensions: "pool:luci.chromium.try"
+      }
+      contact_team_email: "chrome-gpu-infra@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+    }
+    builders {
       name: "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -58933,13 +59165,11 @@
     builders {
       name: "linux-rr-test-launcher-fyi"
       swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
+      dimensions: "builder:linux-rr-test-launcher-fyi"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
       dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
         cipd_version: "latest"
@@ -59050,7 +59280,8 @@
           '    "remote_jobs": 250'
           '  }'
           '}'
-        dimensions: "free_space:"
+        dimensions: "builder:"
+        dimensions: "builderless:1"
         dimensions: "pool:luci.chromium.try"
       }
       contact_team_email: "chrome-browser-infra-team@google.com"
@@ -101041,6 +101272,114 @@
       max_concurrent_builds: 1
     }
     builders {
+      name: "gpu-fyi-try-linux-amd-rx-7600-rel"
+      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.gpu.try"
+      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/gpu-fyi-try-linux-amd-rx-7600-rel/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.linux",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 21600
+      expiration_secs: 7200
+      build_numbers: YES
+      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.use_per_builder_build_dir_name"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.canary_software"
+        value: 5
+      }
+      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|content)/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/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Linux Builder\">GPU FYI Linux Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux FYI Release (AMD RX 7600)\">Linux FYI Release (AMD RX 7600)</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
+      contact_team_email: "chrome-gpu-infra@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+      max_concurrent_builds: 1
+    }
+    builders {
       name: "gpu-fyi-try-linux-intel-exp"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -104683,6 +105022,114 @@
       max_concurrent_builds: 1
     }
     builders {
+      name: "gpu-fyi-try-win11-amd-rel-64"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:8"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Windows"
+      dimensions: "pool:luci.chromium.gpu.try"
+      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/gpu-fyi-try-win11-amd-rel-64/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.win",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium_trybot"'
+        '}'
+      execution_timeout_secs: 21600
+      expiration_secs: 7200
+      build_numbers: YES
+      service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
+      experiments {
+        key: "chromium.use_per_builder_build_dir_name"
+        value: 100
+      }
+      experiments {
+        key: "luci.buildbucket.canary_software"
+        value: 5
+      }
+      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|content)/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/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
+            }
+          }
+        }
+        history_options {
+          use_invocation_timestamp: true
+        }
+      }
+      description_html: "This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Win x64 Builder\">GPU FYI Win x64 Builder</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win11 FYI x64 Release (AMD RX 7600)\">Win11 FYI x64 Release (AMD RX 7600)</a></li></ul><br/>Builder owner: <a href=mailto:chrome-gpu-infra@google.com>chrome-gpu-infra@google.com</a>"
+      contact_team_email: "chrome-gpu-infra@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+      max_concurrent_builds: 1
+    }
+    builders {
       name: "gpu-fyi-try-win11-nvidia-4070-rel-64"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 9b022056..88f30cd 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -14635,7 +14635,12 @@
   builders {
     name: "buildbucket/luci.chromium.ci/Linux FYI Release (AMD RX 5500 XT)"
     category: "chromium.gpu.fyi|Linux|AMD"
-    short_name: "rel"
+    short_name: "5500"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Linux FYI Release (AMD RX 7600)"
+    category: "chromium.gpu.fyi|Linux|AMD"
+    short_name: "7600"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/GPU FYI Linux Builder (dbg)"
@@ -14818,6 +14823,11 @@
     short_name: "rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Win11 FYI x64 Release (AMD RX 7600)"
+    category: "chromium.gpu.fyi|Windows|11|x64|AMD"
+    short_name: "rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win11 FYI x64 Release (NVIDIA RTX 4070 Super)"
     category: "chromium.gpu.fyi|Windows|11|x64|Nvidia"
     short_name: "4070"
@@ -15407,6 +15417,11 @@
     short_name: "rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Win11 FYI x64 Release (AMD RX 7600)"
+    category: "Windows|11|x64|AMD"
+    short_name: "rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win11 FYI x64 Release (NVIDIA RTX 4070 Super)"
     category: "Windows|11|x64|Nvidia"
     short_name: "4070"
@@ -15509,7 +15524,12 @@
   builders {
     name: "buildbucket/luci.chromium.ci/Linux FYI Release (AMD RX 5500 XT)"
     category: "Linux|AMD"
-    short_name: "rel"
+    short_name: "5500"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.ci/Linux FYI Release (AMD RX 7600)"
+    category: "Linux|AMD"
+    short_name: "7600"
   }
   builders {
     name: "buildbucket/luci.chromium.ci/Linux FYI Release (Intel UHD 630)"
@@ -24103,6 +24123,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-amd-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-amd-rx-7600-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-intel-exp"
   }
   builders {
@@ -24205,6 +24228,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-rel-64"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win11-amd-rel-64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-win11-nvidia-4070-rel-64"
   }
   builders {
@@ -25843,6 +25869,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-amd-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-amd-rx-7600-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-linux-intel-exp"
   }
   builders {
@@ -26410,6 +26439,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-win10-nvidia-rel-64"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-win11-amd-rel-64"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-win11-nvidia-4070-rel-64"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 640170c..b87d5a2 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -1718,6 +1718,15 @@
   }
 }
 job {
+  id: "Linux FYI Release (AMD RX 7600)"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Linux FYI Release (AMD RX 7600)"
+  }
+}
+job {
   id: "Linux FYI Release (Intel UHD 630)"
   realm: "ci"
   buildbucket {
@@ -3202,6 +3211,15 @@
   }
 }
 job {
+  id: "Win11 FYI x64 Release (AMD RX 7600)"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "Win11 FYI x64 Release (AMD RX 7600)"
+  }
+}
+job {
   id: "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)"
   realm: "ci"
   buildbucket {
diff --git a/infra/config/generated/luci/realms.cfg b/infra/config/generated/luci/realms.cfg
index a1b407f..2da6748c 100644
--- a/infra/config/generated/luci/realms.cfg
+++ b/infra/config/generated/luci/realms.cfg
@@ -373,6 +373,7 @@
         values: "Linux FYI Experimental Release (Intel UHD 630)"
         values: "Linux FYI Experimental Release (NVIDIA)"
         values: "Linux FYI Release (AMD RX 5500 XT)"
+        values: "Linux FYI Release (AMD RX 7600)"
         values: "Linux FYI Release (Intel UHD 630)"
         values: "Linux FYI Release (Intel UHD 770)"
         values: "Linux FYI Release (NVIDIA)"
@@ -405,6 +406,7 @@
         values: "Win10 FYI x64 Release XR Perf (NVIDIA)"
         values: "Win10 FYI x86 Release (NVIDIA)"
         values: "Win11 FYI arm64 Release (Qualcomm Adreno 690)"
+        values: "Win11 FYI x64 Release (AMD RX 7600)"
         values: "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)"
         values: "android-angle-chromium-arm64-pixel2"
         values: "ios-angle-intel"
diff --git a/infra/config/generated/sheriff-rotations/chromium.gpu.txt b/infra/config/generated/sheriff-rotations/chromium.gpu.txt
index d7902f1..042d463 100644
--- a/infra/config/generated/sheriff-rotations/chromium.gpu.txt
+++ b/infra/config/generated/sheriff-rotations/chromium.gpu.txt
@@ -34,6 +34,7 @@
 ci/Linux FYI Experimental Release (NVIDIA)
 ci/Linux FYI GPU TSAN Release
 ci/Linux FYI Release (AMD RX 5500 XT)
+ci/Linux FYI Release (AMD RX 7600)
 ci/Linux FYI Release (Intel UHD 630)
 ci/Linux FYI Release (Intel UHD 770)
 ci/Linux FYI Release (NVIDIA)
@@ -72,6 +73,7 @@
 ci/Win10 x64 Debug (NVIDIA)
 ci/Win10 x64 Release (NVIDIA)
 ci/Win11 FYI arm64 Release (Qualcomm Adreno 690)
+ci/Win11 FYI x64 Release (AMD RX 7600)
 ci/Win11 FYI x64 Release (NVIDIA RTX 4070 Super)
 ci/linux-swangle-chromium-x64
 ci/linux-swangle-chromium-x64-exp
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index 0e75885..7e2669eb 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -465,6 +465,17 @@
       },
     },
   },
+  'linux_amd_rx_7600_stable': {
+    'fail_if_unused': False,
+    'swarming': {
+      'dimensions': {
+        'gpu': '1002:7480-24.2.8',
+        'os': 'Ubuntu-24.04',
+        'display_attached': '1',
+        'pool': 'chromium.tests.gpu',
+      },
+    },
+  },
   'linux_intel_uhd_630_experimental': {
     'fail_if_unused': False,
     'swarming': {
@@ -863,6 +874,17 @@
       },
     },
   },
+  'win11_amd_rx_7600_stable': {
+    'fail_if_unused': False,
+    'swarming': {
+      'dimensions': {
+        'display_attached': '1',
+        'gpu': '1002:7480-31.0.24033.1003',
+        'os': 'Windows-11-26100',
+        'pool': 'chromium.tests.gpu',
+      },
+    },
+  },
   'win11_nvidia_rtx_4070_super_stable': {
     'fail_if_unused': False,
     'swarming': {
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
index 7bd33ed..c0ba5e1 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -858,6 +858,7 @@
     executable = "recipe:chromium_rr/test_launcher",
     schedule = "triggered",
     triggered_by = [],
+    builderless = False,
     os = os.LINUX_DEFAULT,
     console_view_entry = consoles.console_view_entry(
         category = "linux",
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index bcb432fa..6c6b290 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -1481,7 +1481,48 @@
     ),
     console_view_entry = consoles.console_view_entry(
         category = "Linux|AMD",
-        short_name = "rel",
+        short_name = "5500",
+    ),
+)
+
+ci.thin_tester(
+    name = "Linux FYI Release (AMD RX 7600)",
+    description_html = "Runs release GPU tests on stable Linux/AMD RX 7600 configs",
+    triggered_by = ["GPU FYI Linux Builder"],
+    builder_spec = builder_config.builder_spec(
+        execution_mode = builder_config.execution_mode.TEST,
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.LINUX,
+        ),
+        run_tests_serially = True,
+    ),
+    targets = targets.bundle(
+        targets = [
+            "gpu_noop_sleep_telemetry_test",
+            # TODO(crbug.com/396611134): Enable actual tests.
+            # "gpu_fyi_linux_release_gtests",
+            # "gpu_fyi_linux_release_telemetry_tests",
+        ],
+        mixins = [
+            "linux_amd_rx_7600_stable",
+        ],
+    ),
+    targets_settings = targets.settings(
+        browser_config = targets.browser_config.RELEASE,
+        os_type = targets.os_type.LINUX,
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|AMD",
+        short_name = "7600",
     ),
 )
 
@@ -2862,6 +2903,60 @@
 )
 
 ci.thin_tester(
+    name = "Win11 FYI x64 Release (AMD RX 7600)",
+    description_html = "Runs release GPU tests on stable Windows 11/AMD RX 7600 configs",
+    triggered_by = ["GPU FYI Win x64 Builder"],
+    builder_spec = builder_config.builder_spec(
+        execution_mode = builder_config.execution_mode.TEST,
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+            target_platform = builder_config.target_platform.WIN,
+        ),
+        run_tests_serially = True,
+    ),
+    targets = targets.bundle(
+        targets = [
+            "gpu_noop_sleep_telemetry_test",
+            # TODO(crbug.com/396611135): Enable actual tests.
+            # "gpu_fyi_win_gtests",
+            # "gpu_fyi_win_amd_release_telemetry_tests",
+        ],
+        mixins = [
+            "win11_amd_rx_7600_stable",
+        ],
+        per_test_modifications = {
+            # "gl_unittests": targets.mixin(
+            #     args = [
+            #         "--test-launcher-filter-file=../../testing/buildbot/filters/win.amd.7600.gl_unittests.filter",
+            #     ],
+            # ),
+            # "media_foundation_browser_tests": targets.remove(
+            #     reason = [
+            #         "TODO(crbug.com/40912267): Enable Media Foundation browser tests on ",
+            #         "gpu bots once the Windows OS supports HW secure decryption.",
+            #     ],
+            # ),
+        },
+    ),
+    targets_settings = targets.settings(
+        browser_config = targets.browser_config.RELEASE_X64,
+        os_type = targets.os_type.WINDOWS,
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|11|x64|AMD",
+        short_name = "rel",
+    ),
+)
+
+ci.thin_tester(
     name = "Win11 FYI x64 Release (NVIDIA RTX 4070 Super)",
     description_html = "Runs release GPU tests on stable Windows 11/NVIDIA RTX 4070 Super configs",
     triggered_by = ["GPU FYI Win x64 Builder"],
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index 7bf30e38..b60caf7 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -236,6 +236,15 @@
 )
 
 gpu_linux_builder(
+    name = "gpu-fyi-try-linux-amd-rx-7600-rel",
+    mirrors = [
+        "ci/GPU FYI Linux Builder",
+        "ci/Linux FYI Release (AMD RX 7600)",
+    ],
+    gn_args = "ci/GPU FYI Linux Builder",
+)
+
+gpu_linux_builder(
     name = "gpu-fyi-try-linux-intel-exp",
     mirrors = [
         "ci/GPU FYI Linux Builder",
@@ -598,6 +607,15 @@
 )
 
 gpu_win_builder(
+    name = "gpu-fyi-try-win11-amd-rel-64",
+    mirrors = [
+        "ci/GPU FYI Win x64 Builder",
+        "ci/Win11 FYI x64 Release (AMD RX 7600)",
+    ],
+    gn_args = "ci/GPU FYI Win x64 Builder",
+)
+
+gpu_win_builder(
     name = "gpu-fyi-try-win11-nvidia-4070-rel-64",
     description_html = "Runs GPU tests on NVIDIA RTX 4070 Super GPUs",
     mirrors = [
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 2f3d0fda..6da1e0c 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -1337,6 +1337,21 @@
 )
 
 targets.mixin(
+    name = "linux_amd_rx_7600_stable",
+    # We always need this entry to be generated since it is used by
+    # //content/test/gpu/find_bad_machines.py.
+    generate_pyl_entry = targets.IGNORE_UNUSED,
+    swarming = targets.swarming(
+        dimensions = {
+            "gpu": "1002:7480-24.2.8",
+            "os": "Ubuntu-24.04",
+            "display_attached": "1",
+            "pool": "chromium.tests.gpu",
+        },
+    ),
+)
+
+targets.mixin(
     name = "linux_intel_uhd_630_experimental",
     # We always need this entry to be generated since it is used by
     # //content/test/gpu/find_bad_machines.py.
@@ -2267,6 +2282,21 @@
 )
 
 targets.mixin(
+    name = "win11_amd_rx_7600_stable",
+    # We always need this entry to be generated since it is used by
+    # //content/test/gpu/find_bad_machines.py.
+    generate_pyl_entry = targets.IGNORE_UNUSED,
+    swarming = targets.swarming(
+        dimensions = {
+            "display_attached": "1",
+            "gpu": "1002:7480-31.0.24033.1003",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu",
+        },
+    ),
+)
+
+targets.mixin(
     name = "win10_gce_gpu_pool",
     generate_pyl_entry = targets.IGNORE_UNUSED,
     swarming = targets.swarming(
diff --git a/internal b/internal
index 89050d2..7a7ac80 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 89050d27a6a3b76131a68e53f1b595f111c45531
+Subproject commit 7a7ac80a328065cac1905dae0f019dee50e2ca76
diff --git a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm
index 4d66cbd..0746cba 100644
--- a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm
+++ b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm
@@ -16,6 +16,7 @@
 #import "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #import "components/autofill/core/browser/form_structure.h"
 #import "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
+#import "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #import "components/autofill/core/browser/suggestions/suggestion_type.h"
 #import "components/autofill/core/browser/ui/payments/card_unmask_authentication_selection_dialog_controller_impl.h"
 #import "components/autofill/core/browser/ui/payments/virtual_card_enroll_ui_model.h"
@@ -547,10 +548,8 @@
               kCompleteCreditCardForm)) {
     return;
   }
-  if (manager.client()
-          .GetPersonalDataManager()
-          .payments_data_manager()
-          .GetCreditCardsToSuggest()
+  if (autofill::GetCreditCardsToSuggest(
+          manager.client().GetPersonalDataManager().payments_data_manager())
           .empty()) {
     return;
   }
diff --git a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
index 9b5cdfd..79d77ad6 100644
--- a/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/bottom_sheet/payments_suggestion_bottom_sheet_mediator.mm
@@ -14,6 +14,7 @@
 #import "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #import "components/autofill/core/browser/data_manager/personal_data_manager_observer.h"
 #import "components/autofill/core/browser/foundations/autofill_manager.h"
+#import "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #import "components/autofill/core/common/autofill_payments_features.h"
 #import "components/autofill/ios/browser/autofill_driver_ios.h"
 #import "components/autofill/ios/browser/credit_card_util.h"
@@ -214,8 +215,8 @@
     return;
   }
 
-  const auto& creditCards =
-      _personalDataManager->payments_data_manager().GetCreditCardsToSuggest();
+  const auto& creditCards = autofill::GetCreditCardsToSuggest(
+      _personalDataManager->payments_data_manager());
   if (creditCards.empty()) {
     [_consumer dismiss];
     return;
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_mediator.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_mediator.mm
index 9077cc98..05d736f 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_mediator.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_mediator.mm
@@ -13,6 +13,7 @@
 #import "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #import "components/autofill/core/browser/data_model/credit_card.h"
 #import "components/autofill/core/browser/foundations/browser_autofill_manager.h"
+#import "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
 #import "components/autofill/core/common/autofill_payments_features.h"
 #import "components/autofill/ios/browser/personal_data_manager_observer_bridge.h"
 #import "ios/chrome/browser/autofill/ui_bundled/manual_fill/card_consumer.h"
@@ -62,7 +63,8 @@
 std::vector<CreditCard> FetchCards(
     const autofill::PersonalDataManager& personal_data_manager) {
   std::vector<const CreditCard*> fetched_cards =
-      personal_data_manager.payments_data_manager().GetCreditCardsToSuggest();
+      autofill::GetCreditCardsToSuggest(
+          personal_data_manager.payments_data_manager());
   std::vector<CreditCard> cards;
   cards.reserve(fetched_cards.size());
 
diff --git a/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn
index 7f7f7488..c6984d5 100644
--- a/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/autofill/BUILD.gn
@@ -152,6 +152,7 @@
     "//base",
     "//components/autofill/core/browser:test_support",
     "//components/autofill/ios/common",
+    "//components/policy:policy_code_generate",
     "//components/strings",
     "//components/sync/base:features",
     "//ios/chrome/app/strings",
@@ -159,6 +160,7 @@
     "//ios/chrome/browser/autofill/ui_bundled:eg_test_support+eg2",
     "//ios/chrome/browser/autofill/ui_bundled/address_editor:constants",
     "//ios/chrome/browser/metrics/model:eg_test_support+eg2",
+    "//ios/chrome/browser/policy/model:eg_test_support+eg2",
     "//ios/chrome/browser/settings/ui_bundled:settings_root_constants",
     "//ios/chrome/browser/shared/ui/elements:eg_test_support+eg2",
     "//ios/chrome/browser/signin/model:fake_system_identity",
diff --git a/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_settings_egtest.mm b/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_settings_egtest.mm
index 8031730..8299840 100644
--- a/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_settings_egtest.mm
+++ b/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_settings_egtest.mm
@@ -8,11 +8,13 @@
 #import "base/test/ios/wait_util.h"
 #import "components/autofill/core/common/autofill_features.h"
 #import "components/autofill/ios/common/features.h"
+#import "components/policy/policy_constants.h"
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h"
 #import "ios/chrome/browser/autofill/ui_bundled/address_editor/autofill_constants.h"
 #import "ios/chrome/browser/autofill/ui_bundled/autofill_app_interface.h"
+#import "ios/chrome/browser/policy/model/policy_earl_grey_utils.h"
 #import "ios/chrome/browser/settings/ui_bundled/autofill/autofill_settings_constants.h"
 #import "ios/chrome/browser/settings/ui_bundled/settings_root_table_constants.h"
 #import "ios/chrome/browser/shared/ui/elements/activity_overlay_egtest_util.h"
@@ -35,6 +37,7 @@
 using chrome_test_util::SettingsMenuBackButton;
 using chrome_test_util::SettingsToolbarAddButton;
 using chrome_test_util::TabGridEditButton;
+using policy_test_utils::SetPolicy;
 
 namespace {
 
@@ -160,7 +163,9 @@
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config = [super appConfigurationForTestCase];
 
-  if ([self isRunningTest:@selector(testBottomToolbarAddButtonVisibility)]) {
+  if ([self isRunningTest:@selector(testBottomToolbarAddButtonVisibility)] ||
+      [self isRunningTest:@selector(testToggleToolbarAddButtonBySwitch)] ||
+      [self isRunningTest:@selector(testToggleToolbarAddButtonByPolicy)]) {
     config.features_enabled.push_back(kAddAddressManually);
     config.features_enabled.push_back(
         kAutofillDynamicallyLoadsFieldsForAddressInput);
@@ -352,6 +357,59 @@
       assertWithMatcher:grey_sufficientlyVisible()];
 }
 
+// Checks that the toolbar "Add" button's enabled state changes based on the
+// "Autofill profiles" switch.
+- (void)testToggleToolbarAddButtonBySwitch {
+  [AutofillAppInterface saveExampleProfile];
+  [self openAutofillProfilesSettings];
+
+  // Toggle the "Autofill profiles" switch off.
+  [[EarlGrey
+      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
+                                   kAutofillAddressSwitchViewId,
+                                   /*is_toggled_on=*/YES, /*is_enabled=*/YES)]
+      performAction:chrome_test_util::TurnTableViewSwitchOn(NO)];
+
+  // Verify the "Add" button is disabled.
+  [[EarlGrey selectElementWithMatcher:SettingsToolbarAddButton()]
+      assertWithMatcher:grey_not(grey_enabled())];
+
+  // Toggle the "Autofill profiles" switch back on.
+  [[EarlGrey
+      selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
+                                   kAutofillAddressSwitchViewId,
+                                   /*is_toggled_on=*/NO, /*is_enabled=*/YES)]
+      performAction:chrome_test_util::TurnTableViewSwitchOn(YES)];
+
+  // Verify the "Add" button is enabled.
+  [[EarlGrey selectElementWithMatcher:SettingsToolbarAddButton()]
+      assertWithMatcher:grey_enabled()];
+}
+
+// Checks that the toolbar "Add" button's enabled state changes based on the
+// AutofillAddressEnabled Enterprise Policy.
+- (void)testToggleToolbarAddButtonByPolicy {
+  // Force the preference off via policy.
+  SetPolicy(false, policy::key::kAutofillAddressEnabled);
+
+  [self openAutofillProfilesSettings];
+
+  // Verify the "Add" button is disabled.
+  [[EarlGrey selectElementWithMatcher:SettingsToolbarAddButton()]
+      assertWithMatcher:grey_not(grey_enabled())];
+
+  [self exitSettingsMenu];
+
+  // Force the preference on via policy.
+  SetPolicy(true, policy::key::kAutofillAddressEnabled);
+
+  [self openAutofillProfilesSettings];
+
+  // Verify the "Add" button is enabled.
+  [[EarlGrey selectElementWithMatcher:SettingsToolbarAddButton()]
+      assertWithMatcher:grey_enabled()];
+}
+
 // Checks that the Autofill profile switch can be toggled on/off and the list of
 // Autofill profiles is not affected by it.
 - (void)testToggleAutofillProfileSwitch {
diff --git a/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_table_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_table_view_controller.mm
index 462997d..79bc1edb 100644
--- a/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_table_view_controller.mm
+++ b/ios/chrome/browser/settings/ui_bundled/autofill/autofill_profile_table_view_controller.mm
@@ -572,9 +572,10 @@
 #pragma mark - Switch Callbacks
 
 - (void)autofillAddressSwitchChanged:(UISwitch*)switchView {
-  [self setSwitchItemOn:[switchView isOn]
-               itemType:ItemTypeAutofillAddressSwitch];
-  [self setAutofillProfileEnabled:[switchView isOn]];
+  BOOL switchOn = [switchView isOn];
+  [self setSwitchItemOn:switchOn itemType:ItemTypeAutofillAddressSwitch];
+  [self setAutofillProfileEnabled:switchOn];
+  _addButtonInToolbar.enabled = switchOn;
 }
 
 #pragma mark - Switch Helpers
@@ -659,9 +660,7 @@
   if (!_addButtonInToolbar) {
     _addButtonInToolbar =
         [self addButtonWithAction:@selector(handleAddAddress)];
-    _addButtonInToolbar.enabled = YES;
-    // TODO(crbug.com/395114433): Change the status of the Button based on
-    // Enterprise Policy and Save Address Toggle.
+    _addButtonInToolbar.enabled = [self isAutofillProfileEnabled];
   }
   return _addButtonInToolbar;
 }
diff --git a/ios_internal b/ios_internal
index 9518013..6444bdf 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 9518013ca7508d5b342ad76d9b3d91185c606312
+Subproject commit 6444bdf131517e4efff8b82981cdc601b2e92e47
diff --git a/media/cast/BUILD.gn b/media/cast/BUILD.gn
index 4222581..0fca432 100644
--- a/media/cast/BUILD.gn
+++ b/media/cast/BUILD.gn
@@ -98,7 +98,6 @@
 
   deps = [
     ":common",
-    "//third_party/libaom:libaom_buildflags",
     "//third_party/openscreen/src/cast/streaming:common",
     "//third_party/opus",
   ]
diff --git a/media/cast/encoding/encoding_support.cc b/media/cast/encoding/encoding_support.cc
index 1587555c..fc6bb09 100644
--- a/media/cast/encoding/encoding_support.cc
+++ b/media/cast/encoding/encoding_support.cc
@@ -12,7 +12,7 @@
 #include "media/base/media_switches.h"
 #include "media/base/video_codecs.h"
 #include "media/cast/encoding/external_video_encoder.h"
-#include "third_party/libaom/libaom_buildflags.h"
+#include "media/media_buildflags.h"
 
 namespace media::cast::encoding_support {
 namespace {
diff --git a/media/cast/encoding/media_video_encoder_wrapper.cc b/media/cast/encoding/media_video_encoder_wrapper.cc
index 5b12a6e..3e5d39d 100644
--- a/media/cast/encoding/media_video_encoder_wrapper.cc
+++ b/media/cast/encoding/media_video_encoder_wrapper.cc
@@ -24,11 +24,11 @@
 #include "media/cast/constants.h"
 #include "media/cast/encoding/fake_software_video_encoder.h"
 #include "media/cast/encoding/video_encoder.h"
+#include "media/media_buildflags.h"
 #include "media/video/gpu_video_accelerator_factories.h"
 #include "media/video/video_encode_accelerator_adapter.h"
 #include "media/video/video_encoder_info.h"
 #include "media_video_encoder_wrapper.h"
-#include "third_party/libaom/libaom_buildflags.h"
 
 #if BUILDFLAG(ENABLE_LIBVPX)
 #include "media/video/vpx_video_encoder.h"
diff --git a/media/cast/encoding/video_encoder_impl.cc b/media/cast/encoding/video_encoder_impl.cc
index 4a5c8034..4f5b23d 100644
--- a/media/cast/encoding/video_encoder_impl.cc
+++ b/media/cast/encoding/video_encoder_impl.cc
@@ -11,15 +11,16 @@
 #include "base/functional/callback.h"
 #include "base/functional/callback_helpers.h"
 #include "media/base/video_codecs.h"
-#include "media/base/video_frame.h"
-#include "third_party/libaom/libaom_buildflags.h"
-#if BUILDFLAG(ENABLE_LIBAOM)
-#include "media/cast/encoding/av1_encoder.h"
-#endif
 #include "media/base/video_encoder_metrics_provider.h"
+#include "media/base/video_frame.h"
 #include "media/cast/common/sender_encoded_frame.h"
 #include "media/cast/encoding/fake_software_video_encoder.h"
 #include "media/cast/encoding/vpx_encoder.h"
+#include "media/media_buildflags.h"
+
+#if BUILDFLAG(ENABLE_LIBAOM)
+#include "media/cast/encoding/av1_encoder.h"
+#endif
 
 namespace media {
 namespace cast {
diff --git a/remoting/host/desktop_display_info_loader_win.cc b/remoting/host/desktop_display_info_loader_win.cc
index 90a875b..df88461 100644
--- a/remoting/host/desktop_display_info_loader_win.cc
+++ b/remoting/host/desktop_display_info_loader_win.cc
@@ -48,6 +48,15 @@
       is_default = true;
     }
 
+    // Make a second call to get the monitor name for the device.
+    DISPLAY_DEVICE monitor = {};
+    monitor.cb = sizeof(monitor);
+    std::string monitor_name;
+    if (EnumDisplayDevices(device.DeviceName, 0, &monitor, 0)) {
+      // Call succeeded, DeviceString should contain the monitor name.
+      monitor_name = base::WideToUTF8(monitor.DeviceString);
+    }
+
     // Get additional info about device.
     DEVMODE devmode;
     devmode.dmSize = sizeof(devmode);
@@ -59,7 +68,7 @@
     displays.emplace_back(
         /* id */ device_index, x, y, devmode.dmPelsWidth, devmode.dmPelsHeight,
         /* dpi */ devmode.dmLogPixels, devmode.dmBitsPerPel, is_default,
-        base::WideToUTF8(devmode.dmDeviceName));
+        monitor_name);
 
     lowest_x = std::min(x, lowest_x);
     lowest_y = std::min(y, lowest_y);
diff --git a/services/video_effects/calculators/background_blur_calculator_webgpu.cc b/services/video_effects/calculators/background_blur_calculator_webgpu.cc
index 521be0b..80e10bc 100644
--- a/services/video_effects/calculators/background_blur_calculator_webgpu.cc
+++ b/services/video_effects/calculators/background_blur_calculator_webgpu.cc
@@ -312,21 +312,52 @@
 
   CHECK(compute_pipeline_);
 
+  mediapipe::Packet& config_packet = cc->Inputs().Index(0).Value();
+  if (config_packet.IsEmpty()) {
+    return absl::InternalError("Runtime configuration not present!");
+  }
+
   mediapipe::Packet& input_packet = cc->Inputs().Index(1).Value();
   auto input_buffer = input_packet.Get<mediapipe::GpuBuffer>();
   auto input_texture_view =
       input_buffer.GetReadView<mediapipe::WebGpuTextureView>();
 
-  mediapipe::Packet& mask_packet = cc->Inputs().Index(2).Value();
-  auto mask_buffer = mask_packet.Get<mediapipe::GpuBuffer>();
-  auto mask_texture_view =
-      mask_buffer.GetReadView<mediapipe::WebGpuTextureView>();
-
   mediapipe::Packet& output_packet = cc->Inputs().Index(3).Value();
   auto output_buffer = output_packet.Get<mediapipe::GpuBuffer>();
   auto output_texture_view =
       output_buffer.GetWriteView<mediapipe::WebGpuTextureView>();
 
+  if (config_packet.Get<RuntimeConfig>().blur_state != BlurState::kEnabled) {
+    wgpu::CommandEncoder command_encoder = device.CreateCommandEncoder();
+    wgpu::ImageCopyTexture source = {.texture = input_texture_view.texture()};
+    wgpu::ImageCopyTexture destination = {.texture =
+                                              output_texture_view.texture()};
+    wgpu::Extent3D extent = {
+        .width = static_cast<uint32_t>(input_buffer.width()),
+        .height = static_cast<uint32_t>(input_buffer.height()),
+        .depthOrArrayLayers = 1};
+    command_encoder.CopyTextureToTexture(&source, &destination, &extent);
+    wgpu::CommandBufferDescriptor command_buffer_descriptor = {
+        .label = "BackgroundBlurCalculatorWebGpuCommandBufferJustCopy",
+    };
+    wgpu::CommandBuffer command_buffer =
+        command_encoder.Finish(&command_buffer_descriptor);
+    device.GetQueue().Submit(1, &command_buffer);
+    cc->Outputs().Index(0).AddPacket(output_packet);
+    return absl::OkStatus();
+  }
+
+  // Mask can only be accessed if the previous calculator produced it, and it
+  // should have done so iff runtime config was enabled:
+  mediapipe::Packet& mask_packet = cc->Inputs().Index(2).Value();
+  if (mask_packet.IsEmpty()) {
+    return absl::InternalError(
+        "Mask is not present even though blur is enabled!");
+  }
+  auto mask_buffer = mask_packet.Get<mediapipe::GpuBuffer>();
+  auto mask_texture_view =
+      mask_buffer.GetReadView<mediapipe::WebGpuTextureView>();
+
   std::vector<wgpu::BindGroupEntry> bind_group_entries;
   bind_group_entries.push_back({
       .binding = 0,
diff --git a/services/video_effects/calculators/inference_calculator_webgpu.cc b/services/video_effects/calculators/inference_calculator_webgpu.cc
index 66d792732..1f6e4d5 100644
--- a/services/video_effects/calculators/inference_calculator_webgpu.cc
+++ b/services/video_effects/calculators/inference_calculator_webgpu.cc
@@ -110,15 +110,16 @@
   CHECK(ml_api);
 
   mediapipe::Packet& config_packet = cc->Inputs().Index(0).Value();
+  if (config_packet.IsEmpty()) {
+    return absl::InternalError("Runtime configuration not present!");
+  }
 
   mediapipe::Packet& input_frame = cc->Inputs().Index(1).Value();
   mediapipe::GpuBuffer input_buffer = input_frame.Get<mediapipe::GpuBuffer>();
   auto input_buffer_view =
       input_buffer.GetReadView<mediapipe::WebGpuTextureView>();
 
-  if (!config_packet.IsEmpty() &&
-      config_packet.Get<RuntimeConfig>().blur_state == BlurState::kEnabled) {
-    // The model we use assumes 256x144 buffers.
+  if (config_packet.Get<RuntimeConfig>().blur_state == BlurState::kEnabled) {
     mediapipe::GpuBuffer output_buffer(kBufferWidth, kBufferHeight,
                                        kBufferFormat);
     auto output_buffer_view =
diff --git a/services/video_effects/video_effects_processor_impl.cc b/services/video_effects/video_effects_processor_impl.cc
index ede6374..66767fe2 100644
--- a/services/video_effects/video_effects_processor_impl.cc
+++ b/services/video_effects/video_effects_processor_impl.cc
@@ -13,6 +13,7 @@
 #include "gpu/ipc/client/client_shared_image_interface.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "services/video_effects/calculators/video_effects_graph_config.h"
 #include "services/video_effects/public/mojom/video_effects_processor.mojom.h"
 #include "services/video_effects/video_effects_processor_webgpu.h"
 #include "services/video_effects/video_effects_service_impl.h"
@@ -30,6 +31,7 @@
     : device_(device),
       manager_remote_(std::move(manager_remote)),
       processor_receiver_(this, std::move(processor_receiver)),
+      configuration_observer_receiver_(this),
       gpu_channel_host_provider_(gpu_channel_host_provider),
       on_unrecoverable_error_(std::move(on_unrecoverable_error)) {
   CHECK(gpu_channel_host_provider_);
@@ -120,7 +122,13 @@
 void VideoEffectsProcessorImpl::OnConfigurationChanged(
     media::mojom::VideoEffectsConfigurationPtr configuration) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // TODO(b/374149033): save configuration, to be passed to `processor_webgpu_`.
+
+  // We've seen a config, so let's make sure `runtime_config_` isn't nullopt.
+  // Existence or absence of `configuration->blur` controls whether blur effect
+  // is enabled or not:
+  runtime_config_.emplace(RuntimeConfig{
+      .blur_state =
+          !!configuration->blur ? BlurState::kEnabled : BlurState::kDisabled});
 }
 
 void VideoEffectsProcessorImpl::OnMojoDisconnected() {
@@ -150,15 +158,16 @@
     return;
   }
 
-  if (!initialized_) {
+  if (!initialized_ || !runtime_config_.has_value()) {
     std::move(callback).Run(
         mojom::PostProcessResult::NewError(mojom::PostProcessError::kNotReady));
     return;
   }
 
-  processor_webgpu_->PostProcess(
-      std::move(input_frame_data), std::move(input_frame_info),
-      std::move(result_frame_data), result_pixel_format, std::move(callback));
+  processor_webgpu_->PostProcess(*runtime_config_, std::move(input_frame_data),
+                                 std::move(input_frame_info),
+                                 std::move(result_frame_data),
+                                 result_pixel_format, std::move(callback));
 }
 
 void VideoEffectsProcessorImpl::MaybeCallOnUnrecoverableError() {
diff --git a/services/video_effects/video_effects_processor_impl.h b/services/video_effects/video_effects_processor_impl.h
index 1003fdb..e5bcc32 100644
--- a/services/video_effects/video_effects_processor_impl.h
+++ b/services/video_effects/video_effects_processor_impl.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_VIDEO_EFFECTS_VIDEO_EFFECTS_PROCESSOR_IMPL_H_
 #define SERVICES_VIDEO_EFFECTS_VIDEO_EFFECTS_PROCESSOR_IMPL_H_
 
+#include <optional>
 #include <vector>
 
 #include "base/functional/callback_forward.h"
@@ -19,6 +20,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "services/video_effects/calculators/video_effects_graph_config.h"
 #include "services/video_effects/gpu_channel_host_provider.h"
 #include "services/video_effects/public/mojom/video_effects_processor.mojom.h"
 #include "services/video_effects/video_effects_processor_webgpu.h"
@@ -69,7 +71,7 @@
   void OnContextLost(scoped_refptr<GpuChannelHostProvider>) override;
   void OnPermanentError(scoped_refptr<GpuChannelHostProvider>) override;
 
-  // media::mojom::VideoEffectsConfigurationObserver impl.
+  // media::mojom::VideoEffectsConfigurationObserver:
   void OnConfigurationChanged(
       media::mojom::VideoEffectsConfigurationPtr configuration) override;
 
@@ -93,6 +95,8 @@
   mojo::Receiver<media::mojom::VideoEffectsConfigurationObserver>
       configuration_observer_{this};
   mojo::Receiver<mojom::VideoEffectsProcessor> processor_receiver_;
+  mojo::Receiver<media::mojom::VideoEffectsConfigurationObserver>
+      configuration_observer_receiver_;
 
   scoped_refptr<GpuChannelHostProvider> gpu_channel_host_provider_;
 
@@ -107,6 +111,9 @@
 
   std::unique_ptr<VideoEffectsProcessorWebGpu> processor_webgpu_;
 
+  // Most recently seen runtime config.
+  std::optional<RuntimeConfig> runtime_config_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Must be last:
diff --git a/services/video_effects/video_effects_processor_webgpu.cc b/services/video_effects/video_effects_processor_webgpu.cc
index c0b1018..0cb1d77 100644
--- a/services/video_effects/video_effects_processor_webgpu.cc
+++ b/services/video_effects/video_effects_processor_webgpu.cc
@@ -305,6 +305,7 @@
 // to make it more clear which part of the code corresponds to which step from
 // the diagram.
 void VideoEffectsProcessorWebGpu::PostProcess(
+    const RuntimeConfig& runtime_config,
     media::mojom::VideoBufferHandlePtr input_frame_data,
     media::mojom::VideoFrameInfoPtr input_frame_info,
     media::mojom::VideoBufferHandlePtr result_frame_data,
@@ -380,7 +381,7 @@
   // ImportSI(s1)
   scoped_refptr<gpu::ClientSharedImage> in_plane =
       shared_image_interface_->ImportSharedImage(
-          input_frame_data->get_shared_image_handle()->shared_image);
+          std::move(input_frame_data->get_shared_image_handle()->shared_image));
   CHECK(in_plane);
   // s3=CreateSI()
   auto in_image =
@@ -398,7 +399,7 @@
       result_frame_data->get_shared_image_handle()->sync_token);
   // ImportSI(s2)
   auto out_plane = shared_image_interface_->ImportSharedImage(
-      result_frame_data->get_shared_image_handle()->shared_image);
+      std::move(result_frame_data->get_shared_image_handle()->shared_image));
   CHECK(out_plane);
   // s4=CreateSI()
   auto out_image =
@@ -543,10 +544,9 @@
   // that `ProcessFrame()` returned does not mean that the frame was already
   // processed - just that the appropriate commands have been scheduled to run
   // on the GPU. Same for `WaitUntilIdle()`.
-  RuntimeConfig config = {.blur_state = BlurState::kEnabled};
   CHECK(graph_->ProcessFrame(input_frame_info->timestamp, in_texture,
                              in_texture_downscaled_float32, out_texture,
-                             config));
+                             runtime_config));
   // We can block since all the graph does is it schedules more work on the GPU.
   // This itself should be near-instant, so the call won't block for long.
   CHECK(graph_->WaitUntilIdle());
diff --git a/services/video_effects/video_effects_processor_webgpu.h b/services/video_effects/video_effects_processor_webgpu.h
index c5ab609..56b5ca4 100644
--- a/services/video_effects/video_effects_processor_webgpu.h
+++ b/services/video_effects/video_effects_processor_webgpu.h
@@ -11,6 +11,7 @@
 #include "base/sequence_checker.h"
 #include "components/viz/common/gpu/raster_context_provider.h"
 #include "media/capture/mojom/video_capture_buffer.mojom-forward.h"
+#include "services/video_effects/calculators/video_effects_graph_config.h"
 #include "services/video_effects/calculators/video_effects_graph_webgpu.h"
 #include "services/video_effects/public/mojom/video_effects_processor.mojom.h"
 #include "third_party/dawn/include/dawn/webgpu_cpp.h"
@@ -46,6 +47,7 @@
   bool Initialize();
 
   void PostProcess(
+      const RuntimeConfig& runtime_config,
       media::mojom::VideoBufferHandlePtr input_frame_data,
       media::mojom::VideoFrameInfoPtr input_frame_info,
       media::mojom::VideoBufferHandlePtr result_frame_data,
diff --git a/services/video_effects/video_effects_service_impl.h b/services/video_effects/video_effects_service_impl.h
index f39cc05..c941b38 100644
--- a/services/video_effects/video_effects_service_impl.h
+++ b/services/video_effects/video_effects_service_impl.h
@@ -33,12 +33,6 @@
 class VideoEffectsServiceImpl : public mojom::VideoEffectsService,
                                 public GpuChannelHostProvider::Observer {
  public:
-  // Similarly to `VideoCaptureServiceImpl`, `VideoEfffectsServiceImpl` needs
-  // to receive something that returns `gpu::GpuChannelHost` instances in order
-  // to be able to communicate with the GPU service - this is passed in via the
-  // `gpu_channel_host_provider`.
-  // `receiver` is the receiving end of the mojo pipe used to communicate with
-  // this instance.
   explicit VideoEffectsServiceImpl(
       mojo::PendingReceiver<mojom::VideoEffectsService> receiver,
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
diff --git a/services/webnn/tflite/op_resolver.cc b/services/webnn/tflite/op_resolver.cc
index 3c5da92d..5ab4dd6 100644
--- a/services/webnn/tflite/op_resolver.cc
+++ b/services/webnn/tflite/op_resolver.cc
@@ -4,6 +4,7 @@
 
 #include "services/webnn/tflite/op_resolver.h"
 
+#include "services/webnn/buildflags.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
 #include "third_party/tflite/buildflags.h"
 #include "third_party/tflite/src/tensorflow/lite/kernels/builtin_op_kernels.h"
@@ -17,7 +18,7 @@
 #include "third_party/tflite/src/tensorflow/lite/tflite_with_xnnpack_optional.h"
 #endif
 
-#if defined(WEBNN_USE_CHROME_ML_API)
+#if BUILDFLAG(WEBNN_USE_CHROME_ML_API)
 #include "services/on_device_model/ml/chrome_ml.h"  // nogncheck
 #include "services/on_device_model/ml/chrome_ml_api.h"  // nogncheck
 #endif
@@ -289,7 +290,7 @@
   }
 #endif
 
-#if defined(WEBNN_USE_CHROME_ML_API)
+#if BUILDFLAG(WEBNN_USE_CHROME_ML_API)
   if (options.device == mojom::CreateContextOptions::Device::kGpu) {
     // TODO(crbug.com/394119734): Simplify this check once these functions are
     // always available.
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 0e75885..7e2669eb 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -465,6 +465,17 @@
       },
     },
   },
+  'linux_amd_rx_7600_stable': {
+    'fail_if_unused': False,
+    'swarming': {
+      'dimensions': {
+        'gpu': '1002:7480-24.2.8',
+        'os': 'Ubuntu-24.04',
+        'display_attached': '1',
+        'pool': 'chromium.tests.gpu',
+      },
+    },
+  },
   'linux_intel_uhd_630_experimental': {
     'fail_if_unused': False,
     'swarming': {
@@ -863,6 +874,17 @@
       },
     },
   },
+  'win11_amd_rx_7600_stable': {
+    'fail_if_unused': False,
+    'swarming': {
+      'dimensions': {
+        'display_attached': '1',
+        'gpu': '1002:7480-31.0.24033.1003',
+        'os': 'Windows-11-26100',
+        'pool': 'chromium.tests.gpu',
+      },
+    },
+  },
   'win11_nvidia_rtx_4070_super_stable': {
     'fail_if_unused': False,
     'swarming': {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 453f92e6..79af3f11 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -7011,11 +7011,31 @@
             ],
             "experiments": [
                 {
-                    "name": "EnabledGroupA",
+                    "name": "EnabledGroupA_20250123",
                     "params": {
                         "multiplier": "1.5"
                     },
                     "enable_features": [
+                        "CrOSLateBootSwapZramDisksize",
+                        "SkipDiscardDrivenByStaleSignal"
+                    ]
+                },
+                {
+                    "name": "EnabledGroupB_20250123",
+                    "params": {
+                        "multiplier": "1.25"
+                    },
+                    "enable_features": [
+                        "CrOSLateBootSwapZramDisksize",
+                        "SkipDiscardDrivenByStaleSignal"
+                    ]
+                },
+                {
+                    "name": "EnabledSkipDiscard_20250123",
+                    "enable_features": [
+                        "SkipDiscardDrivenByStaleSignal"
+                    ],
+                    "disable_features": [
                         "CrOSLateBootSwapZramDisksize"
                     ]
                 }
@@ -11503,30 +11523,6 @@
             ]
         }
     ],
-    "HttpsFirstModeV2ForEngagedSites": [
-        {
-            "platforms": [
-                "windows",
-                "chromeos",
-                "mac",
-                "linux"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "http-add-threshold": "1",
-                        "http-remove-threshold": "5",
-                        "https-add-threshold": "80",
-                        "https-remove-threshold": "75"
-                    },
-                    "enable_features": [
-                        "HttpsFirstModeV2ForEngagedSites"
-                    ]
-                }
-            ]
-        }
-    ],
     "HttpsFirstModeV2ForTypicallySecureUsers": [
         {
             "platforms": [
@@ -18909,6 +18905,21 @@
             ]
         }
     ],
+    "QuickShareV2": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "QuickShareV2"
+                    ]
+                }
+            ]
+        }
+    ],
     "RTCAlignReceivedEncodedVideoTransforms": [
         {
             "platforms": [
@@ -22527,6 +22538,7 @@
         {
             "platforms": [
                 "android",
+                "android_webview",
                 "chromeos",
                 "fuchsia",
                 "linux",
diff --git a/third_party/angle b/third_party/angle
index 8dda514..bc40362 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 8dda514cb833c0c8392a4521f6d15454a053ebf7
+Subproject commit bc40362b76d834549424022530a5e11933ff4862
diff --git a/third_party/blink/renderer/core/css/build.gni b/third_party/blink/renderer/core/css/build.gni
index c24a8d9a..3e60825 100644
--- a/third_party/blink/renderer/core/css/build.gni
+++ b/third_party/blink/renderer/core/css/build.gni
@@ -485,6 +485,7 @@
   "font_size_functions.cc",
   "font_size_functions.h",
   "hash_tools.h",
+  "if_test.h",
   "inline_css_style_declaration.cc",
   "inline_css_style_declaration.h",
   "invalidation/invalidation_flags.cc",
@@ -559,6 +560,8 @@
   "parser/container_query_parser.h",
   "parser/css_at_rule_id.cc",
   "parser/css_at_rule_id.h",
+  "parser/css_if_parser.cc",
+  "parser/css_if_parser.h",
   "parser/css_lazy_parsing_state.cc",
   "parser/css_lazy_parsing_state.h",
   "parser/css_lazy_property_parser.cc",
@@ -878,6 +881,7 @@
   "parser/allowed_rules_test.cc",
   "parser/at_rule_descriptor_parser_test.cc",
   "parser/container_query_parser_test.cc",
+  "parser/css_if_parser_test.cc",
   "parser/css_lazy_parsing_test.cc",
   "parser/css_parser_fast_paths_test.cc",
   "parser/css_parser_impl_test.cc",
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index cc718b5d..956119bc 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -1997,5 +1997,6 @@
     // inline conditional
     "if",
     "else",
+    "media",
   ],
 }
diff --git a/third_party/blink/renderer/core/css/if_test.h b/third_party/blink/renderer/core/css/if_test.h
new file mode 100644
index 0000000..c912d08
--- /dev/null
+++ b/third_party/blink/renderer/core/css/if_test.h
@@ -0,0 +1,34 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_IF_TEST_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_IF_TEST_H_
+
+#include "third_party/blink/renderer/core/css/media_query.h"
+#include "third_party/blink/renderer/core/css/media_query_exp.h"
+
+namespace blink {
+
+// https://drafts.csswg.org/css-values-5/#typedef-if-test
+class IfTest {
+  STACK_ALLOCATED();
+
+ public:
+  explicit IfTest(const MediaQueryExpNode* style_test)
+      : style_test_(style_test), media_test_(nullptr) {}
+  explicit IfTest(const MediaQuery* media_test)
+      : style_test_(nullptr), media_test_(media_test) {}
+
+  const MediaQueryExpNode* GetStyleTest() { return style_test_; }
+
+  const MediaQuery* GetMediaTest() { return media_test_; }
+
+ private:
+  const MediaQueryExpNode* style_test_;
+  const MediaQuery* media_test_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_IF_TEST_H_
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser.cc b/third_party/blink/renderer/core/css/parser/container_query_parser.cc
index a041ebf8..277d7db 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser.cc
@@ -86,25 +86,6 @@
   bool SupportsRange() const override { return true; }
 };
 
-class StyleFeatureSet : public MediaQueryParser::FeatureSet {
-  STACK_ALLOCATED();
-
- public:
-  bool IsAllowed(const AtomicString& feature) const override {
-    // TODO(crbug.com/1302630): Only support querying custom properties for now.
-    return CSSVariableParser::IsValidVariableName(feature);
-  }
-  bool IsAllowedWithoutValue(const AtomicString& feature,
-                             const ExecutionContext*) const override {
-    return true;
-  }
-  bool IsCaseSensitive(const AtomicString& feature) const override {
-    // TODO(crbug.com/1302630): non-custom properties are case-insensitive.
-    return true;
-  }
-  bool SupportsRange() const override { return false; }
-};
-
 class StateFeatureSet : public MediaQueryParser::FeatureSet {
   STACK_ALLOCATED();
 
@@ -229,29 +210,6 @@
       stream);
 }
 
-// <if-test> = style( <style-query> )
-// <style-query>     = not <style-in-parens>
-//                   | <style-in-parens> [ [ and <style-in-parens> ]* | [ or
-//                   <style-in-parens> ]* ] | <style-feature>
-// <style-in-parens> = ( <style-query> )
-//                   | ( <style-feature> )
-//                   | <general-enclosed>
-const MediaQueryExpNode* ContainerQueryParser::ConsumeIfTest(
-    CSSParserTokenStream& stream) {
-  if (stream.Peek().GetType() == kFunctionToken &&
-      stream.Peek().FunctionId() == CSSValueID::kStyle) {
-    CSSParserTokenStream::RestoringBlockGuard guard(stream);
-    stream.ConsumeWhitespace();
-    if (const MediaQueryExpNode* query =
-            ConsumeFeatureQuery(stream, StyleFeatureSet())) {
-      guard.Release();
-      stream.ConsumeWhitespace();
-      return MediaQueryExpNode::Function(query, AtomicString("style"));
-    }
-  }
-  return nullptr;
-}
-
 const MediaQueryExpNode* ContainerQueryParser::ConsumeFeatureQuery(
     CSSParserTokenStream& stream,
     const FeatureSet& feature_set) {
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser.h b/third_party/blink/renderer/core/css/parser/container_query_parser.h
index 73d338d..84484a0 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser.h
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser.h
@@ -5,16 +5,16 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_CONTAINER_QUERY_PARSER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_CONTAINER_QUERY_PARSER_H_
 
-#include <optional>
-
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/container_query.h"
 #include "third_party/blink/renderer/core/css/media_query_exp.h"
+#include "third_party/blink/renderer/core/css/parser/css_variable_parser.h"
 #include "third_party/blink/renderer/core/css/parser/media_query_parser.h"
 
 namespace blink {
 
 class CSSParserContext;
+class CSSIfParser;
 
 class CORE_EXPORT ContainerQueryParser {
   STACK_ALLOCATED();
@@ -26,12 +26,29 @@
   const MediaQueryExpNode* ParseCondition(String);
   const MediaQueryExpNode* ParseCondition(CSSParserTokenStream&);
 
-  // Used for inline if() function. Supports only style() queries for now.
-  // https://drafts.csswg.org/css-values-5/#if-notation
-  const MediaQueryExpNode* ConsumeIfTest(CSSParserTokenStream&);
+  class StyleFeatureSet : public MediaQueryParser::FeatureSet {
+    STACK_ALLOCATED();
+
+   public:
+    bool IsAllowed(const AtomicString& feature) const override {
+      // TODO(crbug.com/40217044): Only support querying custom properties for
+      // now.
+      return CSSVariableParser::IsValidVariableName(feature);
+    }
+    bool IsAllowedWithoutValue(const AtomicString& feature,
+                               const ExecutionContext*) const override {
+      return true;
+    }
+    bool IsCaseSensitive(const AtomicString& feature) const override {
+      // TODO(crbug.com/40217044): non-custom properties are case-insensitive.
+      return true;
+    }
+    bool SupportsRange() const override { return false; }
+  };
 
  private:
   friend class ContainerQueryParserTest;
+  friend class CSSIfParser;
 
   using FeatureSet = MediaQueryParser::FeatureSet;
 
diff --git a/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc b/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
index 9a8b61e..c1769c1 100644
--- a/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/container_query_parser_test.cc
@@ -117,60 +117,6 @@
             ParseQuery("size(width)", container_query_parser_func));
 }
 
-TEST_F(ContainerQueryParserTest, ParseStyleQuery) {
-  auto container_query_parser_func = [](String string,
-                                        ContainerQueryParser& parser) {
-    CSSParserTokenStream stream(string);
-    return parser.ConsumeIfTest(stream);
-  };
-  const char* valid_tests[] = {
-      // clang-format off
-    "style(--x)",
-    "style(((--x)))",
-     "style((--x) and (--y: 10))",
-     "style((--x) and ((--y) or (not (--z))))",
-    "style(not (--x))",
-    "style(--x: var(--y))",
-    "style((--y: green) and (--x: 3))",
-    "style(((--x: 3px) and (--y: 3)) or (not (--z: 6px)))",
-      // clang-format on
-  };
-
-  for (const char* test : valid_tests) {
-    EXPECT_EQ(String(test), ParseQuery(test, container_query_parser_func));
-  }
-
-  const char* invalid_parse_time_tests[] = {
-      // clang-format off
-    "(min-width: 100px)",
-    "not (width)",
-    "(width) and (height)",
-    "(((style(--x))))",
-    "not style(--x)",
-    "style(width)",
-    "style(invalid)",
-    "(style(--x: 3px) and style(--y: 3)) or (not style(--z: 6px))",
-      // clang-format on
-  };
-
-  for (const char* test : invalid_parse_time_tests) {
-    EXPECT_FALSE(ParseQuery(test, container_query_parser_func));
-  }
-
-  const char* invalid_tests[] = {
-      // clang-format off
-    "style(style(--x))",
-    "style(var(--x))",
-    "style(attr(data-foo))",
-    "style(var(--x): green) and style(var(--x): 3)",
-      // clang-format on
-  };
-
-  for (const char* test : invalid_tests) {
-    EXPECT_EQ("<unknown>", ParseQuery(test, container_query_parser_func));
-  }
-}
-
 // This test exists primarily to not lose coverage of
 // `ContainerQueryParser::ConsumeFeatureQuery`, which is unused until
 // style() queries are supported (crbug.com/1302630).
diff --git a/third_party/blink/renderer/core/css/parser/css_if_parser.cc b/third_party/blink/renderer/core/css/parser/css_if_parser.cc
new file mode 100644
index 0000000..0135143
--- /dev/null
+++ b/third_party/blink/renderer/core/css/parser/css_if_parser.cc
@@ -0,0 +1,46 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/parser/css_if_parser.h"
+
+#include "third_party/blink/renderer/core/css/if_test.h"
+#include "third_party/blink/renderer/core/css/parser/container_query_parser.h"
+
+namespace blink {
+
+CSSIfParser::CSSIfParser(const CSSParserContext& context)
+    : container_query_parser_(ContainerQueryParser(context)),
+      media_query_parser_(MediaQueryParser::kMediaQuerySetParser,
+                          kHTMLStandardMode,
+                          context.GetExecutionContext(),
+                          MediaQueryParser::SyntaxLevel::kLevel4) {}
+
+// <if-test> = media( <media-query> ) | style( <style-query> )
+std::optional<IfTest> CSSIfParser::ConsumeIfTest(CSSParserTokenStream& stream) {
+  if (stream.Peek().GetType() == kFunctionToken &&
+      stream.Peek().FunctionId() == CSSValueID::kStyle) {
+    CSSParserTokenStream::RestoringBlockGuard guard(stream);
+    stream.ConsumeWhitespace();
+    if (const MediaQueryExpNode* query =
+            container_query_parser_.ConsumeFeatureQuery(
+                stream, ContainerQueryParser::StyleFeatureSet())) {
+      guard.Release();
+      stream.ConsumeWhitespace();
+      return IfTest(MediaQueryExpNode::Function(query, AtomicString("style")));
+    }
+  }
+  if (stream.Peek().GetType() == kFunctionToken &&
+      stream.Peek().FunctionId() == CSSValueID::kMedia) {
+    CSSParserTokenStream::RestoringBlockGuard guard(stream);
+    stream.ConsumeWhitespace();
+    if (const MediaQuery* query = media_query_parser_.ConsumeQuery(stream)) {
+      guard.Release();
+      stream.ConsumeWhitespace();
+      return IfTest(query);
+    }
+  }
+  return std::nullopt;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/css_if_parser.h b/third_party/blink/renderer/core/css/parser/css_if_parser.h
new file mode 100644
index 0000000..0336d041
--- /dev/null
+++ b/third_party/blink/renderer/core/css/parser/css_if_parser.h
@@ -0,0 +1,35 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_CSS_IF_PARSER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_CSS_IF_PARSER_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/if_test.h"
+#include "third_party/blink/renderer/core/css/media_query_exp.h"
+#include "third_party/blink/renderer/core/css/parser/container_query_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
+#include "third_party/blink/renderer/core/css/parser/media_query_parser.h"
+
+namespace blink {
+
+class CORE_EXPORT CSSIfParser {
+  STACK_ALLOCATED();
+
+ public:
+  explicit CSSIfParser(const CSSParserContext&);
+
+  // Used for inline if() function. Supports only style() and media() queries in
+  // condition for now. https://drafts.csswg.org/css-values-5/#if-notation
+  std::optional<IfTest> ConsumeIfTest(CSSParserTokenStream&);
+
+ private:
+  ContainerQueryParser container_query_parser_;
+  MediaQueryParser media_query_parser_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_CSS_IF_PARSER_H_
diff --git a/third_party/blink/renderer/core/css/parser/css_if_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_if_parser_test.cc
new file mode 100644
index 0000000..670453a0
--- /dev/null
+++ b/third_party/blink/renderer/core/css/parser/css_if_parser_test.cc
@@ -0,0 +1,112 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/css/parser/css_if_parser.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
+#include "third_party/blink/renderer/core/css/parser/css_parser_token_stream.h"
+#include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
+#include "third_party/blink/renderer/core/testing/page_test_base.h"
+#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+enum ParseResult { kParsed, kUnknown, kInvalid };
+
+class CSSIfParserTest : public PageTestBase {
+ public:
+  ParseResult ParseQuery(String string) {
+    const auto* context = MakeGarbageCollected<CSSParserContext>(GetDocument());
+    CSSIfParser parser(*context);
+    CSSParserTokenStream stream(string);
+    std::optional<IfTest> if_test = parser.ConsumeIfTest(stream);
+    if (!if_test.has_value()) {
+      return ParseResult::kInvalid;
+    }
+
+    if (const MediaQueryExpNode* style_test = if_test->GetStyleTest()) {
+      if (style_test->HasUnknown()) {
+        return ParseResult::kUnknown;
+      }
+      return ParseResult::kParsed;
+    }
+
+    if (const MediaQuery* media_test = if_test->GetMediaTest()) {
+      if (media_test->HasUnknown()) {
+        return ParseResult::kUnknown;
+      }
+      return ParseResult::kParsed;
+    }
+    return ParseResult::kInvalid;
+  }
+};
+
+TEST_F(CSSIfParserTest, ConsumeValidCondition) {
+  ScopedCSSInlineIfForStyleQueriesForTest scoped_style_feature(true);
+  ScopedCSSInlineIfForMediaQueriesForTest scoped_media_feature(true);
+  const char* valid_known_tests[] = {
+      // clang-format off
+    "style(--x)",
+    "style(((--x)))",
+    "style((--x) and (--y: 10))",
+    "style((--x) and ((--y) or (not (--z))))",
+    "style(not (--x))",
+    "style(--x: var(--y))",
+    "style((--y: green) and (--x: 3))",
+    "style(((--x: 3px) and (--y: 3)) or (not (--z: 6px)))",
+    "media(screen)",
+    "media(screen and (color))",
+    "media(all and (min-width:500px))",
+    "media((min-width : 500px))",
+    "media(not (min-width : -100px))",
+    "media(only screen and (color))",
+    "media((min-width: 30em) and (max-width: 50em))"
+      // clang-format on
+  };
+
+  for (const char* test : valid_known_tests) {
+    EXPECT_EQ(ParseQuery(test), ParseResult::kParsed);
+  }
+}
+
+TEST_F(CSSIfParserTest, ConsumeUnknownCondition) {
+  const char* valid_unknown_tests[] = {
+      // clang-format off
+    "style(style(--x))"
+    "style(var(--x))"
+    "style(attr(data-foo))"
+    "style(var(--x): green) and style(var(--x): 3)"
+      // clang-format on
+  };
+
+  for (const char* test : valid_unknown_tests) {
+    EXPECT_EQ(ParseQuery(test), ParseResult::kUnknown);
+  }
+}
+
+TEST_F(CSSIfParserTest, ConsumeInvalidCondition) {
+  ScopedCSSInlineIfForStyleQueriesForTest scoped_style_feature(true);
+  ScopedCSSInlineIfForMediaQueriesForTest scoped_media_feature(true);
+  const char* invalid_parse_time_tests[] = {
+      // clang-format off
+    "(min-width: 100px)",
+    "not (width)",
+    "(width) and (height)",
+    "(((style(--x))))",
+    "not style(--x)",
+    "style(width)",
+    "style(invalid)",
+    "(style(--x: 3px) and style(--y: 3)) or (not style(--z: 6px))",
+      // clang-format on
+  };
+
+  for (const char* test : invalid_parse_time_tests) {
+    EXPECT_EQ(ParseQuery(test), ParseResult::kInvalid);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/css_variable_parser.cc b/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
index d8a7bd0d..4854276 100644
--- a/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_variable_parser.cc
@@ -11,7 +11,8 @@
 #include "third_party/blink/renderer/core/css/css_syntax_component.h"
 #include "third_party/blink/renderer/core/css/css_syntax_definition.h"
 #include "third_party/blink/renderer/core/css/css_unparsed_declaration_value.h"
-#include "third_party/blink/renderer/core/css/parser/container_query_parser.h"
+#include "third_party/blink/renderer/core/css/if_test.h"
+#include "third_party/blink/renderer/core/css/parser/css_if_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token.h"
 #include "third_party/blink/renderer/core/css/properties/css_parsing_utils.h"
 #include "third_party/blink/renderer/core/css/resolver/style_cascade.h"
@@ -232,14 +233,17 @@
     return true;
   }
 
-  ContainerQueryParser parser(context);
+  CSSIfParser parser(context);
 
-  const MediaQueryExpNode* exp_node = parser.ConsumeIfTest(stream);
-  if (!exp_node) {
+  std::optional<IfTest> if_test = parser.ConsumeIfTest(stream);
+  if (!if_test.has_value()) {
     return false;
   }
-
   stream.ConsumeWhitespace();
+
+  if (if_test->GetMediaTest()) {
+    return RuntimeEnabledFeatures::CSSInlineIfForMediaQueriesEnabled();
+  }
   return true;
 }
 
diff --git a/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc b/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc
index 3e81148..576dac8 100644
--- a/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_variable_parser_test.cc
@@ -133,6 +133,9 @@
     "if(style(--prop: abc) : true_val;)",
     "if(style(--prop: abc): true_val; else : false_val;)",
     "if(style(--prop: abc) : true_val; else : false_val)",
+    "if(media(screen): true_val; else: false_val;)",
+    "if(media((min-width : 500px)): true_val; else: false_val;)",
+    "if(media(media(screen and (color))): true_val; else: false_val;)",
     // clang-format on
 };
 
@@ -157,6 +160,8 @@
     "if((style(--prop1: abc)): val1; else: val2)","if((style(--prop1: abc)): val1; else: val2)",
     "if(not style(--prop: abc): true_val; else: false_val; (not style(--prop: def)): true_val)",
     "if(not style(--prop: abc): true_val; (not style(--prop: def)): true_val)",
+    "if(media(min-width : 500px): true_val; else: false_val;)",
+    "if(media(only (min-width : 500px)): true_val; else: false_val;)",
     // clang-format on
 };
 
@@ -343,7 +348,8 @@
 INSTANTIATE_TEST_SUITE_P(All, ValidIfTest, testing::ValuesIn(valid_if_values));
 
 TEST_P(ValidIfTest, ContainsValidIf) {
-  ScopedCSSInlineIfForStyleQueriesForTest scoped_feature(true);
+  ScopedCSSInlineIfForStyleQueriesForTest scoped_style_feature(true);
+  ScopedCSSInlineIfForMediaQueriesForTest scoped_media_feature(true);
 
   SCOPED_TRACE(GetParam());
   CSSParserTokenStream stream{GetParam()};
@@ -365,7 +371,8 @@
                          testing::ValuesIn(invalid_if_values));
 
 TEST_P(InvalidIfTest, ContainsInvalidIf) {
-  ScopedCSSInlineIfForStyleQueriesForTest scoped_feature(true);
+  ScopedCSSInlineIfForStyleQueriesForTest scoped_style_feature(true);
+  ScopedCSSInlineIfForMediaQueriesForTest scoped_media_feature(true);
 
   SCOPED_TRACE(GetParam());
   CSSParserTokenStream stream{GetParam()};
diff --git a/third_party/blink/renderer/core/css/parser/media_query_parser.h b/third_party/blink/renderer/core/css/parser/media_query_parser.h
index 3297e0d5..ea6f586 100644
--- a/third_party/blink/renderer/core/css/parser/media_query_parser.h
+++ b/third_party/blink/renderer/core/css/parser/media_query_parser.h
@@ -18,6 +18,7 @@
 class MediaQuerySet;
 class CSSParserContext;
 class ContainerQueryParser;
+class CSSIfParser;
 
 class CORE_EXPORT MediaQueryParser {
   STACK_ALLOCATED();
@@ -57,6 +58,7 @@
 
  private:
   friend class ContainerQueryParser;
+  friend class CSSIfParser;
 
   enum ParserType {
     kMediaQuerySetParser,
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade.cc b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
index 7c023e9..5ca3925 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade.cc
@@ -36,9 +36,10 @@
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/css/css_variable_data.h"
 #include "third_party/blink/renderer/core/css/document_style_environment_variables.h"
+#include "third_party/blink/renderer/core/css/if_test.h"
 #include "third_party/blink/renderer/core/css/kleene_value.h"
 #include "third_party/blink/renderer/core/css/media_eval_utils.h"
-#include "third_party/blink/renderer/core/css/parser/container_query_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_if_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_fast_paths.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_local_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token.h"
@@ -2184,21 +2185,28 @@
     return true;
   }
 
-  ContainerQueryParser parser(context);
+  CSSIfParser parser(context);
 
-  const MediaQueryExpNode* exp_node = parser.ConsumeIfTest(stream);
-  DCHECK(exp_node);
+  std::optional<IfTest> if_test = parser.ConsumeIfTest(stream);
+  DCHECK(if_test.has_value());
 
-  stream.ConsumeWhitespace();
-  DCHECK_EQ(stream.Peek().GetType(), kColonToken);
-  stream.ConsumeIncludingWhitespace();
+  // style()
+  if (const MediaQueryExpNode* style_test = if_test->GetStyleTest()) {
+    stream.ConsumeWhitespace();
+    DCHECK_EQ(stream.Peek().GetType(), kColonToken);
+    stream.ConsumeIncludingWhitespace();
 
-  return MediaEval(*exp_node, [this, &resolver, &context, &function_context,
-                               &is_attr_tainted](
-                                  const MediaQueryFeatureExpNode& feature) {
-           return EvalIfStyleFeature(feature, resolver, context,
-                                     function_context, is_attr_tainted);
-         }) == KleeneValue::kTrue;
+    return MediaEval(*style_test, [this, &resolver, &context, &function_context,
+                                   &is_attr_tainted](
+                                      const MediaQueryFeatureExpNode& feature) {
+             return EvalIfStyleFeature(feature, resolver, context,
+                                       function_context, is_attr_tainted);
+           }) == KleeneValue::kTrue;
+  }
+
+  // TODO(crbug.com/346977961): Support media() and supports() queries in if()
+  // condition.
+  return false;
 }
 
 bool StyleCascade::ResolveIfInto(CSSParserTokenStream& stream,
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index 0833a0d5..ef19692 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -1862,25 +1862,15 @@
       fetcher->GetProperties().GetFetchClientSettingsObject();
 
   FetchManagerResourceRequestContext resource_request_context;
-  if (!RuntimeEnabledFeatures::
-          MinimimalResourceRequestPrepBeforeCacheLookupEnabled()) {
-    if (PrepareResourceRequest(
-            kFetchLaterResourceType, fetch_client_settings_object, params,
-            fetcher->Context(), unused_virtual_time_pauser,
-            resource_request_context, KURL()) != std::nullopt) {
-      return nullptr;
-    }
-  } else {
-    if (PrepareResourceRequestForCacheAccess(
-            kFetchLaterResourceType, fetch_client_settings_object, KURL(),
-            resource_request_context, fetcher->Context(),
-            params) != std::nullopt) {
-      return nullptr;
-    }
-    UpgradeResourceRequestForLoaderNew(
-        kFetchLaterResourceType, params, fetcher->Context(),
-        resource_request_context, unused_virtual_time_pauser);
+  if (PrepareResourceRequestForCacheAccess(
+          kFetchLaterResourceType, fetch_client_settings_object, KURL(),
+          resource_request_context, fetcher->Context(),
+          params) != std::nullopt) {
+    return nullptr;
   }
+  UpgradeResourceRequestForLoader(kFetchLaterResourceType, params,
+                                  fetcher->Context(), resource_request_context,
+                                  unused_virtual_time_pauser);
 
   // From `ResourceFetcher::StartLoad()`:
   ScriptForbiddenScope script_forbidden_scope;
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 e315da6..e6fe5ef 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -996,11 +996,13 @@
   bool was_dirty = NeedsLayout();
 #endif
   if ((intersection_observation_state_ < kRequired &&
-       ShouldThrottleRendering()) ||
+       ShouldThrottleRendering() && !needs_update_delayed_intersection_) ||
       Lifecycle().LifecyclePostponed() || !frame_->GetDocument()->IsActive()) {
     return;
   }
 
+  needs_update_delayed_intersection_ = false;
+
   if (frame_->IsOutermostMainFrame()) {
     EnsureOverlayInterstitialAdDetector().MaybeFireDetection(frame_.Get());
     EnsureStickyAdDetector().MaybeFireDetection(frame_.Get());
@@ -4329,20 +4331,24 @@
 
 void LocalFrameView::ScheduleDelayedIntersection(base::TimeDelta delay) {
   if (RuntimeEnabledFeatures::ForceDelayedIntersectionUpdateEnabled()) {
-    if (!delayed_intersection_timer_.IsActive() ||
-        delayed_intersection_timer_.NextFireInterval() > delay) {
-      delayed_intersection_timer_.Stop();
-      delayed_intersection_timer_.StartOneShot(delay, FROM_HERE);
+    auto& timer = frame_->LocalFrameRoot().View()->delayed_intersection_timer_;
+    if (!timer.IsActive() || timer.NextFireInterval() > delay) {
+      timer.Stop();
+      timer.StartOneShot(delay, FROM_HERE);
     }
   } else {
     ScheduleAnimation(delay);
   }
 }
 
+bool LocalFrameView::HasScheduledDelayedIntersectionForTesting() const {
+  return delayed_intersection_timer_.IsActive();
+}
+
 void LocalFrameView::DelayedIntersectionTimerFired(TimerBase*) {
   CHECK(RuntimeEnabledFeatures::ForceDelayedIntersectionUpdateEnabled());
-  // Force an intersection update even if the frame is throttled.
-  SetIntersectionObservationState(LocalFrameView::kRequired);
+  DCHECK(frame_->IsLocalRoot());
+  needs_update_delayed_intersection_ = true;
   ScheduleAnimation();
 }
 
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 c3883c1..39241fc 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -262,6 +262,10 @@
   void ForceUpdateViewportIntersections();
 
   void ScheduleDelayedIntersection(base::TimeDelta);
+  bool HasScheduledDelayedIntersectionForTesting() const;
+  bool NeedsUpdateDelayedIntersectionForTesting() const {
+    return needs_update_delayed_intersection_;
+  }
 
   void SetPaintArtifactCompositorNeedsUpdate();
 
@@ -1170,7 +1174,16 @@
 
   IntersectionObservationState intersection_observation_state_;
   gfx::Vector2dF accumulated_scroll_delta_since_last_intersection_update_;
+  // Used only if the frame is the local root.
   HeapTaskRunnerTimer<LocalFrameView> delayed_intersection_timer_;
+  // Set on the local root when the above timer is fired. Will force update
+  // even if the local frame tree is throttled. It's different from
+  // IntersectionObservationState::kRequired in that
+  // 1) It will only update of intersections with pending delayed updates
+  //    (i.e. IntersectionObservation::needs_update_ is true).
+  // 2) It won't force document lifecycle updates. Dirty layout will be treated
+  //    as degenerate "not intersecting" status.
+  bool needs_update_delayed_intersection_ = false;
 
   mojom::blink::ViewportIntersectionState last_intersection_state_;
 
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observation.h b/third_party/blink/renderer/core/intersection_observer/intersection_observation.h
index d01c243..dcfa702b 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observation.h
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observation.h
@@ -79,6 +79,7 @@
   void Trace(Visitor*) const;
 
   bool CanUseCachedRectsForTesting(bool scroll_and_visibility_only) const;
+  bool HasPendingUpdateForTesting() const { return needs_update_; }
 
  private:
   bool ShouldCompute(unsigned flags) const;
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
index 7879d2bf..2244e9ca 100644
--- a/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
+++ b/third_party/blink/renderer/core/intersection_observer/intersection_observer_test.cc
@@ -3295,36 +3295,71 @@
   Element* target = GetDocument().getElementById(AtomicString("target"));
   ASSERT_TRUE(target);
   observer->observe(target);
+  const IntersectionObservation* observation = observer->Observations().front();
+  const LocalFrameView* frame_view = GetDocument().View();
 
   Compositor().BeginFrame();
   test::RunPendingTasks();
   EXPECT_EQ(observer_delegate->CallCount(), 1);
   EXPECT_EQ(observer_delegate->EntryCount(), 1);
   EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_EQ(LocalFrameView::kNotNeeded,
+            frame_view->GetIntersectionObservationStateForTesting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
 
   target->SetInlineStyleProperty(CSSPropertyID::kTop, "2000px");
   Compositor().BeginFrame();
   test::RunPendingTasks();
   EXPECT_EQ(observer_delegate->CallCount(), 1);
+  EXPECT_EQ(LocalFrameView::kNotNeeded,
+            frame_view->GetIntersectionObservationStateForTesting());
+  EXPECT_TRUE(frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(frame_view->NeedsUpdateDelayedIntersectionForTesting());
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
 
-  task_environment().FastForwardUntilNoTasksRemain();
+  task_environment().FastForwardBy(base::Seconds(1));
+  EXPECT_FALSE(frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_TRUE(frame_view->NeedsUpdateDelayedIntersectionForTesting());
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_EQ(LocalFrameView::kNotNeeded,
+            frame_view->GetIntersectionObservationStateForTesting());
   Compositor().BeginFrame();
   test::RunPendingTasks();
   EXPECT_EQ(observer_delegate->CallCount(), 2);
   EXPECT_EQ(observer_delegate->EntryCount(), 2);
   EXPECT_FALSE(observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(frame_view->NeedsUpdateDelayedIntersectionForTesting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
+  EXPECT_EQ(LocalFrameView::kNotNeeded,
+            frame_view->GetIntersectionObservationStateForTesting());
 
   target->SetInlineStyleProperty(CSSPropertyID::kTop, "0");
   Compositor().BeginFrame();
   test::RunPendingTasks();
   EXPECT_EQ(observer_delegate->CallCount(), 2);
+  EXPECT_TRUE(frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(frame_view->NeedsUpdateDelayedIntersectionForTesting());
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_EQ(LocalFrameView::kNotNeeded,
+            frame_view->GetIntersectionObservationStateForTesting());
 
-  task_environment().FastForwardUntilNoTasksRemain();
+  task_environment().FastForwardBy(base::Seconds(1));
+  EXPECT_FALSE(frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_TRUE(frame_view->NeedsUpdateDelayedIntersectionForTesting());
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_EQ(LocalFrameView::kNotNeeded,
+            frame_view->GetIntersectionObservationStateForTesting());
   Compositor().BeginFrame();
   test::RunPendingTasks();
   EXPECT_EQ(observer_delegate->CallCount(), 3);
   EXPECT_EQ(observer_delegate->EntryCount(), 3);
   EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(frame_view->NeedsUpdateDelayedIntersectionForTesting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
+  EXPECT_EQ(LocalFrameView::kNotNeeded,
+            frame_view->GetIntersectionObservationStateForTesting());
 }
 
 TEST_F(IntersectionObserverTest, DelayedAndNonDelayedIntersections) {
@@ -3339,11 +3374,16 @@
 
   TestIntersectionObserverDelegate* delayed_observer_delegate =
       MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
-  IntersectionObserver* delayed_observer =
+  IntersectionObserver* delayed_observer1 =
       MakeGarbageCollected<IntersectionObserver>(
           *delayed_observer_delegate, std::nullopt,
           IntersectionObserver::Params{.thresholds = {0},
-                                       .delay = base::Seconds(1)});
+                                       .delay = base::Seconds(2)});
+  IntersectionObserver* delayed_observer2 =
+      MakeGarbageCollected<IntersectionObserver>(
+          *delayed_observer_delegate, std::nullopt,
+          IntersectionObserver::Params{.thresholds = {0},
+                                       .delay = base::Seconds(3)});
   TestIntersectionObserverDelegate* non_delayed_observer_delegate =
       MakeGarbageCollected<TestIntersectionObserverDelegate>(GetDocument());
   IntersectionObserver* non_delayed_observer =
@@ -3353,52 +3393,115 @@
 
   Element* delayed = GetDocument().getElementById(AtomicString("delayed"));
   ASSERT_TRUE(delayed);
-  delayed_observer->observe(delayed);
-  Element* non_delayed = GetDocument().getElementById(AtomicString("delayed"));
+  delayed_observer1->observe(delayed);
+  const IntersectionObservation* delayed_observation1 =
+      delayed_observer1->Observations().front();
+  delayed_observer2->observe(delayed);
+  const IntersectionObservation* delayed_observation2 =
+      delayed_observer2->Observations().front();
+  Element* non_delayed =
+      GetDocument().getElementById(AtomicString("non-delayed"));
   ASSERT_TRUE(non_delayed);
   non_delayed_observer->observe(non_delayed);
+  const IntersectionObservation* non_delayed_observation =
+      non_delayed_observer->Observations().front();
 
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_EQ(delayed_observer_delegate->CallCount(), 1);
-  EXPECT_EQ(delayed_observer_delegate->EntryCount(), 1);
-  EXPECT_TRUE(delayed_observer_delegate->LastEntry()->isIntersecting());
-  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 1);
-  EXPECT_EQ(non_delayed_observer_delegate->EntryCount(), 1);
-  EXPECT_TRUE(non_delayed_observer_delegate->LastEntry()->isIntersecting());
-
-  delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "2000px");
-  non_delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "2000px");
-  Compositor().BeginFrame();
-  test::RunPendingTasks();
-  EXPECT_EQ(delayed_observer_delegate->CallCount(), 1);
-  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 2);
-  EXPECT_EQ(non_delayed_observer_delegate->EntryCount(), 2);
-  EXPECT_FALSE(non_delayed_observer_delegate->LastEntry()->isIntersecting());
-
-  task_environment().FastForwardUntilNoTasksRemain();
-  Compositor().BeginFrame();
-  test::RunPendingTasks();
   EXPECT_EQ(delayed_observer_delegate->CallCount(), 2);
   EXPECT_EQ(delayed_observer_delegate->EntryCount(), 2);
-  EXPECT_FALSE(delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_TRUE(delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_FALSE(delayed_observation2->HasPendingUpdateForTesting());
+  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 1);
+  EXPECT_EQ(non_delayed_observer_delegate->EntryCount(), 1);
+  EXPECT_TRUE(non_delayed_observer_delegate->LastEntry()->isIntersecting());
 
-  delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "0");
-  non_delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "0");
+  // Both elements move out of view.
+  delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "2000px");
+  non_delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "2000px");
   Compositor().BeginFrame();
   test::RunPendingTasks();
   EXPECT_EQ(delayed_observer_delegate->CallCount(), 2);
-  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 3);
-  EXPECT_EQ(non_delayed_observer_delegate->EntryCount(), 3);
-  EXPECT_TRUE(non_delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_TRUE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_TRUE(delayed_observation2->HasPendingUpdateForTesting());
+  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 2);
+  EXPECT_EQ(non_delayed_observer_delegate->EntryCount(), 2);
+  EXPECT_FALSE(non_delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(non_delayed_observation->HasPendingUpdateForTesting());
 
-  task_environment().FastForwardUntilNoTasksRemain();
+  task_environment().FastForwardBy(base::Seconds(2));
+  // observer1 updates and notifies "no intersection".
   Compositor().BeginFrame();
   test::RunPendingTasks();
   EXPECT_EQ(delayed_observer_delegate->CallCount(), 3);
   EXPECT_EQ(delayed_observer_delegate->EntryCount(), 3);
+  EXPECT_FALSE(delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_TRUE(delayed_observation2->HasPendingUpdateForTesting());
+
+  task_environment().FastForwardBy(base::Seconds(1));
+  // observer2 updates and notifies "no intersection".
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  EXPECT_EQ(delayed_observer_delegate->CallCount(), 4);
+  EXPECT_EQ(delayed_observer_delegate->EntryCount(), 4);
+  EXPECT_FALSE(delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_FALSE(delayed_observation2->HasPendingUpdateForTesting());
+
+  // Both elements move into the view.
+  delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "0");
+  non_delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "0");
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  EXPECT_EQ(delayed_observer_delegate->CallCount(), 4);
+  EXPECT_TRUE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_TRUE(delayed_observation2->HasPendingUpdateForTesting());
+  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 3);
+  EXPECT_EQ(non_delayed_observer_delegate->EntryCount(), 3);
+  EXPECT_TRUE(non_delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(non_delayed_observation->HasPendingUpdateForTesting());
+
+  task_environment().FastForwardBy(base::Seconds(2));
+  // observer1 updates and notifies intersection.
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  EXPECT_EQ(delayed_observer_delegate->CallCount(), 5);
+  EXPECT_EQ(delayed_observer_delegate->EntryCount(), 5);
+  EXPECT_FALSE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_TRUE(delayed_observation2->HasPendingUpdateForTesting());
   EXPECT_TRUE(delayed_observer_delegate->LastEntry()->isIntersecting());
   EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 3);
+
+  // Before observer2 updates, the element moves out of view again.
+  delayed->SetInlineStyleProperty(CSSPropertyID::kTop, "2000px");
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  EXPECT_EQ(delayed_observer_delegate->CallCount(), 5);
+  EXPECT_TRUE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_TRUE(delayed_observation2->HasPendingUpdateForTesting());
+  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 3);
+
+  task_environment().FastForwardBy(base::Seconds(1));
+  // observer2 updates, but doesn't send notification because intersection is
+  // the same as its last update.
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  EXPECT_EQ(delayed_observer_delegate->CallCount(), 5);
+  EXPECT_TRUE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_FALSE(delayed_observation2->HasPendingUpdateForTesting());
+  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 3);
+
+  task_environment().FastForwardBy(base::Seconds(1));
+  // observer1 updates and notifies "no intersection".
+  Compositor().BeginFrame();
+  test::RunPendingTasks();
+  EXPECT_EQ(delayed_observer_delegate->CallCount(), 6);
+  EXPECT_FALSE(delayed_observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(delayed_observation1->HasPendingUpdateForTesting());
+  EXPECT_FALSE(delayed_observation2->HasPendingUpdateForTesting());
+  EXPECT_EQ(non_delayed_observer_delegate->CallCount(), 3);
 }
 
 TEST_F(IntersectionObserverTest, IntersectionWithMarginInThrottledFrame) {
@@ -3508,13 +3611,17 @@
   Element* target = iframe_document->getElementById(AtomicString("target"));
   ASSERT_TRUE(target);
   observer->observe(target);
+  const IntersectionObservation* observation = observer->Observations().front();
+  LocalFrameView* root_frame_view = GetDocument().View();
+  LocalFrameView* iframe_view = iframe_document->View();
 
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_FALSE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_FALSE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 1);
   EXPECT_EQ(observer_delegate->EntryCount(), 1);
   EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
 
   // Move the iframe out of view, but the target is still in the margin.
   GetDocument()
@@ -3522,21 +3629,24 @@
       ->SetInlineStyleProperty(CSSPropertyID::kTop, "600px");
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 1);
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_TRUE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 
-  task_environment().FastForwardUntilNoTasksRemain();
-  EXPECT_EQ(
-      LocalFrameView::kRequired,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  task_environment().FastForwardBy(base::Seconds(1));
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_TRUE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   // `target` is still in the intersection margin.
   EXPECT_EQ(observer_delegate->CallCount(), 1);
-  EXPECT_EQ(
-      LocalFrameView::kNotNeeded,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 
   // Move the iframe further down, to move the target out of the margin.
   GetDocument()
@@ -3544,22 +3654,25 @@
       ->SetInlineStyleProperty(CSSPropertyID::kTop, "1000px");
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 1);
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_TRUE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 
-  task_environment().FastForwardUntilNoTasksRemain();
-  EXPECT_EQ(
-      LocalFrameView::kRequired,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  task_environment().FastForwardBy(base::Seconds(1));
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_TRUE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 2);
   EXPECT_EQ(observer_delegate->EntryCount(), 2);
   EXPECT_FALSE(observer_delegate->LastEntry()->isIntersecting());
-  EXPECT_EQ(
-      LocalFrameView::kNotNeeded,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 
   // Move the iframe up, still out of view, but the target will be in the
   // margin.
@@ -3568,46 +3681,52 @@
       ->SetInlineStyleProperty(CSSPropertyID::kTop, "600px");
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 2);
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_TRUE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 
-  task_environment().FastForwardUntilNoTasksRemain();
-  EXPECT_EQ(
-      LocalFrameView::kRequired,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  task_environment().FastForwardBy(base::Seconds(1));
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_TRUE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 3);
   EXPECT_EQ(observer_delegate->EntryCount(), 3);
   EXPECT_TRUE(observer_delegate->LastEntry()->isIntersecting());
-  EXPECT_EQ(
-      LocalFrameView::kNotNeeded,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 
   // Move the iframe down again and mark the iframe's view needs layout.
   GetDocument()
       .getElementById(AtomicString("iframe"))
       ->SetInlineStyleProperty(CSSPropertyID::kTop, "1000px");
-  iframe_document->View()->SetNeedsLayout();
+  iframe_view->SetNeedsLayout();
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 3);
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_TRUE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 
-  task_environment().FastForwardUntilNoTasksRemain();
-  EXPECT_EQ(
-      LocalFrameView::kRequired,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  task_environment().FastForwardBy(base::Seconds(1));
+  EXPECT_TRUE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_TRUE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
   Compositor().BeginFrame();
   test::RunPendingTasks();
-  EXPECT_TRUE(iframe_document->View()->IsHiddenForThrottling());
+  EXPECT_TRUE(iframe_view->IsHiddenForThrottling());
   EXPECT_EQ(observer_delegate->CallCount(), 4);
   EXPECT_EQ(observer_delegate->EntryCount(), 4);
   EXPECT_FALSE(observer_delegate->LastEntry()->isIntersecting());
-  EXPECT_EQ(
-      LocalFrameView::kNotNeeded,
-      iframe_document->View()->GetIntersectionObservationStateForTesting());
+  EXPECT_FALSE(observation->HasPendingUpdateForTesting());
+  EXPECT_FALSE(root_frame_view->HasScheduledDelayedIntersectionForTesting());
+  EXPECT_FALSE(root_frame_view->NeedsUpdateDelayedIntersectionForTesting());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/inline/line_breaker.h b/third_party/blink/renderer/core/layout/inline/line_breaker.h
index e9eadc9..ad47fb6 100644
--- a/third_party/blink/renderer/core/layout/inline/line_breaker.h
+++ b/third_party/blink/renderer/core/layout/inline/line_breaker.h
@@ -484,11 +484,6 @@
 
   bool* depends_on_block_constraints_out_ = nullptr;
 
-  // Keep the last item |HandleTextForFastMinContent()| has handled. This is
-  // used to fallback the last word to |HandleText()|.
-  // TODO(crbug.com/333630754): Remove when `FasterMinContent` is stabilized.
-  const InlineItem* fast_min_content_item_ = nullptr;
-
   // The current base direction for the bidi algorithm.
   // This is copied from InlineNode, then updated after each forced line break
   // if 'unicode-bidi: plaintext'.
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 1885d810..b9099faf 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -394,12 +394,7 @@
   // TODO(yhirano): Clarify which statements are actually needed when
   // this is called during redirect.
   const bool for_redirect = request.GetRedirectInfo().has_value();
-  const bool minimal_prep = RuntimeEnabledFeatures::
-      MinimimalResourceRequestPrepBeforeCacheLookupEnabled();
 
-  if (!minimal_prep) {
-    SetFirstPartyCookie(request);
-  }
   if (request.GetRequestContext() ==
       mojom::blink::RequestContextType::SERVICE_WORKER) {
     // The top frame origin is defined to be null for service worker main
@@ -418,17 +413,6 @@
   request.SetStorageAccessApiStatus(
       document_->GetExecutionContext()->GetStorageAccessApiStatus());
 
-  if (!minimal_prep) {
-    if (document_loader_->ForceFetchCacheMode()) {
-      request.SetCacheMode(*document_loader_->ForceFetchCacheMode());
-    }
-    if (const AttributionSrcLoader* attribution_src_loader =
-            GetFrame()->GetAttributionSrcLoader()) {
-      request.SetAttributionReportingSupport(
-          attribution_src_loader->GetSupport());
-    }
-  }
-
   // If the original request included the attribute to opt-in to shared storage,
   // then update eligibility for the current (possibly redirected) request. Note
   // that if the original request didn't opt-in, then the original request and
@@ -447,9 +431,6 @@
       RuntimeEnabledFeatures::CompressionDictionaryTransportEnabled(
           GetExecutionContext()));
 
-  if (!minimal_prep) {
-    WillSendRequest(request);
-  }
   GetLocalFrameClient()->DispatchFinalizeRequest(request);
   FrameScheduler* frame_scheduler = GetFrame()->GetFrameScheduler();
   if (!for_redirect && frame_scheduler) {
@@ -832,8 +813,6 @@
 void FrameFetchContext::PopulateResourceRequestBeforeCacheAccess(
     const ResourceLoaderOptions& options,
     ResourceRequest& request) {
-  DCHECK(RuntimeEnabledFeatures::
-             MinimimalResourceRequestPrepBeforeCacheLookupEnabled());
   if (!GetResourceFetcherProperties().IsDetached()) {
     probe::SetDevToolsIds(Probe(), request, options.initiator_info);
   }
@@ -868,13 +847,6 @@
     const std::optional<float> resource_width,
     ResourceRequest& request,
     const ResourceLoaderOptions& options) {
-  if (!RuntimeEnabledFeatures::
-          MinimimalResourceRequestPrepBeforeCacheLookupEnabled()) {
-    if (!GetResourceFetcherProperties().IsDetached()) {
-      probe::SetDevToolsIds(Probe(), request, options.initiator_info);
-    }
-    ModifyRequestForMixedContentUpgrade(request);
-  }
   AddClientHintsIfNecessary(resource_width, request);
   AddReducedAcceptLanguageIfNecessary(request);
 }
@@ -1303,8 +1275,7 @@
   if (blocked_reason) {
     return blocked_reason;
   }
-  if (detached || !RuntimeEnabledFeatures::
-                      MinimimalResourceRequestPrepBeforeCacheLookupEnabled()) {
+  if (detached) {
     return std::nullopt;
   }
   if (!resource_request.Url().IsValid()) {
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
index 5776d11d..62db6aa5 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
@@ -198,14 +198,6 @@
   request.SetStorageAccessApiStatus(
       GetExecutionContext()->GetStorageAccessApiStatus());
 
-  if (!RuntimeEnabledFeatures::
-          MinimimalResourceRequestPrepBeforeCacheLookupEnabled()) {
-    std::optional<WebURL> overriden_url =
-        web_context_->WillSendRequest(request.Url());
-    if (overriden_url.has_value()) {
-      request.SetUrl(*overriden_url);
-    }
-  }
   WrappedResourceRequest webreq(request);
   web_context_->FinalizeRequest(webreq);
   if (auto* worker_scope = DynamicTo<WorkerGlobalScope>(*global_scope_)) {
@@ -252,9 +244,6 @@
 void WorkerFetchContext::PopulateResourceRequestBeforeCacheAccess(
     const ResourceLoaderOptions& options,
     ResourceRequest& request) {
-  DCHECK(RuntimeEnabledFeatures::
-             MinimimalResourceRequestPrepBeforeCacheLookupEnabled());
-
   if (!RuntimeEnabledFeatures::PreloadLinkRelDataUrlsEnabled()) {
     ModifyRequestForMixedContentUpgrade(request);
   }
@@ -275,14 +264,6 @@
     const ResourceLoaderOptions& options) {
   if (!GetResourceFetcherProperties().IsDetached())
     probe::SetDevToolsIds(Probe(), out_request, options.initiator_info);
-  if (!RuntimeEnabledFeatures::
-          MinimimalResourceRequestPrepBeforeCacheLookupEnabled()) {
-    MixedContentChecker::UpgradeInsecureRequest(
-        out_request,
-        &GetResourceFetcherProperties().GetFetchClientSettingsObject(),
-        global_scope_, mojom::RequestContextFrameType::kNone,
-        global_scope_->ContentSettingsClient());
-  }
   SetFirstPartyCookie(out_request);
   if (!out_request.TopFrameOrigin())
     out_request.SetTopFrameOrigin(GetTopFrameOrigin());
diff --git a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
index 959f4db..f6e533f 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_inline_text_box.cc
@@ -390,10 +390,6 @@
   // necessary to again recompute this part of the tree.
   parent_ = parent;
   UpdateCachedAttributeValuesIfNeeded(false);
-
-#if DCHECK_IS_ON()
-  is_initialized_ = true;
-#endif
 }
 
 void AXInlineTextBox::Detach() {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index c4b6b63..94d7e47 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -763,10 +763,6 @@
   // Set the parent again, this time via SetParent(), so that all related checks
   // and calls occur now that we have the role and updated cached values.
   SetParent(parent_);
-
-#if DCHECK_IS_ON()
-  is_initialized_ = true;
-#endif
 }
 
 void AXObject::Detach() {
@@ -8410,14 +8406,6 @@
 }
 
 String AXObject::ToString(bool verbose) const {
-#if DCHECK_IS_ON()
-  CHECK(is_initialized_) << "Init() must be called before ToString().";
-#endif
-
-  CHECK(!(parent_ == nullptr && role_ == ax::mojom::blink::Role::kUnknown &&
-          id_ == 0))
-      << "Calling ToString() on an AxObject before Init() is not allowed.";
-
   // Build a friendly name for debugging the object.
   // If verbose, build a longer name name in the form of:
   // CheckBox axid#28 <input.someClass#cbox1> name="checkbox"
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index be69c2e..fdfdcc2 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -255,7 +255,6 @@
   bool is_initializing_ = false;
   bool is_computing_role_ = false;
   bool is_updating_cached_values_ = false;
-  bool is_initialized_ = false;
 #endif
 #if !defined(NDEBUG)
   // Keep track of what the object used to be, to make it easier to debug
@@ -1476,7 +1475,6 @@
   void PreSerializationConsistencyCheck() const;
 
   // Returns a string representation of this object.
-  // Must only be used after `init()`has been called.
   String ToString(bool verbose = true) const;
 
   void PopulateAXRelativeBounds(ui::AXRelativeBounds& bounds,
diff --git a/third_party/blink/renderer/modules/speech/speech_recognition.cc b/third_party/blink/renderer/modules/speech/speech_recognition.cc
index 6b6f510..fb6fc7f 100644
--- a/third_party/blink/renderer/modules/speech/speech_recognition.cc
+++ b/third_party/blink/renderer/modules/speech/speech_recognition.cc
@@ -169,7 +169,9 @@
   LocalDOMWindow& window = *LocalDOMWindow::From(script_state);
   auto* controller = SpeechRecognitionController::From(window);
 
-  if (!controller) {
+  if (!controller || !script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Execution context is detached.");
     return EmptyPromise();
   }
 
@@ -194,7 +196,9 @@
   LocalDOMWindow& window = *LocalDOMWindow::From(script_state);
   auto* controller = SpeechRecognitionController::From(window);
 
-  if (!controller) {
+  if (!controller || !script_state->ContextIsValid()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Execution context is detached.");
     return EmptyPromise();
   }
 
diff --git a/third_party/blink/renderer/modules/webcodecs/BUILD.gn b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
index 303dc15..3425364 100644
--- a/third_party/blink/renderer/modules/webcodecs/BUILD.gn
+++ b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
@@ -104,7 +104,6 @@
     "//media/mojo:buildflags",
     "//media/mojo/clients",
     "//media/mojo/mojom",
-    "//third_party/libaom:libaom_buildflags",
     "//third_party/libyuv:libyuv",
     "//third_party/opus",
   ]
diff --git a/third_party/blink/renderer/platform/graphics/graphics_types.h b/third_party/blink/renderer/platform/graphics/graphics_types.h
index eac83d9..caf61df 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_types.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_types.h
@@ -29,7 +29,7 @@
 #include "cc/paint/paint_flags.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/skia/include/core/SkPaint.h"
-#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkPathTypes.h"
 
 namespace gfx {
 class ColorSpace;
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
index ae4f4b9..c2ef86f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher.cc
@@ -3594,15 +3594,6 @@
          resource_type == ResourceType::kRaw ||
          resource_type == ResourceType::kXSLStyleSheet);
 
-  if (!RuntimeEnabledFeatures::
-          MinimimalResourceRequestPrepBeforeCacheLookupEnabled()) {
-    params_.OverrideContentType(factory_.ContentType());
-    return PrepareResourceRequest(
-        resource_type, fetcher_.properties_->GetFetchClientSettingsObject(),
-        params_, fetcher_.Context(), pauser, *this,
-        bundle_url_for_uuid_resources_);
-  }
-
   std::optional<ResourceRequestBlockedReason> blocked_reason =
       PrepareResourceRequestForCacheAccess(
           resource_type, fetcher_.properties_->GetFetchClientSettingsObject(),
@@ -3627,8 +3618,8 @@
   }
   was_upgrade_for_loader_called_ = true;
   params_.OverrideContentType(factory_.ContentType());
-  UpgradeResourceRequestForLoaderNew(factory_.GetType(), params_,
-                                     fetcher_.Context(), *this, pauser);
+  UpgradeResourceRequestForLoader(factory_.GetType(), params_,
+                                  fetcher_.Context(), *this, pauser);
 }
 
 ResourceLoadPriority
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.cc b/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.cc
index a8c0ecad..a50cc64 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.cc
@@ -143,145 +143,12 @@
   NOTREACHED();
 }
 
-std::optional<ResourceRequestBlockedReason> PrepareResourceRequest(
-    ResourceType resource_type,
-    const FetchClientSettingsObject& fetch_client_settings_object,
-    FetchParameters& params,
-    FetchContext& context,
-    WebScopedVirtualTimePauser& virtual_time_pauser,
-    ResourceRequestContext& resource_request_context,
-    const KURL& bundle_url_for_uuid_resources) {
-  ResourceRequest& resource_request = params.MutableResourceRequest();
-  const ResourceLoaderOptions& options = params.Options();
-  DCHECK(!RuntimeEnabledFeatures::
-             MinimimalResourceRequestPrepBeforeCacheLookupEnabled());
-  const ReportingDisposition reporting_disposition =
-      CalculateReportingDisposition(params);
-
-  // Note that resource_request.GetRedirectInfo() may be non-null here since
-  // e.g. ThreadableLoader may create a new Resource from a ResourceRequest that
-  // originates from the ResourceRequest passed to the redirect handling
-  // callback.
-
-  // Before modifying the request for CSP, evaluate report-only headers. This
-  // allows site owners to learn about requests that are being modified
-  // (e.g. mixed content that is being upgraded by upgrade-insecure-requests).
-  const std::optional<ResourceRequest::RedirectInfo>& redirect_info =
-      resource_request.GetRedirectInfo();
-  const KURL& url_before_redirects =
-      redirect_info ? redirect_info->original_url : params.Url();
-  const ResourceRequestHead::RedirectStatus redirect_status =
-      redirect_info ? ResourceRequestHead::RedirectStatus::kFollowedRedirect
-                    : ResourceRequestHead::RedirectStatus::kNoRedirect;
-  context.CheckCSPForRequest(
-      resource_request.GetRequestContext(),
-      resource_request.GetRequestDestination(), resource_request.GetMode(),
-      MemoryCache::RemoveFragmentIdentifierIfNeeded(
-          bundle_url_for_uuid_resources.IsValid()
-              ? bundle_url_for_uuid_resources
-              : params.Url()),
-      options, reporting_disposition,
-      MemoryCache::RemoveFragmentIdentifierIfNeeded(url_before_redirects),
-      redirect_status);
-
-  // This may modify params.Url() (via the resource_request argument).
-  context.UpgradeResourceRequestForLoader(
-      resource_type, params.GetResourceWidth(), resource_request, options);
-  if (!params.Url().IsValid()) {
-    return ResourceRequestBlockedReason::kOther;
-  }
-
-  ResourceLoadPriority computed_load_priority = resource_request.Priority();
-  // We should only compute the priority for ResourceRequests whose priority has
-  // not already been set.
-  if (!resource_request.PriorityHasBeenSet()) {
-    computed_load_priority =
-        resource_request_context.ComputeLoadPriority(params);
-  }
-  CHECK_NE(computed_load_priority, ResourceLoadPriority::kUnresolved);
-  resource_request.SetPriority(computed_load_priority);
-  resource_request.SetPriorityIncremental(ShouldLoadIncremental(resource_type));
-  resource_request.SetRenderBlockingBehavior(
-      params.GetRenderBlockingBehavior());
-
-  if (resource_request.GetCacheMode() ==
-      mojom::blink::FetchCacheMode::kDefault) {
-    resource_request.SetCacheMode(context.ResourceRequestCachePolicy(
-        resource_request, resource_type, params.Defer()));
-  }
-  if (resource_request.GetRequestContext() ==
-      mojom::blink::RequestContextType::UNSPECIFIED) {
-    resource_request.SetRequestContext(ResourceFetcher::DetermineRequestContext(
-        resource_type, ResourceFetcher::kImageNotImageSet));
-    resource_request.SetRequestDestination(
-        ResourceFetcher::DetermineRequestDestination(resource_type));
-  }
-
-  if (resource_type == ResourceType::kLinkPrefetch) {
-    // Add the "Purpose: prefetch" header to requests for prefetch.
-    resource_request.SetPurposeHeader("prefetch");
-  } else if (context.IsPrerendering()) {
-    // Add the "Sec-Purpose: prefetch;prerender" header to requests issued from
-    // prerendered pages. Add "Purpose: prefetch" as well for compatibility
-    // concerns (See https://github.com/WICG/nav-speculation/issues/133).
-    resource_request.SetHttpHeaderField(http_names::kSecPurpose,
-                                        AtomicString("prefetch;prerender"));
-    resource_request.SetPurposeHeader("prefetch");
-  }
-
-  // Indicate whether the network stack can return a stale resource. If a
-  // stale resource is returned a StaleRevalidation request will be scheduled.
-  // Explicitly disallow stale responses for fetchers that don't have SWR
-  // enabled (via origin trial), and non-GET requests.
-  resource_request.SetAllowStaleResponse(resource_request.HttpMethod() ==
-                                             http_names::kGET &&
-                                         !params.IsStaleRevalidation());
-
-  SetReferrer(resource_request, fetch_client_settings_object);
-
-  context.AddAdditionalRequestHeaders(resource_request);
-
-  resource_request_context.RecordTrace();
-
-  const std::optional<ResourceRequestBlockedReason> blocked_reason =
-      context.CanRequest(resource_type, resource_request,
-                         MemoryCache::RemoveFragmentIdentifierIfNeeded(
-                             bundle_url_for_uuid_resources.IsValid()
-                                 ? bundle_url_for_uuid_resources
-                                 : params.Url()),
-                         options, reporting_disposition,
-                         resource_request.GetRedirectInfo());
-
-  if (context.CalculateIfAdSubresource(resource_request,
-                                       std::nullopt /* alias_url */,
-                                       resource_type, options.initiator_info)) {
-    resource_request.SetIsAdResource();
-  }
-
-  if (blocked_reason) {
-    return blocked_reason;
-  }
-
-  // For initial requests, call PrepareRequest() here before revalidation
-  // policy is determined.
-  context.PrepareRequest(resource_request, params.MutableOptions(),
-                         virtual_time_pauser, resource_type);
-
-  if (!params.Url().IsValid()) {
-    return ResourceRequestBlockedReason::kOther;
-  }
-
-  return blocked_reason;
-}
-
-void UpgradeResourceRequestForLoaderNew(
+void UpgradeResourceRequestForLoader(
     ResourceType resource_type,
     FetchParameters& params,
     FetchContext& context,
     ResourceRequestContext& resource_request_context,
     WebScopedVirtualTimePauser& virtual_time_pauser) {
-  DCHECK(RuntimeEnabledFeatures::
-             MinimimalResourceRequestPrepBeforeCacheLookupEnabled());
   ResourceRequest& resource_request = params.MutableResourceRequest();
   const ResourceLoaderOptions& options = params.Options();
 
@@ -338,8 +205,6 @@
     ResourceRequestContext& resource_request_context,
     FetchContext& context,
     FetchParameters& params) {
-  DCHECK(RuntimeEnabledFeatures::
-             MinimimalResourceRequestPrepBeforeCacheLookupEnabled());
   ResourceRequest& resource_request = params.MutableResourceRequest();
   const ResourceLoaderOptions& options = params.Options();
   const ReportingDisposition reporting_disposition =
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.h b/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.h
index bfb8c910..c2b0bb3f 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_request_utils.h
@@ -55,15 +55,16 @@
 
 // Prepares the underlying ResourceRequest for `params` with enough information
 // to do a cache lookup. If a cached value is not used,
-// PrepareResourceRequest() must be called.
+// UpgradeResourceRequestForLoader() must be called.
+//
+// `bundle_url_for_uuid_resources` is an optional bundle URL for
+// uuid-in-package: resources for security checks. Should only be set when the
+// request is WebBundle.
 //
 // Returns std::nullopt if loading the ResourceRequest in `params` is not
 // blocked. Otherwise, returns a blocked reason.
 // This method may modify the ResourceRequest in `params` according to
 // `context` and `resource_type`.
-//
-// This function should only be called if
-// MinimimalResourceRequestPrepBeforeCacheLookupEnabled is enabled.
 BLINK_PLATFORM_EXPORT std::optional<ResourceRequestBlockedReason>
 PrepareResourceRequestForCacheAccess(
     ResourceType type,
@@ -73,29 +74,7 @@
     FetchContext& context,
     FetchParameters& params);
 
-// `virtual_time_pauser` may be set by this method.
-//
-// `compute_load_priority_callback` is used to compute the priority of the
-// ResourceRequest if not yet set before calling this function.
-//
-// `trace_callback` is executed at some point to enable tracing within this
-// function.
-//
-// `bundle_url_for_uuid_resources` is an optional bundle URL for
-// uuid-in-package: resources for security checks. Should only be set when the
-// request is WebBundle.
-// NOTE: PrepareResourceRequest  is temporary and will only enabled if a bug is
-// encountered (it's behind a kill switch).
-BLINK_PLATFORM_EXPORT std::optional<ResourceRequestBlockedReason>
-PrepareResourceRequest(
-    ResourceType resource_type,
-    const FetchClientSettingsObject& fetch_client_settings_object,
-    FetchParameters& params,
-    FetchContext& context,
-    WebScopedVirtualTimePauser& virtual_time_pauser,
-    ResourceRequestContext& resource_request_context,
-    const KURL& bundle_url_for_uuid_resources);
-BLINK_PLATFORM_EXPORT void UpgradeResourceRequestForLoaderNew(
+BLINK_PLATFORM_EXPORT void UpgradeResourceRequestForLoader(
     ResourceType resource_type,
     FetchParameters& params,
     FetchContext& context,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 61a94e97..baf476b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1114,6 +1114,13 @@
       status: "stable",
     },
     {
+      // CSS if() function for custom properties. Flag is used only for media
+      // queries support in conditions.
+      // https://drafts.csswg.org/css-values-5/#if-notation.
+      name: "CSSInlineIfForMediaQueries",
+      status: "test",
+    },
+    {
       // CSS if() function for custom properties. Flag is used only for style
       // queries support in conditions.
       // https://drafts.csswg.org/css-values-5/#if-notation.
@@ -1410,7 +1417,7 @@
       // rendering using alternate content in the UA shadowroot which is
       // customizable.
       name: "CustomizableSelect",
-      status: "experimental",
+      status: "stable",
       // SelectParserRelaxation is needed to allow more content in <select>
       // than just <option>s etc.
       depends_on: ["SelectParserRelaxation"],
@@ -2870,11 +2877,6 @@
       name: "MiddleClickAutoscroll",
       status: "test",
     },
-    // Killswitch. Remove after 1 or 2 stable releases.
-    {
-      name: "MinimimalResourceRequestPrepBeforeCacheLookup",
-      status: "stable",
-    },
     {
       name: "MobileLayoutTheme",
     },
@@ -3975,7 +3977,7 @@
       // only <option>, <optgroup>, and <hr>.
       // https://github.com/whatwg/html/issues/10310
       name: "SelectParserRelaxation",
-      status: "experimental",
+      status: "stable",
       public: true,
     },
     {
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index c21568b..36dbc64 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -1488,5 +1488,5 @@
 crbug.com/393175977 [ Debug Mac14 ] http/tests/inspector-protocol/tracing/screenshots.js [ Slow ]
 
 # Gardener 2025-02-18
-crbug.com/391006701 [ Debug ] virtual/gpu-rasterization/images/yuv-decode-eligible/color-profile-image-profile-match.html [ Slow ]
-crbug.com/391006701 [ Debug ] virtual/gpu-rasterization/images/yuv-decode-eligible/webp-image-decoding.html [ Slow ]
+crbug.com/391006701 [ Debug ] images/yuv-decode-eligible/color-profile-image-profile-match.html [ Slow ]
+crbug.com/391006701 [ Debug ] images/yuv-decode-eligible/webp-image-decoding.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestLists/content_shell.filter b/third_party/blink/web_tests/TestLists/content_shell.filter
index 400734a..517dfa6e 100644
--- a/third_party/blink/web_tests/TestLists/content_shell.filter
+++ b/third_party/blink/web_tests/TestLists/content_shell.filter
@@ -1054,9 +1054,6 @@
 virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/moving-between-documents/after-prepare-*
 virtual/threaded-preload-scanner/external/wpt/html/semantics/scripting-1/the-script-element/moving-between-documents/ordering/*
 virtual/url-non-special/external/wpt/url/failure.html
-virtual/verify-minimal-resource-request-kill-switch/external/wpt/content-security-policy/report-hash/*
-virtual/verify-minimal-resource-request-kill-switch/external/wpt/fetch/fetch-later/new-window.tentative.https.window.html
-virtual/verify-minimal-resource-request-kill-switch/external/wpt/fetch/stale-while-revalidate/stale-*
 virtual/view-transition-layered-capture/*
 virtual/view-transition-mpa-serialization/*
 virtual/view-transition-mpa-serialization/wpt_internal/*
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 9f2a42ef..cb880e3 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -28,33 +28,6 @@
   "TODO(crbug.com/380755410): Find a way to do this systematically.",
 
   {
-    "prefix": "verify-minimal-resource-request-kill-switch",
-    "platforms": ["Linux"],
-    "bases": [
-      "http/tests/cache",
-      "http/tests/devtools",
-      "http/tests/fetch",
-      "http/tests/inspector-protocol",
-      "http/tests/loading",
-      "http/tests/prefetch",
-      "http/tests/preload",
-      "http/tests/security",
-      "http/tests/streams",
-      "http/tests/xmlhttprequest",
-      "external/wpt/content-security-policy/",
-      "external/wpt/cors",
-      "external/wpt/fetch",
-      "external/wpt/loading",
-      "external/wpt/preload",
-      "external/wpt/streams",
-      "external/wpt/webtransport"],
-    "args": [
-      "--disable-features=MinimimalResourceRequestPrepBeforeCacheLookup"
-    ],
-    "owners": ["sky@google.com"],
-    "expires": "Nov 1, 2024"
-  },
-  {
     "prefix": "align-surface-layer",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [
diff --git a/third_party/blink/web_tests/external/wpt/ai/translator/ai_translator_translate.tentative.https.any.js b/third_party/blink/web_tests/external/wpt/ai/translator/ai_translator_translate.tentative.https.any.js
index 5cd8cb8..926325b 100644
--- a/third_party/blink/web_tests/external/wpt/ai/translator/ai_translator_translate.tentative.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/ai/translator/ai_translator_translate.tentative.https.any.js
@@ -138,3 +138,34 @@
     assert_equals(progressEvent.total, 1);
   }
 }, 'AITranslatorFactory.create() monitor option is called correctly.');
+
+promise_test(async t => {
+  const translator =
+      await ai.translator.create({sourceLanguage: 'en', targetLanguage: 'ja'});
+
+  // Strings containing only white space are not translatable.
+  const nonTranslatableStrings = ['', ' ', '     ', ' \r\n\t\f'];
+
+  // Strings containing only control characters are not translatable.
+  for (let c = 0; c < 0x1F; c++) {
+    nonTranslatableStrings.push(String.fromCharCode(c));
+  }
+
+  const translatedNonTranslatableString = await Promise.all(
+      nonTranslatableStrings.map(str => translator.translate(str)));
+
+  // Non translatable strings should be echoed back
+  assert_array_equals(translatedNonTranslatableString, nonTranslatableStrings);
+
+  // Adding translatable text makes it translatable.
+  const translatableStrings =
+      nonTranslatableStrings.map(str => `Hello ${str} world`);
+
+  const translatedTranslatableString = await Promise.all(
+      translatableStrings.map(str => translator.translate(str)));
+
+  // All the strings should have been translated in some way.
+  for (let i = 0; i < translatableStrings.length; i++) {
+    assert_not_equals(translatedTranslatableString[i], translatableStrings[i]);
+  }
+}, 'AITranslator.translate() echos non-translatable content');
diff --git a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-availableOnDevice.https.html b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-availableOnDevice.https.html
index 5b39597..fd8e75fd 100644
--- a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-availableOnDevice.https.html
+++ b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-availableOnDevice.https.html
@@ -21,4 +21,21 @@
     "The resolved value of the availableOnDevice promise should be a boolean."
   );
 }, "SpeechRecognition.availableOnDevice resolves with a boolean value.");
+
+promise_test(async (t) => {
+  const iframe = document.createElement("iframe");
+  document.body.appendChild(iframe);
+  const frameWindow = iframe.contentWindow;
+  const frameDOMException = frameWindow.DOMException;
+  const frameSpeechRecognition =
+    frameWindow.SpeechRecognition || frameWindow.webkitSpeechRecognition;
+
+  iframe.remove();
+  await promise_rejects_dom(
+    t,
+    "InvalidStateError",
+    frameDOMException,
+    frameSpeechRecognition.availableOnDevice("en-US"),
+  );
+}, "SpeechRecognition.availableOnDevice rejects in a detached context.");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html
index 6c6fa37b..08d370d 100644
--- a/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html
+++ b/third_party/blink/web_tests/external/wpt/speech-api/SpeechRecognition-installOnDevice.https.html
@@ -38,4 +38,21 @@
     "installOnDevice should resolve with `false` when called with an unsupported language code."
   );
 }, "SpeechRecognition.installOnDevice resolves with a boolean value.");
+
+promise_test(async (t) => {
+  const iframe = document.createElement("iframe");
+  document.body.appendChild(iframe);
+  const frameWindow = iframe.contentWindow;
+  const frameDOMException = frameWindow.DOMException;
+  const frameSpeechRecognition =
+    frameWindow.SpeechRecognition || frameWindow.webkitSpeechRecognition;
+
+  iframe.remove();
+  await promise_rejects_dom(
+    t,
+    "InvalidStateError",
+    frameDOMException,
+    frameSpeechRecognition.installOnDevice("en-US"),
+  );
+}, "SpeechRecognition.installOnDevice rejects in a detached context.");
 </script>
diff --git a/third_party/blink/web_tests/virtual/stable/inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-input-value-expected.txt b/third_party/blink/web_tests/virtual/stable/inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-input-value-expected.txt
deleted file mode 100644
index a7c9d03..0000000
--- a/third_party/blink/web_tests/virtual/stable/inspector-protocol/dom-snapshot/dom-snapshot-getSnapshot-input-value-expected.txt
+++ /dev/null
@@ -1,480 +0,0 @@
-Tests DOMSnapshot.getSnapshot method returning input values.
-{
-    computedStyles : <object>
-    domNodes : [
-        [0] : {
-            backendNodeId : <number>
-            baseURL : <string>
-            childNodeIndexes : [
-                [0] : 1
-            ]
-            documentEncoding : windows-1252
-            documentURL : <string>
-            frameId : <string>
-            layoutNodeIndex : 0
-            nodeName : #document
-            nodeType : 9
-            nodeValue : 
-            scrollOffsetX : 0
-            scrollOffsetY : 0
-        }
-        [1] : {
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 2
-                [1] : 3
-            ]
-            frameId : <string>
-            layoutNodeIndex : 1
-            nodeName : HTML
-            nodeType : 1
-            nodeValue : 
-        }
-        [2] : {
-            backendNodeId : <number>
-            nodeName : HEAD
-            nodeType : 1
-            nodeValue : 
-        }
-        [3] : {
-            attributes : [
-                [0] : {
-                    name : class
-                    value : body-class
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 4
-                [1] : 5
-                [2] : 35
-            ]
-            layoutNodeIndex : 2
-            nodeName : BODY
-            nodeType : 1
-            nodeValue : 
-        }
-        [4] : {
-            backendNodeId : <number>
-            nodeName : #text
-            nodeType : 3
-            nodeValue :  
-        }
-        [5] : {
-            attributes : [
-                [0] : {
-                    name : style
-                    value : width: 200px
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 6
-                [1] : 7
-                [2] : 15
-                [3] : 16
-                [4] : 19
-                [5] : 20
-                [6] : 22
-                [7] : 23
-                [8] : 24
-                [9] : 25
-                [10] : 26
-                [11] : 27
-                [12] : 28
-                [13] : 29
-                [14] : 30
-                [15] : 31
-                [16] : 34
-            ]
-            layoutNodeIndex : 3
-            nodeName : DIV
-            nodeType : 1
-            nodeValue : 
-        }
-        [6] : {
-            backendNodeId : <number>
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [7] : {
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 8
-                [1] : 10
-            ]
-            layoutNodeIndex : 4
-            nodeName : SELECT
-            nodeType : 1
-            nodeValue : 
-        }
-        [8] : {
-            attributes : [
-                [0] : {
-                    name : aria-hidden
-                    value : true
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 9
-            ]
-            layoutNodeIndex : 5
-            nodeName : DIV
-            nodeType : 1
-            nodeValue : 
-            shadowRootType : user-agent
-        }
-        [9] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 6
-            nodeName : #text
-            nodeType : 3
-            nodeValue : Option 1
-            shadowRootType : user-agent
-        }
-        [10] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : select-options
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 11
-                [1] : 13
-            ]
-            nodeName : SLOT
-            nodeType : 1
-            nodeValue : 
-            shadowRootType : user-agent
-        }
-        [11] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : OptionSelected
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 12
-            ]
-            nodeName : OPTION
-            nodeType : 1
-            nodeValue : 
-            optionSelected : true
-        }
-        [12] : {
-            backendNodeId : <number>
-            nodeName : #text
-            nodeType : 3
-            nodeValue : Option 1
-            shadowRootType : user-agent
-        }
-        [13] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : OptionNotSelected
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 14
-            ]
-            nodeName : OPTION
-            nodeType : 1
-            nodeValue : 
-            optionSelected : false
-        }
-        [14] : {
-            backendNodeId : <number>
-            nodeName : #text
-            nodeType : 3
-            nodeValue : Option 2
-            shadowRootType : user-agent
-        }
-        [15] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 7
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [16] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : TextInput
-                }
-                [1] : {
-                    name : value
-                    value : InputValue
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 17
-            ]
-            inputValue : InputValue
-            isClickable : true
-            layoutNodeIndex : 8
-            nodeName : INPUT
-            nodeType : 1
-            nodeValue : 
-        }
-        [17] : {
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 18
-            ]
-            isClickable : true
-            layoutNodeIndex : 9
-            nodeName : DIV
-            nodeType : 1
-            nodeValue : 
-            shadowRootType : user-agent
-        }
-        [18] : {
-            backendNodeId : <number>
-            isClickable : true
-            layoutNodeIndex : 10
-            nodeName : #text
-            nodeType : 3
-            nodeValue : InputValue
-            shadowRootType : user-agent
-        }
-        [19] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 11
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [20] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : ButtonInput
-                }
-                [1] : {
-                    name : type
-                    value : button
-                }
-                [2] : {
-                    name : value
-                    value : ButtonValue
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 21
-            ]
-            inputValue : ButtonValue
-            isClickable : true
-            layoutNodeIndex : 12
-            nodeName : INPUT
-            nodeType : 1
-            nodeValue : 
-        }
-        [21] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 13
-            nodeName : #text
-            nodeType : 3
-            nodeValue : ButtonValue
-            shadowRootType : user-agent
-        }
-        [22] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 14
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [23] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : RadioInputChecked
-                }
-                [1] : {
-                    name : value
-                    value : RadioInputValue
-                }
-                [2] : {
-                    name : type
-                    value : radio
-                }
-                [3] : {
-                    name : checked
-                    value : 
-                }
-            ]
-            backendNodeId : <number>
-            inputChecked : true
-            inputValue : RadioInputValue
-            isClickable : true
-            layoutNodeIndex : 15
-            nodeName : INPUT
-            nodeType : 1
-            nodeValue : 
-        }
-        [24] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 16
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [25] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : RadioInputUnchecked
-                }
-                [1] : {
-                    name : type
-                    value : radio
-                }
-            ]
-            backendNodeId : <number>
-            inputChecked : false
-            inputValue : on
-            isClickable : true
-            layoutNodeIndex : 17
-            nodeName : INPUT
-            nodeType : 1
-            nodeValue : 
-        }
-        [26] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 18
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [27] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : CheckboxInputChecked
-                }
-                [1] : {
-                    name : value
-                    value : CheckInputValue
-                }
-                [2] : {
-                    name : type
-                    value : checkbox
-                }
-                [3] : {
-                    name : checked
-                    value : 
-                }
-            ]
-            backendNodeId : <number>
-            inputChecked : true
-            inputValue : CheckInputValue
-            isClickable : true
-            layoutNodeIndex : 19
-            nodeName : INPUT
-            nodeType : 1
-            nodeValue : 
-        }
-        [28] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 20
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [29] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : CheckboxInputUnchecked
-                }
-                [1] : {
-                    name : type
-                    value : checkbox
-                }
-                [2] : {
-                    name : value
-                    value : 
-                }
-            ]
-            backendNodeId : <number>
-            inputChecked : false
-            inputValue : 
-            isClickable : true
-            layoutNodeIndex : 21
-            nodeName : INPUT
-            nodeType : 1
-            nodeValue : 
-        }
-        [30] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 22
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-        [31] : {
-            attributes : [
-                [0] : {
-                    name : id
-                    value : TextArea
-                }
-            ]
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 32
-            ]
-            layoutNodeIndex : 23
-            nodeName : TEXTAREA
-            nodeType : 1
-            nodeValue : 
-            textValue : TextAreaValue
-        }
-        [32] : {
-            backendNodeId : <number>
-            childNodeIndexes : [
-                [0] : 33
-            ]
-            isClickable : true
-            layoutNodeIndex : 24
-            nodeName : DIV
-            nodeType : 1
-            nodeValue : 
-            shadowRootType : user-agent
-        }
-        [33] : {
-            backendNodeId : <number>
-            isClickable : true
-            layoutNodeIndex : 25
-            nodeName : #text
-            nodeType : 3
-            nodeValue : TextAreaValue
-            shadowRootType : user-agent
-        }
-        [34] : {
-            backendNodeId : <number>
-            layoutNodeIndex : 26
-            nodeName : #text
-            nodeType : 3
-            nodeValue :  
-        }
-        [35] : {
-            backendNodeId : <number>
-            nodeName : #text
-            nodeType : 3
-            nodeValue :    
-        }
-    ]
-    layoutTreeNodes : <object>
-}
-
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
index a50a9e0..0fac161 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/element-instance-property-listing-expected.txt
@@ -992,6 +992,7 @@
     property validity
     property value
     property willValidate
+html element selectedcontent
 html element slot
     property assign
     property assignedElements
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 0682e600..ec63db8 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4547,6 +4547,9 @@
     setter selectedIndex
     setter size
     setter value
+interface HTMLSelectedContentElement : HTMLElement
+    attribute @@toStringTag
+    method constructor
 interface HTMLSlotElement : HTMLElement
     attribute @@toStringTag
     getter name
diff --git a/third_party/blink/web_tests/virtual/verify-minimal-resource-request-kill-switch/README.md b/third_party/blink/web_tests/virtual/verify-minimal-resource-request-kill-switch/README.md
deleted file mode 100644
index 90905a46..0000000
--- a/third_party/blink/web_tests/virtual/verify-minimal-resource-request-kill-switch/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Test suite that disables MinimimalResourceRequestPrepBeforeCacheLookup
-
-This virtual suites verifies the kill switch
-MinimimalResourceRequestPrepBeforeCacheLookup works. The kill switch is rather
-invasive, and easy to regress.
-
-See bug 359910398.
diff --git a/third_party/catapult b/third_party/catapult
index 756ddec..cfa9c31 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 756ddec12fabb51aa3743ce9878b3242244806fa
+Subproject commit cfa9c31cbefb033f14b4281483ab9abcf43e4dc6
diff --git a/third_party/dawn b/third_party/dawn
index cb2eb64..18f45d0 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit cb2eb64cf9e272cf20b97b3b5a03558160ac1fa5
+Subproject commit 18f45d0373057395c9875b455035ef19ca6a655c
diff --git a/third_party/depot_tools b/third_party/depot_tools
index 0a3addb..e69b086 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit 0a3addbf632a21bd96c25107719749c76746b977
+Subproject commit e69b086c0f3764fe1211435b297f793e02ecd13e
diff --git a/third_party/libaom/BUILD.gn b/third_party/libaom/BUILD.gn
index 1d548ad..ba300a2f 100644
--- a/third_party/libaom/BUILD.gn
+++ b/third_party/libaom/BUILD.gn
@@ -12,12 +12,6 @@
 import("//third_party/libaom/options.gni")
 import("//third_party/nasm/nasm_assemble.gni")
 
-buildflag_header("libaom_buildflags") {
-  header = "libaom_buildflags.h"
-
-  flags = [ "ENABLE_LIBAOM=$enable_libaom" ]
-}
-
 # Sets the architecture name for building libaom.
 if (current_cpu == "x86") {
   cpu_arch_full = "ia32"
@@ -382,7 +376,6 @@
     deps += [ "//third_party/cpu_features:ndk_compat" ]
   }
   public_configs = [ ":libaom_public_config" ]
-  public_deps = [ ":libaom_buildflags" ]
 }
 
 static_library("libaomrc") {
diff --git a/third_party/mediapipe/README.chromium b/third_party/mediapipe/README.chromium
index 1029ac8..8a73af4e 100644
--- a/third_party/mediapipe/README.chromium
+++ b/third_party/mediapipe/README.chromium
@@ -73,6 +73,9 @@
  This fixes the issue by using std::numeric_limits instead of NAN defined in
  <cmath>.
 
+* type-map-fix-unused-variable-warning.patch - Replaces `MapName` in
+mediapipe/framework/type_map.h header to work around "unused variable" compiler
+warning (treated as error) when building.
 
 Patches which need to be upstreamed differently than they are patched
 =====================================================================
@@ -101,7 +104,8 @@
 MediaPipe (i.e. we need a replacement for `EnsureEglThreadRelease()` in
 gl_context_egl.cc).
 
-* no-consume.patch - MediaPipe still uses shared_ptr::unique() but only in code that Chrome does not exercise. This needs to actually be fixed properly upstream.
+* no-consume.patch - MediaPipe still uses shared_ptr::unique() but only in code
+that Chrome does not exercise. This needs to actually be fixed properly upstream.
 
 Other patches
 =============
diff --git a/third_party/mediapipe/patches/type-map-fix-unused-variable-warning.patch b/third_party/mediapipe/patches/type-map-fix-unused-variable-warning.patch
new file mode 100644
index 0000000..516ac3f
--- /dev/null
+++ b/third_party/mediapipe/patches/type-map-fix-unused-variable-warning.patch
@@ -0,0 +1,25 @@
+From 2c3b206fd7bde1b86c65c440008afd382f040d3f Mon Sep 17 00:00:00 2001
+From: Piotr Bialecki <bialpio@chromium.org>
+Date: Fri, 14 Feb 2025 14:52:58 -0800
+Subject: [PATCH] Fix unused variable compiler warning in type_map.h
+
+---
+ third_party/mediapipe/src/mediapipe/framework/type_map.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/third_party/mediapipe/src/mediapipe/framework/type_map.h b/third_party/mediapipe/src/mediapipe/framework/type_map.h
+index f03f48ce7851c..123668766bd98 100644
+--- a/third_party/mediapipe/src/mediapipe/framework/type_map.h
++++ b/third_party/mediapipe/src/mediapipe/framework/type_map.h
+@@ -148,7 +148,7 @@ class StaticMap {
+    public:
+     ValueInserter(const char* file_and_line, const KeyType& key,
+                   const MediaPipeTypeData& value) {
+-      MapName* static_map = GetMap();
++      StaticMap* static_map = GetMap();
+       absl::MutexLock l(&(static_map->map_lock_));
+ 
+       typename MapType::iterator it = static_map->internal_map_.find(key);
+-- 
+2.47.1.windows.2
+
diff --git a/third_party/mediapipe/src/mediapipe/framework/type_map.h b/third_party/mediapipe/src/mediapipe/framework/type_map.h
index 9c93374..d7dca6110 100644
--- a/third_party/mediapipe/src/mediapipe/framework/type_map.h
+++ b/third_party/mediapipe/src/mediapipe/framework/type_map.h
@@ -148,7 +148,7 @@
    public:
     ValueInserter(const char* file_and_line, const KeyType& key,
                   const MediaPipeTypeData& value) {
-      MapName* static_map = GetMap();
+      StaticMap* static_map = GetMap();
       absl::MutexLock l(&(static_map->map_lock_));
 
       typename MapType::iterator it = static_map->internal_map_.find(key);
diff --git a/third_party/perfetto b/third_party/perfetto
index 4ce2473..26425fc 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 4ce2473d1db95a2a4e9648941165a209feb94f39
+Subproject commit 26425fcdd18c91a10f59740765c53c5079cf61e3
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index cdf2938..fc2030c8 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit cdf2938973d2a4e7c33fdf24012565f3cbf961a2
+Subproject commit fc2030c82b883131bf452ba2be36e41f95ae5475
diff --git a/third_party/webrtc b/third_party/webrtc
index 4f2c1b8..4aaaa684 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 4f2c1b8f947a15efd795df758c11b06a4e94646b
+Subproject commit 4aaaa6848bc2521f0ab75a46c4bfeaf68b8b9ffb
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 548f6929..232b845 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10661,6 +10661,8 @@
   <int value="-1374377956" label="SeparateWebAppShortcutBadgeIcon:disabled"/>
   <int value="-1374048865" label="CpuAffinityRestrictToLittleCores:enabled"/>
   <int value="-1373942769" label="WebAuthenticationCtap2:disabled"/>
+  <int value="-1373772799"
+      label="DisableFacilitatedPaymentsMerchantAllowlist:disabled"/>
   <int value="-1373705581" label="ManualSaving:enabled"/>
   <int value="-1373048294"
       label="ChromeCleanupScanCompletedNotification:enabled"/>
@@ -19201,6 +19203,8 @@
   <int value="1975657253" label="ScalableAppList:disabled"/>
   <int value="1976644015" label="enable-forbid-sync-xhr-in-page-dismissal"/>
   <int value="1976885976" label="DefaultChatWebApp:disabled"/>
+  <int value="1978453875"
+      label="DisableFacilitatedPaymentsMerchantAllowlist:enabled"/>
   <int value="1978548617" label="FilesZipMount:enabled"/>
   <int value="1978570295" label="RawDraw:disabled"/>
   <int value="1978633725" label="EnableWifiQos:disabled"/>
diff --git a/tools/metrics/histograms/metadata/ash/enums.xml b/tools/metrics/histograms/metadata/ash/enums.xml
index 5a30d9f..2c760e8d 100644
--- a/tools/metrics/histograms/metadata/ash/enums.xml
+++ b/tools/metrics/histograms/metadata/ash/enums.xml
@@ -2031,6 +2031,37 @@
   <int value="25" label="New Google Doc populated action failed on execution"/>
   <int value="26"
       label="Copy to clipboard populated action failed on execution"/>
+  <int value="27"
+      label="UI access checks resulted in &quot;do not show UI&quot;"/>
+  <int value="28"
+      label="UI access checks resulted in &quot;show UI, but user not
+             consented&quot;"/>
+  <int value="29"
+      label="UI access checks resulted in &quot;show UI and user
+             consented&quot;"/>
+  <int value="30"
+      label="&quot;Do not show UI&quot; resulted from no Shell instance"/>
+  <int value="31"
+      label="&quot;Do not show UI&quot; resulted from no ScannerController on
+             Shell"/>
+  <int value="32"
+      label="&quot;Do not show UI&quot; resulted from enterprise policy"/>
+  <int value="33"
+      label="&quot;Do not show UI&quot; resulted from no profile scoped
+             delegate"/>
+  <int value="34"
+      label="&quot;Do not show UI&quot; resulted from settings toggle"/>
+  <int value="35"
+      label="&quot;Do not show UI&quot; resulted from feature flag"/>
+  <int value="36"
+      label="&quot;Do not show UI&quot; resulted from feature management"/>
+  <int value="37" label="&quot;Do not show UI&quot; resulted from secret key"/>
+  <int value="38"
+      label="&quot;Do not show UI&quot; resulted from account capabilities"/>
+  <int value="39" label="&quot;Do not show UI&quot; resulted from country"/>
+  <int value="40" label="&quot;Do not show UI&quot; resulted from kiosk mode"/>
+  <int value="41" label="Launcher was shown without Sunfish-session button"/>
+  <int value="42" label="Launcher was shown with Sunfish-session button"/>
 </enum>
 
 <!-- LINT.ThenChange(//ash/scanner/scanner_metrics.h:ScannerFeatureUserState) -->
diff --git a/tools/metrics/histograms/metadata/platform/histograms.xml b/tools/metrics/histograms/metadata/platform/histograms.xml
index 7775f77..0eee14d 100644
--- a/tools/metrics/histograms/metadata/platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/platform/histograms.xml
@@ -1797,6 +1797,16 @@
   </summary>
 </histogram>
 
+<histogram name="Platform.Missive.StartStatus"
+    enum="EnterpriseCloudReportingStatusCode" expires_after="2025-12-01">
+  <owner>lbaraz@chromium.org</owner>
+  <owner>cros-reporting-team@google.com</owner>
+  <summary>
+    Recorded status of every instance missive starts (usually once per device
+    boot).
+  </summary>
+</histogram>
+
 <histogram name="Platform.Missive.StorageDegradationAmount" units="KiB"
     expires_after="2025-08-03">
   <owner>lbaraz@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index c56d302b..0e16045 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/v49.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "9ce3a3e0b5548a8bd974f6f8b9c036f2995bc5bc",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/bd0d3f095b06f47fe223562d2a5ab758327e9e7a/trace_processor_shell.exe"
+            "hash": "edd80500fcee7aefcd5e7adb33b12d9da6f0c4c1",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/26425fcdd18c91a10f59740765c53c5079cf61e3/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "a15d8362d80cfd7cd8d785cf6afc22586de688cd",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v49.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "51f6442c2c2f14b81bf367152b28c5765aa5730f",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/707752fc19b83035a35c2d18410d2d0b5b72eb83/trace_processor_shell"
+            "hash": "73bbc23a3152a93029d5b566500d72f7167dee58",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/26425fcdd18c91a10f59740765c53c5079cf61e3/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/v8 b/v8
index fe2f760..d0992c4 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit fe2f760357d5ca88c93d9a85df65d8857dd6d065
+Subproject commit d0992c4954f47858b3996d9cd34e0f948be36a1e