diff --git a/DEPS b/DEPS
index c9399e7..effd7de 100644
--- a/DEPS
+++ b/DEPS
@@ -280,15 +280,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '648d444741bd3ba3f105aafe4b86841c7f05be9b',
+  'skia_revision': '320586ca7966c2e9fa77456de01f479d7f5a6da0',
   # 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': '5cd1ea449b74b8bc969825e24e9756e335d54e64',
+  'v8_revision': '764291ffbf2b7850d3fc8e25f60ed17fa6044459',
   # 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': '9f6c27832becdd66ae3db6c0bf261b85e6a87c20',
+  'angle_revision': 'd8efbad88cb36a1d0d1baa6317145e80c6b89bf7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -356,7 +356,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': 'd64333a2353b602e2623119369c0b120c505bd22',
+  'crossbench_revision': '7ed7cb238e3999bc37a0bfa6d33c0d17fcadf17f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -372,7 +372,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'cfa1e22c8d59a3f2912aa488172f7185a6eecfad',
+  'devtools_frontend_revision': '6551e43966d7dd4d085ca6c0fc05a9ef2f3f4176',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -496,11 +496,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling llvm-libc
   # and whatever else without interference from each other.
-  'llvm_libc_revision':    'ac382467315c999745520e6d4de94a3e54a95789',
+  'llvm_libc_revision':    '2394cbb7cbc6683052e6ecb33633b293baf161eb',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
-  'libcxx_revision':       '5e0e903f1345443d9e4c5946c9af1fa95a974063',
+  'libcxx_revision':       'd0ddad5b79581e19d8e1aec627bb2ad86e1554cd',
 
   # GN CIPD package version.
   'gn_version': 'git_revision:468c6128db7fabe32a29d4753460ef53594406fc',
@@ -1304,7 +1304,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'e27dfc91559d125063c0d02b92dacd6cb92bc5f9',
+    'd8f582130305674abff573b8d7a7ea738f50c427',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1769,7 +1769,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1a227b9c2e36d43819765488191b04d8089b5709',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0eb1f9f5bb6e004cd0593b65bf01a9022d5b69a4',
       'condition': 'checkout_chromeos',
   },
 
@@ -2301,7 +2301,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '57f2ef884f18866748bf7a36526caab554111a03',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '076d3983da9bbbe312f1c8d5fb77867e9a41779d',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2806,7 +2806,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'VhS75EnBq_0W8DQYz9kUpVmjJv0gozkyTv5icEfWkh4C',
+        'version': 'CyZgpnOcTt-Jzk8OZFcMbrw6v9tBLFPct_NK_RCAXlYC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2817,7 +2817,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': '63KSP5KgoDL_Gxxld7nO6O4oel1ddNG1K4THDh0y-9cC',
+        'version': 'Q0SF8N4aGTPZUoFWAblEDO623G_Vo0VQHgPwOINJMqoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4386,7 +4386,7 @@
 
   'src/components/autofill/core/browser/form_parsing/internal_resources': {
       'url': Var('chrome_git') + '/chrome/components/autofill_regex_patterns.git' + '@' +
-        '9efd7cc2c1a908d3a67865003c25cf09bc876257',
+        '8572e719bbf714846a735cdbfc1380ff6cf6a6a7',
       'condition': 'checkout_src_internal',
   },
 
@@ -4445,7 +4445,7 @@
 
   'src/components/test/data/autofill/heuristics-json/internal': {
       'url': Var('chrome_git') + '/chrome/test/autofill/structured_forms.git' + '@' +
-        '9209becc24ddfd96a485da2298d78d2b27479fef',
+        'e2194054cb0a44ecb7a11bc71a09d0600f3d2a2d',
       'condition': 'checkout_chromium_autofill_test_dependencies',
   },
 
@@ -4475,7 +4475,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '60074ea61a85f6bbe01d1acf803f57a1e29c9218',
+        'b597a98ad10c2c70d645059bf0e56de675144966',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 9d4391f6..0e26f8b 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -287,15 +287,6 @@
                 "When enabled, AutofillAgent will store its cached form and fields as renderer ids "
                         + "instead of holding strong references to blink::WebElement objects."),
         Flag.baseFeature(
-                AutofillFeatures.AUTOFILL_USE_AU_ADDRESS_MODEL,
-                "When enabled, Autofill uses a custom address model for Australia."),
-        Flag.baseFeature(
-                AutofillFeatures.AUTOFILL_USE_CA_ADDRESS_MODEL,
-                "When enabled, Autofill uses a custom address model for Canada."),
-        Flag.baseFeature(
-                AutofillFeatures.AUTOFILL_USE_DE_ADDRESS_MODEL,
-                "When enabled, Autofill uses a custom address model for Germany."),
-        Flag.baseFeature(
                 AutofillFeatures.AUTOFILL_USE_FR_ADDRESS_MODEL,
                 "When enabled, Autofill uses a custom address model for France."),
         Flag.baseFeature(
@@ -695,10 +686,6 @@
                 ContentFeatures.BACK_FORWARD_CACHE_MEDIA_SESSION_SERVICE,
                 "Enables media session usage when bfcache is enabled"),
         Flag.baseFeature(
-                ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION,
-                "Enables text selection menu item modification based on "
-                        + "embedder implementation."),
-        Flag.baseFeature(
                 AwFeatures.WEBVIEW_AUTO_SAA,
                 "Enable auto granting storage access API requests. This will be done "
                         + "if a relationship is detected between the app and the website."),
diff --git a/android_webview/java/src/org/chromium/android_webview/selection/SamsungSelectionActionMenuDelegate.java b/android_webview/java/src/org/chromium/android_webview/selection/SamsungSelectionActionMenuDelegate.java
index cdd1afc9..d93bf3e 100644
--- a/android_webview/java/src/org/chromium/android_webview/selection/SamsungSelectionActionMenuDelegate.java
+++ b/android_webview/java/src/org/chromium/android_webview/selection/SamsungSelectionActionMenuDelegate.java
@@ -31,11 +31,9 @@
 import org.chromium.base.PackageUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.components.autofill.AutofillSelectionActionMenuDelegate;
-import org.chromium.content_public.browser.ContentFeatureMap;
 import org.chromium.content_public.browser.SelectionMenuItem;
 import org.chromium.content_public.browser.SelectionPopupController;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.content_public.common.ContentFeatures;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -249,9 +247,7 @@
     }
 
     public static boolean shouldUseSamsungMenuItemOrdering() {
-        return Build.VERSION.SDK_INT <= Build.VERSION_CODES.VANILLA_ICE_CREAM
-                && isSamsungDevice()
-                && ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION);
+        return Build.VERSION.SDK_INT <= Build.VERSION_CODES.VANILLA_ICE_CREAM && isSamsungDevice();
     }
 
     private static int getMenuItemOrder(@IdRes int id) {
@@ -279,8 +275,7 @@
         }
         if (!isSamsungDevice()
                 || Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE
-                || Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM
-                || !ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION)) {
+                || Build.VERSION.SDK_INT > Build.VERSION_CODES.VANILLA_ICE_CREAM) {
             sIsManageAppsSupported = false;
             return false;
         }
@@ -373,7 +368,6 @@
             @NonNull String selectedText, boolean isSelectionPassword) {
         return isSamsungDevice()
                 && Build.VERSION.SDK_INT == Build.VERSION_CODES.VANILLA_ICE_CREAM
-                && ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION)
                 && !selectedText.isEmpty()
                 && !isSelectionPassword
                 && SCAN_TEXT_ID != 0
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f48229c..7f2b3a83 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -3918,7 +3918,6 @@
     "host/ash_window_tree_host_unified_unittest.cc",
     "ime/ime_controller_impl_unittest.cc",
     "in_session_auth/auth_dialog_contents_view_unittest.cc",
-    "in_session_auth/authentication_dialog_unittest.cc",
     "in_session_auth/in_session_auth_dialog_contents_view_unittest.cc",
     "in_session_auth/webauthn_dialog_controller_impl_unittest.cc",
     "keyboard/keyboard_controller_impl_unittest.cc",
diff --git a/ash/in_session_auth/BUILD.gn b/ash/in_session_auth/BUILD.gn
index 6c079a69..870754d 100644
--- a/ash/in_session_auth/BUILD.gn
+++ b/ash/in_session_auth/BUILD.gn
@@ -10,8 +10,6 @@
   sources = [
     "auth_dialog_contents_view.cc",
     "auth_dialog_contents_view.h",
-    "authentication_dialog.cc",
-    "authentication_dialog.h",
     "in_session_auth_dialog.cc",
     "in_session_auth_dialog.h",
     "in_session_auth_dialog_contents_view.cc",
diff --git a/ash/in_session_auth/authentication_dialog.cc b/ash/in_session_auth/authentication_dialog.cc
deleted file mode 100644
index 223aff0b..0000000
--- a/ash/in_session_auth/authentication_dialog.cc
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/in_session_auth/authentication_dialog.h"
-
-#include <memory>
-#include <optional>
-#include <utility>
-
-#include "ash/public/cpp/in_session_auth_token_provider.h"
-#include "ash/public/cpp/shelf_config.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "base/functional/bind.h"
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "chromeos/ash/components/auth_panel/public/shared_types.h"
-#include "chromeos/ash/components/cryptohome/common_types.h"
-#include "chromeos/ash/components/cryptohome/error_util.h"
-#include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
-#include "chromeos/ash/components/login/auth/auth_performer.h"
-#include "chromeos/ash/components/login/auth/public/auth_session_intent.h"
-#include "chromeos/ash/components/login/auth/public/authentication_error.h"
-#include "chromeos/ash/components/login/auth/public/user_context.h"
-#include "chromeos/ash/components/osauth/public/common_types.h"
-#include "ui/base/ime/text_input_type.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/mojom/dialog_button.mojom.h"
-#include "ui/base/mojom/ui_base_types.mojom-shared.h"
-#include "ui/base/ui_base_types.h"
-#include "ui/display/screen.h"
-#include "ui/gfx/geometry/insets.h"
-#include "ui/views/accessibility/view_accessibility.h"
-#include "ui/views/controls/button/label_button.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/controls/textfield/textfield.h"
-#include "ui/views/layout/flex_layout.h"
-#include "ui/views/layout/layout_provider.h"
-#include "ui/views/layout/layout_types.h"
-#include "ui/views/view_class_properties.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/window/dialog_delegate.h"
-
-namespace ash {
-
-namespace {
-
-void AddMargins(views::View* view) {
-  const auto* layout_provider = views::LayoutProvider::Get();
-  const int horizontal_spacing = layout_provider->GetDistanceMetric(
-      views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
-  const int vertical_spacing = layout_provider->GetDistanceMetric(
-      views::DISTANCE_RELATED_CONTROL_VERTICAL);
-
-  view->SetProperty(views::kMarginsKey,
-                    gfx::Insets::VH(vertical_spacing, horizontal_spacing));
-}
-
-void ConfigurePasswordField(views::Textfield* password_field) {
-  const auto password_field_name =
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_PLACEHOLDER);
-  password_field->GetViewAccessibility().SetName(password_field_name);
-  password_field->SetReadOnly(false);
-  password_field->SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_PASSWORD);
-  password_field->SetPlaceholderText(password_field_name);
-  AddMargins(password_field);
-}
-
-void ConfigureInvalidPasswordLabel(views::Label* invalid_password_label) {
-  invalid_password_label->SetProperty(views::kCrossAxisAlignmentKey,
-                                      views::LayoutAlignment::kStart);
-  invalid_password_label->SetEnabledColor(SK_ColorRED);
-  AddMargins(invalid_password_label);
-}
-
-void CenterWidgetOnPrimaryDisplay(views::Widget* widget) {
-  auto bounds = display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
-  bounds.ClampToCenteredSize(widget->GetContentsView()->GetPreferredSize());
-  widget->SetBounds(bounds);
-}
-
-}  // namespace
-
-AuthenticationDialog::AuthenticationDialog(
-    auth_panel::AuthCompletionCallback on_auth_complete,
-    InSessionAuthTokenProvider* auth_token_provider,
-    std::unique_ptr<AuthPerformer> auth_performer,
-    const AccountId& account_id)
-    : password_field_(AddChildView(std::make_unique<views::Textfield>())),
-      invalid_password_label_(AddChildView(std::make_unique<views::Label>())),
-      on_auth_complete_(std::move(on_auth_complete)),
-      auth_performer_(std::move(auth_performer)),
-      auth_token_provider_(auth_token_provider) {
-  // Dialog setup
-  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
-      views::DistanceMetric::DISTANCE_BUBBLE_PREFERRED_WIDTH));
-  SetTitle(l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_TITLE));
-  SetModalType(ui::mojom::ModalType::kSystem);
-
-  // Callback setup
-  SetCancelCallback(base::BindOnce(&AuthenticationDialog::CancelAuthAttempt,
-                                   base::Unretained(this)));
-  SetCloseCallback(base::BindOnce(&AuthenticationDialog::CancelAuthAttempt,
-                                  base::Unretained(this)));
-
-  SetLayoutManager(std::make_unique<views::FlexLayout>())
-      ->SetOrientation(views::LayoutOrientation::kVertical)
-      .SetCollapseMargins(true);
-
-  ConfigureChildViews();
-
-  // We don't want the user to submit an auth factor to cryptohome before the
-  // auth session has started. We re-enable the UI in `OnAuthSessionStarted`
-  SetUIDisabled(true);
-
-  auto user_context = std::make_unique<UserContext>();
-  user_context->SetAccountId(account_id);
-
-  // TODO(b/240147756): Choose the intent based on
-  // `InSessionAuthDialogController::Reason`.
-  auth_performer_->StartAuthSession(
-      std::move(user_context), /*ephemeral=*/false, AuthSessionIntent::kDecrypt,
-      base::BindOnce(&AuthenticationDialog::OnAuthSessionStarted,
-                     weak_factory_.GetWeakPtr()));
-}
-
-AuthenticationDialog::~AuthenticationDialog() = default;
-
-void AuthenticationDialog::Show() {
-  auto* widget = DialogDelegateView::CreateDialogWidget(this,
-                                                        /*context=*/nullptr,
-                                                        /*parent=*/nullptr);
-  CenterWidgetOnPrimaryDisplay(widget);
-  Init();
-  widget->Show();
-}
-
-void AuthenticationDialog::Init() {
-  ConfigureOkButton();
-  password_field_->RequestFocus();
-}
-
-void AuthenticationDialog::NotifyResult(bool success,
-                                        const AuthProofToken& token,
-                                        base::TimeDelta timeout) {
-  if (on_auth_complete_) {
-    std::move(on_auth_complete_).Run(success, token, timeout);
-  }
-}
-
-void AuthenticationDialog::ConfigureOkButton() {
-  views::LabelButton* ok_button = GetOkButton();
-  ok_button->SetText(
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SUBMIT_BUTTON_ACCESSIBLE_NAME));
-  ok_button->SetCallback(base::BindRepeating(
-      &AuthenticationDialog::ValidateAuthFactor, weak_factory_.GetWeakPtr()));
-}
-
-void AuthenticationDialog::SetUIDisabled(bool is_disabled) {
-  SetButtonEnabled(ui::mojom::DialogButton::kOk, !is_disabled);
-  SetButtonEnabled(ui::mojom::DialogButton::kCancel, !is_disabled);
-  password_field_->SetReadOnly(is_disabled);
-}
-
-void AuthenticationDialog::ValidateAuthFactor() {
-  // Clear warning message.
-  invalid_password_label_->SetText({});
-
-  SetUIDisabled(true);
-
-  const auto* password_factor =
-      user_context_->GetAuthFactorsData().FindAnyPasswordFactor();
-  if (!password_factor) {
-    LOG(ERROR) << "Could not find password key";
-    ShowAuthError();
-    return;
-  }
-
-  cryptohome::KeyLabel key_label = password_factor->ref().label();
-
-  // Create a copy of `user_context_` so that we don't lose it to std::move
-  // for future auth attempts
-  auth_performer_->AuthenticateWithPassword(
-      key_label.value(), base::UTF16ToUTF8(password_field_->GetText()),
-      std::make_unique<UserContext>(*user_context_),
-      base::BindOnce(&AuthenticationDialog::OnAuthFactorValidityChecked,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void AuthenticationDialog::OnAuthFactorValidityChecked(
-    std::unique_ptr<UserContext> user_context,
-    std::optional<AuthenticationError> authentication_error) {
-  if (authentication_error.has_value()) {
-    if (cryptohome::ErrorMatches(
-            authentication_error.value().get_cryptohome_error(),
-            user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN)) {
-      // Auth session expired for some reason, start it again and reattempt
-      // authentication.
-      // TODO(b/240147756): Choose the intent based on
-      // `InSessionAuthDialogController::Reason`.
-      auth_performer_->StartAuthSession(
-          std::move(user_context), /*ephemeral=*/false,
-          AuthSessionIntent::kDecrypt,
-          base::BindOnce(&AuthenticationDialog::OnAuthSessionInvalid,
-                         weak_factory_.GetWeakPtr()));
-      return;
-    }
-    LOG(ERROR) << "An error happened during the attempt to validate"
-                  "the password: "
-               << authentication_error.value().get_cryptohome_error();
-    ShowAuthError();
-    return;
-  }
-
-  is_closing_ = true;
-
-  auth_token_provider_->ExchangeForToken(
-      std::move(user_context),
-      base::BindOnce(&AuthenticationDialog::NotifyResult,
-                     weak_factory_.GetWeakPtr(), /*success=*/true));
-
-  SetUIDisabled(false);
-  CancelDialog();
-  return;
-}
-
-void AuthenticationDialog::ShowAuthError() {
-  password_field_->SetInvalid(true);
-  password_field_->SelectAll(false);
-  invalid_password_label_->SetText(
-      l10n_util::GetStringUTF16(IDS_ASH_LOGIN_ERROR_AUTHENTICATING));
-  SetUIDisabled(false);
-}
-
-void AuthenticationDialog::CancelAuthAttempt() {
-  // If dialog is closing after the submission of a valid auth factor,
-  // we should not notify any parties, as they would have already been
-  // notified after `AuthenticationDialog::OnAuthFactorValidityChecked`
-  if (!is_closing_) {
-    NotifyResult(/*success=*/false, /*token=*/{}, /*timeout=*/{});
-  }
-}
-
-void AuthenticationDialog::ConfigureChildViews() {
-  ConfigurePasswordField(password_field_);
-  ConfigureInvalidPasswordLabel(invalid_password_label_);
-}
-
-void AuthenticationDialog::OnAuthSessionInvalid(
-    bool user_exists,
-    std::unique_ptr<UserContext> user_context,
-    std::optional<AuthenticationError> authentication_error) {
-  OnAuthSessionStarted(user_exists, std::move(user_context),
-                       authentication_error);
-  ValidateAuthFactor();
-}
-
-void AuthenticationDialog::OnAuthSessionStarted(
-    bool user_exists,
-    std::unique_ptr<UserContext> user_context,
-    std::optional<AuthenticationError> authentication_error) {
-  if (authentication_error.has_value()) {
-    LOG(ERROR) << "Error starting authsession for in session authentication: "
-               << authentication_error.value().get_cryptohome_error();
-    CancelAuthAttempt();
-  } else if (!user_exists) {
-    LOG(ERROR) << "Attempting to authenticate a user which does not exist. "
-                  "Aborting authentication attempt";
-    CancelAuthAttempt();
-  } else {
-    user_context_ = std::move(user_context);
-    SetUIDisabled(false);
-  }
-}
-
-}  // namespace ash
diff --git a/ash/in_session_auth/authentication_dialog.h b/ash/in_session_auth/authentication_dialog.h
deleted file mode 100644
index 88c42ff8..0000000
--- a/ash/in_session_auth/authentication_dialog.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_IN_SESSION_AUTH_AUTHENTICATION_DIALOG_H_
-#define ASH_IN_SESSION_AUTH_AUTHENTICATION_DIALOG_H_
-
-#include <memory>
-#include <optional>
-
-#include "ash/public/cpp/in_session_auth_token_provider.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/time/time.h"
-#include "chromeos/ash/components/auth_panel/public/shared_types.h"
-#include "chromeos/ash/components/login/auth/auth_performer.h"
-#include "chromeos/ash/components/login/auth/public/user_context.h"
-#include "chromeos/ash/components/osauth/public/common_types.h"
-#include "components/account_id/account_id.h"
-#include "ui/views/controls/label.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/window/dialog_delegate.h"
-
-namespace views {
-class Textfield;
-}
-
-namespace ash {
-
-class AuthenticationError;
-
-// To be used for in-session authentication. Currently, only password
-// is supported, however, there are plans to enrich this dialog to eventually
-// support all configured forms of authentication on the system.
-class AuthenticationDialog : public views::DialogDelegateView {
- public:
-  class TestApi {
-   public:
-    explicit TestApi(AuthenticationDialog* dialog) : dialog_(dialog) {}
-
-    views::Textfield* GetPasswordTextfield() {
-      return dialog_->password_field_;
-    }
-
-   private:
-    raw_ptr<AuthenticationDialog, AcrossTasksDanglingUntriaged> const dialog_;
-  };
-
-  // |on_auth_complete| is called when the user has been authenticated
-  // or when the dialog has been aborted
-  explicit AuthenticationDialog(
-      auth_panel::AuthCompletionCallback on_auth_complete,
-      InSessionAuthTokenProvider* auth_token_provider,
-      std::unique_ptr<AuthPerformer> auth_performer,
-      const AccountId& account_id);
-
-  ~AuthenticationDialog() override;
-
-  // Creates and displays a new instance of a widget that hosts the
-  // AuthenticationDialog.
-  void Show();
-
- private:
-  // Called post widget initialization. For now, this configures the Ok button
-  // with custom behavior needed to handle retry of password entry. Also focuses
-  // the text input field.
-  void Init();
-
-  // Calls `on_auth_complere_` with `success` == true if
-  // authentication was successful, and `success` == false if the dialog was
-  // aborted.
-  void NotifyResult(bool success,
-                    const AuthProofToken& token,
-                    base::TimeDelta timeout);
-
-  // Modifies the Ok button to display the proper string and registers
-  // `ValidateAuthFactor` as a callback.
-  void ConfigureOkButton();
-
-  // Disables the use of the OK and Cancel buttons, makes password text field
-  // read-only.
-  void SetUIDisabled(bool is_disabled);
-
-  // Registered as a callback to the Ok button. Disables UI, and validates the
-  // auth factor.
-  void ValidateAuthFactor();
-
-  // Passed as a callback to `AuthPerformer::AuthenticateWithPassword`, notifies
-  // the dialog of authentication success or failure, in case of failure we
-  // modify the UI appropriately, in case of success we close the dialog.
-  void OnAuthFactorValidityChecked(
-      std::unique_ptr<UserContext> user_context,
-      std::optional<AuthenticationError> cryptohome_error);
-
-  // Show an auth error in the UI and mark the password field as invalid.
-  void ShowAuthError();
-
-  // Registered as a callback to the Cancel and Close buttons. Calls
-  // `NotifyResult` with `success` == false.
-  void CancelAuthAttempt();
-
-  // Configures the different subviews such as the password textfield and the
-  // error message label.
-  void ConfigureChildViews();
-
-  // Passed as a callback to `AuthPerformer::StartAuthSession` in
-  // `OnAuthFactorValidityChecked` when trying to validate the password
-  // and discovering that the auth session is no longer active
-  void OnAuthSessionInvalid(bool user_exists,
-                            std::unique_ptr<UserContext> user_context,
-                            std::optional<AuthenticationError> auth_error);
-
-  // Passed as a callback to `AuthPerformer::StartAuthSession`. Saves the
-  // password key label to pass it later to authentication attempts and handles
-  // errors from cryptohome
-  void OnAuthSessionStarted(bool user_exists,
-                            std::unique_ptr<UserContext> user_context,
-                            std::optional<AuthenticationError> auth_error);
-
-  raw_ptr<views::Textfield> password_field_;
-  raw_ptr<views::Label> invalid_password_label_;
-
-  // See implementation of `CancelAuthAttempt` for details.
-  bool is_closing_ = false;
-
-  auth_panel::AuthCompletionCallback on_auth_complete_;
-
-  // Called when user submits an auth factor to check its validity
-  std::unique_ptr<AuthPerformer> auth_performer_;
-
-  // Non owning pointer, initialized and owned by
-  // `ChromeBrowserMainExtraPartsAsh`.
-  // `auth_token_provider_` will outlive this dialog since it will
-  // be destroyed after `AshShellInit`, which owns the aura
-  // window hierarchy.
-  raw_ptr<InSessionAuthTokenProvider> auth_token_provider_;
-
-  std::unique_ptr<UserContext> user_context_;
-
-  base::WeakPtrFactory<AuthenticationDialog> weak_factory_{this};
-};
-
-}  // namespace ash
-
-#endif  // ASH_IN_SESSION_AUTH_AUTHENTICATION_DIALOG_H_
diff --git a/ash/in_session_auth/authentication_dialog_unittest.cc b/ash/in_session_auth/authentication_dialog_unittest.cc
deleted file mode 100644
index ebfb5922..0000000
--- a/ash/in_session_auth/authentication_dialog_unittest.cc
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/in_session_auth/authentication_dialog.h"
-
-#include <memory>
-#include <optional>
-#include <utility>
-
-#include "ash/public/cpp/in_session_auth_token_provider.h"
-#include "ash/public/cpp/test/mock_in_session_auth_token_provider.h"
-#include "ash/test/ash_test_base.h"
-#include "base/memory/raw_ptr.h"
-#include "base/test/bind.h"
-#include "base/time/time.h"
-#include "chromeos/ash/components/cryptohome/auth_factor.h"
-#include "chromeos/ash/components/cryptohome/common_types.h"
-#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
-#include "chromeos/ash/components/cryptohome/error_types.h"
-#include "chromeos/ash/components/dbus/cryptohome/UserDataAuth.pb.h"
-#include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
-#include "chromeos/ash/components/login/auth/auth_performer.h"
-#include "chromeos/ash/components/login/auth/mock_auth_performer.h"
-#include "chromeos/ash/components/login/auth/public/auth_callbacks.h"
-#include "chromeos/ash/components/login/auth/public/auth_session_intent.h"
-#include "chromeos/ash/components/login/auth/public/authentication_error.h"
-#include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
-#include "chromeos/ash/components/login/auth/public/session_auth_factors.h"
-#include "chromeos/ash/components/osauth/public/common_types.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/strings/ascii.h"
-#include "ui/events/event_constants.h"
-#include "ui/events/keycodes/keyboard_codes_posix.h"
-#include "ui/views/controls/textfield/textfield.h"
-
-namespace ash {
-
-namespace {
-
-using ::cryptohome::KeyLabel;
-using ::testing::_;
-
-const char kTestAccount[] = "user@test.com";
-const char kExpectedPassword[] = "qwerty";
-AuthProofToken kToken = "auth-proof-token";
-
-}  // namespace
-
-class AuthenticationDialogTest : public AshTestBase {
- public:
-  AuthenticationDialogTest() = default;
-
-  void SetUp() override {
-    AshTestBase::SetUp();
-    UserDataAuthClient::InitializeFake();
-    auth_token_provider_ = std::make_unique<MockInSessionAuthTokenProvider>();
-  }
-
-  void StartAuthSession(std::unique_ptr<UserContext> user_context,
-                        bool /*ephemeral*/,
-                        AuthSessionIntent /*intent*/,
-                        AuthPerformer::StartSessionCallback callback) {
-    cryptohome::AuthFactorRef ref{cryptohome::AuthFactorType::kPassword,
-                                  KeyLabel(kCryptohomeGaiaKeyLabel)};
-    cryptohome::AuthFactorCommonMetadata metadata{};
-    cryptohome::AuthFactor factor{std::move(ref), std::move(metadata)};
-    user_context->SetSessionAuthFactors(
-        SessionAuthFactors{{std::move(factor)}});
-    std::move(callback).Run(true, std::move(user_context), std::nullopt);
-  }
-
-  void GetAuthToken(std::unique_ptr<UserContext> user_context,
-                    InSessionAuthTokenProvider::OnAuthTokenGenerated callback) {
-    std::move(callback).Run(kToken, base::Minutes(5));
-  }
-
- protected:
-  void CreateAndShowDialog() {
-    auto auth_performer =
-        std::make_unique<MockAuthPerformer>(UserDataAuthClient::Get());
-    auth_performer_ = auth_performer.get();
-
-    EXPECT_CALL(*auth_performer_, StartAuthSession)
-        .WillRepeatedly(
-            testing::Invoke(this, &AuthenticationDialogTest::StartAuthSession));
-
-    // `dialog_` is a `DialogDelegateView` and will be owned by the
-    // underlying widget.
-    dialog_ = new AuthenticationDialog(
-        base::BindLambdaForTesting([&](bool success,
-                                       const AuthProofToken& token,
-                                       base::TimeDelta timeout) {
-          success_ = success;
-          token_ = token;
-        }),
-        auth_token_provider_.get(), std::move(auth_performer),
-        AccountId::FromUserEmail(kTestAccount));
-
-    test_api_ = std::make_unique<AuthenticationDialog::TestApi>(dialog_);
-
-    dialog_->Show();
-  }
-
-  void TypePassword(const std::string& password) {
-    auto* generator = GetEventGenerator();
-    generator->MoveMouseTo(
-        test_api_->GetPasswordTextfield()->GetBoundsInScreen().CenterPoint());
-    generator->ClickLeftButton();
-
-    for (char c : password) {
-      EXPECT_TRUE(absl::ascii_isalpha(static_cast<unsigned char>(c)));
-      generator->PressAndReleaseKey(
-          static_cast<ui::KeyboardCode>(ui::KeyboardCode::VKEY_A + (c - 'a')),
-          ui::EF_NONE);
-    }
-  }
-
-  void PressOkButton() {
-    auto* generator = GetEventGenerator();
-    generator->MoveMouseTo(
-        dialog_->GetOkButton()->GetBoundsInScreen().CenterPoint());
-    generator->ClickLeftButton();
-  }
-
-  std::optional<bool> success_;
-  AuthProofToken token_;
-  raw_ptr<AuthenticationDialog, AcrossTasksDanglingUntriaged> dialog_;
-  std::unique_ptr<MockInSessionAuthTokenProvider> auth_token_provider_;
-  raw_ptr<MockAuthPerformer, AcrossTasksDanglingUntriaged> auth_performer_;
-  std::unique_ptr<AuthenticationDialog::TestApi> test_api_;
-};
-
-TEST_F(AuthenticationDialogTest, CallbackCalledOnCancel) {
-  CreateAndShowDialog();
-  dialog_->Cancel();
-  EXPECT_TRUE(success_.has_value());
-  EXPECT_EQ(success_.value(), false);
-}
-
-TEST_F(AuthenticationDialogTest, CallbackCalledOnClose) {
-  CreateAndShowDialog();
-  dialog_->Close();
-  EXPECT_TRUE(success_.has_value());
-  EXPECT_EQ(success_.value(), false);
-}
-
-TEST_F(AuthenticationDialogTest, CorrectPasswordProvided) {
-  CreateAndShowDialog();
-  TypePassword(kExpectedPassword);
-
-  EXPECT_CALL(*auth_performer_,
-              AuthenticateWithPassword(kCryptohomeGaiaKeyLabel,
-                                       kExpectedPassword, _, _))
-      .WillOnce([](const std::string& key_label, const std::string& password,
-                   std::unique_ptr<UserContext> user_context,
-                   AuthOperationCallback callback) {
-        std::move(callback).Run(std::move(user_context), std::nullopt);
-      });
-
-  EXPECT_CALL(*auth_token_provider_, ExchangeForToken)
-      .WillOnce(testing::Invoke(this, &AuthenticationDialogTest::GetAuthToken));
-
-  PressOkButton();
-
-  EXPECT_TRUE(success_.has_value());
-  EXPECT_TRUE(success_.value());
-  EXPECT_EQ(token_, kToken);
-}
-
-TEST_F(AuthenticationDialogTest, IncorrectPasswordProvidedThenCorrect) {
-  CreateAndShowDialog();
-  TypePassword("ytrewq");
-
-  EXPECT_CALL(*auth_performer_,
-              AuthenticateWithPassword(kCryptohomeGaiaKeyLabel, _, _, _))
-      .WillRepeatedly([](const std::string& key_label,
-                         const std::string& password,
-                         std::unique_ptr<UserContext> user_context,
-                         AuthOperationCallback callback) {
-        std::move(callback).Run(
-            std::move(user_context),
-            password == kExpectedPassword
-                ? std::nullopt
-                : std::optional<AuthenticationError>{AuthenticationError{
-                      cryptohome::ErrorWrapper::CreateFromErrorCodeOnly(
-                          user_data_auth::
-                              CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND)}});
-      });
-
-  PressOkButton();
-  TypePassword(kExpectedPassword);
-
-  EXPECT_CALL(*auth_token_provider_, ExchangeForToken)
-      .WillOnce(testing::Invoke(this, &AuthenticationDialogTest::GetAuthToken));
-
-  PressOkButton();
-
-  EXPECT_TRUE(success_.has_value());
-  EXPECT_TRUE(success_.value());
-  EXPECT_EQ(token_, kToken);
-}
-
-TEST_F(AuthenticationDialogTest, AuthSessionRestartedWhenExpired) {
-  CreateAndShowDialog();
-  TypePassword(kExpectedPassword);
-
-  int number_of_calls = 0;
-  EXPECT_CALL(*auth_performer_,
-              AuthenticateWithPassword(kCryptohomeGaiaKeyLabel,
-                                       kExpectedPassword, _, _))
-      .WillRepeatedly(
-          [&number_of_calls](const std::string& key_label,
-                             const std::string& password,
-                             std::unique_ptr<UserContext> user_context,
-                             AuthOperationCallback callback) {
-            std::move(callback).Run(
-                std::move(user_context),
-                number_of_calls++
-                    ? std::nullopt
-                    : std::optional<AuthenticationError>{AuthenticationError{
-                          cryptohome::ErrorWrapper::CreateFromErrorCodeOnly(
-                              user_data_auth::
-                                  CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN)}});
-          });
-
-  EXPECT_CALL(*auth_token_provider_, ExchangeForToken)
-      .WillOnce(testing::Invoke(this, &AuthenticationDialogTest::GetAuthToken));
-
-  PressOkButton();
-
-  EXPECT_TRUE(success_.has_value());
-  EXPECT_TRUE(success_.value());
-  EXPECT_EQ(token_, kToken);
-}
-
-}  // namespace ash
diff --git a/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc b/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc
index 07f516f..929ab89 100644
--- a/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc
+++ b/ash/in_session_auth/in_session_auth_dialog_controller_impl.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
-#include "ash/in_session_auth/authentication_dialog.h"
 #include "ash/in_session_auth/in_session_auth_dialog_contents_view.h"
 #include "ash/public/cpp/auth/active_session_auth_controller.h"
 #include "ash/public/cpp/in_session_auth_dialog_controller.h"
@@ -131,25 +130,14 @@
   DCHECK(account_id.is_valid());
   DCHECK_NE(auth_token_provider_, nullptr);
 
-  if (reason == Reason::kAccessPasswordManager &&
-      features::IsUseAuthPanelInSessionEnabled()) {
+  if (reason == Reason::kAccessPasswordManager) {
     Shell::Get()->active_session_auth_controller()->ShowAuthDialog(
         std::make_unique<PasswordManagerAuthRequest>(
             base::UTF8ToUTF16(prompt.value_or("")),
             std::move(on_auth_complete)));
-  } else if (reason == Reason::kAccessAuthenticationSettings &&
-             features::IsUseAuthPanelInSessionEnabled()) {
+  } else if (reason == Reason::kAccessAuthenticationSettings) {
     Shell::Get()->active_session_auth_controller()->ShowAuthDialog(
         std::make_unique<SettingsAuthRequest>(std::move(on_auth_complete)));
-  } else {
-    // We don't manage the lifetime of `AuthenticationDialog` here.
-    // `AuthenticatonDialog` is-a View and it is instead owned by it's widget,
-    // which would properly delete it when the widget is closed.
-    (new AuthenticationDialog(
-         std::move(on_auth_complete), auth_token_provider_,
-         std::make_unique<AuthPerformer>(UserDataAuthClient::Get()),
-         account_id))
-        ->Show();
   }
 }
 
diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java
index 0d29c14..0c1cb4fe 100644
--- a/base/android/java/src/org/chromium/base/ContentUriUtils.java
+++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java
@@ -4,6 +4,8 @@
 
 package org.chromium.base;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -15,7 +17,6 @@
 import android.text.TextUtils;
 import android.webkit.MimeTypeMap;
 
-import androidx.annotation.Nullable;
 import androidx.documentfile.provider.DocumentFile;
 
 import org.jni_zero.CalledByNative;
@@ -23,11 +24,15 @@
 import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
 import java.io.IOException;
 import java.util.List;
 
 /** This class provides methods to access content URI schemes. */
 @JNINamespace("base")
+@NullMarked
 public abstract class ContentUriUtils {
     private static final String TAG = "ContentUriUtils";
     private static final String PATH_TREE = "tree";
@@ -103,6 +108,7 @@
      * @param nativeVector vector to populate with results via Natives#addFileInfoToVector(). Called
      *     only if file is found.
      */
+    @SuppressWarnings("NullAway") // Using broad try/catch to catch NullPointerException
     private static void populateFileInfo(String uriString, boolean listFiles, long nativeVector) {
         String[] columns = {
             DocumentsContract.Document.COLUMN_DOCUMENT_ID,
@@ -145,7 +151,7 @@
         }
 
         ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
-        try (Cursor c = resolver.query(queryUri, columns, null, null, null)) {
+        try (Cursor c = assumeNonNull(resolver.query(queryUri, columns, null, null, null))) {
             while (c.moveToNext()) {
                 String uri =
                         c.isNull(0)
@@ -211,9 +217,8 @@
      * @param uriString the content URI to look up.
      * @return MIME type or null if the input params are empty or invalid.
      */
-    @Nullable
     @CalledByNative
-    public static String getMimeType(@JniType("std::string") String uriString) {
+    public static @Nullable String getMimeType(@JniType("std::string") String uriString) {
         ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
         Uri uri = Uri.parse(uriString);
         if (isVirtualDocument(uri)) {
@@ -232,8 +237,8 @@
      *     security issues.
      * @return AssetFileDescriptor of the content URI, or NULL if the file does not exist.
      */
-    @Nullable
-    private static AssetFileDescriptor getAssetFileDescriptor(String uriString, String mode) {
+    private static @Nullable AssetFileDescriptor getAssetFileDescriptor(
+            String uriString, String mode) {
         if ("w".equals(mode)) {
             Log.e(TAG, "Cannot open files with mode 'w'");
             return null;
@@ -272,6 +277,7 @@
      * @return the display name of the @code uri if present in the database or an empty string
      *     otherwise.
      */
+    @SuppressWarnings("NullAway") // Using broad try/catch to catch NullPointerException
     public static String getDisplayName(Uri uri, Context context, String columnField) {
         if (uri == null) return "";
 
@@ -328,9 +334,8 @@
      * @param uriString the content URI to look up.
      * @return the display name of the uri if present in the database or null otherwise.
      */
-    @Nullable
     @CalledByNative
-    public static String maybeGetDisplayName(@JniType("std::string") String uriString) {
+    public static @Nullable String maybeGetDisplayName(@JniType("std::string") String uriString) {
         Uri uri = Uri.parse(uriString);
 
         try {
@@ -435,9 +440,8 @@
      * @return uri
      * @see DocumentsContract#buildDocumentUriUsingTree(Uri, String)
      */
-    @Nullable
     @CalledByNative
-    public static String buildDocumentUriUsingTree(
+    public static @Nullable String buildDocumentUriUsingTree(
             @JniType("std::string") String treeUri,
             @JniType("std::string") String encodedDocumentId) {
         try {
@@ -460,9 +464,8 @@
      * @param create set to true if document should be created if it doesn't exist.
      * @return Uri or null if no match is found and create is not set.
      */
-    @Nullable
     @CalledByNative
-    public static String getChildDocumentOrQuery(
+    public static @Nullable String getChildDocumentOrQuery(
             @JniType("std::string") String parentUri,
             @JniType("std::string") String displayName,
             @JniType("std::string") String mimeType,
@@ -518,8 +521,8 @@
      * @param displayName the display name of the document to find.
      * @return the child document if found, or null.
      */
-    private static Uri findChild(
-            String authority, String tree, String parentDocumentId, String displayName) {
+    private static @Nullable Uri findChild(
+            @Nullable String authority, String tree, String parentDocumentId, String displayName) {
         ContentResolver resolver = ContextUtils.getApplicationContext().getContentResolver();
         String[] columns = {
             DocumentsContract.Document.COLUMN_DISPLAY_NAME,
@@ -530,7 +533,8 @@
         // Use a selection to match displayName, but don't trust that it actually works.
         String selection = String.format("%s = ?", DocumentsContract.Document.COLUMN_DISPLAY_NAME);
         String[] selectionArgs = {displayName};
-        try (Cursor c = resolver.query(queryUri, columns, selection, selectionArgs, null)) {
+        try (Cursor c =
+                assumeNonNull(resolver.query(queryUri, columns, selection, selectionArgs, null))) {
             while (c.moveToNext()) {
                 // Verify display-name matches, and we have a valid docid.
                 if (c.isNull(0) || c.isNull(1) || !displayName.equals(c.getString(0))) {
@@ -573,8 +577,9 @@
      * @param create if true, the document will be created if it does not exist.
      * @return The URI of the created document or null
      */
+    @SuppressWarnings("NullAway") // Using broad try/catch to catch NullPointerException
     @CalledByNative
-    private static String getDocumentFromQuery(
+    private static @Nullable String getDocumentFromQuery(
             @JniType("std::string") String queryUriString, boolean create) {
         if (!isCreateChildDocumentQuery(queryUriString)) {
             return null;
@@ -613,8 +618,8 @@
     interface Natives {
         void addFileInfoToVector(
                 long vectorPointer,
-                String uri,
-                String displayName,
+                @Nullable String uri,
+                @Nullable String displayName,
                 boolean isDirectory,
                 long size,
                 long lastModified);
diff --git a/base/tracing/stdlib/chrome/chrome_scrolls.sql b/base/tracing/stdlib/chrome/chrome_scrolls.sql
index f0fb9c5..4237c6b 100644
--- a/base/tracing/stdlib/chrome/chrome_scrolls.sql
+++ b/base/tracing/stdlib/chrome/chrome_scrolls.sql
@@ -229,7 +229,6 @@
 )
 SELECT
   id,
-  -- TODO(b:380868337): Check/fix this for flings.
   coalesced_into,
   is_presented,
   is_janky,
@@ -399,11 +398,7 @@
 LEFT JOIN chrome_graphics_pipeline_display_frame_steps viz_swap_buffers_step
   ON
     viz_swap_buffers_step.display_trace_id = refs.display_trace_id
-    AND viz_swap_buffers_step.step = 'STEP_BUFFER_SWAP_POST_SUBMIT'
-WHERE
-  chrome_coalesced_input.presented_latency_id IS NOT NULL
-  -- Flings don't coalesce inputs.
-  OR chrome_event_latency.event_type = 'INERTIAL_GESTURE_SCROLL_UPDATE';
+    AND viz_swap_buffers_step.step = 'STEP_BUFFER_SWAP_POST_SUBMIT';
 
 -- Timestamps and durations for the frame-associated (after coalescing inputs
 -- into a frame) stages of a scroll.
@@ -848,7 +843,6 @@
   input.is_janky,
   input.is_inertial,
   input.is_first,
-  -- TODO(b:380868337): Check/fix this for flings.
   input.coalesced_into IS NOT NULL AS is_coalesced,
   -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   -- No applicable utid (duration between two threads).
diff --git a/base/tracing/stdlib/chrome/graphics_pipeline.sql b/base/tracing/stdlib/chrome/graphics_pipeline.sql
index be0eaa8..4f7c6ef 100644
--- a/base/tracing/stdlib/chrome/graphics_pipeline.sql
+++ b/base/tracing/stdlib/chrome/graphics_pipeline.sql
@@ -31,17 +31,35 @@
   -- Start time of the parent Chrome scheduler task (if any) of this step.
   task_start_time_ts TIMESTAMP)
 AS
+WITH
+-- Same places in Chromium (e.g. WebView) emit -1 as the `surface_frame_trace_id`,
+-- which blows up the joins on that value. Replace them with NULLs to avoid that.
+raw_data AS (
+  SELECT
+    id,
+    ts,
+    dur,
+    extract_arg(arg_set_id, 'chrome_graphics_pipeline.step') AS step,
+    extract_arg(arg_set_id, 'chrome_graphics_pipeline.surface_frame_trace_id')
+      AS surface_frame_trace_raw_id,
+    utid,
+    ts - (
+      EXTRACT_ARG(
+        thread_slice.arg_set_id,
+        'current_task.event_offset_from_task_start_time_us') * 1000
+    ) AS task_start_time_ts
+  FROM thread_slice
+  WHERE name = 'Graphics.Pipeline' AND surface_frame_trace_raw_id IS NOT NULL
+)
 SELECT
   id,
   ts,
   dur,
-  extract_arg(arg_set_id, 'chrome_graphics_pipeline.step') AS step,
-  extract_arg(arg_set_id, 'chrome_graphics_pipeline.surface_frame_trace_id')
-    AS surface_frame_trace_id,
+  step,
+  NULLIF(surface_frame_trace_raw_id, -1) AS surface_frame_trace_id,
   utid,
-  ts - (EXTRACT_ARG(thread_slice.arg_set_id, 'current_task.event_offset_from_task_start_time_us') * 1000) AS task_start_time_ts
-FROM thread_slice
-WHERE name = 'Graphics.Pipeline' AND surface_frame_trace_id IS NOT NULL;
+  task_start_time_ts
+FROM raw_data;
 
 -- `Graphics.Pipeline` steps corresponding to work done on creating and
 -- presenting one frame during/after surface aggregation. Covers steps:
diff --git a/base/tracing/stdlib/chrome/input.sql b/base/tracing/stdlib/chrome/input.sql
index 000894a..75db2b1 100644
--- a/base/tracing/stdlib/chrome/input.sql
+++ b/base/tracing/stdlib/chrome/input.sql
@@ -94,11 +94,13 @@
   USING (latency_id)
 WHERE chrome_inputs.input_type IS NOT NULL;
 
--- For each input, get the latency id of the input that it was coalesced into.
+-- For each input, if it was coalesced into another input, get the other input's
+-- latency id.
 CREATE PERFETTO TABLE chrome_coalesced_inputs(
   -- The `latency_id` of the coalesced input.
   coalesced_latency_id LONG,
-  -- The `latency_id` of the input that the current input was coalesced into.
+  -- The `latency_id` of the other input that the current input was coalesced
+  -- into. Guaranteed to be different from `coalesced_latency_id`.
   presented_latency_id LONG
 ) AS
 SELECT
@@ -108,7 +110,8 @@
 JOIN slice USING (slice_id)
 JOIN args USING (arg_set_id)
 WHERE step.step = 'STEP_RESAMPLE_SCROLL_EVENTS'
-  AND args.flat_key = 'chrome_latency_info.coalesced_trace_ids';
+  AND args.flat_key = 'chrome_latency_info.coalesced_trace_ids'
+  AND coalesced_latency_id != presented_latency_id;
 
 
 -- Each scroll update event (except flings) in Chrome starts its life as a touch
diff --git a/base/tracing/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py b/base/tracing/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
index 928b69c..b64c97d 100755
--- a/base/tracing/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
+++ b/base/tracing/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
@@ -490,16 +490,16 @@
         """,
         out=Csv("""
         "coalesced_latency_id","presented_latency_id"
-        -2143831735395280239,-2143831735395280239
         -2143831735395280183,-2143831735395280179
-        -2143831735395280179,-2143831735395280179
-        -2143831735395280166,-2143831735395280166
         -2143831735395280158,-2143831735395280153
-        -2143831735395280153,-2143831735395280153
         -2143831735395280150,-2143831735395280146
-        -2143831735395280146,-2143831735395280146
         -2143831735395280144,-2143831735395280139
-        -2143831735395280139,-2143831735395280139
+        -2143831735395280133,-2143831735395280132
+        -2143831735395279828,-2143831735395279840
+        -2143831735395279804,-2143831735395279784
+        -2143831735395279796,-2143831735395279808
+        -2143831735395279788,-2143831735395279704
+        -2143831735395279780,-2143831735395279792
         """))
 
   def test_chrome_touch_move_to_scroll_update(self):
@@ -603,7 +603,7 @@
         -2143831735395280246,"[NULL]",1,0,1,0,1292554196968270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10980,1292554198042257,362000,1292554198404257,890953,4,11000,1292554199295210,110000,1292554199405210,9963000,11025,1292554209368210,90000,1292554209458210
         -2143831735395280244,"[NULL]",1,0,1,0,1292554163682270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10860,1292554164468257,393000,1292554164861257,513953,4,10876,1292554165375210,127000,1292554165502210,10798000,10908,1292554176300210,226000,1292554176526210
         -2143831735395280242,"[NULL]",1,0,1,0,1292554174786270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10899,1292554175708257,321000,1292554176029257,697953,4,10915,1292554176727210,107000,1292554176834210,10177000,10947,1292554187011210,88000,1292554187099210
-        -2143831735395280239,-2143831735395280239,1,0,1,0,1292554086893270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10555,1292554086897257,128000,1292554087025257,1290953,4,10586,1292554088316210,79000,1292554088395210,9853000,10620,1292554098248210,177000,1292554098425210
+        -2143831735395280239,"[NULL]",1,0,1,0,1292554086893270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10555,1292554086897257,128000,1292554087025257,1290953,4,10586,1292554088316210,79000,1292554088395210,9853000,10620,1292554098248210,177000,1292554098425210
         -2143831735395280229,"[NULL]",1,0,1,0,1292554119302270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10699,1292554120042257,327000,1292554120369257,167953,4,10714,1292554120537210,94000,1292554120631210,10935000,10750,1292554131566210,158000,1292554131724210
         -2143831735395280227,"[NULL]",1,0,1,0,1292554097138270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10611,1292554097987257,189000,1292554098176257,366953,4,10626,1292554098543210,76000,1292554098619210,10479000,10662,1292554109098210,151000,1292554109249210
         -2143831735395280226,"[NULL]",1,0,1,0,1292554108216270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",10657,1292554108988257,322000,1292554109310257,80953,4,10666,1292554109391210,100000,1292554109491210,10760000,10706,1292554120251210,138000,1292554120389210
@@ -615,8 +615,8 @@
         -2143831735395280196,"[NULL]",1,0,1,0,1292554252553270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",11172,1292554253301257,345000,1292554253646257,819953,4,11187,1292554254466210,119000,1292554254585210,11932000,11223,1292554266517210,117000,1292554266634210
         -2143831735395280194,"[NULL]",0,0,1,0,1292554263653270,"[NULL]",1,"[NULL]","[NULL]","[NULL]",11211,1292554264600257,279000,1292554264879257,1915953,4,11227,1292554266795210,193000,1292554266988210,9556000,11259,1292554276544210,133000,1292554276677210
         -2143831735395280183,-2143831735395280179,0,0,0,0,1292554034979270,3955987,1,10192,1292554038935257,286000,10197,1292554039221257,141000,1292554039362257,17953,4,10210,1292554039380210,124000,1292554039504210,3940000,10230,1292554043444210,101000,1292554043545210
-        -2143831735395280179,-2143831735395280179,1,0,0,0,1292554029441270,7839987,1,10172,1292554037281257,337000,10177,1292554037618257,167000,1292554037785257,451953,4,10189,1292554038237210,89000,1292554038326210,4800000,10229,1292554043126210,303000,1292554043429210
-        -2143831735395280166,-2143831735395280166,1,0,0,1,1292554023976270,3704987,1,10071,1292554027681257,2166000,10102,1292554029847257,236000,1292554030083257,276953,4,10123,1292554030360210,377000,1292554030737210,-68000,10128,1292554030669210,56000,1292554030725210
+        -2143831735395280179,"[NULL]",1,0,0,0,1292554029441270,7839987,1,10172,1292554037281257,337000,10177,1292554037618257,167000,1292554037785257,451953,4,10189,1292554038237210,89000,1292554038326210,4800000,10229,1292554043126210,303000,1292554043429210
+        -2143831735395280166,"[NULL]",1,0,0,1,1292554023976270,3704987,1,10071,1292554027681257,2166000,10102,1292554029847257,236000,1292554030083257,276953,4,10123,1292554030360210,377000,1292554030737210,-68000,10128,1292554030669210,56000,1292554030725210
   """))
 
   def test_chrome_scroll_update_frame_info(self):
@@ -756,7 +756,7 @@
         -2143831735395280246,11.111000,1,0,1,0,0,1292554196968270,"[NULL]","[NULL]",1292554198042257,1292554198404257,"[NULL]",362000,1292554199295210,1292554199405210,890953,110000,1292554209368210,1292554209458210,9963000,90000,1292554209783210,325000,1292554210109210,1292554210447210,326000,338000,1292554210586633,1292554210744633,139423,158000,1292554210805633,61000,1292554211914633,1109000,1292554212678131,1292554213136131,763498,458000,1292554225142270,1292554231869270,1292554241331270,12006139,6727000,9462000
         -2143831735395280244,11.111000,1,0,1,0,0,1292554163682270,"[NULL]","[NULL]",1292554164468257,1292554164861257,"[NULL]",393000,1292554165375210,1292554165502210,513953,127000,1292554176300210,1292554176526210,10798000,226000,1292554176906210,380000,1292554177277210,1292554177579210,371000,302000,1292554177714633,1292554177819633,135423,105000,1292554177866633,47000,1292554178899633,1033000,1292554180264131,1292554180732131,1364498,468000,1292554191858270,1292554198113270,1292554208013270,11126139,6255000,9900000
         -2143831735395280242,11.111000,1,0,1,0,0,1292554174786270,"[NULL]","[NULL]",1292554175708257,1292554176029257,"[NULL]",321000,1292554176727210,1292554176834210,697953,107000,1292554187011210,1292554187099210,10177000,88000,1292554187399210,300000,1292554187782210,1292554188084210,383000,302000,1292554188237633,1292554188386633,153423,149000,1292554188443633,57000,1292554189523633,1080000,1292554191152131,1292554191689131,1628498,537000,1292554203177270,1292554209528270,1292554219116270,11488139,6351000,9588000
-        -2143831735395280239,11.111000,1,0,1,0,1,1292554086893270,"[NULL]","[NULL]",1292554086897257,1292554087025257,"[NULL]",128000,1292554088316210,1292554088395210,1290953,79000,1292554097735210,1292554098425210,9340000,690000,1292554098654210,229000,1292554098936210,1292554099173210,282000,237000,1292554099294633,1292554099407633,121423,113000,1292554099469633,62000,1292554100422633,953000,1292554101634131,1292554101998131,1211498,364000,1292554114587270,1292554120289270,1292554130314270,12589139,5702000,10025000
+        -2143831735395280239,11.111000,1,0,1,0,0,1292554086893270,"[NULL]","[NULL]",1292554086897257,1292554087025257,"[NULL]",128000,1292554088316210,1292554088395210,1290953,79000,1292554097735210,1292554098425210,9340000,690000,1292554098654210,229000,1292554098936210,1292554099173210,282000,237000,1292554099294633,1292554099407633,121423,113000,1292554099469633,62000,1292554100422633,953000,1292554101634131,1292554101998131,1211498,364000,1292554114587270,1292554120289270,1292554130314270,12589139,5702000,10025000
         -2143831735395280229,11.111000,1,0,1,0,0,1292554119302270,"[NULL]","[NULL]",1292554120042257,1292554120369257,"[NULL]",327000,1292554120537210,1292554120631210,167953,94000,1292554131566210,1292554131724210,10935000,158000,1292554132003210,279000,1292554132424210,1292554132933210,421000,509000,1292554133046633,1292554133166633,113423,120000,1292554133262633,96000,1292554134357633,1095000,1292554135827131,1292554136326131,1469498,499000,1292554147582270,1292554154188270,1292554163654270,11256139,6606000,9466000
         -2143831735395280227,11.111000,1,0,1,0,0,1292554097138270,"[NULL]","[NULL]",1292554097987257,1292554098176257,"[NULL]",189000,1292554098543210,1292554098619210,366953,76000,1292554109098210,1292554109249210,10479000,151000,1292554109537210,288000,1292554109843210,1292554110086210,306000,243000,1292554110300633,1292554110419633,214423,119000,1292554110461633,42000,1292554111414633,953000,1292554112146131,1292554112586131,731498,440000,1292554125377270,1292554131616270,1292554141341270,12791139,6239000,9725000
         -2143831735395280226,11.111000,1,0,1,0,0,1292554108216270,"[NULL]","[NULL]",1292554108988257,1292554109310257,"[NULL]",322000,1292554109391210,1292554109491210,80953,100000,1292554120251210,1292554120389210,10760000,138000,1292554120708210,319000,1292554120998210,1292554121193210,290000,195000,1292554121383633,1292554121472633,190423,89000,1292554121514633,42000,1292554122437633,923000,1292554123980131,1292554124406131,1542498,426000,1292554136757270,1292554143046270,1292554152550270,12351139,6289000,9504000
@@ -768,8 +768,8 @@
         -2143831735395280196,11.111000,1,0,1,0,0,1292554252553270,"[NULL]","[NULL]",1292554253301257,1292554253646257,"[NULL]",345000,1292554254466210,1292554254585210,819953,119000,1292554266517210,1292554266634210,11932000,117000,1292554267072210,438000,1292554267538210,1292554267740210,466000,202000,1292554268209633,1292554268405633,469423,196000,1292554268466633,61000,1292554269613633,1147000,1292554271890131,1292554272585131,2276498,695000,1292554280861270,1292554287004270,1292554296910270,8276139,6143000,9906000
         -2143831735395280194,0.000000,0,0,1,0,0,1292554263653270,"[NULL]","[NULL]",1292554264600257,1292554264879257,"[NULL]",279000,1292554266795210,1292554266988210,1915953,193000,1292554276544210,1292554276677210,9556000,133000,"[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]"
         -2143831735395280183,0.000000,0,0,0,0,1,1292554034979270,1292554038935257,3955987,1292554039221257,1292554039362257,286000,141000,1292554039380210,1292554039504210,17953,124000,1292554043444210,1292554043545210,3940000,101000,"[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]"
-        -2143831735395280179,11.111000,1,0,0,0,1,1292554029441270,1292554037281257,7839987,1292554037618257,1292554037785257,337000,167000,1292554038237210,1292554038326210,451953,89000,1292554042749210,1292554043429210,4423000,680000,1292554043721210,292000,1292554044172210,1292554044487210,451000,315000,1292554044656633,1292554045326633,169423,670000,1292554047111633,1785000,1292554048159633,1048000,1292554049700131,1292554050158131,1540498,458000,1292554058809270,1292554065670270,1292554074705270,8651139,6861000,9035000
-        -2143831735395280166,11.111000,1,0,0,1,1,1292554023976270,1292554027681257,3704987,1292554029847257,1292554030083257,2166000,236000,1292554030360210,1292554030737210,276953,377000,1292554030360210,1292554030725210,-377000,365000,1292554030873210,148000,1292554031441210,1292554031668210,568000,227000,1292554032587633,1292554033341633,919423,754000,"[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",1292554048008270,1292554053830270,1292554063604270,"[NULL]",5822000,9774000
+        -2143831735395280179,11.111000,1,0,0,0,0,1292554029441270,1292554037281257,7839987,1292554037618257,1292554037785257,337000,167000,1292554038237210,1292554038326210,451953,89000,1292554042749210,1292554043429210,4423000,680000,1292554043721210,292000,1292554044172210,1292554044487210,451000,315000,1292554044656633,1292554045326633,169423,670000,1292554047111633,1785000,1292554048159633,1048000,1292554049700131,1292554050158131,1540498,458000,1292554058809270,1292554065670270,1292554074705270,8651139,6861000,9035000
+        -2143831735395280166,11.111000,1,0,0,1,0,1292554023976270,1292554027681257,3704987,1292554029847257,1292554030083257,2166000,236000,1292554030360210,1292554030737210,276953,377000,1292554030360210,1292554030725210,-377000,365000,1292554030873210,148000,1292554031441210,1292554031668210,568000,227000,1292554032587633,1292554033341633,919423,754000,"[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]","[NULL]",1292554048008270,1292554053830270,1292554063604270,"[NULL]",5822000,9774000
   """))
 
   # A trace from M132 (ToT as of adding this test) has the necessary
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni
index 823aa67..b7ca3967 100644
--- a/buildtools/deps_revisions.gni
+++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@
 declare_args() {
   # Used to cause full rebuilds on libc++ rolls. This should be kept in sync
   # with the libcxx_revision var in //DEPS.
-  libcxx_revision = "5e0e903f1345443d9e4c5946c9af1fa95a974063"
+  libcxx_revision = "d0ddad5b79581e19d8e1aec627bb2ad86e1554cd"
 }
diff --git a/chrome/VERSION b/chrome/VERSION
index 86a4248c..6904b33 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=133
 MINOR=0
-BUILD=6900
+BUILD=6901
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
index 60813c9..099ecbf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
@@ -349,15 +349,13 @@
                 addFirstCardPref.setButtonText(
                         getResources()
                                 .getString(R.string.autofill_create_first_credit_card_button_text));
-                // CardWithButtonPreference calls the click listener for button clicks.
-                addFirstCardPref.setOnPreferenceClickListener(
-                        preference -> {
+                addFirstCardPref.setOnButtonClick(
+                        () -> {
                             Intent intent =
                                     SettingsNavigationFactory.createSettingsNavigation()
                                             .createSettingsIntent(
                                                     getActivity(), AutofillLocalCardEditor.class);
                             startActivity(intent);
-                            return true;
                         });
                 getPreferenceScreen().addPreference(addFirstCardPref);
             } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java
index 562a635..7f8afd9e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxSurveyController.java
@@ -9,6 +9,7 @@
 import androidx.annotation.Nullable;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
 
 import org.chromium.base.ResettersForTesting;
 import org.chromium.base.metrics.RecordHistogram;
@@ -42,7 +43,37 @@
 
 /** Class that controls and manages when and if surveys should be shown. */
 public class PrivacySandboxSurveyController {
-    private static final String SENTIMENT_SURVEY_TRIGGER = "privacy-sandbox-sentiment-survey";
+    private enum PrivacySandboxSurveyType {
+        CCT_EEA_ACCEPTED,
+        CCT_EEA_DECLINED,
+        CCT_EEA_CONTROL,
+        CCT_ROW_ACKNOWLEDGED,
+        CCT_ROW_CONTROL,
+        SENTIMENT_SURVEY,
+    }
+
+    private static final Map<PrivacySandboxSurveyType, String> sSurveyTriggers =
+            ImmutableMap.<PrivacySandboxSurveyType, String>builder()
+                    .put(
+                            PrivacySandboxSurveyType.CCT_EEA_ACCEPTED,
+                            "privacy-sandbox-cct-ads-notice-eea-accepted")
+                    .put(
+                            PrivacySandboxSurveyType.CCT_EEA_DECLINED,
+                            "privacy-sandbox-cct-ads-notice-eea-declined")
+                    .put(
+                            PrivacySandboxSurveyType.CCT_EEA_CONTROL,
+                            "privacy-sandbox-cct-ads-notice-eea-control")
+                    .put(
+                            PrivacySandboxSurveyType.CCT_ROW_ACKNOWLEDGED,
+                            "privacy-sandbox-cct-ads-notice-row-acknowledged")
+                    .put(
+                            PrivacySandboxSurveyType.CCT_ROW_CONTROL,
+                            "privacy-sandbox-cct-ads-notice-row-control")
+                    .put(
+                            PrivacySandboxSurveyType.SENTIMENT_SURVEY,
+                            "privacy-sandbox-sentiment-survey")
+                    .build();
+
     private ActivityTabTabObserver mActivityTabTabObserver;
     private Activity mActivity;
     private ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
@@ -90,8 +121,7 @@
         if (BuildConfig.IS_FOR_TEST && !sEnableForTesting) {
             return null;
         }
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.PRIVACY_SANDBOX_SENTIMENT_SURVEY)) {
-            recordSentimentSurveyStatus(PrivacySandboxSentimentSurveyStatus.FEATURE_DISABLED);
+        if (!shouldInitializeForActiveStudy()) {
             return null;
         }
         if (profile.isOffTheRecord()) {
@@ -106,10 +136,10 @@
                 profile);
     }
 
-    private SurveyClient constructSentimentSurveyClient() {
-        SurveyConfig sentimentSurveyConfig = SurveyConfig.get(SENTIMENT_SURVEY_TRIGGER);
-        if (sentimentSurveyConfig == null) {
-            recordSentimentSurveyStatus(PrivacySandboxSentimentSurveyStatus.INVALID_SURVEY_CONFIG);
+    private SurveyClient constructSurveyClient(PrivacySandboxSurveyType survey) {
+        SurveyConfig surveyConfig = SurveyConfig.get(sSurveyTriggers.get(survey));
+        if (surveyConfig == null) {
+            emitInvalidSurveyConfigHistogram(survey);
             return null;
         }
         MessageSurveyUiDelegate messageDelegate =
@@ -120,7 +150,7 @@
                         SurveyClientFactory.getInstance().getCrashUploadPermissionSupplier());
         SurveyClient surveyClient =
                 SurveyClientFactory.getInstance()
-                        .createClient(sentimentSurveyConfig, messageDelegate, mProfile);
+                        .createClient(surveyConfig, messageDelegate, mProfile);
         return surveyClient;
     }
 
@@ -135,13 +165,13 @@
                 mActivity.getResources(), mMessage);
     }
 
-    private void maybeLaunchSurvey() {
-        SurveyClient sentimentSurveyClient = constructSentimentSurveyClient();
-        if (sentimentSurveyClient == null) {
+    private void maybeLaunchSurvey(PrivacySandboxSurveyType survey) {
+        SurveyClient surveyClient = constructSurveyClient(survey);
+        if (surveyClient == null) {
             return;
         }
 
-        sentimentSurveyClient.showSurvey(
+        surveyClient.showSurvey(
                 mActivity,
                 mActivityLifecycleDispatcher,
                 getSentimentSurveyPsb(),
@@ -158,7 +188,7 @@
                         }
                         if (UrlUtilities.isNtpUrl(tab.getUrl())) {
                             if (mHasSeenNtp) {
-                                maybeLaunchSurvey();
+                                maybeLaunchSurvey(PrivacySandboxSurveyType.SENTIMENT_SURVEY);
                             }
                             mHasSeenNtp = true;
                         }
@@ -212,6 +242,18 @@
         }
     }
 
+    private static boolean shouldInitializeForActiveStudy() {
+        // TODO(crbug.com/379930582): Add a check for CCT surveys
+
+        // Sentiment survey should be checked last as it should always be on.
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.PRIVACY_SANDBOX_SENTIMENT_SURVEY)) {
+            emitFeatureDisabledHistogram(PrivacySandboxSurveyType.SENTIMENT_SURVEY);
+            return false;
+        }
+
+        return true;
+    }
+
     private static void recordSentimentSurveyStatus(
             @PrivacySandboxSentimentSurveyStatus int status) {
         RecordHistogram.recordEnumeratedHistogram(
@@ -220,7 +262,30 @@
                 PrivacySandboxSentimentSurveyStatus.MAX_VALUE + 1);
     }
 
-    // Set whether to trigger the start up survey in tests.
+    private static void emitInvalidSurveyConfigHistogram(PrivacySandboxSurveyType survey) {
+        switch (survey) {
+            case SENTIMENT_SURVEY:
+                recordSentimentSurveyStatus(
+                        PrivacySandboxSentimentSurveyStatus.INVALID_SURVEY_CONFIG);
+                return;
+                // TODO(crbug.com/379930582): Add support for CCT survey histograms
+            default:
+                return;
+        }
+    }
+
+    private static void emitFeatureDisabledHistogram(PrivacySandboxSurveyType survey) {
+        switch (survey) {
+            case SENTIMENT_SURVEY:
+                recordSentimentSurveyStatus(PrivacySandboxSentimentSurveyStatus.FEATURE_DISABLED);
+                return;
+                // TODO(crbug.com/379930582): Add support for CCT survey histograms
+            default:
+                return;
+        }
+    }
+
+    /** Set whether to trigger the start up survey in tests. */
     public static void setEnableForTesting() {
         sEnableForTesting = true;
         ResettersForTesting.register(() -> sEnableForTesting = false);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragmentTest.java
index 0847082..14af40a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragmentTest.java
@@ -9,6 +9,7 @@
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.RootMatchers.isDialog;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -1538,9 +1539,7 @@
         assertThat(summary)
                 .isEqualTo(activity.getString(R.string.autofill_create_first_credit_card_summary));
 
-        // Simulate click on the preference.
-        ThreadUtils.runOnUiThreadBlocking(promoPreference::performClick);
-        rule.waitForFragmentToBeShown();
+        onView(withId(R.id.card_button)).perform(click());
 
         // Verify that the local card editor fragment is opened.
         Assert.assertTrue(rule.getLastestShownFragment() instanceof AutofillLocalCardEditor);
diff --git a/chrome/app/password_manager_ui_strings.grdp b/chrome/app/password_manager_ui_strings.grdp
index a0979858..3fb218c 100644
--- a/chrome/app/password_manager_ui_strings.grdp
+++ b/chrome/app/password_manager_ui_strings.grdp
@@ -941,4 +941,13 @@
 ">
     To sign in with a passkey on this device again, you’ll need to verify it’s you. If you have another sign-in option, like a password, you can use it to sign in instead.
   </message>
+  <message name="IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_TITLE" desc="This is the title of the dialog appearing during password change flow. It appears while checking if the user is signed in.">
+    Checking if you're signed in...
+  </message>
+  <message name="IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_DETAILS" desc="This is the detailed explanation in the dialog appearing during password change flow. It appears when checking if the user is signed in.">
+    Chrome can change your password once you're signed in.
+  </message>
+  <message name="IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_CANCEL" desc="The caption on the button, which cancels the password change flow.">
+    Cancel
+  </message>
 </grit-part>
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_CANCEL.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_CANCEL.png.sha1
new file mode 100644
index 0000000..b9d46c5
--- /dev/null
+++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_CANCEL.png.sha1
@@ -0,0 +1 @@
+650a996f4d46021e334da3fe31ee652b4045aa1b
\ No newline at end of file
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_DETAILS.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_DETAILS.png.sha1
new file mode 100644
index 0000000..b9d46c5
--- /dev/null
+++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_DETAILS.png.sha1
@@ -0,0 +1 @@
+650a996f4d46021e334da3fe31ee652b4045aa1b
\ No newline at end of file
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_TITLE.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_TITLE.png.sha1
new file mode 100644
index 0000000..b9d46c5
--- /dev/null
+++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_TITLE.png.sha1
@@ -0,0 +1 @@
+650a996f4d46021e334da3fe31ee652b4045aa1b
\ No newline at end of file
diff --git a/chrome/app/shared_settings_strings.grdp b/chrome/app/shared_settings_strings.grdp
index 4aaf284f..947066be 100644
--- a/chrome/app/shared_settings_strings.grdp
+++ b/chrome/app/shared_settings_strings.grdp
@@ -298,6 +298,9 @@
   <message name="IDS_SETTINGS_TURN_ON_SYNC_BUTTON_UPDATE_SETTINGS_UI_ENABLED" desc="Label of the button in the settings page to turn on Sync.">
     Turn on Sync
   </message>
+  <message name="IDS_SETTINGS_SIGNIN_BUTTON_LABEL_WHEN_SYNC_IS_PAUSED" desc="Label of the button shown in the settings page to Sign in when sync is paused">
+     Sign in as <ph name="EMAIL">$1<ex>johndoe@gmail.com</ex></ph>
+  </message>
 
   <!-- Signout Subpage (strings used by the <settings-signout-dialog> element) -->
   <message name="IDS_SETTINGS_SYNC_DISCONNECT_CONFIRM" desc="The text to display on the button to confirm the user wishes to stop syncing.">
diff --git a/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_SIGNIN_BUTTON_LABEL_WHEN_SYNC_IS_PAUSED.png.sha1 b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_SIGNIN_BUTTON_LABEL_WHEN_SYNC_IS_PAUSED.png.sha1
new file mode 100644
index 0000000..f331c8b8
--- /dev/null
+++ b/chrome/app/shared_settings_strings_grdp/IDS_SETTINGS_SIGNIN_BUTTON_LABEL_WHEN_SYNC_IS_PAUSED.png.sha1
@@ -0,0 +1 @@
+dda67ab090d5b28dc490616baa4ec1ff1acf15a5
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3e96aba..d6dfaf8f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -921,8 +921,7 @@
 const FeatureEntry::FeatureVariation kCctSignInPromptVariations[] = {
     {"always show", kCctSignInPromptAlways, std::size(kCctSignInPromptAlways),
      nullptr},
-    {"for test", kCctSignInTestOnly, std::size(kCctSignInTestOnly),
-     nullptr}};
+    {"for test", kCctSignInTestOnly, std::size(kCctSignInTestOnly), nullptr}};
 #endif
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -4708,13 +4707,6 @@
      flag_descriptions::kBluetoothBtsnoopInternalsDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(
          chromeos::bluetooth::features::kBluetoothBtsnoopInternals)},
-    {"bluetooth-coredump", flag_descriptions::kBluetoothCoredumpName,
-     flag_descriptions::kBluetoothCoredumpDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(chromeos::bluetooth::features::kBluetoothCoredump)},
-    {"bluetooth-floss-coredump", flag_descriptions::kBluetoothFlossCoredumpName,
-     flag_descriptions::kBluetoothFlossCoredumpDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(
-         chromeos::bluetooth::features::kBluetoothFlossCoredump)},
     {"bluetooth-floss-telephony",
      flag_descriptions::kBluetoothFlossTelephonyName,
      flag_descriptions::kBluetoothFlossTelephonyDescription, kOsCrOS,
@@ -11759,7 +11751,7 @@
 #if BUILDFLAG(ENABLE_GLIC)
     {"glic", flag_descriptions::kGlicName, flag_descriptions::kGlicDescription,
      kOsMac | kOsWin | kOsLinux, FEATURE_VALUE_TYPE(features::kGlic)},
-#endif // ENABLE_GLIC
+#endif  // BUILDFLAG(ENABLE_GLIC)
 
 #if BUILDFLAG(IS_ANDROID)
     {"enterprise-real-time-url-check-on-android",
@@ -11840,6 +11832,14 @@
      FEATURE_VALUE_TYPE(switches::kEnableChromeRefreshTokenBinding)},
 #endif  // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
 
+#if BUILDFLAG(IS_CHROMEOS)
+    {"use-managed-print-job-options-in-print-preview",
+     flag_descriptions::kUseManagedPrintJobOptionsInPrintPreviewName,
+     flag_descriptions::kUseManagedPrintJobOptionsInPrintPreviewDescription,
+     kOsCrOS,
+     FEATURE_VALUE_TYPE(features::kUseManagedPrintJobOptionsInPrintPreview)},
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
     // 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/isolated_web_app/kiosk_iwa_manager.cc b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.cc
index ceddf73c..c6b2458 100644
--- a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.cc
+++ b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.cc
@@ -139,6 +139,13 @@
                           weak_ptr_factory_.GetWeakPtr()));
 }
 
+void KioskIwaManager::AddAppForTesting(
+    const policy::DeviceLocalAccount& account) {
+  KioskIwaDataMap dummy;
+  ProcessDeviceLocalAccount(account, dummy);
+  NotifyKioskAppsChanged();
+}
+
 void KioskIwaManager::UpdateAppsFromPolicy() {
   if (!ash::features::IsIsolatedWebAppKioskEnabled()) {
     // keeps KioskIwaManager empty if the feature is disabled.
diff --git a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.h b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.h
index e8197fcd..0bb4888 100644
--- a/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.h
+++ b/chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.h
@@ -61,6 +61,9 @@
   // Starts observing web app updates from App Service in a Kiosk session.
   void StartObservingAppUpdate(Profile* profile, const AccountId& account_id);
 
+  // Adds fake apps in tests.
+  void AddAppForTesting(const policy::DeviceLocalAccount& account);
+
  private:
   void UpdateAppsFromPolicy() override;
 
diff --git a/chrome/browser/ash/app_mode/test/BUILD.gn b/chrome/browser/ash/app_mode/test/BUILD.gn
index 00596eec..2f41a41 100644
--- a/chrome/browser/ash/app_mode/test/BUILD.gn
+++ b/chrome/browser/ash/app_mode/test/BUILD.gn
@@ -14,6 +14,7 @@
     "kiosk_chrome_app_browsertest.cc",
     "kiosk_chrome_app_update_browsertest.cc",
     "kiosk_file_system_volumes_browsertest.cc",
+    "kiosk_settings_browsertest.cc",
     "kiosk_web_app_offline_enabled_browsertest.cc",
     "new_windows_in_kiosk_allowed_browsertest.cc",
     "splash_screen_browsertest.cc",
@@ -25,6 +26,7 @@
     "//chrome/browser/ash/app_mode/web_app",
     "//chrome/browser/ash/login/app_mode/test:test_support",
     "//chrome/browser/chromeos/app_mode",
+    "//chrome/browser/ui:browser_navigator_params_headers",
     "//chrome/test:test_support",
     "//chrome/test:test_support_ui",
     "//content/test:test_support",
diff --git a/chrome/browser/ash/app_mode/test/DEPS b/chrome/browser/ash/app_mode/test/DEPS
index 0dfb334b..fb64a99 100644
--- a/chrome/browser/ash/app_mode/test/DEPS
+++ b/chrome/browser/ash/app_mode/test/DEPS
@@ -4,6 +4,8 @@
 
 include_rules = [
   "+chrome/browser/signin",
+  "+chrome/browser/ui/browser_navigator.h",
+  "+chrome/browser/ui/browser_navigator_params.h",
   "+chrome/browser/ui/test",
   "+chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h",
   "+chrome/browser/ui/webui/ash/login/error_screen_handler.h",
diff --git a/chrome/browser/ash/app_mode/test/kiosk_settings_browsertest.cc b/chrome/browser/ash/app_mode/test/kiosk_settings_browsertest.cc
new file mode 100644
index 0000000..c26cd4e
--- /dev/null
+++ b/chrome/browser/ash/app_mode/test/kiosk_settings_browsertest.cc
@@ -0,0 +1,325 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <cstddef>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "base/check_deref.h"
+#include "base/strings/strcat.h"
+#include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
+#include "chrome/browser/ash/app_mode/kiosk_controller.h"
+#include "chrome/browser/ash/app_mode/test/kiosk_mixin.h"
+#include "chrome/browser/ash/app_mode/test/kiosk_test_utils.h"
+#include "chrome/browser/ash/login/app_mode/test/kiosk_base_test.h"
+#include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h"
+#include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_navigator.h"
+#include "chrome/browser/ui/browser_navigator_params.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "extensions/browser/app_window/app_window.h"
+#include "extensions/browser/app_window/app_window_registry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/page_transition_types.h"
+#include "ui/base/window_open_disposition.h"
+
+namespace ash {
+
+using chromeos::KioskSettingsNavigationThrottle;
+
+namespace {
+
+constexpr std::string_view kSettingsUrl = "https://settings.com";
+
+using kiosk::test::CurrentProfile;
+
+KioskSystemSession& GetKioskSystemSession() {
+  return CHECK_DEREF(KioskController::Get().GetKioskSystemSession());
+}
+
+content::WebContents& ActiveWebContents(Browser& browser) {
+  content::WebContents& web_contents =
+      CHECK_DEREF(browser.tab_strip_model()->GetActiveWebContents());
+  return web_contents;
+}
+
+NavigateParams NavigateAndReturnParams(const GURL& url,
+                                       WindowOpenDisposition disposition) {
+  auto& profile = CurrentProfile();
+  NavigateParams params(&profile, url, ui::PAGE_TRANSITION_AUTO_BOOKMARK);
+  params.disposition = disposition;
+  params.window_action = NavigateParams::SHOW_WINDOW;
+  Navigate(&params);
+  return params;
+}
+
+// Opens a popup at `url` and returns true if the window wasn't force closed.
+bool OpenPopup(const GURL& url) {
+  NavigateAndReturnParams(url, WindowOpenDisposition::NEW_POPUP);
+  return !DidSessionCloseNewWindow(&GetKioskSystemSession());
+}
+
+// Navigates to `url` in the current tab, and returns the browser.
+Browser& NavigateInCurrentTab(const GURL& url) {
+  auto params =
+      NavigateAndReturnParams(url, WindowOpenDisposition::CURRENT_TAB);
+  return CHECK_DEREF(params.browser.get());
+}
+
+GURL NavigateInBrowser(Browser& browser, const GURL& url) {
+  auto& web_contents = ActiveWebContents(browser);
+  NavigateToURLBlockUntilNavigationsComplete(
+      /*web_contents=*/&web_contents, url,
+      /*number_of_navigations=*/1,
+      /*ignore_uncommitted_navigations=*/false);
+  return web_contents.GetLastCommittedURL();
+}
+
+GURL NextCommittedUrl(Browser& browser) {
+  auto& web_contents = ActiveWebContents(browser);
+  content::TestNavigationObserver(&web_contents,
+                                  /*expected_number_of_navigations=*/1)
+      .Wait();
+  return web_contents.GetLastCommittedURL();
+}
+
+// Helper for tests to override the list of settings pages.
+class ScopedSettingsPages {
+ public:
+  explicit ScopedSettingsPages(
+      std::vector<chromeos::KioskSettingsNavigationThrottle::SettingsPage>
+          pages)
+      : pages_(std::move(pages)) {
+    chromeos::KioskSettingsNavigationThrottle::SetSettingPagesForTesting(
+        &pages_);
+  }
+
+  ScopedSettingsPages(const ScopedSettingsPages&) = delete;
+  ScopedSettingsPages& operator=(const ScopedSettingsPages&) = delete;
+
+  ~ScopedSettingsPages() {
+    chromeos::KioskSettingsNavigationThrottle::SetSettingPagesForTesting(
+        nullptr);
+  }
+
+ private:
+  std::vector<chromeos::KioskSettingsNavigationThrottle::SettingsPage> pages_;
+};
+
+}  // namespace
+
+// Verifies settings pages work correctly in Kiosk.
+class KioskSettingsTest
+    : public MixinBasedInProcessBrowserTest,
+      public testing::WithParamInterface<KioskMixin::Config> {
+ public:
+  KioskSettingsTest() = default;
+
+  KioskSettingsTest(const KioskSettingsTest&) = delete;
+  KioskSettingsTest& operator=(const KioskSettingsTest&) = delete;
+
+  ~KioskSettingsTest() override = default;
+
+  void SetUpOnMainThread() override {
+    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
+    ASSERT_TRUE(kiosk_.WaitSessionLaunched());
+  }
+
+  KioskMixin kiosk_{&mixin_host_,
+                    /*cached_configuration=*/GetParam()};
+};
+
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest, CanNavigateToSettingsUrl) {
+  const GURL settings_url(kSettingsUrl);
+
+  ScopedSettingsPages scoped_pages({
+      {/*url=*/settings_url.spec().c_str(), /*allow_subpages=*/false},
+  });
+
+  ASSERT_TRUE(OpenPopup(settings_url));
+
+  auto& session = GetKioskSystemSession();
+  Browser& settings = CHECK_DEREF(session.GetSettingsBrowserForTesting());
+  ASSERT_EQ(NextCommittedUrl(settings), settings_url);
+}
+
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest, CanNavigateToSettingsSubUrl) {
+  const GURL settings_url(kSettingsUrl);
+  const GURL settings_suburl(base::StrCat({kSettingsUrl, "/suburl"}));
+
+  ScopedSettingsPages scoped_pages({
+      {/*url=*/settings_url.spec().c_str(), /*allow_subpages=*/true},
+  });
+
+  ASSERT_TRUE(OpenPopup(settings_suburl));
+
+  auto& session = GetKioskSystemSession();
+  Browser& settings = CHECK_DEREF(session.GetSettingsBrowserForTesting());
+  EXPECT_EQ(NextCommittedUrl(settings), settings_suburl);
+}
+
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest, CannotNavigateToNonSettingsUrl) {
+  const GURL settings_url(kSettingsUrl);
+  const GURL other_url("https://not-settings.com/");
+
+  ScopedSettingsPages scoped_pages({
+      {/*url=*/settings_url.spec().c_str(), /*allow_subpages=*/false},
+  });
+
+  // A new window directly to a disallowed page gets closed.
+  ASSERT_FALSE(OpenPopup(other_url));
+
+  // Navigating away from settings in an existing settings window doesn't work.
+  ASSERT_TRUE(OpenPopup(settings_url));
+
+  auto& session = GetKioskSystemSession();
+  Browser& settings = CHECK_DEREF(session.GetSettingsBrowserForTesting());
+  ASSERT_EQ(NextCommittedUrl(settings), settings_url);
+
+  const GURL committed_url = NavigateInBrowser(settings, other_url);
+  EXPECT_EQ(committed_url, settings_url);
+}
+
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest, CannotNavigateToDisallowedSubUrl) {
+  const GURL settings_url(kSettingsUrl);
+  const GURL settings_suburl(
+      base::StrCat({kSettingsUrl, "/disallowed-suburl"}));
+
+  ScopedSettingsPages scoped_pages({
+      {/*url=*/settings_url.spec().c_str(), /*allow_subpages=*/false},
+  });
+
+  // A new window directly to a non settings URL gets closed.
+  ASSERT_FALSE(OpenPopup(settings_suburl));
+
+  // Navigating away from settings in an existing settings window doesn't work.
+  ASSERT_TRUE(OpenPopup(settings_url));
+  auto& session = GetKioskSystemSession();
+  Browser& settings = CHECK_DEREF(session.GetSettingsBrowserForTesting());
+  ASSERT_EQ(NextCommittedUrl(settings), settings_url);
+
+  const GURL committed_url = NavigateInBrowser(settings, settings_suburl);
+  EXPECT_EQ(committed_url, settings_url);
+}
+
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest, DoesNotOpenTwoSettingsBrowsers) {
+  const GURL settings_url_1("https://settings-one.com/");
+  const GURL settings_url_2("https://settings-two.com/");
+
+  ScopedSettingsPages scoped_pages({
+      {/*url=*/settings_url_1.spec().c_str(), /*allow_subpages=*/false},
+      {/*url=*/settings_url_2.spec().c_str(), /*allow_subpages=*/false},
+  });
+
+  ASSERT_TRUE(OpenPopup(settings_url_1));
+
+  auto& session = GetKioskSystemSession();
+  Browser& first_settings = CHECK_DEREF(session.GetSettingsBrowserForTesting());
+  ASSERT_EQ(NextCommittedUrl(first_settings), settings_url_1);
+
+  ASSERT_TRUE(OpenPopup(settings_url_2));
+
+  Browser& second_settings =
+      CHECK_DEREF(session.GetSettingsBrowserForTesting());
+  ASSERT_EQ(NextCommittedUrl(second_settings), settings_url_2);
+
+  EXPECT_EQ(&first_settings, &second_settings);
+}
+
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest,
+                       NavigatingToSettingsInAppCreatesNewBrowser) {
+  const GURL settings_url(kSettingsUrl);
+
+  ScopedSettingsPages scoped_pages({
+      {/*url=*/settings_url.spec().c_str(), /*allow_subpages=*/false},
+  });
+
+  auto& session = GetKioskSystemSession();
+
+  // Navigation in the current tab creates a new browser of app type, and closes
+  // the non-app one.
+  Browser& browser = NavigateInCurrentTab(settings_url);
+  EXPECT_FALSE(DidSessionCloseNewWindow(&session));
+  EXPECT_FALSE(DidSessionCloseNewWindow(&session));
+
+  Browser* settings = session.GetSettingsBrowserForTesting();
+  ASSERT_NE(settings, nullptr);
+  EXPECT_NE(&browser, settings);
+}
+
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest,
+                       SettingsBrowserIsNullWhenSettingsIsClosed) {
+  const GURL settings_url(kSettingsUrl);
+
+  ScopedSettingsPages scoped_pages({
+      {/*url=*/settings_url.spec().c_str(), /*allow_subpages=*/false},
+  });
+
+  // Settings browser is initially null since there are no settings windows.
+  auto& session = GetKioskSystemSession();
+  ASSERT_EQ(session.GetSettingsBrowserForTesting(), nullptr);
+
+  // Settings browser is no longer null once a settings window opens.
+  ASSERT_TRUE(OpenPopup(settings_url));
+  Browser* settings = session.GetSettingsBrowserForTesting();
+  ASSERT_NE(settings, nullptr);
+
+  // Settings browser becomes null when the settings window closes.
+  CloseBrowserSynchronously(settings);
+  ASSERT_EQ(session.GetSettingsBrowserForTesting(), nullptr);
+}
+
+// Covers crbug.com/245088137, the settings could not reopen after losing focus.
+IN_PROC_BROWSER_TEST_P(KioskSettingsTest, CanRefocusSettings) {
+  const auto& pages = KioskSettingsNavigationThrottle::DefaultSettingsPages();
+  ASSERT_GT(pages.size(), 1UL);
+
+  ASSERT_TRUE(OpenPopup(GURL(pages[0].url)));
+
+  auto& session = GetKioskSystemSession();
+  Browser& settings = CHECK_DEREF(session.GetSettingsBrowserForTesting());
+
+  // The settings browser is focused.
+  EXPECT_TRUE(settings.window()->IsActive());
+
+  // Simulate a focus switch.
+  settings.window()->Deactivate();
+  EXPECT_FALSE(settings.window()->IsActive());
+
+  // Verify focus can switch to any other settings page.
+  for (size_t i = 1; i < pages.size(); i++) {
+    const GURL other_settings_page(pages[i].url);
+
+    // Open another settings browser and expect navigation in the old window.
+    auto& web_contents = ActiveWebContents(settings);
+    content::TestNavigationObserver settings_navigation_observer(&web_contents,
+                                                                 1);
+    ASSERT_TRUE(OpenPopup(other_settings_page));
+    // Wait for navigation to finish.
+    settings_navigation_observer.Wait();
+
+    // The settings browser should not have changed.
+    ASSERT_EQ(&settings, session.GetSettingsBrowserForTesting());
+    EXPECT_EQ(web_contents.GetLastCommittedURL(), other_settings_page);
+
+    // The settings browser should be focused again.
+    EXPECT_TRUE(settings.window()->IsActive());
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    KioskSettingsTest,
+    testing::ValuesIn(KioskMixin::ConfigsToAutoLaunchEachAppType()),
+    KioskMixin::ConfigName);
+
+}  // namespace ash
diff --git a/chrome/browser/ash/login/BUILD.gn b/chrome/browser/ash/login/BUILD.gn
index 706a713..80a24eb 100644
--- a/chrome/browser/ash/login/BUILD.gn
+++ b/chrome/browser/ash/login/BUILD.gn
@@ -537,7 +537,6 @@
     "existing_user_controller_auto_login_unittest.cc",
     "hwid_checker_unittest.cc",
     "onboarding_user_activity_counter_unittest.cc",
-    "pin_update_in_recovery_unittest.cc",
     "profile_auth_data_unittest.cc",
     "signin_partition_manager_unittest.cc",
     "startup_utils_unittest.cc",
@@ -560,7 +559,6 @@
     "//chrome/browser/ash/input_method",
     "//chrome/browser/ash/login/demo_mode",
     "//chrome/browser/ash/login/enrollment:test_support",
-    "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/screens",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/net",
diff --git a/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc b/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
index ff24dcb..2a44e290 100644
--- a/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
+++ b/chrome/browser/ash/login/app_mode/test/kiosk_device_owned_browsertest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include <string>
-#include <vector>
 
 #include "base/check_deref.h"
 #include "base/functional/bind.h"
@@ -12,7 +11,6 @@
 #include "chrome/browser/ash/accessibility/speech_monitor.h"
 #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_controller.h"
-#include "chrome/browser/ash/app_mode/kiosk_system_session.h"
 #include "chrome/browser/ash/login/app_mode/network_ui_controller.h"
 #include "chrome/browser/ash/login/app_mode/test/kiosk_base_test.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
@@ -21,18 +19,15 @@
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/ash/login/login_display_host.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
-#include "chrome/browser/ui/test/test_browser_closed_waiter.h"
 #include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
 #include "chrome/grit/generated_resources.h"
 #include "chromeos/ash/components/settings/cros_settings_names.h"
@@ -42,7 +37,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
-#include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/app_window/app_window.h"
 #include "extensions/browser/app_window/app_window_registry.h"
 #include "extensions/browser/browsertest_util.h"
@@ -54,8 +48,6 @@
 #include "extensions/test/result_catcher.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
-#include "ui/base/page_transition_types.h"
-#include "ui/base/window_open_disposition.h"
 
 namespace ash {
 
@@ -64,24 +56,6 @@
 const test::UIPath kSplashScreenLaunchText = {"app-launch-splash",
                                               "launchText"};
 
-constexpr char kSettingsPage1[] = "chrome://os-settings/manageAccessibility";
-constexpr char kSettingsPage2[] =
-    "chrome-extension://mndnfokpggljbaajbnioimlmbfngpief/chromevox/options/"
-    "options.html";
-
-NavigateParams OpenBrowserWithUrl(
-    const std::string& url,
-    WindowOpenDisposition window_type = WindowOpenDisposition::CURRENT_TAB) {
-  Profile* profile = ProfileManager::GetPrimaryUserProfile();
-
-  NavigateParams params(profile, GURL(url), ui::PAGE_TRANSITION_AUTO_BOOKMARK);
-  params.disposition = window_type;
-  params.window_action = NavigateParams::SHOW_WINDOW;
-  Navigate(&params);
-
-  return params;
-}
-
 void WaitForNetworkTimeoutMessage() {
   test::TestPredicateWaiter(base::BindRepeating([]() {
     return test::OobeJS().GetString(
@@ -91,21 +65,6 @@
   })).Wait();
 }
 
-// Helper class to replace settings urls for KioskSettingsNavigationThrottle.
-class ScopedSettingsPages {
- public:
-  explicit ScopedSettingsPages(
-      std::vector<chromeos::KioskSettingsNavigationThrottle::SettingsPage>*
-          pages) {
-    chromeos::KioskSettingsNavigationThrottle::SetSettingPagesForTesting(pages);
-  }
-
-  ~ScopedSettingsPages() {
-    chromeos::KioskSettingsNavigationThrottle::SetSettingPagesForTesting(
-        nullptr);
-  }
-};
-
 // Helper class to count how many times an extension has been loaded.
 class ExtensionReadyObserver : public extensions::ExtensionRegistryObserver {
  public:
@@ -185,185 +144,6 @@
   WaitForAppLaunchSuccess();
 }
 
-IN_PROC_BROWSER_TEST_F(KioskDeviceOwnedTest, SettingsWindow) {
-  StartAppLaunchFromLoginScreen(NetworkStatus::kOnline);
-  WaitForAppLaunchWithOptions(/*check_launch_data=*/true,
-                              /*terminate_app=*/false,
-                              /*keep_app_open=*/true);
-
-  // At this moment, app session should be initialized.
-  std::vector<chromeos::KioskSettingsNavigationThrottle::SettingsPage>
-      settings_pages = {
-          {"https://page1.com/", /*allow_subpages*/ true},
-          {"https://page2.com/", /*allow_subpages*/ false},
-      };
-
-  const GURL page1("https://page1.com/");
-  const GURL page1_sub("https://page1.com/sub");
-  const GURL page2("https://page2.com/");
-  const GURL page2_sub("https://page2.com/sub");
-  const GURL page3("https://page3.com/");
-
-  // Replace the settings allowlist with `settings_pages`.
-  ScopedSettingsPages pages(&settings_pages);
-  KioskSystemSession* system_session =
-      KioskController::Get().GetKioskSystemSession();
-
-  // App session should be initialized.
-  ASSERT_TRUE(system_session);
-  ASSERT_FALSE(system_session->GetSettingsBrowserForTesting());
-
-  OpenBrowserWithUrl(page1.spec(), WindowOpenDisposition::NEW_POPUP);
-  EXPECT_FALSE(DidSessionCloseNewWindow(system_session));
-
-  Browser* settings_browser = system_session->GetSettingsBrowserForTesting();
-  ASSERT_TRUE(settings_browser);
-
-  content::WebContents* web_contents =
-      settings_browser->tab_strip_model()->GetActiveWebContents();
-  // Try navigating to an allowed subpage.
-  NavigateToURLBlockUntilNavigationsComplete(web_contents, page1_sub, 1);
-  EXPECT_EQ(web_contents->GetLastCommittedURL(), page1_sub);
-
-  // Open another browser with url page2.
-  // Also, expect navigation inside of the old window to page2.
-  content::TestNavigationObserver settings_navigation_observer(web_contents, 1);
-  OpenBrowserWithUrl(page2.spec(), WindowOpenDisposition::NEW_POPUP);
-  EXPECT_FALSE(DidSessionCloseNewWindow(system_session));
-  // Also wait for navigaiton to finish.
-  settings_navigation_observer.Wait();
-
-  // The settings browser should not have changed.
-  ASSERT_EQ(settings_browser, system_session->GetSettingsBrowserForTesting());
-  EXPECT_EQ(web_contents->GetLastCommittedURL(), page2);
-
-  // Try navigating to a disallowed subpage (this won't commit the navigation).
-  NavigateToURLBlockUntilNavigationsComplete(
-      web_contents, page2_sub, 1,
-      /*ignore_uncommitted_navigations=*/false);
-  EXPECT_EQ(web_contents->GetLastCommittedURL(), page2);
-
-  // Try navigating to a disallowed page (this won't commit the navigation).
-  NavigateToURLBlockUntilNavigationsComplete(
-      web_contents, page3, 1,
-      /*ignore_uncommitted_navigations=*/false);
-  EXPECT_EQ(web_contents->GetLastCommittedURL(), page2);
-
-  // Close settings browser, expect the value to be cleared.
-  CloseBrowserSynchronously(settings_browser);
-  EXPECT_FALSE(system_session->GetSettingsBrowserForTesting());
-
-  // Open another browser with url page2, but now of the default type.
-  // This should create a new browser of app type, and close the non-app one.
-  NavigateParams params = OpenBrowserWithUrl(page2.spec());
-  // Wait for two browser handlings -- for non-app and app browser.
-  EXPECT_FALSE(DidSessionCloseNewWindow(system_session));
-  EXPECT_FALSE(DidSessionCloseNewWindow(system_session));
-
-  // One browser should be created.
-  settings_browser = system_session->GetSettingsBrowserForTesting();
-  ASSERT_TRUE(settings_browser);
-  EXPECT_FALSE(params.browser == settings_browser);
-}
-
-// This test covers b/245088137: after opening the settings browser and moving
-// focus to the main kiosk app, the settings browser could not be opened again.
-IN_PROC_BROWSER_TEST_F(KioskDeviceOwnedTest, SettingsWindowShouldBeActive) {
-  StartAppLaunchFromLoginScreen(NetworkStatus::kOnline);
-  WaitForAppLaunchWithOptions(/*check_launch_data=*/true,
-                              /*terminate_app=*/false,
-                              /*keep_app_open=*/true);
-  KioskSystemSession* system_session =
-      KioskController::Get().GetKioskSystemSession();
-
-  // App session should be initialized.
-  ASSERT_TRUE(system_session);
-  ASSERT_FALSE(system_session->GetSettingsBrowserForTesting());
-
-  OpenBrowserWithUrl(kSettingsPage1, WindowOpenDisposition::NEW_POPUP);
-  EXPECT_FALSE(DidSessionCloseNewWindow(system_session));
-
-  Browser* settings_browser = system_session->GetSettingsBrowserForTesting();
-
-  // Make sure the settings browser was opened, and it is focused.
-  ASSERT_TRUE(settings_browser);
-  EXPECT_TRUE(settings_browser->window()->IsActive());
-
-  // Emulate focus switching.
-  settings_browser->window()->Deactivate();
-  EXPECT_FALSE(settings_browser->window()->IsActive());
-
-  content::WebContents* web_contents =
-      settings_browser->tab_strip_model()->GetActiveWebContents();
-
-  // Open another settings browser.
-  // Also, expect navigation inside of the old window to kSettingsPage2.
-  content::TestNavigationObserver settings_navigation_observer(web_contents, 1);
-  OpenBrowserWithUrl(kSettingsPage2, WindowOpenDisposition::NEW_POPUP);
-  EXPECT_FALSE(DidSessionCloseNewWindow(system_session));
-  // Also wait for navigaiton to finish.
-  settings_navigation_observer.Wait();
-
-  // The settings browser should not have changed.
-  ASSERT_EQ(settings_browser, system_session->GetSettingsBrowserForTesting());
-  EXPECT_EQ(web_contents->GetLastCommittedURL(), GURL(kSettingsPage2));
-
-  // The settings browser should be focused again.
-  EXPECT_TRUE(settings_browser->window()->IsActive());
-}
-
-// If only the a11y settings window remains open, it should not be automatically
-// closed in the chrome app kiosk session.
-IN_PROC_BROWSER_TEST_F(KioskDeviceOwnedTest, SettingsWindowRemainsOpen) {
-  StartAppLaunchFromLoginScreen(NetworkStatus::kOnline);
-  WaitForAppLaunchWithOptions(/*check_launch_data=*/true,
-                              /*terminate_app=*/false,
-                              /*keep_app_open=*/true);
-  KioskSystemSession* system_session =
-      KioskController::Get().GetKioskSystemSession();
-  // App session should be initialized.
-  ASSERT_NE(system_session, nullptr);
-
-  OpenA11ySettingsBrowser(system_session);
-  Browser* settings_browser = system_session->GetSettingsBrowserForTesting();
-  ASSERT_NE(settings_browser, nullptr);
-
-  // Only the settings browser is opened.
-  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
-  EXPECT_FALSE(system_session->is_shutting_down());
-}
-
-// Closing the a11y settings window should not exit the chrome app kiosk
-// session.
-IN_PROC_BROWSER_TEST_F(KioskDeviceOwnedTest, CloseSettingsWindow) {
-  StartAppLaunchFromLoginScreen(NetworkStatus::kOnline);
-  WaitForAppLaunchWithOptions(/*check_launch_data=*/true,
-                              /*terminate_app=*/false,
-                              /*keep_app_open=*/true);
-  KioskSystemSession* system_session =
-      KioskController::Get().GetKioskSystemSession();
-  // App session should be initialized.
-  ASSERT_NE(system_session, nullptr);
-
-  OpenA11ySettingsBrowser(system_session);
-  Browser* settings_browser = system_session->GetSettingsBrowserForTesting();
-  ASSERT_NE(settings_browser, nullptr);
-  ASSERT_NE(settings_browser->window(), nullptr);
-
-  // Only the settings browser is opened.
-  EXPECT_EQ(BrowserList::GetInstance()->size(), 1u);
-  EXPECT_FALSE(system_session->is_shutting_down());
-
-  settings_browser->window()->Close();
-  // Ensure `settings_browser` is closed.
-  TestBrowserClosedWaiter browser_closed_waiter{settings_browser};
-  ASSERT_TRUE(browser_closed_waiter.WaitUntilClosed());
-
-  // No browsers are opened in the chrome app kiosk session.
-  EXPECT_EQ(BrowserList::GetInstance()->size(), 0u);
-  EXPECT_FALSE(system_session->is_shutting_down());
-}
-
 // Verifies that an enterprise device does not auto-launch kiosk mode when cros
 // settings are untrusted.
 IN_PROC_BROWSER_TEST_F(KioskDeviceOwnedTest,
diff --git a/chrome/browser/ash/login/pin_update_in_recovery_unittest.cc b/chrome/browser/ash/login/pin_update_in_recovery_unittest.cc
deleted file mode 100644
index 5cb392d..0000000
--- a/chrome/browser/ash/login/pin_update_in_recovery_unittest.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <memory>
-#include <string>
-
-#include "ash/constants/ash_pref_names.h"
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_future.h"
-#include "chrome/browser/ash/login/quick_unlock/auth_token.h"
-#include "chrome/browser/ash/login/quick_unlock/pin_backend.h"
-#include "chrome/browser/ash/login/quick_unlock/quick_unlock_factory.h"
-#include "chrome/test/base/mixin_based_in_process_browser_test.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chromeos/ash/components/cryptohome/system_salt_getter.h"
-#include "chromeos/ash/components/dbus/userdataauth/cryptohome_misc_client.h"
-#include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
-#include "chromeos/ash/components/login/auth/public/cryptohome_key_constants.h"
-#include "chromeos/ash/components/osauth/public/auth_session_storage.h"
-#include "chromeos/ash/components/osauth/public/common_types.h"
-#include "chromeos/ash/services/auth_factor_config/auth_factor_config.h"
-#include "chromeos/ash/services/auth_factor_config/in_process_instances.h"
-#include "chromeos/ash/services/auth_factor_config/pin_factor_editor.h"
-#include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom-forward.h"
-#include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom-shared.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/user_manager/fake_user_manager.h"
-#include "components/user_manager/known_user.h"
-#include "components/user_manager/user_manager_impl.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace ash {
-
-constexpr char kUserEmail[] = "expected_email@example.com";
-constexpr char kOldPin[] = "123456";
-constexpr char kNewPin[] = "234567";
-
-class PinFactorEditorTest : public testing::Test {
- public:
-  PinFactorEditorTest() = default;
-  PinFactorEditorTest(const PinFactorEditorTest&) = delete;
-  PinFactorEditorTest& operator=(const PinFactorEditorTest&) = delete;
-  ~PinFactorEditorTest() override = default;
-
- protected:
-  void SetUp() override {
-    UserDataAuthClient::InitializeFake();
-    InitializeUserManager();
-    AddUserToUserManager();
-    SystemSaltGetter::Initialize();
-    CryptohomeMiscClient::InitializeFake();
-    auth_parts_ = AuthParts::Create(&local_state_);
-    TestingBrowserProcess::GetGlobal()->SetLocalState(&local_state_);
-  }
-
-  void InitializeUserManager() {
-    user_manager::UserManagerImpl::RegisterPrefs(local_state_.registry());
-    user_manager_ =
-        std::make_unique<user_manager::FakeUserManager>(&local_state_);
-    user_manager_->Initialize();
-  }
-
-  void AddUserToUserManager() {
-    account_id_ = AccountId::FromUserEmail(kUserEmail);
-    const user_manager::User* user = user_manager_->AddUser(account_id_);
-    user_manager_->UserLoggedIn(account_id_, user->username_hash(), false,
-                                false);
-    user_manager_->SetUserCryptohomeDataEphemeral(account_id_, false);
-  }
-
-  void AddUserToCryptohome(const AccountId& user) {
-    auto account_identifier =
-        cryptohome::CreateAccountIdentifierFromAccountId(user);
-
-    FakeUserDataAuthClient::TestApi::Get()->SetServiceIsAvailable(true);
-    FakeUserDataAuthClient::TestApi::Get()
-        ->set_supports_low_entropy_credentials(true);
-    FakeUserDataAuthClient::TestApi::Get()->AddExistingUser(account_identifier);
-  }
-
-  void TearDown() override {
-    SystemSaltGetter::Shutdown();
-    CryptohomeMiscClient::Shutdown();
-    auth_parts_.reset();
-    user_manager_->Destroy();
-    user_manager_.reset();
-    UserDataAuthClient::Shutdown();
-    TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
-    quick_unlock::PinBackend::ResetForTesting();
-  }
-
-  std::string HashPin(const std::string& unhashed_pin) {
-    Key key(std::move(unhashed_pin));
-    user_manager::KnownUser known_user(&local_state_);
-    std::string salt;
-    known_user.GetStringPrefForTest(account_id_,
-                                    ash::prefs::kQuickUnlockPinSalt, &salt);
-    key.Transform(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
-    return key.GetSecret();
-  }
-
-  void AddCryptohomePin(const AccountId& user, const std::string& pin) {
-    auto account_identifier =
-        cryptohome::CreateAccountIdentifierFromAccountId(user);
-
-    // Hash the pin, as only hashed secrets appear at the userdataauth
-    // level.
-    Key key(HashPin(pin));
-
-    // Add the pin key to the user.
-    user_data_auth::AuthFactor auth_factor;
-    user_data_auth::AuthInput auth_input;
-
-    auth_factor.set_label(ash::kCryptohomePinLabel);
-    auth_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
-
-    auth_input.mutable_password_input()->set_secret(key.GetSecret());
-
-    FakeUserDataAuthClient::TestApi::Get()->AddAuthFactor(
-        account_identifier, auth_factor, auth_input);
-  }
-
-  std::unique_ptr<ash::UserContext> MakeEmptyUserContext() {
-    std::unique_ptr<ash::UserContext> auth_token_user_context_ =
-        std::make_unique<ash::UserContext>();
-    // Generate an auth token.
-    auth_token_user_context_->SetAccountId(account_id_);
-    auth_token_user_context_->SetSessionLifetime(
-        base::Time::Now() + ash::quick_unlock::AuthToken::kTokenExpiration);
-
-    auto* fake_userdataauth_client_testapi =
-        ash::FakeUserDataAuthClient::TestApi::Get();
-
-    auto session_ids = fake_userdataauth_client_testapi->AddSession(
-        cryptohome::CreateAccountIdentifierFromAccountId(account_id_),
-        /*authenticated=*/true);
-    auth_token_user_context_->SetAuthSessionIds(session_ids.first,
-                                                session_ids.second);
-
-    return auth_token_user_context_;
-  }
-
-  std::unique_ptr<ash::UserContext> MakeUserContextWithPassword() {
-    std::unique_ptr<ash::UserContext> auth_token_user_context_ =
-        MakeEmptyUserContext();
-
-    cryptohome::AuthFactorRef ref(
-        cryptohome::AuthFactorType::kPassword,
-        cryptohome::KeyLabel{kCryptohomeGaiaKeyLabel});
-    cryptohome::AuthFactor password(ref,
-                                    cryptohome::AuthFactorCommonMetadata());
-
-    cryptohome::AuthFactorsSet factors;
-    factors.Put(cryptohome::AuthFactorType::kPassword);
-    factors.Put(cryptohome::AuthFactorType::kRecovery);
-
-    auth_token_user_context_->SetAuthFactorsConfiguration(
-        ash::AuthFactorsConfiguration({password}, factors));
-
-    return auth_token_user_context_;
-  }
-
-  std::unique_ptr<ash::UserContext> MakeUserContextWithPin() {
-    std::unique_ptr<ash::UserContext> auth_token_user_context_ =
-        MakeEmptyUserContext();
-
-    cryptohome::AuthFactorsSet factors;
-    factors.Put(cryptohome::AuthFactorType::kPin);
-    factors.Put(cryptohome::AuthFactorType::kRecovery);
-
-    cryptohome::AuthFactorRef ref(cryptohome::AuthFactorType::kPin,
-                                  cryptohome::KeyLabel{kCryptohomePinLabel});
-    cryptohome::AuthFactor pin(ref, cryptohome::AuthFactorCommonMetadata());
-
-    auth_token_user_context_->SetAuthFactorsConfiguration(
-        ash::AuthFactorsConfiguration({pin}, factors));
-
-    return auth_token_user_context_;
-  }
-
-  void VerifyPinUpdateSuccess(
-      base::test::TestFuture<auth::mojom::ConfigureResult>& future) {
-    EXPECT_EQ(future.Get(), auth::mojom::ConfigureResult::kSuccess);
-    EXPECT_TRUE(
-        FakeUserDataAuthClient::Get()
-            ->WasCalled<
-                FakeUserDataAuthClient::Operation::kUpdateAuthFactor>());
-    auto update_auth_factor_request =
-        FakeUserDataAuthClient::Get()
-            ->GetLastRequest<
-                FakeUserDataAuthClient::Operation::kUpdateAuthFactor>();
-    EXPECT_EQ(update_auth_factor_request.auth_factor().type(),
-              user_data_auth::AUTH_FACTOR_TYPE_PIN);
-    EXPECT_EQ(update_auth_factor_request.auth_input().pin_input().secret(),
-              HashPin(kNewPin));
-  }
-
-  std::unique_ptr<auth::PinFactorEditor> GetPinFactorEditor() {
-    auto* pin_backend = quick_unlock::PinBackend::GetInstance();
-
-    auth_factor_config_ = std::make_unique<auth::AuthFactorConfig>(
-        &quick_unlock::QuickUnlockFactory::GetDelegate(), &local_state_);
-    auto pin_factor_editor = std::make_unique<auth::PinFactorEditor>(
-        auth_factor_config_.get(), pin_backend);
-
-    return pin_factor_editor;
-  }
-
-  AccountId account_id_;
-  std::unique_ptr<auth::AuthFactorConfig> auth_factor_config_;
-  base::test::TaskEnvironment env_;
-  TestingPrefServiceSimple local_state_;
-  std::unique_ptr<user_manager::FakeUserManager> user_manager_;
-  std::unique_ptr<AuthParts> auth_parts_;
-};
-
-// Assert that we get an error if we try to update the pin via PinFactorEditor
-// when there's no crypthome pin already set for the user.
-TEST_F(PinFactorEditorTest, CannotUpdatePinIfNotAlreadyPresent) {
-  AddUserToCryptohome(account_id_);
-  const std::string token =
-      ash::AuthSessionStorage::Get()->Store(MakeUserContextWithPassword());
-
-  std::unique_ptr<auth::PinFactorEditor> pin_factor_editor =
-      GetPinFactorEditor();
-
-  base::test::TestFuture<auth::mojom::ConfigureResult> future;
-
-  pin_factor_editor->UpdatePin(token, kNewPin, future.GetCallback());
-
-  EXPECT_EQ(future.Get(), auth::mojom::ConfigureResult::kFatalError);
-}
-
-// Assert that we can update the user's cryptohome pin when one is already
-// present.
-TEST_F(PinFactorEditorTest, CanUpdatePinIfAlreadyPresent) {
-  AddUserToCryptohome(account_id_);
-  AddCryptohomePin(account_id_, kOldPin);
-
-  const std::string token =
-      ash::AuthSessionStorage::Get()->Store(MakeUserContextWithPin());
-
-  std::unique_ptr<auth::PinFactorEditor> pin_factor_editor =
-      GetPinFactorEditor();
-
-  base::test::TestFuture<auth::mojom::ConfigureResult> future;
-
-  pin_factor_editor->UpdatePin(token, kNewPin, future.GetCallback());
-
-  VerifyPinUpdateSuccess(future);
-}
-
-}  // namespace ash
diff --git a/chrome/browser/ash/policy/skyvault/local_files_migration_manager.cc b/chrome/browser/ash/policy/skyvault/local_files_migration_manager.cc
index bd9db59..ff80c18 100644
--- a/chrome/browser/ash/policy/skyvault/local_files_migration_manager.cc
+++ b/chrome/browser/ash/policy/skyvault/local_files_migration_manager.cc
@@ -230,6 +230,16 @@
       pref_service->SetTime(prefs::kSkyVaultMigrationStartTime, base::Time());
       SkyVaultMigrationResetHistogram(true);
     }
+    // If migration is not configured, check whether there are already no files
+    // to migrate.
+    if (!local_user_files_allowed_) {
+      DCHECK(!IsMigrationEnabled(cloud_provider_));
+      base::ThreadPool::PostTaskAndReplyWithResult(
+          FROM_HERE, {base::MayBlock()},
+          base::BindOnce(&IsMyFilesEmpty, profile),
+          base::BindOnce(&LocalFilesMigrationManager::OnMyFilesChecked,
+                         weak_factory_.GetWeakPtr()));
+    }
     return;
   }
   // Migration is enabled.
@@ -305,10 +315,20 @@
 
   if (local_user_files_allowed_ || !IsMigrationEnabled(cloud_provider_)) {
     MaybeStopMigration(cloud_provider_old);
+    SkyVaultMigrationResetHistogram(true);
     if (local_user_files_allowed_) {
       SetLocalUserFilesWriteEnabled(/*enabled=*/true);
+    } else {
+      CHECK(state_ == State::kUninitialized);
+      // If migration is not configured, check whether there are already no
+      // files to migrate.
+      DCHECK(!IsMigrationEnabled(cloud_provider_));
+      base::ThreadPool::PostTaskAndReplyWithResult(
+          FROM_HERE, {base::MayBlock()},
+          base::BindOnce(&IsMyFilesEmpty, profile),
+          base::BindOnce(&LocalFilesMigrationManager::OnMyFilesChecked,
+                         weak_factory_.GetWeakPtr()));
     }
-    SkyVaultMigrationResetHistogram(true);
     return;
   }
   SkyVaultMigrationEnabledHistogram(cloud_provider_, true);
@@ -355,7 +375,17 @@
 }
 
 void LocalFilesMigrationManager::OnMyFilesChecked(bool is_empty) {
-  if (local_user_files_allowed_ || !IsMigrationEnabled(cloud_provider_)) {
+  if (local_user_files_allowed_) {
+    return;
+  }
+  if (!IsMigrationEnabled(cloud_provider_)) {
+    // If migration is not configured, but no files - proceed to clean up.
+    if (is_empty) {
+      // Notify to unmount local folder in volume manager.
+      NotifySuccess();
+      SetState(State::kCleanup);
+      CleanupLocalFiles();
+    }
     return;
   }
 
@@ -364,7 +394,7 @@
     // observers and also cleanup empty folders.
     if (state_ != State::kCompleted) {
       NotifySuccess();
-      state_ = State::kCleanup;
+      SetState(State::kCleanup);
     }
   }
 
diff --git a/chrome/browser/ash/policy/skyvault/local_files_migration_manager_browsertest.cc b/chrome/browser/ash/policy/skyvault/local_files_migration_manager_browsertest.cc
index 2bbc9dd..397056b 100644
--- a/chrome/browser/ash/policy/skyvault/local_files_migration_manager_browsertest.cc
+++ b/chrome/browser/ash/policy/skyvault/local_files_migration_manager_browsertest.cc
@@ -439,7 +439,29 @@
 }
 
 IN_PROC_BROWSER_TEST_F(LocalFilesMigrationManagerTest,
-                       NoMigrationIfNoDestination) {
+                       MigrationCompleteIfNoDestinationAndEmpty) {
+  EXPECT_CALL(observer_, OnMigrationReset).Times(1);
+  EXPECT_CALL(observer_, OnMigrationSucceeded).Times(1);
+  base::RunLoop run_loop;
+  // Write access will be disallowed.
+  EXPECT_CALL(userdataauth_,
+              SetUserDataStorageWriteEnabled(WithEnabled(false), _))
+      .WillOnce(testing::DoAll(
+          base::test::RunClosure(run_loop.QuitClosure()),
+          ReplyWith(::user_data_auth::SetUserDataStorageWriteEnabledReply())));
+  SetMigrationPolicies(/*local_user_files_allowed=*/false,
+                       /*destination=*/kReadOnly);
+  run_loop.Run();
+
+  histogram_tester_.ExpectBucketCount(
+      "Enterprise.SkyVault.LocalStorage.Enabled", false, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(LocalFilesMigrationManagerTest,
+                       NoMigrationIfNoDestinationAndNonEmpty) {
+  SetUpMyFiles();
+  CreateTestFile(kTestFile, my_files_dir_);
+
   EXPECT_CALL(observer_, OnMigrationReset).Times(1);
   SetMigrationPolicies(/*local_user_files_allowed=*/false,
                        /*destination=*/kReadOnly);
diff --git a/chrome/browser/ash/policy/status_collector/BUILD.gn b/chrome/browser/ash/policy/status_collector/BUILD.gn
index 0ae46f2..f1bd7bf 100644
--- a/chrome/browser/ash/policy/status_collector/BUILD.gn
+++ b/chrome/browser/ash/policy/status_collector/BUILD.gn
@@ -172,6 +172,7 @@
     "//chrome/browser/apps/app_service",
     "//chrome/browser/apps/app_service:test_support",
     "//chrome/browser/ash/app_mode",
+    "//chrome/browser/ash/app_mode/isolated_web_app",
     "//chrome/browser/ash/app_mode/web_app",
     "//chrome/browser/ash/child_accounts",
     "//chrome/browser/ash/child_accounts/time_limits",
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector.cc b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
index 2cba80c1..393a59d9 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector.cc
@@ -662,8 +662,7 @@
       return em::ActiveTimePeriod::SESSION_WEB_KIOSK;
 
     case DeviceLocalAccountType::kKioskIsolatedWebApp:
-      // TODO(crbug.com/369516363): add ActiveTimePeriod value for IWA.
-      return em::ActiveTimePeriod::SESSION_WEB_KIOSK;
+      return em::ActiveTimePeriod::SESSION_IWA_KIOSK;
 
     default:
       NOTREACHED();
diff --git a/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc b/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
index 40b182515..b03fb87 100644
--- a/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
+++ b/chrome/browser/ash/policy/status_collector/device_status_collector_browsertest.cc
@@ -49,6 +49,7 @@
 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/app_service_test.h"
 #include "chrome/browser/apps/app_service/publisher_host.h"
+#include "chrome/browser/ash/app_mode/isolated_web_app/kiosk_iwa_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_data.h"
 #include "chrome/browser/ash/app_mode/kiosk_chrome_app_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_cryptohome_remover.h"
@@ -294,8 +295,12 @@
 
 const char kKioskAccountId[] = "kiosk_user@localhost";
 const char kWebKioskAccountId[] = "web_kiosk_user@localhost";
+const char kIwaKioskAccountId[] = "iwa_kiosk_user@localhost";
 const char kKioskAppId[] = "kiosk_app_id";
 const char kWebKioskAppUrl[] = "http://example.com";
+const char kIwaKioskWebBundleId[] =
+    "aerugqztij5biqquuk3mfwpsaibuegaqcitgfchwuosuofdjabzqaaic";
+const char kIwaKioskUpdateUrl[] = "https://example.com/update.json";
 const char kExternalMountPoint[] = "/a/b/c";
 const char kPublicAccountId[] = "public_user@localhost";
 const char kArcStatus[] = R"(
@@ -853,6 +858,11 @@
             DeviceLocalAccount::EphemeralMode::kUnset,
             fake_web_kiosk_app_basic_info_,
             kWebKioskAccountId),
+        fake_iwa_kiosk_basic_info_(kIwaKioskWebBundleId, kIwaKioskUpdateUrl),
+        fake_iwa_kiosk_device_local_account_(
+            DeviceLocalAccount::EphemeralMode::kUnset,
+            fake_iwa_kiosk_basic_info_,
+            kIwaKioskAccountId),
         user_data_dir_override_(chrome::DIR_USER_DATA),
         crash_dumps_dir_override_(chrome::DIR_CRASH_DUMPS) {
     scoped_stub_install_attributes_.Get()->SetCloudManaged("managed.com",
@@ -1092,6 +1102,9 @@
       case DeviceLocalAccountType::kWebKioskApp:
         user = user_manager->AddWebKioskAppUser(account_id);
         break;
+      case DeviceLocalAccountType::kKioskIsolatedWebApp:
+        user = user_manager->AddKioskIwaUser(account_id);
+        break;
       default:
         FAIL() << "Unexpected kiosk app type.";
     }
@@ -1160,6 +1173,22 @@
     base::RunLoop().RunUntilIdle();
   }
 
+  void MockAutoLaunchKioskIwa(
+      const DeviceLocalAccount& auto_launch_app_account) {
+    kiosk_iwa_manager_ = std::make_unique<ash::KioskIwaManager>();
+    kiosk_iwa_manager_->AddAppForTesting(auto_launch_app_account);
+
+    std::vector<DeviceLocalAccount> accounts;
+    accounts.push_back(auto_launch_app_account);
+    SetDeviceLocalAccountsForTesting(&owner_settings_service_, accounts);
+
+    owner_settings_service_.SetString(
+        ash::kAccountsPrefDeviceLocalAccountAutoLoginId,
+        auto_launch_app_account.account_id);
+
+    base::RunLoop().RunUntilIdle();
+  }
+
   void AddActivityPeriodForUser(const std::string& user_email) {
     base::Time start_time = test_clock_.Now();
     test_clock_.Advance(base::Hours(1));
@@ -1194,6 +1223,8 @@
   std::unique_ptr<TestingProfile> testing_profile_;
   // Only set after MockAutoLaunchWebKioskApp was called.
   std::unique_ptr<ash::WebKioskAppManager> web_kiosk_app_manager_;
+  // Only set after MockAutoLaunchKioskIwa was called.
+  std::unique_ptr<ash::KioskIwaManager> kiosk_iwa_manager_;
   // Only set after MockAutoLaunchKioskAppWithRequiredPlatformVersion was
   // called.
   std::unique_ptr<ash::KioskChromeAppManager> kiosk_chrome_app_manager_;
@@ -1208,6 +1239,8 @@
   const DeviceLocalAccount fake_kiosk_device_local_account_;
   const WebKioskAppBasicInfo fake_web_kiosk_app_basic_info_;
   const DeviceLocalAccount fake_web_kiosk_device_local_account_;
+  const IsolatedWebAppKioskBasicInfo fake_iwa_kiosk_basic_info_;
+  const DeviceLocalAccount fake_iwa_kiosk_device_local_account_;
   base::ScopedPathOverride user_data_dir_override_;
   base::ScopedPathOverride crash_dumps_dir_override_;
   raw_ptr<ash::FakeUpdateEngineClient, DanglingUntriaged> update_engine_client_;
@@ -1570,6 +1603,35 @@
             device_status_.active_periods(0).session_type());
 }
 
+TEST_F(DeviceStatusCollectorTest, ActivityWithIwaKioskUser) {
+  DisableDefaultSettings();
+  scoped_feature_list_.InitAndEnableFeature(
+      features::kActivityReportingSessionType);
+  ui::IdleState test_states[] = {ui::IDLE_STATE_ACTIVE, ui::IDLE_STATE_ACTIVE,
+                                 ui::IDLE_STATE_ACTIVE};
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ash::kReportDeviceActivityTimes, true);
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ash::kReportDeviceUsers, true);
+  const AccountId kiosk_account_id(AccountId::FromUserEmail(
+      "public@isolated-kiosk-apps.device-local.localhost"));
+  auto* user_manager = GetFakeChromeUserManager();
+  auto* user = user_manager->AddPublicAccountUser(kiosk_account_id);
+  user_manager->UserLoggedIn(kiosk_account_id, user->username_hash(),
+                             /*browser_restart=*/false,
+                             /*is_child=*/false);
+
+  EXPECT_FALSE(status_collector_->IsReportingActivityTimes());
+  EXPECT_FALSE(status_collector_->IsReportingUsers());
+
+  status_collector_->Simulate(test_states, 3);
+  GetStatus();
+  EXPECT_EQ(1, device_status_.active_periods_size());
+  EXPECT_TRUE(device_status_.active_periods(0).user_email().empty());
+  EXPECT_EQ(em::ActiveTimePeriod::SESSION_IWA_KIOSK,
+            device_status_.active_periods(0).session_type());
+}
+
 TEST_F(DeviceStatusCollectorTest, ActivityWithAffiliatedUser) {
   DisableDefaultSettings();
   scoped_feature_list_.InitAndEnableFeature(
@@ -2618,6 +2680,28 @@
   EXPECT_FALSE(session_status_.has_user_dm_token());
 }
 
+TEST_F(DeviceStatusCollectorTest, ReportIwaKioskSessionStatus) {
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ash::kReportDeviceSessionStatus, true);
+  status_collector_->set_kiosk_account(std::make_unique<DeviceLocalAccount>(
+      fake_iwa_kiosk_device_local_account_));
+
+  // Set up a device-local account for single-app Web kiosk mode.
+  MockRunningKioskApp(fake_iwa_kiosk_device_local_account_,
+                      DeviceLocalAccountType::kKioskIsolatedWebApp);
+
+  GetStatus();
+  EXPECT_TRUE(got_session_status_);
+  ASSERT_EQ(1, session_status_.installed_apps_size());
+  EXPECT_EQ(kIwaKioskAccountId, session_status_.device_local_account_id());
+  const em::AppStatus app = session_status_.installed_apps(0);
+  EXPECT_EQ(kIwaKioskWebBundleId, app.app_id());
+  EXPECT_FALSE(app.has_status());
+  EXPECT_FALSE(app.has_error());
+  // Expect no User DM Token for kiosk sessions.
+  EXPECT_FALSE(session_status_.has_user_dm_token());
+}
+
 TEST_F(DeviceStatusCollectorTest, NoOsUpdateStatusByDefault) {
   auto scoped_version = MockPlatformVersion(kDefaultPlatformVersion);
   MockAutoLaunchKioskAppWithRequiredPlatformVersion(
@@ -2900,6 +2984,25 @@
   EXPECT_FALSE(app.has_error());
 }
 
+TEST_F(DeviceStatusCollectorTest, ReportRunningIwaKioskApp) {
+  DisableDefaultSettings();
+  scoped_testing_cros_settings_.device_settings()->SetBoolean(
+      ash::kReportRunningKioskApp, true);
+  MockAutoLaunchKioskIwa(fake_iwa_kiosk_device_local_account_);
+
+  MockRunningKioskApp(fake_iwa_kiosk_device_local_account_,
+                      DeviceLocalAccountType::kKioskIsolatedWebApp);
+  status_collector_->set_kiosk_account(std::make_unique<DeviceLocalAccount>(
+      fake_iwa_kiosk_device_local_account_));
+
+  GetStatus();
+  ASSERT_TRUE(device_status_.has_running_kiosk_app());
+  const em::AppStatus app = device_status_.running_kiosk_app();
+  EXPECT_EQ(kIwaKioskWebBundleId, app.app_id());
+  EXPECT_FALSE(app.has_status());
+  EXPECT_FALSE(app.has_error());
+}
+
 TEST_F(DeviceStatusCollectorTest, TestSoundVolume) {
   // Expect the sound volume to be reported by default (default sound volume
   // used in testing is 75).
diff --git a/chrome/browser/autofill/autocomplete_browsertest.cc b/chrome/browser/autofill/autocomplete_browsertest.cc
index a5e1637..f830898 100644
--- a/chrome/browser/autofill/autocomplete_browsertest.cc
+++ b/chrome/browser/autofill/autocomplete_browsertest.cc
@@ -21,10 +21,10 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/content/browser/test_autofill_manager_injector.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager_test_utils.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/suggestions/suggestions_context.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/test_autofill_manager_waiter.h"
diff --git a/chrome/browser/autofill/autocomplete_history_manager_factory.cc b/chrome/browser/autofill/autocomplete_history_manager_factory.cc
index e9d8e50..ea7fabe5 100644
--- a/chrome/browser/autofill/autocomplete_history_manager_factory.cc
+++ b/chrome/browser/autofill/autocomplete_history_manager_factory.cc
@@ -7,7 +7,7 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/webdata_services/web_data_service_factory.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 
 namespace autofill {
diff --git a/chrome/browser/autofill/merchant_promo_code_manager_factory.cc b/chrome/browser/autofill/merchant_promo_code_manager_factory.cc
index 92ebc52..e39e49d3 100644
--- a/chrome/browser/autofill/merchant_promo_code_manager_factory.cc
+++ b/chrome/browser/autofill/merchant_promo_code_manager_factory.cc
@@ -7,7 +7,7 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
-#include "components/autofill/core/browser/merchant_promo_code_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 
 namespace autofill {
 
diff --git a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc
index 9094a17..71a475df 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.cc
@@ -4,33 +4,31 @@
 
 #include "chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h"
 
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/check_is_test.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/web_contents.h"
 
 namespace chromeos {
 
 namespace {
-// List of pages, which along with their subpages are allowed in kiosk mode.
-KioskSettingsNavigationThrottle::SettingsPage kSettingsPages[] = {
-    {"chrome://os-settings/manageAccessibility", true},
-    {"chrome-extension://mndnfokpggljbaajbnioimlmbfngpief/chromevox/options/"
-     "options.html",
-     false},
-    {"chrome-extension://klbcgckkldhdhonijdbnhhaiedfkllef/", true},
-    {"chrome-extension://gjjabgpgjpampikjhjpfhneeoapjbjaf/", true},
-    {"chrome-extension://dakbfdmgjiabojdgbiljlhgjbokobjpg/", true}};
 
-// This list is used in tests to replace default `kSettingsPages` items.
-std::vector<KioskSettingsNavigationThrottle::SettingsPage>*
+// This list is used in tests to override `DefaultSettingsPages`.
+const std::vector<KioskSettingsNavigationThrottle::SettingsPage>*
     g_test_settings_pages = nullptr;
 
-// WebContents that are marked with this UserData key should be restricted.
+// `WebContents` that are marked with this UserData key should be restricted.
 const void* const kRestrictedSettingsWindowKey = &kRestrictedSettingsWindowKey;
 
-bool CheckUrlMatchSettingsPage(
+bool UrlMatchesSettingsPage(
     const KioskSettingsNavigationThrottle::SettingsPage& page,
     const std::string& url) {
   return (page.allow_subpages &&
@@ -43,17 +41,30 @@
 }  // namespace
 
 // static
+const std::vector<KioskSettingsNavigationThrottle::SettingsPage>&
+KioskSettingsNavigationThrottle::DefaultSettingsPages() {
+  static base::NoDestructor<std::vector<SettingsPage>> settings_pages{
+      {SettingsPage{"chrome://os-settings/manageAccessibility", true},
+       SettingsPage{
+           "chrome-extension://mndnfokpggljbaajbnioimlmbfngpief/chromevox/"
+           "options/options.html",
+           false},
+       SettingsPage{"chrome-extension://klbcgckkldhdhonijdbnhhaiedfkllef/",
+                    true},
+       SettingsPage{"chrome-extension://gjjabgpgjpampikjhjpfhneeoapjbjaf/",
+                    true},
+       SettingsPage{"chrome-extension://dakbfdmgjiabojdgbiljlhgjbokobjpg/",
+                    true}}};
+
+  return *settings_pages;
+}
+
+// static
 bool KioskSettingsNavigationThrottle::IsSettingsPage(const std::string& url) {
-  if (g_test_settings_pages) {
-    for (auto& page : *g_test_settings_pages) {
-      if (CheckUrlMatchSettingsPage(page, url)) {
-        return true;
-      }
-    }
-    return false;
-  }
-  for (auto& page : kSettingsPages) {
-    if (CheckUrlMatchSettingsPage(page, url)) {
+  auto& pages = g_test_settings_pages != nullptr ? *g_test_settings_pages
+                                                 : DefaultSettingsPages();
+  for (auto& page : pages) {
+    if (UrlMatchesSettingsPage(page, url)) {
       return true;
     }
   }
@@ -62,7 +73,8 @@
 
 // static
 void KioskSettingsNavigationThrottle::SetSettingPagesForTesting(
-    std::vector<SettingsPage>* pages) {
+    const std::vector<SettingsPage>* pages) {
+  CHECK_IS_TEST();
   g_test_settings_pages = pages;
 }
 
diff --git a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h
index 5666f875b..2c2ef2f2 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_settings_navigation_throttle.h
@@ -5,7 +5,9 @@
 #ifndef CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_SETTINGS_NAVIGATION_THROTTLE_H_
 #define CHROME_BROWSER_CHROMEOS_APP_MODE_KIOSK_SETTINGS_NAVIGATION_THROTTLE_H_
 
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "content/public/browser/navigation_throttle.h"
 
@@ -21,12 +23,16 @@
     bool allow_subpages = false;
   };
 
+  // Returns the default list of settings pages allowed to open in Kiosk mode.
+  // Can be overridden in tests with `SetSettingPagesForTesting`.
+  static const std::vector<SettingsPage>& DefaultSettingsPages();
+
   // Whether this page is a settings page that is allowed to be open in kiosk
   // mode.
   static bool IsSettingsPage(const std::string& url);
 
   // Replaces the list of allowed settings plages with the provided one.
-  static void SetSettingPagesForTesting(std::vector<SettingsPage>* pages);
+  static void SetSettingPagesForTesting(const std::vector<SettingsPage>* pages);
 
   static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
       content::NavigationHandle* handle);
diff --git a/chrome/browser/enterprise/browser_management/browser_management_service.cc b/chrome/browser/enterprise/browser_management/browser_management_service.cc
index 7d6eb124..b486625 100644
--- a/chrome/browser/enterprise/browser_management/browser_management_service.cc
+++ b/chrome/browser/enterprise/browser_management/browser_management_service.cc
@@ -45,36 +45,40 @@
     : ManagementService(GetManagementStatusProviders(profile)) {
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
   base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE, base::BindOnce(&BrowserManagementService::UpdateManagementIcon,
-                                weak_ptr_factory_.GetWeakPtr(), profile));
+      FROM_HERE,
+      base::BindOnce(&BrowserManagementService::UpdateManagementIconForProfile,
+                     weak_ptr_factory_.GetWeakPtr(), profile));
   pref_change_registrar_.Init(profile->GetPrefs());
   pref_change_registrar_.Add(
-      prefs::kEnterpriseLogoUrl,
-      base::BindRepeating(&BrowserManagementService::UpdateManagementIcon,
-                          weak_ptr_factory_.GetWeakPtr(), profile));
+      prefs::kEnterpriseLogoUrlForProfile,
+      base::BindRepeating(
+          &BrowserManagementService::UpdateManagementIconForProfile,
+          weak_ptr_factory_.GetWeakPtr(), profile));
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 }
 
-ui::ImageModel* BrowserManagementService::GetManagementIcon() {
+ui::ImageModel* BrowserManagementService::GetManagementIconForProfile() {
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-  return management_icon_.IsEmpty() ? nullptr : &management_icon_;
+  return management_icon_for_profile_.IsEmpty() ? nullptr
+                                                : &management_icon_for_profile_;
 #else
   return nullptr;
 #endif
 }
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
-void BrowserManagementService::UpdateManagementIcon(Profile* profile) {
+void BrowserManagementService::UpdateManagementIconForProfile(
+    Profile* profile) {
   enterprise_util::GetManagementIcon(
       GURL(profile->GetPrefs()->GetString(prefs::kEnterpriseLogoUrlForProfile)),
       profile,
-      base::BindOnce(&BrowserManagementService::SetManagementIcon,
+      base::BindOnce(&BrowserManagementService::SetManagementIconForProfile,
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void BrowserManagementService::SetManagementIcon(
+void BrowserManagementService::SetManagementIconForProfile(
     const gfx::Image& management_icon) {
-  management_icon_ = ui::ImageModel::FromImage(management_icon);
+  management_icon_for_profile_ = ui::ImageModel::FromImage(management_icon);
 }
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
diff --git a/chrome/browser/enterprise/browser_management/browser_management_service.h b/chrome/browser/enterprise/browser_management/browser_management_service.h
index cc57b6a..a5c28415 100644
--- a/chrome/browser/enterprise/browser_management/browser_management_service.h
+++ b/chrome/browser/enterprise/browser_management/browser_management_service.h
@@ -28,14 +28,17 @@
  public:
   explicit BrowserManagementService(Profile* profile);
   ~BrowserManagementService() override;
-  ui::ImageModel* GetManagementIcon() override;
+
+  // Returns the management icon used to indicate profile level management.
+  ui::ImageModel* GetManagementIconForProfile() override;
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
  private:
-  void UpdateManagementIcon(Profile* profile);
-  void SetManagementIcon(const gfx::Image& management_icon);
+  // Updates the management icon used to indicate profile level management.
+  void UpdateManagementIconForProfile(Profile* profile);
+  void SetManagementIconForProfile(const gfx::Image& management_icon);
 
   PrefChangeRegistrar pref_change_registrar_;
-  ui::ImageModel management_icon_;
+  ui::ImageModel management_icon_for_profile_;
   base::WeakPtrFactory<BrowserManagementService> weak_ptr_factory_{this};
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 };
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 21a7b15..0680714 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -960,6 +960,7 @@
       "//ui/accessibility:ax_enums_mojo",
       "//ui/base",
       "//ui/base:ozone_buildflags",
+      "//ui/base/accelerators/global_accelerator_listener",
       "//ui/base/clipboard",
       "//ui/base/dragdrop/mojom:mojom_shared",
       "//ui/base/idle",
@@ -1352,21 +1353,11 @@
       }
     }
 
-    if (use_ozone) {
-      deps += [ "//ui/ozone" ]
-      sources += [
-        "global_shortcut_listener_ozone.cc",
-        "global_shortcut_listener_ozone.h",
-      ]
-    }
-
     if (is_mac) {
       sources += [
         "api/enterprise_reporting_private/keychain_data_helper_mac.cc",
         "api/enterprise_reporting_private/keychain_data_helper_mac.h",
         "api/image_writer_private/removable_storage_provider_mac.cc",
-        "global_shortcut_listener_mac.h",
-        "global_shortcut_listener_mac.mm",
         "system_display/display_info_provider_mac.cc",
         "system_display/display_info_provider_mac.h",
       ]
@@ -1401,8 +1392,6 @@
         "api/messaging/launch_context_win.cc",
         "external_registry_loader_win.cc",
         "external_registry_loader_win.h",
-        "global_shortcut_listener_win.cc",
-        "global_shortcut_listener_win.h",
         "system_display/display_info_provider_win.cc",
         "system_display/display_info_provider_win.h",
       ]
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
index 025c2bd..24035d0 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_util.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
@@ -318,7 +318,7 @@
   if (!credit_card.nickname().empty()) {
     card.nickname = base::UTF16ToUTF8(credit_card.nickname());
   }
-  gfx::Image* card_art_image = nullptr;
+  const gfx::Image* card_art_image = nullptr;
   if (base::FeatureList::IsEnabled(
           autofill::features::kAutofillEnableCardArtImage)) {
     card_art_image =
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
index c3bc95a..ae41eb56 100644
--- a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h"
 
+#include <algorithm>
+
+#include "base/barrier_callback.h"
 #include "base/functional/bind.h"
 #include "base/metrics/histogram_functions.h"
 #include "build/chromeos_buildflags.h"
@@ -15,6 +18,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_id.h"
+#include "net/cookies/cookie_access_result.h"
 #include "net/cookies/cookie_util.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "url/url_constants.h"
@@ -59,18 +63,25 @@
                                       WebAuthFlow::INTERACTIVE, user_gesture_);
   }
 
+  if (resolution_data_.cookies.empty()) {
+    OnResolutionDataCookiesSet({});
+    return;
+  }
+
   network::mojom::CookieManager* cookie_manager =
       GetCookieManagerForPartition();
   net::CookieOptions options;
+  base::RepeatingCallback<void(net::CookieAccessResult)> cookie_set_callback =
+      base::BarrierCallback<net::CookieAccessResult>(
+          resolution_data_.cookies.size(),
+          base::BindOnce(&GaiaRemoteConsentFlow::OnResolutionDataCookiesSet,
+                         weak_factory.GetWeakPtr()));
   for (const auto& cookie : resolution_data_.cookies) {
     cookie_manager->SetCanonicalCookie(
         cookie,
         net::cookie_util::SimulatedCookieSource(cookie, url::kHttpsScheme),
-        options, network::mojom::CookieManager::SetCanonicalCookieCallback());
+        options, cookie_set_callback);
   }
-
-  web_flow_->Start();
-  web_flow_started_ = true;
 }
 
 void GaiaRemoteConsentFlow::Stop() {
@@ -137,6 +148,22 @@
   return web_flow_.get();
 }
 
+void GaiaRemoteConsentFlow::OnResolutionDataCookiesSet(
+    const std::vector<net::CookieAccessResult>& cookie_set_result) {
+  bool cookies_set_failed = std::ranges::any_of(
+      cookie_set_result, [](const net::CookieAccessResult& result) {
+        return !result.status.IsInclude();
+      });
+
+  if (cookies_set_failed) {
+    GaiaRemoteConsentFlowFailed(SET_RESOLUTION_COOKIES_FAILED);
+    return;
+  }
+
+  web_flow_->Start();
+  web_flow_started_ = true;
+}
+
 void GaiaRemoteConsentFlow::GaiaRemoteConsentFlowFailed(Failure failure) {
   RecordResultHistogram(failure);
   delegate_->OnGaiaRemoteConsentFlowFailed(failure);
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
index bfeae9f..accf528 100644
--- a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h
@@ -12,6 +12,7 @@
 #include "components/signin/public/identity_manager/accounts_cookie_mutator.h"
 #include "content/public/browser/storage_partition.h"
 #include "google_apis/gaia/oauth2_mint_token_flow.h"
+#include "net/cookies/cookie_access_result.h"
 
 namespace extensions {
 
@@ -19,6 +20,7 @@
  public:
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
+  // LINT.IfChange(GaiaRemoteConsentFlowResult)
   enum Failure {
     NONE = 0,
     WINDOW_CLOSED = 1,
@@ -30,8 +32,10 @@
     // Deprecated:
     // USER_NAVIGATED_AWAY = 6,
     CANNOT_CREATE_WINDOW = 7,
-    kMaxValue = CANNOT_CREATE_WINDOW
+    SET_RESOLUTION_COOKIES_FAILED = 8,
+    kMaxValue = SET_RESOLUTION_COOKIES_FAILED
   };
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/signin/enums.xml:GaiaRemoteConsentFlowResult)
 
   class Delegate {
    public:
@@ -72,6 +76,8 @@
   WebAuthFlow* GetWebAuthFlowForTesting() const;
 
  private:
+  void OnResolutionDataCookiesSet(
+      const std::vector<net::CookieAccessResult>& cookie_set_result);
   void GaiaRemoteConsentFlowFailed(Failure failure);
 
   void DetachWebAuthFlow();
diff --git a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
index ff277f8..3a4f5c1 100644
--- a/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
+++ b/chrome/browser/extensions/api/identity/gaia_remote_consent_flow_browsertest.cc
@@ -4,7 +4,10 @@
 
 #include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h"
 
+#include <optional>
+
 #include "base/strings/strcat.h"
+#include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/browser.h"
@@ -23,6 +26,8 @@
 #include "google_apis/gaia/gaia_id.h"
 #include "google_apis/gaia/gaia_switches.h"
 #include "google_apis/gaia/gaia_urls.h"
+#include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_constants.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -41,6 +46,15 @@
 constexpr char kTestAuthSIDCookie[] = "fake-auth-SID-cookie";
 constexpr char kTestAuthLSIDCookie[] = "fake-auth-LSID-cookie";
 
+net::CanonicalCookie CreateCookie(const GURL& url, const std::string& name) {
+  auto cookie = net::CanonicalCookie::CreateSanitizedCookie(
+      url, name, "test_value", "." + url.host(), "/", base::Time(),
+      base::Time(), base::Time(), true, false,
+      net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_DEFAULT,
+      std::nullopt, nullptr);
+  return *cookie;
+}
+
 }  // namespace
 
 class MockGaiaRemoteConsentFlowDelegate
@@ -97,6 +111,8 @@
     command_line->AppendSwitchASCII(switches::kGoogleApisUrl, base_url.spec());
     command_line->AppendSwitchASCII(switches::kOAuth2ClientID, base_url.spec());
 
+    consent_url_ = fake_gaia_test_server()->GetURL("/title1.html");
+
     fake_gaia_.Initialize();
     fake_gaia_.MapEmailToGaiaId(kTestEmail, GaiaId(kGaiaId));
 
@@ -124,21 +140,25 @@
     return primary_account_info;
   }
 
-  void CreateGaiaRemoteConsentFlow(const GURL& url) {
+  void CreateGaiaRemoteConsentFlow(
+      const GURL& url,
+      const std::vector<net::CanonicalCookie>& resolution_cookies = {}) {
     CoreAccountInfo account_info = CreateFakeAccountInfoAndSetAsPrimary();
     ExtensionTokenKey token_key("extension_id", account_info,
                                 std::set<std::string>());
     RemoteConsentResolutionData resolution_data;
     resolution_data.url = url;
+    resolution_data.cookies = resolution_cookies;
 
     flow_ = std::make_unique<GaiaRemoteConsentFlow>(&mock(), profile(),
                                                     token_key, resolution_data,
                                                     /*user_gesture=*/true);
   }
 
-  void LaunchAndWaitGaiaRemoteConsentFlow() {
-    GURL url = fake_gaia_test_server()->GetURL("/title1.html");
-    CreateGaiaRemoteConsentFlow(url);
+  void LaunchAndWaitGaiaRemoteConsentFlow(
+      const GURL& url,
+      const std::vector<net::CanonicalCookie>& resolution_cookies = {}) {
+    CreateGaiaRemoteConsentFlow(url, resolution_cookies);
 
     content::TestNavigationObserver navigation_observer(url);
     navigation_observer.StartWatchingNewWebContents();
@@ -171,10 +191,14 @@
 
   GaiaRemoteConsentFlow* flow() { return flow_.get(); }
 
+  const GURL& consent_url() { return consent_url_; }
+
  private:
   std::unique_ptr<GaiaRemoteConsentFlow> flow_;
+  GURL consent_url_;
 
-  MockGaiaRemoteConsentFlowDelegate mock_gaia_remote_consent_flow_delegate_;
+  testing::StrictMock<MockGaiaRemoteConsentFlowDelegate>
+      mock_gaia_remote_consent_flow_delegate_;
 
   net::EmbeddedTestServer fake_gaia_test_server_;
   FakeGaia fake_gaia_;
@@ -184,7 +208,7 @@
 
 IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest,
                        SimulateInvalidConsent) {
-  LaunchAndWaitGaiaRemoteConsentFlow();
+  LaunchAndWaitGaiaRemoteConsentFlow(consent_url());
 
   EXPECT_CALL(mock(),
               OnGaiaRemoteConsentFlowFailed(
@@ -193,7 +217,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest, SimulateNoGrant) {
-  LaunchAndWaitGaiaRemoteConsentFlow();
+  LaunchAndWaitGaiaRemoteConsentFlow(consent_url());
 
   EXPECT_CALL(mock(), OnGaiaRemoteConsentFlowFailed(
                           GaiaRemoteConsentFlow::Failure::NO_GRANT));
@@ -204,7 +228,20 @@
 
 IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest,
                        SimulateAccessGranted) {
-  LaunchAndWaitGaiaRemoteConsentFlow();
+  LaunchAndWaitGaiaRemoteConsentFlow(consent_url());
+
+  std::string approved_consent = gaia::GenerateOAuth2MintTokenConsentResult(
+      /*approved=*/true, "consent_granted", kGaiaId);
+  EXPECT_CALL(mock(),
+              OnGaiaRemoteConsentFlowApproved(approved_consent, kGaiaId));
+  SimulateConsentResult(approved_consent);
+}
+
+IN_PROC_BROWSER_TEST_F(GaiaRemoteConsentFlowParamBrowserTest,
+                       SimulateAccessGrantedWithCookies) {
+  LaunchAndWaitGaiaRemoteConsentFlow(consent_url(),
+                                     {CreateCookie(consent_url(), "cookie1"),
+                                      CreateCookie(consent_url(), "cookie2")});
 
   std::string approved_consent = gaia::GenerateOAuth2MintTokenConsentResult(
       /*approved=*/true, "consent_granted", kGaiaId);
diff --git a/chrome/browser/extensions/api/identity/identity_constants.cc b/chrome/browser/extensions/api/identity/identity_constants.cc
index a15e875..e0e5fcc3 100644
--- a/chrome/browser/extensions/api/identity/identity_constants.cc
+++ b/chrome/browser/extensions/api/identity/identity_constants.cc
@@ -26,6 +26,9 @@
 const char kPageLoadFailure[] = "Authorization page could not be loaded.";
 const char kPageLoadTimedOut[] = "Authorization page load timed out.";
 const char kInvalidConsentResult[] = "Returned an invalid consent result.";
+const char kCannotSetRemoteConsentResolutionCookies[] =
+    "Couldn't set up remote consent resolution cookies to display an "
+    "authorization page";
 const char kCannotCreateWindow[] =
     "Couldn't create a browser window to display an authorization page.";
 const char kInvalidURLScheme[] =
diff --git a/chrome/browser/extensions/api/identity/identity_constants.h b/chrome/browser/extensions/api/identity/identity_constants.h
index af6878d..1c7d490 100644
--- a/chrome/browser/extensions/api/identity/identity_constants.h
+++ b/chrome/browser/extensions/api/identity/identity_constants.h
@@ -23,6 +23,7 @@
 extern const char kPageLoadFailure[];
 extern const char kPageLoadTimedOut[];
 extern const char kInvalidConsentResult[];
+extern const char kCannotSetRemoteConsentResolutionCookies[];
 extern const char kCannotCreateWindow[];
 extern const char kInvalidURLScheme[];
 extern const char kBrowserContextShutDown[];
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
index 44ccaee..9d3eca6 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc
@@ -73,6 +73,8 @@
       return identity_constants::kCannotCreateWindow;
     case State::kBrowserContextShutDown:
       return identity_constants::kBrowserContextShutDown;
+    case State::kSetRemoteConsentResolutionCookiesFailed:
+      return identity_constants::kCannotSetRemoteConsentResolutionCookies;
   }
 }
 
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
index b84c306..51dd978 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h
@@ -14,6 +14,7 @@
  public:
   // These values are persisted to logs. Entries should not be renumbered and
   // numeric values should never be reused.
+  // LINT.IfChange(GetAuthTokenResult)
   enum class State {
     kNone = 0,
     kInvalidClientId = 1,
@@ -46,8 +47,10 @@
     kInteractivityDenied = 28,
     kCannotCreateWindow = 29,
     kBrowserContextShutDown = 30,
-    kMaxValue = kBrowserContextShutDown,
+    kSetRemoteConsentResolutionCookiesFailed = 31,
+    kMaxValue = kSetRemoteConsentResolutionCookiesFailed,
   };
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/signin/enums.xml:GetAuthTokenResult)
 
   // Constructs a |State::kMintTokenAuthFailure| error with an
   // |error_message|.
diff --git a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
index 978cdf3..ba91392a 100644
--- a/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_auth_token_function.cc
@@ -18,6 +18,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h"
 #include "chrome/browser/extensions/api/identity/identity_api.h"
 #include "chrome/browser/extensions/api/identity/identity_get_auth_token_error.h"
 #include "chrome/browser/extensions/extension_util.h"
@@ -638,6 +639,13 @@
     case GaiaRemoteConsentFlow::CANNOT_CREATE_WINDOW:
       error = IdentityGetAuthTokenError(
           IdentityGetAuthTokenError::State::kCannotCreateWindow);
+      break;
+
+    case GaiaRemoteConsentFlow::SET_RESOLUTION_COOKIES_FAILED:
+      error = IdentityGetAuthTokenError(
+          IdentityGetAuthTokenError::State::
+              kSetRemoteConsentResolutionCookiesFailed);
+      break;
   }
 
   CompleteFunctionWithError(error);
diff --git a/chrome/browser/extensions/global_shortcut_listener.cc b/chrome/browser/extensions/global_shortcut_listener.cc
index b413086c..725ab038 100644
--- a/chrome/browser/extensions/global_shortcut_listener.cc
+++ b/chrome/browser/extensions/global_shortcut_listener.cc
@@ -4,102 +4,117 @@
 
 #include "chrome/browser/extensions/global_shortcut_listener.h"
 
+#include <algorithm>
+
 #include "base/check.h"
+#include "base/containers/contains.h"
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
 #include "base/not_fatal_until.h"
 #include "base/notreached.h"
+#include "build/config/linux/dbus/buildflags.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/accelerators/command.h"
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
+
+#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
+#include "base/feature_list.h"
+#include "chrome/browser/extensions/global_shortcut_listener_linux.h"
+#endif
 
 using content::BrowserThread;
 
 namespace extensions {
 
-GlobalShortcutListener::GlobalShortcutListener()
-    : shortcut_handling_suspended_(false) {
+namespace {
+#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
+BASE_FEATURE(kGlobalShortcutsPortal,
+             "GlobalShortcutsPortal",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
+}  // namespace
+
+// static
+GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
+  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  static GlobalShortcutListener* instance =
+      new GlobalShortcutListener(ui::GlobalAcceleratorListener::GetInstance());
+  if (instance) {
+    return instance;
+  }
+#if BUILDFLAG(IS_OZONE) && BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
+  if (base::FeatureList::IsEnabled(kGlobalShortcutsPortal)) {
+    static GlobalShortcutListenerLinux* linux_instance =
+        new GlobalShortcutListenerLinux(
+            ui::GlobalAcceleratorListener::GetInstance(), nullptr);
+    return linux_instance;
+  }
+#endif
+  return nullptr;
+}
+
+GlobalShortcutListener::GlobalShortcutListener(
+    ui::GlobalAcceleratorListener* global_accelerator_listener)
+    : global_accelerator_listener_(global_accelerator_listener) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
 
-GlobalShortcutListener::~GlobalShortcutListener() {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(accelerator_map_.empty());  // Make sure we've cleaned up.
-}
+GlobalShortcutListener::~GlobalShortcutListener() = default;
 
 bool GlobalShortcutListener::RegisterAccelerator(
-    const ui::Accelerator& accelerator, Observer* observer) {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (IsShortcutHandlingSuspended())
-    return false;
-
-  AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator);
-  if (it != accelerator_map_.end()) {
-    // The accelerator has been registered.
+    const ui::Accelerator& accelerator,
+    Observer* observer) {
+  if (IsShortcutHandlingSuspended()) {
     return false;
   }
 
-  if (!RegisterAcceleratorImpl(accelerator)) {
-    // If the platform-specific registration fails, mostly likely the shortcut
-    // has been registered by other native applications.
-    return false;
-  }
-
-  if (accelerator_map_.empty())
-    StartListening();
-
-  accelerator_map_[accelerator] = observer;
-  return true;
+  accelerators_.push_back(accelerator);
+  return global_accelerator_listener_->RegisterAccelerator(accelerator,
+                                                           observer);
 }
 
 void GlobalShortcutListener::UnregisterAccelerator(
-    const ui::Accelerator& accelerator, Observer* observer) {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (IsShortcutHandlingSuspended())
+    const ui::Accelerator& accelerator,
+    Observer* observer) {
+  if (IsShortcutHandlingSuspended()) {
     return;
+  }
 
-  auto it = accelerator_map_.find(accelerator);
-  // We should never get asked to unregister something that we didn't register.
-  CHECK(it != accelerator_map_.end(), base::NotFatalUntil::M130);
-  // The caller should call this function with the right observer.
-  DCHECK(it->second == observer);
-
-  UnregisterAcceleratorImpl(accelerator);
-  accelerator_map_.erase(it);
-  if (accelerator_map_.empty())
-    StopListening();
+  global_accelerator_listener_->UnregisterAccelerator(accelerator, observer);
+  RemoveAccelerator(accelerator);
 }
 
 void GlobalShortcutListener::UnregisterAccelerators(Observer* observer) {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (IsShortcutHandlingSuspended())
+  if (IsShortcutHandlingSuspended()) {
     return;
+  }
 
-  auto it = accelerator_map_.begin();
-  while (it != accelerator_map_.end()) {
-    if (it->second == observer) {
-      auto to_remove = it++;
-      UnregisterAccelerator(to_remove->first, observer);
-    } else {
-      ++it;
-    }
+  std::vector<ui::Accelerator> removed =
+      global_accelerator_listener_->UnregisterAccelerators(observer);
+  for (const ui::Accelerator& accelerator : removed) {
+    RemoveAccelerator(accelerator);
   }
 }
 
 void GlobalShortcutListener::SetShortcutHandlingSuspended(bool suspended) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  if (shortcut_handling_suspended_ == suspended)
+  if (shortcut_handling_suspended_ == suspended) {
     return;
+  }
 
   shortcut_handling_suspended_ = suspended;
-  for (auto it = accelerator_map_.begin(); it != accelerator_map_.end(); ++it) {
+  for (auto& accelerator : accelerators_) {
     // On Linux, when shortcut handling is suspended we cannot simply early
     // return in NotifyKeyPressed (similar to what we do for non-global
     // shortcuts) because we'd eat the keyboard event thereby preventing the
     // user from setting the shortcut. Therefore we must unregister while
     // handling is suspended and register when handling resumes.
-    if (shortcut_handling_suspended_)
-      UnregisterAcceleratorImpl(it->first);
-    else
-      RegisterAcceleratorImpl(it->first);
+    if (shortcut_handling_suspended_) {
+      global_accelerator_listener_->StopListeningForAccelerator(accelerator);
+    } else {
+      global_accelerator_listener_->StartListeningForAccelerator(accelerator);
+    }
   }
 }
 
@@ -117,16 +132,9 @@
     const ui::CommandMap& commands,
     Observer* observer) {}
 
-void GlobalShortcutListener::NotifyKeyPressed(
+void GlobalShortcutListener::RemoveAccelerator(
     const ui::Accelerator& accelerator) {
-  auto iter = accelerator_map_.find(accelerator);
-  if (iter == accelerator_map_.end()) {
-    // This should never occur, because if it does, we have failed to unregister
-    // or failed to clean up the map after unregistering the shortcut.
-    NOTREACHED();
-  }
-
-  iter->second->OnKeyPressed(accelerator);
+  std::erase(accelerators_, accelerator);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/global_shortcut_listener.h b/chrome/browser/extensions/global_shortcut_listener.h
index 667530c9..0fa4697 100644
--- a/chrome/browser/extensions/global_shortcut_listener.h
+++ b/chrome/browser/extensions/global_shortcut_listener.h
@@ -7,9 +7,13 @@
 
 #include <map>
 #include <string>
+#include <memory>
 
 #include "base/memory/raw_ptr.h"
 #include "ui/base/accelerators/command.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/accelerators/command.h"
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
 
 namespace ui {
 class Accelerator;
@@ -17,16 +21,14 @@
 
 namespace extensions {
 
-// Platform-neutral implementation of a class that keeps track of observers and
-// monitors keystrokes. It relays messages to the appropriate observer when a
-// global shortcut has been struck by the user.
+// Extensions-specific wrapper on the general purpose
+// `ui::GlobalAcceleratorListener`. Most calls are delegated to the general
+// purpose service, but there is also a special Linux Dbus implementation that
+// applies only to extensions.
 class GlobalShortcutListener {
  public:
-  class Observer {
+  class Observer : public ui::GlobalAcceleratorListener::Observer {
    public:
-    // Called when your global shortcut (|accelerator|) is struck.
-    virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0;
-
     // Called when a command should be executed directly.
     virtual void ExecuteCommand(const std::string& accelerator_group_id,
                                 const std::string& command_id) = 0;
@@ -79,37 +81,23 @@
                                  Observer* observer);
 
  protected:
-  GlobalShortcutListener();
-
-  // Called by platform specific implementations of this class whenever a key
-  // is struck. Only called for keys that have an observer registered.
-  void NotifyKeyPressed(const ui::Accelerator& accelerator);
+  explicit GlobalShortcutListener(
+      ui::GlobalAcceleratorListener* global_shortcut_listener);
 
  private:
-  // The following methods are implemented by platform-specific implementations
-  // of this class.
-  //
-  // Start/StopListening are called when transitioning between zero and nonzero
-  // registered accelerators. StartListening will be called after
-  // RegisterAcceleratorImpl and StopListening will be called after
-  // UnregisterAcceleratorImpl.
-  //
-  // For RegisterAcceleratorImpl, implementations return false if registration
-  // did not complete successfully.
-  virtual void StartListening() = 0;
-  virtual void StopListening() = 0;
-  virtual bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) = 0;
-  virtual void UnregisterAcceleratorImpl(
-      const ui::Accelerator& accelerator) = 0;
+  // Removes an accelerator from the list of accelerators registered with this
+  // class.
+  void RemoveAccelerator(const ui::Accelerator& accelerator);
 
-  // The map of accelerators that have been successfully registered as global
-  // shortcuts and their observer.
-  typedef std::map<ui::Accelerator, raw_ptr<Observer, CtnExperimental>>
-      AcceleratorMap;
-  AcceleratorMap accelerator_map_;
+  // GlobalShortcutListener instance to which where most calls are delegated.
+  raw_ptr<ui::GlobalAcceleratorListener> global_accelerator_listener_;
+
+  // Local tracking of accelerators that need to be registered or unregistered
+  // when shortcut handling is suspended.
+  std::vector<ui::Accelerator> accelerators_;
 
   // Keeps track of whether shortcut handling is currently suspended.
-  bool shortcut_handling_suspended_;
+  bool shortcut_handling_suspended_ = false;
 };
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/global_shortcut_listener_linux.cc b/chrome/browser/extensions/global_shortcut_listener_linux.cc
index c6ae8c2..b4bb0dc3 100644
--- a/chrome/browser/extensions/global_shortcut_listener_linux.cc
+++ b/chrome/browser/extensions/global_shortcut_listener_linux.cc
@@ -14,6 +14,7 @@
 #include "base/nix/xdg_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/global_shortcut_listener.h"
 #include "components/dbus/properties/types.h"
 #include "components/dbus/thread_linux/dbus_thread_linux.h"
 #include "components/dbus/utils/check_for_service_and_start.h"
@@ -32,8 +33,9 @@
 using DbusShortcuts = DbusArray<DbusShortcut>;
 
 GlobalShortcutListenerLinux::GlobalShortcutListenerLinux(
+    ui::GlobalAcceleratorListener* global_shortcut_listener,
     scoped_refptr<dbus::Bus> bus)
-    : bus_(std::move(bus)) {
+    : GlobalShortcutListener(global_shortcut_listener), bus_(std::move(bus)) {
   if (!bus_) {
     dbus::Bus::Options options;
     options.bus_type = dbus::Bus::SESSION;
@@ -119,21 +121,6 @@
                      weak_ptr_factory_.GetWeakPtr(), session_key));
 }
 
-void GlobalShortcutListenerLinux::StartListening() {}
-
-void GlobalShortcutListenerLinux::StopListening() {}
-
-bool GlobalShortcutListenerLinux::RegisterAcceleratorImpl(
-    const ui::Accelerator& accelerator) {
-  // Shortcut registration is now handled in OnCommandsChanged()
-  return false;
-}
-
-void GlobalShortcutListenerLinux::UnregisterAcceleratorImpl(
-    const ui::Accelerator& accelerator) {
-  // Shortcut unregistration is now handled per extension
-}
-
 void GlobalShortcutListenerLinux::UnregisterAccelerators(Observer* observer) {
   std::vector<SessionKey> remove;
   for (const auto& [key, context] : session_map_) {
diff --git a/chrome/browser/extensions/global_shortcut_listener_linux.h b/chrome/browser/extensions/global_shortcut_listener_linux.h
index b3d83d6..01c1afa 100644
--- a/chrome/browser/extensions/global_shortcut_listener_linux.h
+++ b/chrome/browser/extensions/global_shortcut_listener_linux.h
@@ -31,7 +31,9 @@
 // interface.
 class GlobalShortcutListenerLinux : public GlobalShortcutListener {
  public:
-  explicit GlobalShortcutListenerLinux(scoped_refptr<dbus::Bus> bus);
+  GlobalShortcutListenerLinux(
+      ui::GlobalAcceleratorListener* global_shortcut_listener,
+      scoped_refptr<dbus::Bus> bus);
 
   GlobalShortcutListenerLinux(const GlobalShortcutListenerLinux&) = delete;
   GlobalShortcutListenerLinux& operator=(const GlobalShortcutListenerLinux&) =
@@ -86,10 +88,6 @@
   using SessionMapPair = std::pair<SessionKey, std::unique_ptr<SessionContext>>;
 
   // GlobalShortcutListener:
-  void StartListening() override;
-  void StopListening() override;
-  bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
-  void UnregisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
   void UnregisterAccelerators(Observer* observer) override;
   bool IsRegistrationHandledExternally() const override;
   void OnCommandsChanged(const std::string& accelerator_group_id,
diff --git a/chrome/browser/extensions/global_shortcut_listener_linux_unittest.cc b/chrome/browser/extensions/global_shortcut_listener_linux_unittest.cc
index 6987749..edf22b4b 100644
--- a/chrome/browser/extensions/global_shortcut_listener_linux_unittest.cc
+++ b/chrome/browser/extensions/global_shortcut_listener_linux_unittest.cc
@@ -18,6 +18,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/accelerators/command.h"
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
 
 using ::testing::_;
 using ::testing::AtLeast;
@@ -150,8 +151,8 @@
             activated_callback = signal_callback;
           }));
 
-  auto global_shortcut_listener =
-      std::make_unique<GlobalShortcutListenerLinux>(mock_bus);
+  auto global_shortcut_listener = std::make_unique<GlobalShortcutListenerLinux>(
+      ui::GlobalAcceleratorListener::GetInstance(), mock_bus);
   auto observer = std::make_unique<MockObserver>();
 
   // These object proxies have unique generated names, so are initialized when
diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.h b/chrome/browser/extensions/global_shortcut_listener_mac.h
deleted file mode 100644
index a661380..0000000
--- a/chrome/browser/extensions/global_shortcut_listener_mac.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
-#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
-
-#include "chrome/browser/extensions/global_shortcut_listener.h"
-
-#include <Carbon/Carbon.h>
-
-#include <map>
-#include <memory>
-
-#include "ui/base/accelerators/media_keys_listener.h"
-
-namespace extensions {
-
-// Mac-specific implementation of the GlobalShortcutListener class that
-// listens for global shortcuts. Handles basic keyboard intercepting and
-// forwards its output to the base class for processing.
-//
-// This class does two things:
-// 1. Intercepts media keys. Uses an event tap for intercepting media keys
-// (PlayPause, NextTrack, PreviousTrack).
-// 2. Binds keyboard shortcuts (hot keys). Carbon RegisterEventHotKey API for
-// binding to non-media key global hot keys (eg. Command-Shift-1).
-class GlobalShortcutListenerMac : public GlobalShortcutListener,
-                                  public ui::MediaKeysListener::Delegate {
- public:
-  GlobalShortcutListenerMac();
-
-  GlobalShortcutListenerMac(const GlobalShortcutListenerMac&) = delete;
-  GlobalShortcutListenerMac& operator=(const GlobalShortcutListenerMac&) =
-      delete;
-
-  ~GlobalShortcutListenerMac() override;
-
- private:
-  using KeyId = int;
-  using AcceleratorIdMap = std::map<ui::Accelerator, KeyId>;
-  using IdAcceleratorMap = std::map<KeyId, ui::Accelerator>;
-  using IdHotKeyRefMap = std::map<KeyId, EventHotKeyRef>;
-
-  // Keyboard event callbacks.
-  void OnHotKeyEvent(EventHotKeyID hot_key_id);
-
-  // GlobalShortcutListener implementation.
-  void StartListening() override;
-  void StopListening() override;
-  bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
-  void UnregisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
-
-  // ui::MediaKeysListener::Delegate:
-  void OnMediaKeysAccelerator(const ui::Accelerator& accelerator) override;
-
-  // Mac-specific functions for registering hot keys with modifiers.
-  bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id);
-  void UnregisterHotKey(const ui::Accelerator& accelerator);
-
-  // Enable and disable the hot key event handler.
-  void StartWatchingHotKeys();
-  void StopWatchingHotKeys();
-
-  // Whether or not any hot keys are currently registered.
-  bool IsAnyHotKeyRegistered();
-
-  // The callback for when a hot key event happens.
-  static OSStatus HotKeyHandler(
-      EventHandlerCallRef next_handler, EventRef event, void* user_data);
-
-  // Whether this object is listening for global shortcuts.
-  bool is_listening_ = false;
-
-  // The hotkey identifier for the next global shortcut that is added.
-  KeyId hot_key_id_ = 0;
-
-  // A map of all hotkeys (media keys and shortcuts) mapping to their
-  // corresponding hotkey IDs. For quickly finding if an accelerator is
-  // registered.
-  AcceleratorIdMap accelerator_ids_;
-
-  // The inverse map for quickly looking up accelerators by hotkey id.
-  IdAcceleratorMap id_accelerators_;
-
-  // Keyboard shortcut IDs to hotkeys map for unregistration.
-  IdHotKeyRefMap id_hot_key_refs_;
-
-  // Event handler for keyboard shortcut hot keys.
-  EventHandlerRef event_handler_ = nullptr;
-
-  // Media keys listener.
-  std::unique_ptr<ui::MediaKeysListener> media_keys_listener_;
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_MAC_H_
diff --git a/chrome/browser/extensions/global_shortcut_listener_ozone.cc b/chrome/browser/extensions/global_shortcut_listener_ozone.cc
deleted file mode 100644
index b1c2751..0000000
--- a/chrome/browser/extensions/global_shortcut_listener_ozone.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2013 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/extensions/global_shortcut_listener_ozone.h"
-
-#include "base/containers/contains.h"
-#include "base/memory/ptr_util.h"
-#include "base/no_destructor.h"
-#include "build/config/linux/dbus/buildflags.h"
-#include "content/public/browser/browser_thread.h"
-#include "ui/base/accelerators/accelerator.h"
-#include "ui/ozone/public/ozone_platform.h"
-
-#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
-#include "base/feature_list.h"
-#include "chrome/browser/extensions/global_shortcut_listener_linux.h"
-#endif
-
-using content::BrowserThread;
-
-namespace extensions {
-
-namespace {
-
-#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
-BASE_FEATURE(kGlobalShortcutsPortal,
-             "GlobalShortcutsPortal",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-#endif
-
-}  // namespace
-
-// static
-std::unique_ptr<GlobalShortcutListener> GlobalShortcutListenerOzone::Create() {
-  auto listener = std::make_unique<GlobalShortcutListenerOzone>(
-      base::PassKey<GlobalShortcutListenerOzone>());
-  if (listener->platform_global_shortcut_listener_) {
-    return listener;
-  }
-#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
-  if (base::FeatureList::IsEnabled(kGlobalShortcutsPortal)) {
-    return std::make_unique<GlobalShortcutListenerLinux>(nullptr);
-  }
-#endif
-  return nullptr;
-}
-
-GlobalShortcutListenerOzone::GlobalShortcutListenerOzone(
-    base::PassKey<GlobalShortcutListenerOzone>) {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
-  platform_global_shortcut_listener_ =
-      ui::OzonePlatform::GetInstance()->GetPlatformGlobalShortcutListener(this);
-}
-
-GlobalShortcutListenerOzone::~GlobalShortcutListenerOzone() {
-  if (is_listening_) {
-    StopListening();
-  }
-
-  if (platform_global_shortcut_listener_) {
-    platform_global_shortcut_listener_->ResetDelegate();
-  }
-}
-
-void GlobalShortcutListenerOzone::StartListening() {
-  DCHECK(!is_listening_);
-  DCHECK(!registered_hot_keys_.empty());
-
-  if (platform_global_shortcut_listener_) {
-    platform_global_shortcut_listener_->StartListening();
-  }
-
-  is_listening_ = true;
-}
-
-void GlobalShortcutListenerOzone::StopListening() {
-  DCHECK(is_listening_);
-  DCHECK(registered_hot_keys_.empty());
-
-  if (platform_global_shortcut_listener_) {
-    platform_global_shortcut_listener_->StopListening();
-  }
-
-  is_listening_ = false;
-}
-
-bool GlobalShortcutListenerOzone::RegisterAcceleratorImpl(
-    const ui::Accelerator& accelerator) {
-  DCHECK(!base::Contains(registered_hot_keys_, accelerator));
-
-  if (!platform_global_shortcut_listener_) {
-    return false;
-  }
-
-  const bool registered =
-      platform_global_shortcut_listener_->RegisterAccelerator(
-          accelerator.key_code(), accelerator.IsAltDown(),
-          accelerator.IsCtrlDown(), accelerator.IsShiftDown());
-  if (registered) {
-    registered_hot_keys_.insert(accelerator);
-  }
-  return registered;
-}
-
-void GlobalShortcutListenerOzone::UnregisterAcceleratorImpl(
-    const ui::Accelerator& accelerator) {
-  DCHECK(base::Contains(registered_hot_keys_, accelerator));
-  // Otherwise how could the accelerator be registered?
-  DCHECK(platform_global_shortcut_listener_);
-
-  platform_global_shortcut_listener_->UnregisterAccelerator(
-      accelerator.key_code(), accelerator.IsAltDown(), accelerator.IsCtrlDown(),
-      accelerator.IsShiftDown());
-  registered_hot_keys_.erase(accelerator);
-}
-
-void GlobalShortcutListenerOzone::OnKeyPressed(ui::KeyboardCode key_code,
-                                               bool is_alt_down,
-                                               bool is_ctrl_down,
-                                               bool is_shift_down) {
-  int modifiers = 0;
-  if (is_alt_down) {
-    modifiers |= ui::EF_ALT_DOWN;
-  }
-  if (is_ctrl_down) {
-    modifiers |= ui::EF_CONTROL_DOWN;
-  }
-  if (is_shift_down) {
-    modifiers |= ui::EF_SHIFT_DOWN;
-  }
-
-  NotifyKeyPressed(ui::Accelerator(key_code, modifiers));
-}
-
-void GlobalShortcutListenerOzone::OnPlatformListenerDestroyed() {
-  platform_global_shortcut_listener_ = nullptr;
-}
-
-// static
-GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
-  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  static const base::NoDestructor<std::unique_ptr<GlobalShortcutListener>>
-      instance(GlobalShortcutListenerOzone::Create());
-  return instance->get();
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/global_shortcut_listener_ozone.h b/chrome/browser/extensions/global_shortcut_listener_ozone.h
deleted file mode 100644
index 90abe5d0..0000000
--- a/chrome/browser/extensions/global_shortcut_listener_ozone.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_OZONE_H_
-#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_OZONE_H_
-
-#include <memory>
-#include <set>
-
-#include "base/memory/raw_ptr.h"
-#include "base/types/pass_key.h"
-#include "chrome/browser/extensions/global_shortcut_listener.h"
-#include "ui/events/keycodes/keyboard_codes.h"
-#include "ui/ozone/public/platform_global_shortcut_listener.h"
-
-namespace ui {
-class Accelerator;
-}  // namespace ui
-
-namespace extensions {
-
-// Ozone-specific implementation of the GlobalShortcutListener interface.
-//
-// Connects Aura with the platform implementation, and manages data conversions
-// required on the way: Aura operates with ui::Accelerator while the platform is
-// only aware of the basic components such as the key code and modifiers.
-class GlobalShortcutListenerOzone
-    : public GlobalShortcutListener,
-      public ui::PlatformGlobalShortcutListenerDelegate {
- public:
-  static std::unique_ptr<GlobalShortcutListener> Create();
-
-  // Clients should use Create() instead of using this constructor.
-  explicit GlobalShortcutListenerOzone(
-      base::PassKey<GlobalShortcutListenerOzone>);
-
-  GlobalShortcutListenerOzone(const GlobalShortcutListenerOzone&) = delete;
-  GlobalShortcutListenerOzone& operator=(const GlobalShortcutListenerOzone&) =
-      delete;
-
-  ~GlobalShortcutListenerOzone() override;
-
- private:
-  // GlobalShortcutListener:
-  void StartListening() override;
-  void StopListening() override;
-  bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
-  void UnregisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
-
-  // ui::PlatformGlobalShortcutListenerDelegate:
-  void OnKeyPressed(ui::KeyboardCode key_code,
-                    bool is_alt_down,
-                    bool is_ctrl_down,
-                    bool is_shift_down) override;
-  void OnPlatformListenerDestroyed() override;
-
-  bool is_listening_ = false;
-  std::set<ui::Accelerator> registered_hot_keys_;
-
-  // The platform implementation.
-  raw_ptr<ui::PlatformGlobalShortcutListener>
-      platform_global_shortcut_listener_ = nullptr;
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_OZONE_H_
diff --git a/chrome/browser/extensions/global_shortcut_listener_unittest.cc b/chrome/browser/extensions/global_shortcut_listener_unittest.cc
index 8a4f45e..1b3c52f 100644
--- a/chrome/browser/extensions/global_shortcut_listener_unittest.cc
+++ b/chrome/browser/extensions/global_shortcut_listener_unittest.cc
@@ -20,12 +20,14 @@
 // Test implementation of GlobalShortcutListener that doesn't delegate
 // to an OS-specific implementation. All this does is fail to register an
 // accelerator if it has already been registered.
-class GlobalShortcutListenerForTesting final : public GlobalShortcutListener {
+class BaseGlobalShortcutListenerForTesting final
+    : public ui::GlobalAcceleratorListener {
  public:
   void StartListening() override {}
   void StopListening() override {}
 
-  bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override {
+  bool StartListeningForAccelerator(
+      const ui::Accelerator& accelerator) override {
     if (registered_accelerators_.contains(accelerator)) {
       return false;
     }
@@ -34,7 +36,8 @@
     return true;
   }
 
-  void UnregisterAcceleratorImpl(const ui::Accelerator& accelerator) override {
+  void StopListeningForAccelerator(
+      const ui::Accelerator& accelerator) override {
     registered_accelerators_.erase(accelerator);
   }
 
@@ -42,6 +45,14 @@
   std::set<ui::Accelerator> registered_accelerators_;
 };
 
+class ExtensionsGlobalShortcutListenerForTesting final
+    : public extensions::GlobalShortcutListener {
+ public:
+  explicit ExtensionsGlobalShortcutListenerForTesting(
+      ui::GlobalAcceleratorListener* global_shortcut_listener)
+      : extensions::GlobalShortcutListener(global_shortcut_listener) {}
+};
+
 class TestObserver final : public GlobalShortcutListener::Observer {
  public:
   virtual ~TestObserver() = default;
@@ -52,39 +63,45 @@
                       const std::string& command_id) override {}
 };
 
-class GlobalShortcutListenerTest : public testing::Test {
+class ExtensionsGlobalShortcutListenerTest : public testing::Test {
  public:
-  GlobalShortcutListenerTest()
+  ExtensionsGlobalShortcutListenerTest()
       : task_environment_(content::BrowserTaskEnvironment::MainThreadType::UI) {
-    listener_ = std::make_unique<GlobalShortcutListenerForTesting>();
+    ui_listener_ = std::make_unique<BaseGlobalShortcutListenerForTesting>();
+    extensions_listener_ =
+        std::make_unique<ExtensionsGlobalShortcutListenerForTesting>(
+            ui_listener_.get());
   }
 
-  GlobalShortcutListenerTest(const GlobalShortcutListenerTest&) = delete;
-  GlobalShortcutListenerTest& operator=(const GlobalShortcutListenerTest&) =
-      delete;
+  ExtensionsGlobalShortcutListenerTest(
+      const ExtensionsGlobalShortcutListenerTest&) = delete;
+  ExtensionsGlobalShortcutListenerTest& operator=(
+      const ExtensionsGlobalShortcutListenerTest&) = delete;
 
   void SetUp() override {
-    listener_->SetShortcutHandlingSuspended(false);
+    extensions_listener_->SetShortcutHandlingSuspended(false);
     observer_ = std::make_unique<TestObserver>();
   }
   void TearDown() override {
     observer_ = nullptr;
-    listener_ = nullptr;
+    extensions_listener_ = nullptr;
+    ui_listener_ = nullptr;
   }
 
   GlobalShortcutListener::Observer* GetObserver() { return observer_.get(); }
 
-  GlobalShortcutListener* GetListener() { return listener_.get(); }
+  GlobalShortcutListener* GetListener() { return extensions_listener_.get(); }
 
  private:
-  std::unique_ptr<GlobalShortcutListenerForTesting> listener_;
+  std::unique_ptr<GlobalShortcutListener> extensions_listener_;
+  std::unique_ptr<BaseGlobalShortcutListenerForTesting> ui_listener_;
   std::unique_ptr<TestObserver> observer_ = nullptr;
   // A UI environment is required since GlobalShortcutListener (base class of
   // GlobalShortcutListenerLinux) CHECKs that it's running on a UI thread.
   content::BrowserTaskEnvironment task_environment_;
 };
 
-TEST_F(GlobalShortcutListenerTest, RegistersAccelerators) {
+TEST_F(ExtensionsGlobalShortcutListenerTest, RegistersAccelerators) {
   GlobalShortcutListener* listener = GetListener();
   const ui::Accelerator accelerator_a(ui::VKEY_A, ui::EF_NONE);
 
@@ -98,7 +115,7 @@
   listener->UnregisterAccelerator(accelerator_a, GetObserver());
 }
 
-TEST_F(GlobalShortcutListenerTest, SuspendsShortcutHandling) {
+TEST_F(ExtensionsGlobalShortcutListenerTest, SuspendsShortcutHandling) {
   GlobalShortcutListener* listener = GetListener();
   const ui::Accelerator accelerator_b(ui::VKEY_B, ui::EF_NONE);
 
diff --git a/chrome/browser/extensions/global_shortcut_listener_win.h b/chrome/browser/extensions/global_shortcut_listener_win.h
deleted file mode 100644
index 6424692..0000000
--- a/chrome/browser/extensions/global_shortcut_listener_win.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2013 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_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
-#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
-
-#include <windows.h>
-
-#include <memory>
-
-#include "chrome/browser/extensions/global_shortcut_listener.h"
-#include "ui/base/accelerators/media_keys_listener.h"
-
-namespace gfx {
-
-class SingletonHwndHotKeyObserver;
-
-}  // namespace gfx
-
-namespace extensions {
-
-// Windows-specific implementation of the GlobalShortcutListener class that
-// listens for global shortcuts. Handles setting up a keyboard hook and
-// forwarding its output to the base class for processing.
-class GlobalShortcutListenerWin : public GlobalShortcutListener,
-                                  public ui::MediaKeysListener::Delegate {
- public:
-  GlobalShortcutListenerWin();
-
-  GlobalShortcutListenerWin(const GlobalShortcutListenerWin&) = delete;
-  GlobalShortcutListenerWin& operator=(const GlobalShortcutListenerWin&) =
-      delete;
-
-  ~GlobalShortcutListenerWin() override;
-
- private:
-  // The implementation of our Window Proc, called by SingletonHwndObserver.
-  void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
-
-  // GlobalShortcutListener implementation.
-  void StartListening() override;
-  void StopListening() override;
-  bool RegisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
-  void UnregisterAcceleratorImpl(const ui::Accelerator& accelerator) override;
-
-  // ui::MediaKeysListener::Delegate implementation.
-  void OnMediaKeysAccelerator(const ui::Accelerator& accelerator) override;
-
-  // Whether this object is listening for global shortcuts.
-  bool is_listening_;
-
-  // The number of media keys currently registered.
-  int registered_media_keys_ = 0;
-
-  // A map of registered accelerators and their registration ids. The value is
-  // null for media keys if kHardwareMediaKeyHandling is true.
-  using HotKeyMap = std::map<ui::Accelerator,
-                             std::unique_ptr<gfx::SingletonHwndHotKeyObserver>>;
-  HotKeyMap hotkeys_;
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_WIN_H_
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 27154eb..8bf2dda 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -965,21 +965,11 @@
     "expiry_milestone": 140
   },
   {
-    "name": "bluetooth-coredump",
-    "owners": ["mmandlik@chromium.org", "chromeos-bt-team@google.com"],
-    "expiry_milestone": 120
-  },
-  {
     "name": "bluetooth-floss-availability-check",
     "owners": [ "howardchung@google.com", "chromeos-bt-team@google.com" ],
     "expiry_milestone": 136
   },
   {
-    "name": "bluetooth-floss-coredump",
-    "owners": ["mmandlik@chromium.org", "chromeos-bt-team@google.com"],
-    "expiry_milestone": 124
-  },
-  {
     "name": "bluetooth-floss-telephony",
     "owners": [ "whalechang@google.com", "chromeos-audio-sw@google.com" ],
     "expiry_milestone": 140
@@ -8996,6 +8986,11 @@
     "expiry_milestone": 140
   },
   {
+    "name": "use-managed-print-job-options-in-print-preview",
+    "owners": ["nedol@google.com", "chromeos-commercial-printing@google.com"],
+    "expiry_milestone": 150
+  },
+  {
     "name": "use_messages_staging_url",
     "owners": [ "azeemarshad@chromium.org", "khorimoto@chromium.org", "jonmann@chromium.org" ],
     // This flag is required for QA and dogfood testing.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 40e027d..d19e72c 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -6058,19 +6058,6 @@
     "Enable Floss to create a Bluetooth HID device that allows applications to "
     "access Bluetooth telephony functions through WebHID.";
 
-const char kBluetoothCoredumpName[] = "Enable Bluetooth Device Coredump";
-const char kBluetoothCoredumpDescription[] =
-    "Enable Bluetooth coredump collection if supported. Please note that "
-    "coredumps are only collected when hardware exceptions occur and are "
-    "used for debugging such exceptions.";
-
-const char kBluetoothFlossCoredumpName[] =
-    "Enable Bluetooth Device Coredump for Floss";
-const char kBluetoothFlossCoredumpDescription[] =
-    "Enable Bluetooth coredump collection if supported. Please note that "
-    "coredumps are only collected when hardware exceptions occur and are "
-    "used for debugging such exceptions.";
-
 const char kBluetoothUseFlossName[] = "Use Floss instead of BlueZ";
 const char kBluetoothUseFlossDescription[] =
     "Enables using Floss (also known as Fluoride, Android's Bluetooth stack) "
@@ -7376,6 +7363,12 @@
     "will be used. Note that IPv6 (DHCPv6-PD) will always use the latest "
     "dhcpcd.";
 
+const char kUseManagedPrintJobOptionsInPrintPreviewName[] =
+    "Use managed print job options in print preview";
+const char kUseManagedPrintJobOptionsInPrintPreviewDescription[] =
+    "Use managed print job options set via "
+    "DevicePrinters/PrinterBulkConfiguration policy in print preview.";
+
 const char kUiDevToolsName[] = "Enable native UI inspection";
 const char kUiDevToolsDescription[] =
     "Enables inspection of native UI elements. For local inspection use "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 5e484c6..2d450b4 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -3516,12 +3516,6 @@
 extern const char kBluetoothFlossTelephonyName[];
 extern const char kBluetoothFlossTelephonyDescription[];
 
-extern const char kBluetoothCoredumpName[];
-extern const char kBluetoothCoredumpDescription[];
-
-extern const char kBluetoothFlossCoredumpName[];
-extern const char kBluetoothFlossCoredumpDescription[];
-
 extern const char kBluetoothUseFlossName[];
 extern const char kBluetoothUseFlossDescription[];
 
@@ -4300,6 +4294,9 @@
 extern const char kUseLegacyDHCPCDName[];
 extern const char kUseLegacyDHCPCDDescription[];
 
+extern const char kUseManagedPrintJobOptionsInPrintPreviewName[];
+extern const char kUseManagedPrintJobOptionsInPrintPreviewDescription[];
+
 extern const char kVcDlcUiName[];
 extern const char kVcDlcUiDescription[];
 
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index 49031ed..ea3fcc1 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -20,6 +20,8 @@
 
 source_set("backend_public") {
   sources = [
+    "login_db_deprecation_runner_factory.cc",
+    "login_db_deprecation_runner_factory.h",
     "password_manager_settings_service_android_impl.cc",
     "password_manager_settings_service_android_impl.h",
   ]
@@ -31,6 +33,8 @@
     "//chrome/browser/profiles:profile",
     "//components/keyed_service/content:content",
     "//components/password_manager/core/browser:browser",
+    "//components/password_manager/core/browser:password_manager_buildflags",
+    "//components/password_manager/core/browser/export",
     "//components/password_manager/core/browser/features:password_features",
     "//components/prefs:prefs",
     "//components/sync/service",
@@ -557,6 +561,7 @@
     "fake_password_manager_lifecycle_helper.h",
     "first_cct_page_load_marker_unittest.cc",
     "generated_password_saved_message_delegate_unittest.cc",
+    "login_db_deprecation_runner_factory_unittest.cc",
     "mock_password_checkup_launcher_helper.cc",
     "mock_password_checkup_launcher_helper.h",
     "mock_password_manager_error_message_helper_bridge.cc",
diff --git a/chrome/browser/password_manager/android/login_db_deprecation_runner_factory.cc b/chrome/browser/password_manager/android/login_db_deprecation_runner_factory.cc
new file mode 100644
index 0000000..f17d166
--- /dev/null
+++ b/chrome/browser/password_manager/android/login_db_deprecation_runner_factory.cc
@@ -0,0 +1,65 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/android/login_db_deprecation_runner_factory.h"
+
+#include "base/feature_list.h"
+#include "base/no_destructor.h"
+#include "build/build_config.h"
+#include "chrome/browser/password_manager/android/password_manager_util_bridge.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/password_manager/core/browser/export/login_db_deprecation_runner.h"
+#include "components/password_manager/core/browser/features/password_features.h"
+#include "components/password_manager/core/browser/password_manager_buildflags.h"
+#include "components/password_manager/core/browser/split_stores_and_local_upm.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_context.h"
+
+LoginDbDeprecationRunnerFactory::LoginDbDeprecationRunnerFactory()
+    : ProfileKeyedServiceFactory("LoginDbDeprecationRunnerFactory",
+                                 ProfileSelections::BuildForRegularProfile()) {}
+LoginDbDeprecationRunnerFactory::~LoginDbDeprecationRunnerFactory() = default;
+
+LoginDbDeprecationRunnerFactory*
+LoginDbDeprecationRunnerFactory::GetInstance() {
+  static base::NoDestructor<LoginDbDeprecationRunnerFactory> instance;
+  return instance.get();
+}
+
+password_manager::LoginDbDeprecationRunner*
+LoginDbDeprecationRunnerFactory::GetForProfile(Profile* profile) {
+  return static_cast<password_manager::LoginDbDeprecationRunner*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+std::unique_ptr<KeyedService>
+LoginDbDeprecationRunnerFactory::BuildServiceInstanceForBrowserContext(
+    content::BrowserContext* context) const {
+#if BUILDFLAG(USE_LOGIN_DATABASE_AS_BACKEND)
+  return nullptr;
+#endif
+  Profile* profile = Profile::FromBrowserContext(context);
+  PrefService* prefs = profile->GetPrefs();
+
+  // If the client is already migrated there is no need for export.
+  if (password_manager::UsesSplitStoresAndUPMForLocal(prefs)) {
+    return nullptr;
+  }
+
+  // If there are no passwords saved, there is nothing to export prior to
+  // deprecation.
+  if (prefs->GetBoolean(
+          password_manager::prefs::kEmptyProfileStoreLoginDatabase)) {
+    return nullptr;
+  }
+
+  if (!base::FeatureList::IsEnabled(
+          password_manager::features::kLoginDbDeprecationAndroid)) {
+    return nullptr;
+  }
+
+  return std::make_unique<password_manager::LoginDbDeprecationRunner>(
+      profile->GetPath());
+}
diff --git a/chrome/browser/password_manager/android/login_db_deprecation_runner_factory.h b/chrome/browser/password_manager/android/login_db_deprecation_runner_factory.h
new file mode 100644
index 0000000..d68a9af
--- /dev/null
+++ b/chrome/browser/password_manager/android/login_db_deprecation_runner_factory.h
@@ -0,0 +1,34 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_LOGIN_DB_DEPRECATION_RUNNER_FACTORY_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_LOGIN_DB_DEPRECATION_RUNNER_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "base/types/pass_key.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "components/password_manager/core/browser/export/login_db_deprecation_runner.h"
+#include "content/public/browser/browser_context.h"
+
+// Creates the `LoginDbDeprecationRunner` which runs the pre-deprecation
+// export for unmigrated passwords on Android.
+class LoginDbDeprecationRunnerFactory : public ProfileKeyedServiceFactory {
+ public:
+  static LoginDbDeprecationRunnerFactory* GetInstance();
+  static password_manager::LoginDbDeprecationRunner* GetForProfile(
+      Profile* profile);
+
+ private:
+  friend class base::NoDestructor<LoginDbDeprecationRunnerFactory>;
+
+  LoginDbDeprecationRunnerFactory();
+  ~LoginDbDeprecationRunnerFactory() override;
+
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* context) const override;
+
+  base::RepeatingCallback<bool()> internal_backend_checker_;
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_LOGIN_DB_DEPRECATION_RUNNER_FACTORY_H_
diff --git a/chrome/browser/password_manager/android/login_db_deprecation_runner_factory_unittest.cc b/chrome/browser/password_manager/android/login_db_deprecation_runner_factory_unittest.cc
new file mode 100644
index 0000000..f71f0446
--- /dev/null
+++ b/chrome/browser/password_manager/android/login_db_deprecation_runner_factory_unittest.cc
@@ -0,0 +1,90 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/android/login_db_deprecation_runner_factory.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/password_manager/core/browser/features/password_features.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class LoginDbDeprecationRunnerFactoryTest : public testing::Test {
+ public:
+  LoginDbDeprecationRunnerFactoryTest() = default;
+
+ protected:
+  base::test::ScopedFeatureList scoped_feature_list_;
+  content::BrowserTaskEnvironment task_env_;
+  TestingProfile testing_profile_;
+};
+
+TEST_F(LoginDbDeprecationRunnerFactoryTest, NullServiceIfMigrated) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kLoginDbDeprecationAndroid);
+  PrefService* prefs = testing_profile_.GetPrefs();
+  prefs->SetInteger(
+      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
+      static_cast<int>(
+          password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOn));
+  EXPECT_FALSE(
+      LoginDbDeprecationRunnerFactory::GetForProfile(&testing_profile_));
+}
+
+TEST_F(LoginDbDeprecationRunnerFactoryTest, NullServiceIfEmptyDb) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kLoginDbDeprecationAndroid);
+  PrefService* prefs = testing_profile_.GetPrefs();
+  prefs->SetBoolean(password_manager::prefs::kEmptyProfileStoreLoginDatabase,
+                    true);
+  EXPECT_FALSE(
+      LoginDbDeprecationRunnerFactory::GetForProfile(&testing_profile_));
+}
+
+TEST_F(LoginDbDeprecationRunnerFactoryTest, NullServiceIfFlagOff) {
+  scoped_feature_list_.InitAndDisableFeature(
+      password_manager::features::kLoginDbDeprecationAndroid);
+  PrefService* prefs = testing_profile_.GetPrefs();
+  prefs->SetInteger(
+      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
+      static_cast<int>(
+          password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff));
+  prefs->SetBoolean(password_manager::prefs::kEmptyProfileStoreLoginDatabase,
+                    false);
+  EXPECT_FALSE(
+      LoginDbDeprecationRunnerFactory::GetForProfile(&testing_profile_));
+}
+
+TEST_F(LoginDbDeprecationRunnerFactoryTest,
+       NonNullServiceIfNotEligibleForMigration) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kLoginDbDeprecationAndroid);
+  PrefService* prefs = testing_profile_.GetPrefs();
+  prefs->SetInteger(
+      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
+      static_cast<int>(
+          password_manager::prefs::UseUpmLocalAndSeparateStoresState::kOff));
+  prefs->SetBoolean(password_manager::prefs::kEmptyProfileStoreLoginDatabase,
+                    false);
+  EXPECT_TRUE(
+      LoginDbDeprecationRunnerFactory::GetForProfile(&testing_profile_));
+}
+
+TEST_F(LoginDbDeprecationRunnerFactoryTest, NonNullServiceIfMigrationPending) {
+  scoped_feature_list_.InitAndEnableFeature(
+      password_manager::features::kLoginDbDeprecationAndroid);
+  PrefService* prefs = testing_profile_.GetPrefs();
+  prefs->SetInteger(
+      password_manager::prefs::kPasswordsUseUPMLocalAndSeparateStores,
+      static_cast<int>(
+          password_manager::prefs::UseUpmLocalAndSeparateStoresState::
+              kOffAndMigrationPending));
+  prefs->SetBoolean(password_manager::prefs::kEmptyProfileStoreLoginDatabase,
+                    false);
+  EXPECT_TRUE(
+      LoginDbDeprecationRunnerFactory::GetForProfile(&testing_profile_));
+}
diff --git a/chrome/browser/password_manager/android/password_manager_util_bridge.cc b/chrome/browser/password_manager/android/password_manager_util_bridge.cc
index 746bfa2..029e718 100644
--- a/chrome/browser/password_manager/android/password_manager_util_bridge.cc
+++ b/chrome/browser/password_manager/android/password_manager_util_bridge.cc
@@ -64,4 +64,5 @@
   return Java_PasswordManagerUtilBridge_isPlayStoreAppPresent(
       base::android::AttachCurrentThread());
 }
+
 }  // namespace password_manager_android_util
diff --git a/chrome/browser/password_manager/password_change_browsertest.cc b/chrome/browser/password_manager/password_change_browsertest.cc
index 17bcb30..a54de35 100644
--- a/chrome/browser/password_manager/password_change_browsertest.cc
+++ b/chrome/browser/password_manager/password_change_browsertest.cc
@@ -13,13 +13,17 @@
 #include "chrome/browser/password_manager/profile_password_store_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/passwords/bubble_controllers/password_bubble_controller_base.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
+#include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/affiliations/core/browser/mock_affiliation_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
 
 using affiliations::AffiliationService;
 using affiliations::MockAffiliationService;
@@ -327,3 +331,33 @@
   CheckThatCredentialsStored(base::UTF16ToUTF8(form.username_value),
                              new_password);
 }
+
+IN_PROC_BROWSER_TEST_F(PasswordChangeBrowserTest,
+                       SignInCheckBubbleIsHiddenWhenStateIsUpdated) {
+  GURL main_url("https://example.com/");
+
+  EXPECT_CALL(*affiliation_service(), GetChangePasswordURL(main_url))
+      .WillOnce(testing::Return(embedded_test_server()->GetURL(
+          "/password/update_form_empty_fields.html")));
+
+  password_change_service()->StartPasswordChange(main_url, u"test", u"pa$$word",
+                                                 WebContents());
+
+  // Activate tab with password change to simplify testing.
+  SetWebContents(browser()->tab_strip_model()->GetWebContentsAt(1));
+  PasswordsNavigationObserver navigation_observer(WebContents());
+  EXPECT_TRUE(navigation_observer.Wait());
+
+  PasswordBubbleViewBase::ShowBubble(
+      WebContents(), LocationBarBubbleDelegateView::USER_GESTURE);
+  auto* bubble = PasswordBubbleViewBase::manage_password_bubble();
+  ASSERT_EQ(
+      bubble->GetController()->GetTitle(),
+      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_TITLE));
+
+  WaitForElementValue("password", "pa$$word");
+  // The state should have changed to `kChangingPassword` and the sign in check
+  // bubble should not show any more.
+  bubble = PasswordBubbleViewBase::manage_password_bubble();
+  ASSERT_FALSE(bubble);
+}
diff --git a/chrome/browser/password_manager/password_change_delegate.h b/chrome/browser/password_manager/password_change_delegate.h
index 9ef1c85..b7d336f 100644
--- a/chrome/browser/password_manager/password_change_delegate.h
+++ b/chrome/browser/password_manager/password_change_delegate.h
@@ -62,6 +62,8 @@
   // Adds/removes an observer.
   virtual void AddObserver(Observer* observer) = 0;
   virtual void RemoveObserver(Observer* observer) = 0;
+
+  virtual base::WeakPtr<PasswordChangeDelegate> AsWeakPtr() = 0;
 };
 
 #endif  // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_DELEGATE_H_
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.cc b/chrome/browser/password_manager/password_change_delegate_impl.cc
index 42fef6d9..e9066f75 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.cc
+++ b/chrome/browser/password_manager/password_change_delegate_impl.cc
@@ -188,3 +188,7 @@
   form_manager_->PresaveGeneratedPassword(form.form_data, generated_password_);
   UpdateState(PasswordChangeDelegate::State::kChangingPassword);
 }
+
+base::WeakPtr<PasswordChangeDelegate> PasswordChangeDelegateImpl::AsWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.h b/chrome/browser/password_manager/password_change_delegate_impl.h
index 293cfe1..f13add4 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.h
+++ b/chrome/browser/password_manager/password_change_delegate_impl.h
@@ -40,6 +40,8 @@
   PasswordChangeDelegateImpl& operator=(const PasswordChangeDelegateImpl&) =
       delete;
 
+  base::WeakPtr<PasswordChangeDelegate> AsWeakPtr() override;
+
  private:
   // password_manager::PasswordFormManagerObserver Impl
   void OnPasswordFormParsed(
diff --git a/chrome/browser/password_manager/password_change_delegate_mock.cc b/chrome/browser/password_manager/password_change_delegate_mock.cc
new file mode 100644
index 0000000..191e551
--- /dev/null
+++ b/chrome/browser/password_manager/password_change_delegate_mock.cc
@@ -0,0 +1,12 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/password_manager/password_change_delegate_mock.h"
+
+PasswordChangeDelegateMock::PasswordChangeDelegateMock() = default;
+PasswordChangeDelegateMock::~PasswordChangeDelegateMock() = default;
+
+base::WeakPtr<PasswordChangeDelegate> PasswordChangeDelegateMock::AsWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
diff --git a/chrome/browser/password_manager/password_change_delegate_mock.h b/chrome/browser/password_manager/password_change_delegate_mock.h
new file mode 100644
index 0000000..da22304
--- /dev/null
+++ b/chrome/browser/password_manager/password_change_delegate_mock.h
@@ -0,0 +1,45 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_DELEGATE_MOCK_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_DELEGATE_MOCK_H_
+
+#include "chrome/browser/password_manager/password_change_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+class WebContents;
+}
+
+class PasswordChangeDelegateMock final : public PasswordChangeDelegate {
+ public:
+  PasswordChangeDelegateMock();
+  PasswordChangeDelegateMock(const PasswordChangeDelegateMock&) = delete;
+  PasswordChangeDelegateMock& operator=(const PasswordChangeDelegateMock&) =
+      delete;
+  ~PasswordChangeDelegateMock() override;
+
+  MOCK_METHOD(bool,
+              IsPasswordChangeOngoing,
+              (content::WebContents*),
+              (override));
+  MOCK_METHOD(PasswordChangeDelegate::State,
+              GetCurrentState,
+              (),
+              (const override));
+  MOCK_METHOD(void, Stop, (), (override));
+  MOCK_METHOD(void,
+              SuccessfulSubmissionDetected,
+              (content::WebContents*),
+              (override));
+  MOCK_METHOD(void, AddObserver, (Observer*), (override));
+  MOCK_METHOD(void, RemoveObserver, (Observer*), (override));
+
+  base::WeakPtr<PasswordChangeDelegate> AsWeakPtr() override;
+
+ private:
+  base::WeakPtrFactory<PasswordChangeDelegate> weak_ptr_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_DELEGATE_MOCK_H_
diff --git a/chrome/browser/password_manager/profile_password_store_factory.cc b/chrome/browser/password_manager/profile_password_store_factory.cc
index 4d1ad455..03c6e51b 100644
--- a/chrome/browser/password_manager/profile_password_store_factory.cc
+++ b/chrome/browser/password_manager/profile_password_store_factory.cc
@@ -29,7 +29,9 @@
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+
 #if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/password_manager/android/login_db_deprecation_runner_factory.h"
 #include "chrome/browser/password_manager/android/password_manager_util_bridge.h"
 #endif  // BUILDFLAG(IS_ANDROID)
 
@@ -99,7 +101,14 @@
 
   password_affiliation_adapter->RegisterPasswordStore(ps.get());
   affiliation_service->RegisterSource(std::move(password_affiliation_adapter));
-
+#if BUILDFLAG(IS_ANDROID)
+  CHECK(password_manager_android_util::IsInternalBackendPresent());
+  password_manager::LoginDbDeprecationRunner* login_db_deprecation_runner =
+      LoginDbDeprecationRunnerFactory::GetForProfile(profile);
+  if (login_db_deprecation_runner) {
+    login_db_deprecation_runner->StartExport(ps);
+  }
+#endif
   DelayReportingPasswordStoreMetrics(profile);
 
   return ps;
@@ -143,6 +152,9 @@
               .Build()) {
   DependsOn(AffiliationServiceFactory::GetInstance());
   DependsOn(CredentialsCleanerRunnerFactory::GetInstance());
+#if BUILDFLAG(IS_ANDROID)
+  DependsOn(LoginDbDeprecationRunnerFactory::GetInstance());
+#endif
 }
 
 ProfilePasswordStoreFactory::~ProfilePasswordStoreFactory() = default;
diff --git a/chrome/browser/permissions/system/system_permission_settings.cc b/chrome/browser/permissions/system/system_permission_settings.cc
index 31cb720b0..6c7543e1 100644
--- a/chrome/browser/permissions/system/system_permission_settings.cc
+++ b/chrome/browser/permissions/system/system_permission_settings.cc
@@ -20,6 +20,7 @@
 
 PlatformHandle* instance_for_testing_ = nullptr;
 bool g_mock_system_prompt_for_testing_ = false;
+bool g_mock_system_permission_denied_for_testing_ = false;
 
 std::map<ContentSettingsType, bool>& GlobalTestingBlockOverrides() {
   static std::map<ContentSettingsType, bool> g_testing_block_overrides;
@@ -51,9 +52,18 @@
 base::AutoReset<bool> MockSystemPromptForTesting() {
   return base::AutoReset<bool>(&g_mock_system_prompt_for_testing_, true);
 }
+// static
+base::AutoReset<bool> MockShowSystemSettingsForTesting() {
+  return base::AutoReset<bool>(&g_mock_system_permission_denied_for_testing_,
+                               true);
+}
 
 // static
 bool IsDenied(ContentSettingsType type) {
+  if (g_mock_system_permission_denied_for_testing_) {
+    return true;
+  }
+
   if (GlobalTestingBlockOverrides().find(type) !=
       GlobalTestingBlockOverrides().end()) {
     return GlobalTestingBlockOverrides().at(type);
diff --git a/chrome/browser/permissions/system/system_permission_settings.h b/chrome/browser/permissions/system/system_permission_settings.h
index d0c6208..74f2782 100644
--- a/chrome/browser/permissions/system/system_permission_settings.h
+++ b/chrome/browser/permissions/system/system_permission_settings.h
@@ -49,6 +49,10 @@
 // permissions.
 base::AutoReset<bool> MockSystemPromptForTesting();
 
+// For testing purposes only. Mocks that Chrome doesn't have access to a system
+// level permission.
+base::AutoReset<bool> MockShowSystemSettingsForTesting();
+
 // Check whether the system blocks the access to the specified content type /
 // permission.
 bool IsDenied(ContentSettingsType type);
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 924d7439..daa5ef7f 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2733,7 +2733,7 @@
       false));
 
   handlers->AddHandler(std::make_unique<SimplePolicyHandler>(
-      key::kEnterpriseCustomLabel, prefs::kEnterpriseCustomLabel,
+      key::kEnterpriseCustomLabel, prefs::kEnterpriseCustomLabelForBrowser,
       base::Value::Type::STRING));
   handlers->AddHandler(std::make_unique<CloudUserOnlyPolicyHandler>(
       std::make_unique<SimplePolicyHandler>(
@@ -2741,7 +2741,7 @@
           base::Value::Type::STRING)));
 
   handlers->AddHandler(std::make_unique<URLPolicyHandler>(
-      key::kEnterpriseLogoUrl, prefs::kEnterpriseLogoUrl));
+      key::kEnterpriseLogoUrl, prefs::kEnterpriseLogoUrlForBrowser));
   handlers->AddHandler(std::make_unique<CloudUserOnlyPolicyHandler>(
       std::make_unique<URLPolicyHandler>(key::kEnterpriseLogoUrl,
                                          prefs::kEnterpriseLogoUrlForProfile)));
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.html b/chrome/browser/resources/settings/people_page/sync_account_control.html
index a2a29042..8c90b0e 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.html
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.html
@@ -205,12 +205,22 @@
           $i18n{turnOffSync}
         </cr-button>
         <cr-button id="sync-error-button" class="action-button cr-button-gap"
-            hidden="[[!shouldShowErrorActionButton_(syncStatus,
-                showSetupButtons_)]]"
+            hidden="[[!shouldShowErrorActionButton_(syncStatus.signedInState,
+            syncStatus.hasError, syncStatus.statusAction, showSetupButtons_)]]"
             on-click="onErrorButtonClick_"
             disabled="[[syncStatus.firstSetupInProgress]]">
           [[syncStatus.statusActionText]]
         </cr-button>
+        <cr-button class="action-button cr-button-gap" on-click="onSigninClick_"
+        id="sync-paused"
+        hidden="[[!shouldSyncPausedSigninButton_(syncStatus.signedInState)]]">
+        <img class="account-icon small" alt=""
+        src="[[getAccountImageSrc_(shownAccount_.avatarImage)]]"
+        slot="prefix-icon">
+        [[getSyncPausedButtonLabel_(
+          '$i18nPolymer{syncPausedButtonLabel}',
+          shownAccount_.givenName)]]
+        </cr-button>
         <div id="setup-buttons" hidden="[[!showSetupButtons_]]"
             class="cr-button-gap">
           <cr-button on-click="onSetupCancel_">$i18n{cancel}</cr-button>
diff --git a/chrome/browser/resources/settings/people_page/sync_account_control.ts b/chrome/browser/resources/settings/people_page/sync_account_control.ts
index 7462267..bd07ca6 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.ts
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.ts
@@ -243,6 +243,11 @@
         email;
   }
 
+  private getSyncPausedButtonLabel_(
+      syncPausedButtonLabel: string, givenName: string): string {
+    return loadTimeData.substituteString(syncPausedButtonLabel, givenName);
+  }
+
   private getTurnOnSyncLabel_(peopleSignIn: string, turnOnSync: string):
       string {
     return loadTimeData.getBoolean('isImprovedSettingsUIOnDesktopEnabled') ?
@@ -347,7 +352,21 @@
    * has sync enabled or if the property to hide the banner was explicitly set.
    */
   private shouldHideBanner_(): boolean {
-    return this.hideBanner || (!!this.syncStatus && this.isSyncing_());
+    if (!loadTimeData.getBoolean('isImprovedSettingsUIOnDesktopEnabled')) {
+      return this.hideBanner || !!this.syncStatus && this.isSyncing_();
+    }
+
+    switch (this.syncStatus.signedInState) {
+      case SignedInState.SYNCING:
+      case SignedInState.SIGNED_IN:
+      case SignedInState.SIGNED_IN_PAUSED:
+      case SignedInState.SIGNED_OUT:
+        return false;
+      case SignedInState.WEB_ONLY_SIGNED_IN:
+        return true;
+    }
+
+    assertNotReached('Invalid SignedInState');
   }
 
   /**
@@ -363,10 +382,22 @@
   }
 
   private shouldShowTurnOffButton_(): boolean {
+    if (loadTimeData.getBoolean('isImprovedSettingsUIOnDesktopEnabled') &&
+        (this.syncStatus.signedInState === SignedInState.SYNCING) &&
+        !!this.syncStatus.hasError) {
+      return false;
+    }
+
     return !this.hideButtons && !this.showSetupButtons_ && this.isSyncing_();
   }
 
   private shouldShowErrorActionButton_(): boolean {
+    if (loadTimeData.getBoolean('isImprovedSettingsUIOnDesktopEnabled') &&
+        this.syncStatus.signedInState === SignedInState.SYNCING &&
+        !!this.syncStatus.hasError) {
+      return false;
+    }
+
     if (this.embeddedInSubpage &&
         this.syncStatus.statusAction === StatusAction.ENTER_PASSPHRASE) {
       // In a subpage the passphrase button is not required.
@@ -377,6 +408,14 @@
         this.syncStatus.statusAction !== StatusAction.NO_ACTION;
   }
 
+  private shouldSyncPausedSigninButton_(): boolean {
+    // Only show the button when user is in sync paused state
+    return loadTimeData.getBoolean('isImprovedSettingsUIOnDesktopEnabled') &&
+        this.syncStatus.signedInState === SignedInState.SYNCING &&
+        !!this.syncStatus.hasError;
+  }
+
+
   private shouldAllowAccountSwitch_(): boolean {
     if (this.hideButtons) {
       return false;
@@ -391,8 +430,8 @@
       case SignedInState.SIGNED_OUT:
       case SignedInState.WEB_ONLY_SIGNED_IN:
         return true;
-      case SignedInState.SYNCING:
       case SignedInState.SIGNED_IN_PAUSED:
+      case SignedInState.SYNCING:
         return false;
       case SignedInState.SIGNED_IN:
         return !loadTimeData.getBoolean('isImprovedSettingsUIOnDesktopEnabled');
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index b0450157..be71e9f 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -513,6 +513,11 @@
     response_code = loader_->ResponseInfo()->headers->response_code();
   DVLOG(2) << "Received a response for URL: " << source_url_
            << ": success=" << success << " response_code=" << response_code;
+  RecordHttpResponseOrErrorCode("SBClientDownload.DownloadRequestNetworkResult",
+                                loader_->NetError(), response_code);
+  // TODO: crbug.com/383994656 - Remove these metrics once
+  // SBClientDownload.DownloadRequestNetworkResult available on Stable. Alert
+  // monitoring should also be modified.
   if (success) {
     base::UmaHistogramSparse("SBClientDownload.DownloadRequestResponseCode",
                              response_code);
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index b79d5ee..edec7823 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -1234,6 +1234,7 @@
 }
 
 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadFetchFailed) {
+  base::HistogramTester histogram_tester;
   // HTTP request will fail.
   PrepareResponse(ClientDownloadResponse::SAFE, net::HTTP_INTERNAL_SERVER_ERROR,
                   net::ERR_FAILED);
@@ -1262,6 +1263,10 @@
                           base::Unretained(this), run_loop.QuitClosure()));
   run_loop.Run();
   EXPECT_TRUE(IsResult(DownloadCheckResult::UNKNOWN));
+  histogram_tester.ExpectUniqueSample(
+      /*name=*/"SBClientDownload.DownloadRequestNetworkResult",
+      /*sample=*/net::ERR_FAILED,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(DownloadProtectionServiceTest, CheckClientDownloadSuccess) {
@@ -1292,6 +1297,7 @@
   ClientDownloadResponse expected_response;
 
   {
+    base::HistogramTester histogram_tester;
     RunLoop run_loop;
     download_service_->CheckClientDownload(
         &item,
@@ -1300,6 +1306,10 @@
     run_loop.Run();
     EXPECT_TRUE(IsResult(DownloadCheckResult::SAFE));
     EXPECT_TRUE(HasClientDownloadRequest());
+    histogram_tester.ExpectUniqueSample(
+        /*name=*/"SBClientDownload.DownloadRequestNetworkResult",
+        /*sample=*/200,
+        /*expected_bucket_count=*/1);
     ClearClientDownloadRequest();
   }
   {
@@ -2763,6 +2773,7 @@
 }
 
 TEST_F(DownloadProtectionServiceTest, PPAPIDownloadRequest_FetchFailed) {
+  base::HistogramTester histogram_tester;
   base::FilePath default_file_path(FILE_PATH_LITERAL("/foo/bar/test.crx"));
   std::vector<base::FilePath::StringType> alternate_extensions;
   PrepareResponse(ClientDownloadResponse::DANGEROUS, net::HTTP_OK,
@@ -2782,6 +2793,10 @@
   run_loop.Run();
 
   ASSERT_TRUE(IsResult(DownloadCheckResult::UNKNOWN));
+  histogram_tester.ExpectUniqueSample(
+      /*name=*/"SBClientDownload.PPAPIDownloadRequest.NetworkResult",
+      /*sample=*/net::ERR_FAILED,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(DownloadProtectionServiceTest, PPAPIDownloadRequest_InvalidResponse) {
@@ -2829,6 +2844,7 @@
 }
 
 TEST_F(DownloadProtectionServiceTest, PPAPIDownloadRequest_Payload) {
+  base::HistogramTester histogram_tester;
   RunLoop interceptor_run_loop;
 
   std::string upload_data;
@@ -2869,6 +2885,11 @@
   EXPECT_EQ(".txt", request.alternate_extensions(0));
   EXPECT_EQ(".abc", request.alternate_extensions(1));
   EXPECT_EQ(".sdF", request.alternate_extensions(2));
+
+  histogram_tester.ExpectUniqueSample(
+      /*name=*/"SBClientDownload.PPAPIDownloadRequest.NetworkResult",
+      /*sample=*/200,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(DownloadProtectionServiceTest,
@@ -3812,6 +3833,7 @@
 
 TEST_F(DownloadProtectionServiceTest,
        FileSystemAccessWriteRequest_FetchFailed) {
+  base::HistogramTester histogram_tester;
   // HTTP request will fail.
   PrepareResponse(ClientDownloadResponse::SAFE, net::HTTP_INTERNAL_SERVER_ERROR,
                   net::ERR_FAILED);
@@ -3837,6 +3859,10 @@
                      base::Unretained(this), run_loop.QuitClosure()));
   run_loop.Run();
   EXPECT_TRUE(IsResult(DownloadCheckResult::UNKNOWN));
+  histogram_tester.ExpectUniqueSample(
+      /*name=*/"SBClientDownload.DownloadRequestNetworkResult",
+      /*sample=*/net::ERR_FAILED,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(DownloadProtectionServiceTest, FileSystemAccessWriteRequest_Success) {
@@ -3861,6 +3887,7 @@
   ClientDownloadResponse expected_response;
 
   {
+    base::HistogramTester histogram_tester;
     RunLoop run_loop;
     download_service_->CheckFileSystemAccessWrite(
         CloneFileSystemAccessWriteItem(item.get()),
@@ -3869,6 +3896,10 @@
     run_loop.Run();
     EXPECT_TRUE(IsResult(DownloadCheckResult::SAFE));
     EXPECT_TRUE(HasClientDownloadRequest());
+    histogram_tester.ExpectUniqueSample(
+        /*name=*/"SBClientDownload.DownloadRequestNetworkResult",
+        /*sample=*/200,
+        /*expected_bucket_count=*/1);
     ClearClientDownloadRequest();
   }
   {
diff --git a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
index 4cf9dec..5e179d9 100644
--- a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
@@ -303,6 +303,9 @@
   int response_code = 0;
   if (loader_->ResponseInfo() && loader_->ResponseInfo()->headers)
     response_code = loader_->ResponseInfo()->headers->response_code();
+  RecordHttpResponseOrErrorCode(
+      "SBClientDownload.PPAPIDownloadRequest.NetworkResult",
+      loader_->NetError(), response_code);
   if (loader_->NetError() != net::OK || net::HTTP_OK != response_code) {
     Finish(RequestOutcome::FETCH_FAILED, DownloadCheckResult::UNKNOWN);
     return;
diff --git a/chrome/browser/speech/speech_recognition_service_browsertest.cc b/chrome/browser/speech/speech_recognition_service_browsertest.cc
index a88b9a0..acc10e0 100644
--- a/chrome/browser/speech/speech_recognition_service_browsertest.cc
+++ b/chrome/browser/speech/speech_recognition_service_browsertest.cc
@@ -5,6 +5,7 @@
 #include <algorithm>
 #include <string_view>
 
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -366,7 +367,7 @@
     ASSERT_TRUE(base::ReadFileToString(audio_file, &buffer));
   }
 
-  auto handler = media::WavAudioHandler::Create(buffer);
+  auto handler = media::WavAudioHandler::Create(base::as_byte_span(buffer));
   ASSERT_TRUE(handler.get());
   ASSERT_EQ(handler->GetNumChannels(), kExpectedChannelCount);
 
@@ -422,7 +423,7 @@
     ASSERT_TRUE(base::ReadFileToString(audio_file, &buffer));
   }
 
-  auto handler = media::WavAudioHandler::Create(buffer);
+  auto handler = media::WavAudioHandler::Create(base::as_byte_span(buffer));
   ASSERT_TRUE(handler.get());
   ASSERT_EQ(handler->GetNumChannels(), kExpectedChannelCount);
 
diff --git a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
index 4800bfd..24a48d6 100644
--- a/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
+++ b/chrome/browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc
@@ -575,7 +575,7 @@
 bool SupervisedUserIframeFilterTest::IsInterstitialBeingShownInFrame(
     content::FrameTreeNodeId frame_id) {
   std::string command =
-      "document.getElementsByClassName('supervised-user-block') != null";
+      "document.querySelector('.supervised-user-block') != null";
   return RunCommandAndGetBooleanFromFrame(frame_id, command);
 }
 
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 41f89f3..10fee9c 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -74,7 +74,7 @@
 #include "components/autofill/core/browser/integrators/autofill_optimization_guide.h"
 #include "components/autofill/core/browser/logging/log_router.h"
 #include "components/autofill/core/browser/payments/payments_network_interface.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_otp_input_dialog_controller_impl.h"
 #include "components/autofill/core/browser/ui/popup_open_enums.h"
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 53cd0b8..49c65395 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -31,7 +31,7 @@
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/browser/metrics/form_interactions_ukm_logger.h"
 #include "components/autofill/core/browser/password_form_classification.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/browser/studies/autofill_ablation_study.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
 #include "components/optimization_guide/proto/features/common_quality_data.pb.h"
diff --git a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
index fc951f91..1ecd4ba 100644
--- a/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
+++ b/chrome/browser/ui/autofill/payments/chrome_payments_autofill_client.cc
@@ -28,7 +28,6 @@
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #include "components/autofill/core/browser/data_model/autofill_offer_data.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/autofill/core/browser/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/metrics/payments/risk_data_metrics.h"
 #include "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
 #include "components/autofill/core/browser/payments/autofill_offer_manager.h"
@@ -45,6 +44,7 @@
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/payments_network_interface.h"
 #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/ui/payments/autofill_error_dialog_controller_impl.h"
 #include "components/autofill/core/browser/ui/payments/autofill_progress_dialog_controller_impl.h"
 #include "components/autofill/core/browser/ui/payments/bubble_show_options.h"
diff --git a/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.cc
index 74c304d..92d8bcac 100644
--- a/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.cc
@@ -358,9 +358,7 @@
 IbanBubbleControllerImpl::IbanBubbleControllerImpl(
     content::WebContents* web_contents)
     : AutofillBubbleControllerBase(web_contents),
-      content::WebContentsUserData<IbanBubbleControllerImpl>(*web_contents),
-      personal_data_manager_(PersonalDataManagerFactory::GetForBrowserContext(
-          web_contents->GetBrowserContext())) {}
+      content::WebContentsUserData<IbanBubbleControllerImpl>(*web_contents) {}
 
 IbanBubbleType IbanBubbleControllerImpl::GetBubbleType() const {
   return current_bubble_type_;
diff --git a/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.h
index bf4eb0a..f1454d1f 100644
--- a/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/iban_bubble_controller_impl.h
@@ -125,9 +125,6 @@
   // Returns empty vector if no legal message should be shown.
   const LegalMessageLines& GetLegalMessageLines() const override;
 
-  // Should outlive this object.
-  raw_ptr<PersonalDataManager> personal_data_manager_;
-
   // Observer for when a bubble is created. Initialized only during tests.
   raw_ptr<ObserverForTest> observer_for_testing_ = nullptr;
 
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
index 74e175f..6534c21 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.cc
@@ -60,14 +60,12 @@
 SaveCardBubbleControllerImpl::SaveCardBubbleControllerImpl(
     content::WebContents* web_contents)
     : AutofillBubbleControllerBase(web_contents),
-      content::WebContentsUserData<SaveCardBubbleControllerImpl>(
-          *web_contents) {
-  personal_data_manager_ = PersonalDataManagerFactory::GetForBrowserContext(
-      web_contents->GetBrowserContext());
-
-  sync_service_ = SyncServiceFactory::GetForProfile(
-      Profile::FromBrowserContext(web_contents->GetBrowserContext()));
-}
+      content::WebContentsUserData<SaveCardBubbleControllerImpl>(*web_contents),
+      payments_data_manager_(PersonalDataManagerFactory::GetForBrowserContext(
+                                 web_contents->GetBrowserContext())
+                                 ->payments_data_manager()),
+      sync_service_(SyncServiceFactory::GetForProfile(
+          Profile::FromBrowserContext(web_contents->GetBrowserContext()))) {}
 
 SaveCardBubbleControllerImpl::~SaveCardBubbleControllerImpl() = default;
 
@@ -338,15 +336,9 @@
   if (!identity_manager) {
     return AccountInfo();
   }
-  PersonalDataManager* personal_data_manager =
-      PersonalDataManagerFactory::GetForBrowserContext(profile);
-  if (!personal_data_manager) {
-    return AccountInfo();
-  }
 
   return identity_manager->FindExtendedAccountInfo(
-      personal_data_manager->payments_data_manager()
-          .GetAccountInfoForPaymentsServer());
+      payments_data_manager_->GetAccountInfoForPaymentsServer());
 }
 
 Profile* SaveCardBubbleControllerImpl::GetProfile() const {
@@ -380,9 +372,8 @@
 }
 
 ui::ImageModel SaveCardBubbleControllerImpl::GetCreditCardImage() const {
-  gfx::Image* card_art_image =
-      personal_data_manager_->payments_data_manager()
-          .GetCachedCardArtImageForUrl(card_.card_art_url());
+  const gfx::Image* const card_art_image =
+      payments_data_manager_->GetCachedCardArtImageForUrl(card_.card_art_url());
   return ui::ImageModel::FromImage(
       card_art_image ? *card_art_image
                      : ui::ResourceBundle::GetSharedInstance().GetImageNamed(
@@ -420,12 +411,9 @@
       autofill_metrics::LogSaveCardPromptResultMetric(
           autofill_metrics::SaveCardPromptResult::kAccepted, is_upload_save_,
           is_reshow_, options_,
-          personal_data_manager_->payments_data_manager()
-              .GetPaymentsSigninStateForMetrics(),
+          payments_data_manager_->GetPaymentsSigninStateForMetrics(),
           /*has_saved_cards=*/
-          !personal_data_manager_->payments_data_manager()
-               .GetCreditCards()
-               .empty());
+          !payments_data_manager_->GetCreditCards().empty());
       autofill_metrics::LogCreditCardUploadLoadingViewShownMetric(
           /*is_shown=*/true);
       current_bubble_type_ = BubbleType::UPLOAD_IN_PROGRESS;
@@ -532,12 +520,9 @@
     case BubbleType::UPLOAD_SAVE:
       autofill_metrics::LogSaveCardPromptResultMetric(
           get_metric(closed_reason), is_upload_save_, is_reshow_, options_,
-          personal_data_manager_->payments_data_manager()
-              .GetPaymentsSigninStateForMetrics(),
+          payments_data_manager_->GetPaymentsSigninStateForMetrics(),
           /*has_saved_cards=*/
-          !personal_data_manager_->payments_data_manager()
-               .GetCreditCards()
-               .empty());
+          !payments_data_manager_->GetCreditCards().empty());
       break;
     case BubbleType::UPLOAD_IN_PROGRESS:
       autofill_metrics::LogCreditCardUploadLoadingViewResultMetric(
@@ -641,8 +626,7 @@
     IsPaymentsSyncTransportEnabledWithoutSyncFeature() const {
   // TODO(crbug.com/40067296): Migrate away from IsSyncFeatureEnabled() when the
   // API returns false on desktop.
-  return personal_data_manager_->payments_data_manager()
-             .IsPaymentsDownloadActive() &&
+  return payments_data_manager_->IsPaymentsDownloadActive() &&
          !sync_service_->IsSyncFeatureEnabled();
 }
 
@@ -761,8 +745,7 @@
       autofill_metrics::LogSaveCardPromptOfferMetric(
           autofill_metrics::SaveCardPromptOffer::kShown, is_upload_save_,
           is_reshow_, options_,
-          personal_data_manager_->payments_data_manager()
-              .GetPaymentsSigninStateForMetrics());
+          payments_data_manager_->GetPaymentsSigninStateForMetrics());
       break;
     case BubbleType::UPLOAD_CVC_SAVE:
     case BubbleType::LOCAL_CVC_SAVE:
@@ -825,8 +808,7 @@
       autofill_metrics::LogSaveCardPromptOfferMetric(
           autofill_metrics::SaveCardPromptOffer::kNotShownMaxStrikesReached,
           is_upload_save_, is_reshow_, options_,
-          personal_data_manager_->payments_data_manager()
-              .GetPaymentsSigninStateForMetrics());
+          payments_data_manager_->GetPaymentsSigninStateForMetrics());
       break;
     case BubbleType::UPLOAD_CVC_SAVE:
     case BubbleType::LOCAL_CVC_SAVE:
diff --git a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
index afcb2aa..2539ccf 100644
--- a/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/save_card_bubble_controller_impl.h
@@ -29,6 +29,8 @@
 
 enum class BubbleType;
 
+class PaymentsDataManager;
+
 // Implementation of per-tab class to control the local/server save credit card
 // bubble, the local/server save CVC bubble, and Omnibox icon.
 // TODO(crbug.com/40934022): Refactor SaveCardBubbleControllerImpl to split the
@@ -191,11 +193,9 @@
   // inactive, effectively ending the save card flow.
   void EndSaveCardPromptFlow();
 
-  // Should outlive this object.
-  raw_ptr<PersonalDataManager> personal_data_manager_;
-
-  // Should outlive this object.
-  raw_ptr<syncer::SyncService> sync_service_;
+  // Tied to the profile and outlive this object.
+  const raw_ref<PaymentsDataManager> payments_data_manager_;
+  const raw_ptr<syncer::SyncService> sync_service_;
 
   // Is true only if the [Card saved] label animation should be shown.
   bool should_show_card_saved_label_animation_ = false;
diff --git a/chrome/browser/ui/browser_ui_prefs.cc b/chrome/browser/ui/browser_ui_prefs.cc
index 6beaa87..b6194d65 100644
--- a/chrome/browser/ui/browser_ui_prefs.cc
+++ b/chrome/browser/ui/browser_ui_prefs.cc
@@ -73,8 +73,10 @@
   registry->RegisterTimePref(prefs::kDefaultBrowserFirstShownTime,
                              base::Time());
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-  registry->RegisterStringPref(prefs::kEnterpriseCustomLabel, std::string());
-  registry->RegisterStringPref(prefs::kEnterpriseLogoUrl, std::string());
+  registry->RegisterStringPref(prefs::kEnterpriseCustomLabelForBrowser,
+                               std::string());
+  registry->RegisterStringPref(prefs::kEnterpriseLogoUrlForBrowser,
+                               std::string());
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 }
 
@@ -183,8 +185,6 @@
   registry->RegisterDictionaryPref(prefs::kHttpsUpgradeNavigations);
   registry->RegisterBooleanPref(prefs::kHttpsOnlyModeAutoEnabled, false);
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-  registry->RegisterStringPref(prefs::kEnterpriseLogoUrl, std::string());
-  registry->RegisterStringPref(prefs::kEnterpriseCustomLabel, std::string());
   registry->RegisterStringPref(prefs::kEnterpriseLogoUrlForProfile,
                                std::string());
   registry->RegisterStringPref(prefs::kEnterpriseCustomLabelForProfile,
diff --git a/chrome/browser/ui/managed_ui.cc b/chrome/browser/ui/managed_ui.cc
index f8d472e..3ad8da6 100644
--- a/chrome/browser/ui/managed_ui.cc
+++ b/chrome/browser/ui/managed_ui.cc
@@ -397,7 +397,6 @@
 
 #if !BUILDFLAG(IS_CHROMEOS)
 std::u16string GetManagementBubbleTitle(Profile* profile) {
-  // TODO(347245819): Use EnterpriseCustomLabel for the managers.
   std::optional<std::string> device_manager = GetDeviceManagerIdentity();
 
   switch (GetManagementStringType(profile)) {
@@ -447,7 +446,7 @@
     std::string custom_management_label =
         g_browser_process->local_state()
             ? g_browser_process->local_state()->GetString(
-                  prefs::kEnterpriseCustomLabel)
+                  prefs::kEnterpriseCustomLabelForBrowser)
             : std::string();
     if (!custom_management_label.empty()) {
       return custom_management_label;
@@ -482,7 +481,7 @@
   if (base::FeatureList::IsEnabled(
           features::kEnterpriseManagementDisclaimerUsesCustomLabel)) {
     std::string custom_management_label =
-        profile->GetPrefs()->GetString(prefs::kEnterpriseCustomLabel);
+        profile->GetPrefs()->GetString(prefs::kEnterpriseCustomLabelForProfile);
     if (!custom_management_label.empty()) {
       return custom_management_label;
     }
diff --git a/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.cc b/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.cc
index 9055a4c..8786499 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.cc
+++ b/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.cc
@@ -4,23 +4,69 @@
 
 #include "chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.h"
 
+#include "base/notimplemented.h"
+#include "base/notreached.h"
+#include "chrome/browser/password_manager/password_change_delegate.h"
+#include "chrome/browser/ui/passwords/passwords_model_delegate.h"
+#include "chrome/browser/ui/views/passwords/password_bubble_view_base.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
 PasswordChangeInfoBubbleController::PasswordChangeInfoBubbleController(
-    base::WeakPtr<PasswordsModelDelegate> delegate)
+    base::WeakPtr<PasswordsModelDelegate> delegate,
+    PasswordChangeDelegate::State state)
     : PasswordBubbleControllerBase(
           delegate,
           password_manager::metrics_util::UIDisplayDisposition::
-              PASSWORD_CHANGE_BUBBLE) {}
+              PASSWORD_CHANGE_BUBBLE),
+      state_(state),
+      password_change_delegate_(
+          delegate_->GetPasswordChangeDelegate()->AsWeakPtr()) {
+  password_change_delegate_->AddObserver(this);
+}
 
 PasswordChangeInfoBubbleController::~PasswordChangeInfoBubbleController() {
   OnBubbleClosing();
+  if (!password_change_delegate_) {
+    return;
+  }
+  password_change_delegate_->RemoveObserver(this);
 }
 
 std::u16string PasswordChangeInfoBubbleController::GetTitle() const {
-  // TODO(crbug.com/381053884): Return correct title (depends on
-  // PasswordChangeDelegate::State).
-  return u"";
+  CHECK(password_change_delegate_);
+  switch (password_change_delegate_->GetCurrentState()) {
+    case PasswordChangeDelegate::State::kWaitingForChangePasswordForm:
+      return l10n_util::GetStringUTF16(
+          IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_TITLE);
+    case PasswordChangeDelegate::State::kChangingPassword:
+    case PasswordChangeDelegate::State::kPasswordSuccessfullyChanged:
+    case PasswordChangeDelegate::State::kPasswordChangeFailed:
+      NOTIMPLEMENTED();
+      break;
+  }
+  NOTREACHED();
 }
 
 void PasswordChangeInfoBubbleController::ReportInteractions() {
   // TODO(crbug.com/381053884): Report metrics.
 }
+
+void PasswordChangeInfoBubbleController::OnStateChanged(
+    PasswordChangeDelegate::State new_state) {
+  if (new_state == state_) {
+    return;
+  }
+  PasswordBubbleViewBase::CloseCurrentBubble();
+}
+
+void PasswordChangeInfoBubbleController::OnPasswordChangeStopped(
+    PasswordChangeDelegate* delegate) {
+  PasswordBubbleViewBase::CloseCurrentBubble();
+}
+
+void PasswordChangeInfoBubbleController::CancelPasswordChange() {
+  CHECK(password_change_delegate_);
+  PasswordBubbleViewBase::CloseCurrentBubble();
+  password_change_delegate_->Stop();
+}
diff --git a/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.h b/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.h
index d1075c60..f8140e515 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.h
+++ b/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.h
@@ -7,20 +7,34 @@
 
 #include <string>
 
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/password_manager/password_change_delegate.h"
 #include "chrome/browser/ui/passwords/bubble_controllers/password_bubble_controller_base.h"
 
 // Controller for the views informing the user about the password change flow
 // state.
-class PasswordChangeInfoBubbleController : public PasswordBubbleControllerBase {
+class PasswordChangeInfoBubbleController : public PasswordBubbleControllerBase,
+                                           PasswordChangeDelegate::Observer {
  public:
   explicit PasswordChangeInfoBubbleController(
-      base::WeakPtr<PasswordsModelDelegate> delegate);
+      base::WeakPtr<PasswordsModelDelegate> delegate,
+      PasswordChangeDelegate::State state);
 
   ~PasswordChangeInfoBubbleController() override;
 
   // PasswordBubbleControllerBase methods:
   std::u16string GetTitle() const override;
   void ReportInteractions() override;
+
+  // PasswordChangeDelegate::Observer methods:
+  void OnStateChanged(PasswordChangeDelegate::State new_state) override;
+  void OnPasswordChangeStopped(PasswordChangeDelegate* delegate) override;
+
+  void CancelPasswordChange();
+
+ private:
+  PasswordChangeDelegate::State state_;
+  base::WeakPtr<PasswordChangeDelegate> password_change_delegate_;
 };
 
 #endif  // CHROME_BROWSER_UI_PASSWORDS_BUBBLE_CONTROLLERS_PASSWORD_CHANGE_PASSWORD_CHANGE_INFO_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller_unittest.cc b/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller_unittest.cc
index 67a756af..5fa93b6 100644
--- a/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller_unittest.cc
@@ -4,10 +4,13 @@
 
 #include "chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.h"
 
+#include "chrome/browser/password_manager/password_change_delegate_mock.h"
 #include "chrome/browser/ui/passwords/passwords_model_delegate_mock.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::Return;
+
 class PasswordChangeBubbleControllerTest : public ::testing::Test {
  public:
   PasswordChangeBubbleControllerTest() {
@@ -16,16 +19,24 @@
   }
   void CreateController() {
     EXPECT_CALL(*mock_delegate_, OnBubbleShown());
+    password_change_delegate_ = std::make_unique<PasswordChangeDelegateMock>();
+    ON_CALL(*mock_delegate_, GetPasswordChangeDelegate)
+        .WillByDefault(Return(password_change_delegate_.get()));
     controller_ = std::make_unique<PasswordChangeInfoBubbleController>(
-        mock_delegate_->AsWeakPtr());
+        mock_delegate_->AsWeakPtr(),
+        PasswordChangeDelegate::State::kWaitingForChangePasswordForm);
   }
 
   PasswordsModelDelegateMock* delegate() { return mock_delegate_.get(); }
   PasswordChangeInfoBubbleController* controller() { return controller_.get(); }
+  PasswordChangeDelegateMock* password_change_delegate() {
+    return password_change_delegate_.get();
+  }
 
  private:
   std::unique_ptr<PasswordsModelDelegateMock> mock_delegate_;
   std::unique_ptr<PasswordChangeInfoBubbleController> controller_;
+  std::unique_ptr<PasswordChangeDelegateMock> password_change_delegate_;
 };
 
 TEST_F(PasswordChangeBubbleControllerTest, ControllerDestroyed) {
@@ -34,3 +45,13 @@
   EXPECT_CALL(*delegate(), OnBubbleHidden());
   controller()->OnBubbleClosing();
 }
+
+TEST_F(PasswordChangeBubbleControllerTest, CancelsFlow) {
+  CreateController();
+  ON_CALL(*password_change_delegate(), GetCurrentState)
+      .WillByDefault(
+          Return(PasswordChangeDelegate::State::kWaitingForChangePasswordForm));
+
+  EXPECT_CALL(*password_change_delegate(), Stop);
+  controller()->CancelPasswordChange();
+}
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc
index 2554358..9d4dfe9 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc
@@ -32,9 +32,9 @@
 }
 
 void CredentialLeakDialogControllerImpl::ShowCredentialLeakPrompt(
-    std::unique_ptr<CredentialLeakPrompt> dialog) {
+    CredentialLeakPrompt* dialog) {
   DCHECK(dialog);
-  credential_leak_dialog_ = std::move(dialog);
+  credential_leak_dialog_ = dialog;
   credential_leak_dialog_->ShowCredentialLeakPrompt();
 }
 
@@ -71,7 +71,10 @@
 }
 
 void CredentialLeakDialogControllerImpl::ResetDialog() {
-  credential_leak_dialog_.reset();
+  if (credential_leak_dialog_) {
+    credential_leak_dialog_->ControllerGone();
+    credential_leak_dialog_ = nullptr;
+  }
 }
 
 std::u16string CredentialLeakDialogControllerImpl::GetAcceptButtonLabel()
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h
index 66c1526..d07e104 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_IMPL_H_
 #define CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_CONTROLLER_IMPL_H_
 
-#include <memory>
-
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/passwords/credential_leak_dialog_controller.h"
 #include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
@@ -34,7 +32,7 @@
   ~CredentialLeakDialogControllerImpl() override;
 
   // Pop up the credential leak dialog.
-  void ShowCredentialLeakPrompt(std::unique_ptr<CredentialLeakPrompt> dialog);
+  void ShowCredentialLeakPrompt(CredentialLeakPrompt* dialog);
 
   // CredentialLeakDialogController:
   bool IsShowingAccountChooser() const override;
@@ -50,7 +48,7 @@
   bool ShouldShowCancelButton() const override;
 
  private:
-  std::unique_ptr<CredentialLeakPrompt> credential_leak_dialog_;
+  raw_ptr<CredentialLeakPrompt> credential_leak_dialog_ = nullptr;
   raw_ptr<PasswordsLeakDialogDelegate> delegate_;
   std::unique_ptr<password_manager::LeakDialogTraits> leak_dialog_traits_;
   GURL url_;
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc
index 7861596..ef92498 100644
--- a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc
+++ b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl_unittest.cc
@@ -21,9 +21,6 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-namespace views {
-class Widget;
-}
 namespace {
 
 constexpr ukm::SourceId kTestSourceId = 0x1234;
@@ -51,7 +48,7 @@
   MockCredentialLeakPrompt& operator=(const MockCredentialLeakPrompt&) = delete;
 
   MOCK_METHOD(void, ShowCredentialLeakPrompt, (), (override));
-  MOCK_METHOD(views::Widget*, GetWidgetForTesting, (), (override));
+  MOCK_METHOD(void, ControllerGone, (), (override));
 };
 
 class CredentialLeakDialogControllerTest : public testing::Test {
@@ -69,10 +66,6 @@
         std::move(recorder));
   }
 
-  std::unique_ptr<StrictMock<MockCredentialLeakPrompt>> SetupLeakPrompt() {
-    return std::make_unique<StrictMock<MockCredentialLeakPrompt>>();
-  }
-
   base::HistogramTester& histogram_tester() { return histogram_tester_; }
 
   PasswordsLeakDialogDelegateMock& ui_controller_mock() {
@@ -83,6 +76,8 @@
     return test_ukm_recorder_;
   }
 
+  MockCredentialLeakPrompt& leak_prompt() { return leak_prompt_; }
+
   CredentialLeakDialogControllerImpl& controller() { return *controller_; }
 
  private:
@@ -90,6 +85,7 @@
   base::HistogramTester histogram_tester_;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
   StrictMock<PasswordsLeakDialogDelegateMock> ui_controller_mock_;
+  StrictMock<MockCredentialLeakPrompt> leak_prompt_;
   std::unique_ptr<CredentialLeakDialogControllerImpl> controller_;
 };
 
@@ -114,10 +110,8 @@
   SetUpController(
       CreateLeakType(IsSaved(false), IsReused(false), IsSyncing(false)));
 
-  auto leak_prompt = SetupLeakPrompt();
-
-  EXPECT_CALL(*leak_prompt, ShowCredentialLeakPrompt());
-  controller().ShowCredentialLeakPrompt(std::move(leak_prompt));
+  EXPECT_CALL(leak_prompt(), ShowCredentialLeakPrompt());
+  controller().ShowCredentialLeakPrompt(&leak_prompt());
 
   EXPECT_CALL(ui_controller_mock(), OnLeakDialogHidden());
   controller().OnCloseDialog();
@@ -132,16 +126,16 @@
 
   CheckUkmMetricsExpectations(test_ukm_recorder(), LeakDialogType::kChange,
                               LeakDialogDismissalReason::kNoDirectInteraction);
+
+  EXPECT_CALL(leak_prompt(), ControllerGone());
 }
 
 TEST_F(CredentialLeakDialogControllerTest, CredentialLeakDialogOk) {
   SetUpController(
       CreateLeakType(IsSaved(true), IsReused(false), IsSyncing(false)));
 
-  auto leak_prompt = SetupLeakPrompt();
-
-  EXPECT_CALL(*leak_prompt, ShowCredentialLeakPrompt());
-  controller().ShowCredentialLeakPrompt(std::move(leak_prompt));
+  EXPECT_CALL(leak_prompt(), ShowCredentialLeakPrompt());
+  controller().ShowCredentialLeakPrompt(&leak_prompt());
 
   EXPECT_CALL(ui_controller_mock(), OnLeakDialogHidden());
   controller().OnAcceptDialog();
@@ -156,16 +150,16 @@
 
   CheckUkmMetricsExpectations(test_ukm_recorder(), LeakDialogType::kChange,
                               LeakDialogDismissalReason::kClickedOk);
+
+  EXPECT_CALL(leak_prompt(), ControllerGone());
 }
 
 TEST_F(CredentialLeakDialogControllerTest, CredentialLeakDialogCancel) {
   SetUpController(
       CreateLeakType(IsSaved(false), IsReused(true), IsSyncing(true)));
 
-  auto leak_prompt = SetupLeakPrompt();
-
-  EXPECT_CALL(*leak_prompt, ShowCredentialLeakPrompt());
-  controller().ShowCredentialLeakPrompt(std::move(leak_prompt));
+  EXPECT_CALL(leak_prompt(), ShowCredentialLeakPrompt());
+  controller().ShowCredentialLeakPrompt(&leak_prompt());
 
   EXPECT_CALL(ui_controller_mock(), OnLeakDialogHidden());
   controller().OnCancelDialog();
@@ -181,16 +175,16 @@
   CheckUkmMetricsExpectations(test_ukm_recorder(),
                               LeakDialogType::kCheckupAndChange,
                               LeakDialogDismissalReason::kClickedClose);
+
+  EXPECT_CALL(leak_prompt(), ControllerGone());
 }
 
 TEST_F(CredentialLeakDialogControllerTest, CredentialLeakDialogCheckPasswords) {
   SetUpController(
       CreateLeakType(IsSaved(true), IsReused(true), IsSyncing(true)));
 
-  auto leak_prompt = SetupLeakPrompt();
-
-  EXPECT_CALL(*leak_prompt, ShowCredentialLeakPrompt());
-  controller().ShowCredentialLeakPrompt(std::move(leak_prompt));
+  EXPECT_CALL(leak_prompt(), ShowCredentialLeakPrompt());
+  controller().ShowCredentialLeakPrompt(&leak_prompt());
 
   EXPECT_CALL(
       ui_controller_mock(),
@@ -210,22 +204,23 @@
   CheckUkmMetricsExpectations(
       test_ukm_recorder(), LeakDialogType::kCheckup,
       LeakDialogDismissalReason::kClickedCheckPasswords);
+
+  EXPECT_CALL(leak_prompt(), ControllerGone());
 }
 
 TEST_F(CredentialLeakDialogControllerTest, PasswordChangeStarted) {
   SetUpController(CreateLeakType(IsSaved(false), IsReused(false),
                                  IsSyncing(true), HasChangePasswordUrl(true)));
 
-  auto leak_prompt = SetupLeakPrompt();
-
-  EXPECT_CALL(*leak_prompt, ShowCredentialLeakPrompt());
-  controller().ShowCredentialLeakPrompt(std::move(leak_prompt));
+  EXPECT_CALL(leak_prompt(), ShowCredentialLeakPrompt());
+  controller().ShowCredentialLeakPrompt(&leak_prompt());
 
   EXPECT_CALL(ui_controller_mock(),
               ChangePassword(GURL(kUrl), std::u16string(kUsername),
                              std::u16string(kPassword)));
   EXPECT_CALL(ui_controller_mock(), OnLeakDialogHidden());
   controller().OnAcceptDialog();
+  EXPECT_CALL(leak_prompt(), ControllerGone());
 }
 
 TEST_F(CredentialLeakDialogControllerTest, PasswordChangeNotStarted) {
@@ -234,15 +229,14 @@
   SetUpController(CreateLeakType(IsSaved(false), IsReused(true),
                                  IsSyncing(true), HasChangePasswordUrl(true)));
 
-  auto leak_prompt = SetupLeakPrompt();
-
-  EXPECT_CALL(*leak_prompt, ShowCredentialLeakPrompt());
-  controller().ShowCredentialLeakPrompt(std::move(leak_prompt));
+  EXPECT_CALL(leak_prompt(), ShowCredentialLeakPrompt());
+  controller().ShowCredentialLeakPrompt(&leak_prompt());
 
   EXPECT_CALL(ui_controller_mock(), ChangePassword).Times(0);
   EXPECT_CALL(ui_controller_mock(), NavigateToPasswordCheckup);
   EXPECT_CALL(ui_controller_mock(), OnLeakDialogHidden());
   controller().OnAcceptDialog();
+  EXPECT_CALL(leak_prompt(), ControllerGone());
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index b1f35df6..65a8165 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -1201,8 +1201,7 @@
   return CreateAutoSigninPromptView(controller, web_contents());
 }
 
-std::unique_ptr<CredentialLeakPrompt>
-ManagePasswordsUIController::CreateCredentialLeakPrompt(
+CredentialLeakPrompt* ManagePasswordsUIController::CreateCredentialLeakPrompt(
     CredentialLeakDialogController* controller) {
   return CreateCredentialLeakPromptView(controller, web_contents());
 }
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index 0f3d8fb..7bd6e6d 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -248,7 +248,7 @@
       CredentialManagerDialogController* controller);
 
   // Called to create the credentials leaked dialog.
-  virtual std::unique_ptr<CredentialLeakPrompt> CreateCredentialLeakPrompt(
+  virtual CredentialLeakPrompt* CreateCredentialLeakPrompt(
       CredentialLeakDialogController* controller);
 
   // Check if |web_contents()| is attached to some Browser. Mocked in tests.
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index e379670..ff0e7b2 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -84,9 +84,6 @@
 using ::testing::ReturnRef;
 using ::testing::SaveArg;
 
-namespace views {
-class Widget;
-}
 namespace {
 
 MATCHER_P3(MatchesLoginAndURL,
@@ -119,7 +116,7 @@
 class PasswordLeakDialogMock : public CredentialLeakPrompt {
  public:
   MOCK_METHOD(void, ShowCredentialLeakPrompt, (), (override));
-  MOCK_METHOD(views::Widget*, GetWidgetForTesting, (), (override));
+  MOCK_METHOD(void, ControllerGone, (), (override));
 };
 
 class TestManagePasswordsIconView : public ManagePasswordsIconView {
@@ -176,7 +173,7 @@
               CreateAutoSigninPrompt,
               (CredentialManagerDialogController*),
               (override));
-  MOCK_METHOD(std::unique_ptr<CredentialLeakPrompt>,
+  MOCK_METHOD(CredentialLeakPrompt*,
               CreateCredentialLeakPrompt,
               (CredentialLeakDialogController*),
               (override));
@@ -1654,12 +1651,11 @@
   EXPECT_TRUE(controller()->opened_automatic_bubble());
 
   // Leak detection dialog hides the bubble.
-  auto dialog_prompt = std::make_unique<PasswordLeakDialogMock>();
+  PasswordLeakDialogMock dialog_prompt;
   CredentialLeakDialogController* dialog_controller = nullptr;
-  EXPECT_CALL(*dialog_prompt, ShowCredentialLeakPrompt);
   EXPECT_CALL(*controller(), CreateCredentialLeakPrompt)
-      .WillOnce(DoAll(SaveArg<0>(&dialog_controller),
-                      Return(std::move(dialog_prompt))));
+      .WillOnce(DoAll(SaveArg<0>(&dialog_controller), Return(&dialog_prompt)));
+  EXPECT_CALL(dialog_prompt, ShowCredentialLeakPrompt);
   controller()->OnCredentialLeak(password_manager::LeakedPasswordDetails(
       password_manager::CreateLeakType(password_manager::IsSaved(false),
                                        password_manager::IsReused(false),
@@ -1677,6 +1673,7 @@
           Return(base::span<const password_manager::InteractionsStats>()));
 
   // Close the dialog.
+  EXPECT_CALL(dialog_prompt, ControllerGone);
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   dialog_controller->OnAcceptDialog();
 
@@ -1696,12 +1693,11 @@
   EXPECT_FALSE(controller()->opened_automatic_bubble());
 
   // Leak detection dialog hides the bubble.
-  auto dialog_prompt = std::make_unique<PasswordLeakDialogMock>();
+  PasswordLeakDialogMock dialog_prompt;
   CredentialLeakDialogController* dialog_controller = nullptr;
-  EXPECT_CALL(*dialog_prompt, ShowCredentialLeakPrompt);
   EXPECT_CALL(*controller(), CreateCredentialLeakPrompt)
-      .WillOnce(DoAll(SaveArg<0>(&dialog_controller),
-                      Return(std::move(dialog_prompt))));
+      .WillOnce(DoAll(SaveArg<0>(&dialog_controller), Return(&dialog_prompt)));
+  EXPECT_CALL(dialog_prompt, ShowCredentialLeakPrompt);
   controller()->OnCredentialLeak(password_manager::LeakedPasswordDetails(
       password_manager::CreateLeakType(password_manager::IsSaved(false),
                                        password_manager::IsReused(false),
@@ -1716,6 +1712,7 @@
   EXPECT_CALL(*form_manager_ptr, IsBlocklisted()).WillOnce(Return(true));
 
   // Close the dialog.
+  EXPECT_CALL(dialog_prompt, ControllerGone);
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   dialog_controller->OnAcceptDialog();
 
@@ -1733,13 +1730,11 @@
   EXPECT_TRUE(controller()->opened_automatic_bubble());
 
   // Leak detection dialog hides the bubble.
-  auto dialog_prompt = std::make_unique<PasswordLeakDialogMock>();
-  auto* dialog_prompt_ptr = dialog_prompt.get();
+  PasswordLeakDialogMock dialog_prompt;
   CredentialLeakDialogController* dialog_controller = nullptr;
   EXPECT_CALL(*controller(), CreateCredentialLeakPrompt)
-      .WillOnce(DoAll(SaveArg<0>(&dialog_controller),
-                      Return(std::move(dialog_prompt))));
-  EXPECT_CALL(*dialog_prompt_ptr, ShowCredentialLeakPrompt);
+      .WillOnce(DoAll(SaveArg<0>(&dialog_controller), Return(&dialog_prompt)));
+  EXPECT_CALL(dialog_prompt, ShowCredentialLeakPrompt);
   controller()->OnCredentialLeak(password_manager::LeakedPasswordDetails(
       password_manager::CreateLeakType(password_manager::IsSaved(true),
                                        password_manager::IsReused(false),
@@ -1750,6 +1745,7 @@
   EXPECT_FALSE(controller()->opened_automatic_bubble());
 
   // Close the dialog.
+  EXPECT_CALL(dialog_prompt, ControllerGone);
   EXPECT_CALL(*controller(), OnUpdateBubbleAndIconVisibility());
   dialog_controller->OnAcceptDialog();
 
diff --git a/chrome/browser/ui/passwords/password_dialog_prompts.h b/chrome/browser/ui/passwords/password_dialog_prompts.h
index d858fcd..d9bf3294 100644
--- a/chrome/browser/ui/passwords/password_dialog_prompts.h
+++ b/chrome/browser/ui/passwords/password_dialog_prompts.h
@@ -5,18 +5,12 @@
 #ifndef CHROME_BROWSER_UI_PASSWORDS_PASSWORD_DIALOG_PROMPTS_H_
 #define CHROME_BROWSER_UI_PASSWORDS_PASSWORD_DIALOG_PROMPTS_H_
 
-#include <memory>
-
 #include "third_party/skia/include/core/SkColor.h"
 
 namespace content {
 class WebContents;
 }
 
-namespace views {
-class Widget;
-}
-
 class CredentialLeakDialogController;
 class CredentialManagerDialogController;
 
@@ -61,17 +55,17 @@
  public:
   CredentialLeakPrompt(const CredentialLeakPrompt&) = delete;
   CredentialLeakPrompt& operator=(const CredentialLeakPrompt&) = delete;
-  virtual ~CredentialLeakPrompt() = default;
 
   // Shows the dialog.
   virtual void ShowCredentialLeakPrompt() = 0;
 
-  // Returns the underlying Widget associated with the on-screen prompt. For
-  // Testing Only!
-  virtual views::Widget* GetWidgetForTesting() = 0;
+  // Notifies the UI element that its controller is no longer managing the UI
+  // element. The dialog should close.
+  virtual void ControllerGone() = 0;
 
  protected:
   CredentialLeakPrompt() = default;
+  virtual ~CredentialLeakPrompt() = default;
 };
 
 // Factory function for AccountChooserPrompt on desktop platforms.
@@ -85,7 +79,7 @@
     content::WebContents* web_contents);
 
 // Factory function for CredentialsLeakedPrompt on desktop platforms.
-std::unique_ptr<CredentialLeakPrompt> CreateCredentialLeakPromptView(
+CredentialLeakPrompt* CreateCredentialLeakPromptView(
     CredentialLeakDialogController* controller,
     content::WebContents* web_contents);
 
diff --git a/chrome/browser/ui/tabs/BUILD.gn b/chrome/browser/ui/tabs/BUILD.gn
index bd18f822..972471eb 100644
--- a/chrome/browser/ui/tabs/BUILD.gn
+++ b/chrome/browser/ui/tabs/BUILD.gn
@@ -288,6 +288,8 @@
     public_deps = [ "//chrome/browser:browser_public_dependencies" ]
 
     sources = [
+      "disconnect_file_chooser_on_background_controller.cc",
+      "disconnect_file_chooser_on_background_controller.h",
       "features.cc",
       "hover_tab_selector.cc",
       "pinned_tab_codec.cc",
diff --git a/chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.cc b/chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.cc
new file mode 100644
index 0000000..f302989
--- /dev/null
+++ b/chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.cc
@@ -0,0 +1,30 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.h"
+
+#include "base/functional/bind.h"
+#include "content/public/browser/web_contents.h"
+
+namespace tabs {
+
+DisconnectFileChooserOnBackgroundController::
+    DisconnectFileChooserOnBackgroundController(TabInterface& tab)
+    : will_enter_background_subscription_(
+          tab.RegisterWillEnterBackground(base::BindRepeating(
+              &DisconnectFileChooserOnBackgroundController::WillEnterBackground,
+              base::Unretained(this)))) {}
+
+DisconnectFileChooserOnBackgroundController::
+    ~DisconnectFileChooserOnBackgroundController() = default;
+
+void DisconnectFileChooserOnBackgroundController::WillEnterBackground(
+    TabInterface* tab) {
+  content::WebContents* web_contents = tab->GetContents();
+  if (web_contents) {
+    web_contents->DisconnectFileSelectListenerIfAny();
+  }
+}
+
+}  // namespace tabs
diff --git a/chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.h b/chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.h
new file mode 100644
index 0000000..29cf6be
--- /dev/null
+++ b/chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.h
@@ -0,0 +1,30 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_TABS_DISCONNECT_FILE_CHOOSER_ON_BACKGROUND_CONTROLLER_H_
+#define CHROME_BROWSER_UI_TABS_DISCONNECT_FILE_CHOOSER_ON_BACKGROUND_CONTROLLER_H_
+
+#include "base/callback_list.h"
+#include "chrome/browser/ui/tabs/public/tab_interface.h"
+
+namespace tabs {
+
+// File select dialogs generally do not have any indicator for which tab
+// requested it, which can cause user confusion if the active tab changes for
+// some reason. As a small and quick fix, disconnect the listener when the tab
+// deactivates.
+class DisconnectFileChooserOnBackgroundController {
+ public:
+  explicit DisconnectFileChooserOnBackgroundController(TabInterface& tab);
+  ~DisconnectFileChooserOnBackgroundController();
+
+ private:
+  void WillEnterBackground(TabInterface* tab);
+
+  base::CallbackListSubscription will_enter_background_subscription_;
+};
+
+}  // namespace tabs
+
+#endif  // CHROME_BROWSER_UI_TABS_DISCONNECT_FILE_CHOOSER_ON_BACKGROUND_CONTROLLER_H_
diff --git a/chrome/browser/ui/tabs/features.cc b/chrome/browser/ui/tabs/features.cc
index ae50c257..980167e 100644
--- a/chrome/browser/ui/tabs/features.cc
+++ b/chrome/browser/ui/tabs/features.cc
@@ -10,6 +10,11 @@
 
 namespace tabs {
 
+// Kill switch for disconnecting file select dialog when tab is deactivated.
+BASE_FEATURE(kDisconnectFileChooserOnTabDeactivateKillSwitch,
+             "DisconnectFileChooserOnTabDeactivateKillSwitch",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Splits pinned and unpinned tabs into separate TabStrips.
 // https://crbug.com/1346019
 BASE_FEATURE(kSplitTabStrip,
diff --git a/chrome/browser/ui/tabs/features.h b/chrome/browser/ui/tabs/features.h
index 5033f7b..2c35d93 100644
--- a/chrome/browser/ui/tabs/features.h
+++ b/chrome/browser/ui/tabs/features.h
@@ -11,6 +11,8 @@
 
 // TODO(346837232) move all flags to this file.
 
+BASE_DECLARE_FEATURE(kDisconnectFileChooserOnTabDeactivateKillSwitch);
+
 BASE_DECLARE_FEATURE(kSplitTabStrip);
 
 BASE_DECLARE_FEATURE(kScrollableTabStrip);
diff --git a/chrome/browser/ui/tabs/public/tab_features.h b/chrome/browser/ui/tabs/public/tab_features.h
index efe1b8d..e19b3380 100644
--- a/chrome/browser/ui/tabs/public/tab_features.h
+++ b/chrome/browser/ui/tabs/public/tab_features.h
@@ -73,6 +73,7 @@
 
 namespace tabs {
 
+class DisconnectFileChooserOnBackgroundController;
 class TabInterface;
 class TabDialogManager;
 
@@ -245,6 +246,9 @@
   std::unique_ptr<passage_embeddings::EmbedderTabObserver>
       embedder_tab_observer_;
 
+  std::unique_ptr<DisconnectFileChooserOnBackgroundController>
+      disconnect_file_chooser_on_background_controller_;
+
   // Must be the last member.
   base::WeakPtrFactory<TabFeatures> weak_factory_{this};
 };
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc
index 9e2ba01..7739547 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc
@@ -217,7 +217,14 @@
 }
 
 void TabGroupSyncServiceProxy::AboutToUnShareTabGroup(
-    const LocalTabGroupID& local_group_id) {
+    const LocalTabGroupID& local_group_id,
+    base::OnceClosure on_complete_callback) {
+  NOTIMPLEMENTED();
+}
+
+void TabGroupSyncServiceProxy::OnTabGroupUnShareComplete(
+    const LocalTabGroupID& local_group_id,
+    bool success) {
   NOTIMPLEMENTED();
 }
 
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h
index a6f4df91..acd72101 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h
@@ -74,7 +74,11 @@
 
   void MakeTabGroupShared(const LocalTabGroupID& local_group_id,
                           std::string_view collaboration_id) override;
-  void AboutToUnShareTabGroup(const LocalTabGroupID& local_group_id) override;
+
+  void AboutToUnShareTabGroup(const LocalTabGroupID& local_group_id,
+                              base::OnceClosure on_complete_cb) override;
+  void OnTabGroupUnShareComplete(const LocalTabGroupID& local_group_id,
+                                 bool success) override;
 
   std::vector<SavedTabGroup> GetAllGroups() const override;
   std::optional<SavedTabGroup> GetGroup(const base::Uuid& guid) const override;
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index f8f9f2a4..f72cc5c 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -31,6 +31,8 @@
 #include "chrome/browser/ui/browser_actions.h"
 #include "chrome/browser/ui/commerce/commerce_ui_tab_helper.h"
 #include "chrome/browser/ui/lens/lens_overlay_controller.h"
+#include "chrome/browser/ui/tabs/disconnect_file_chooser_on_background_controller.h"
+#include "chrome/browser/ui/tabs/features.h"
 #include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
 #include "chrome/browser/ui/tabs/public/tab_interface.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_tab_data.h"
@@ -202,6 +204,12 @@
           favicon::ContentFaviconDriver::FromWebContents(tab.GetContents()));
 
   task_manager::WebContentsTags::CreateForTabContents(tab.GetContents());
+
+  if (base::FeatureList::IsEnabled(
+          tabs::kDisconnectFileChooserOnTabDeactivateKillSwitch)) {
+    disconnect_file_chooser_on_background_controller_ =
+        std::make_unique<DisconnectFileChooserOnBackgroundController>(tab);
+  }
 }
 
 TabFeatures::TabFeatures() = default;
diff --git a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc
index b37cfdb..f565a3b 100644
--- a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc
+++ b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.cc
@@ -4,19 +4,12 @@
 
 #include "chrome/browser/ui/views/passwords/credential_leak_dialog_view.h"
 
-#include <memory>
-#include <utility>
-
-#include "base/functional/bind.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/passwords/credential_leak_dialog_controller.h"
-#include "chrome/browser/ui/passwords/password_dialog_prompts.h"
-#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
-#include "chrome/browser/ui/tabs/public/tab_features.h"
-#include "chrome/browser/ui/tabs/public/tab_interface.h"
 #include "chrome/browser/ui/views/accessibility/theme_tracking_non_accessible_image_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/grit/theme_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
 #include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -28,8 +21,7 @@
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/bubble/tooltip_icon.h"
 #include "ui/views/controls/label.h"
-#include "ui/views/view_utils.h"
-#include "ui/views/widget/widget.h"
+#include "ui/views/layout/fill_layout.h"
 
 namespace {
 
@@ -44,76 +36,14 @@
   return explanation_tooltip;
 }
 
-class CredentialLeakPromptImpl : public CredentialLeakPrompt {
- public:
-  CredentialLeakPromptImpl(CredentialLeakDialogController* controller,
-                           content::WebContents* web_contents);
-  CredentialLeakPromptImpl(const CredentialLeakPromptImpl&) = delete;
-  CredentialLeakPromptImpl& operator=(const CredentialLeakPromptImpl&) = delete;
-  ~CredentialLeakPromptImpl() override = default;
-
-  // Overrides from CredentialLeakPrompt:
-  void ShowCredentialLeakPrompt() override;
-  views::Widget* GetWidgetForTesting() override;
-
- private:
-  // Callback to make Widget::Close synchronous.
-  void CloseWidget(views::Widget::ClosedReason closed_reason);
-
-  std::unique_ptr<CredentialLeakDialogView> credential_leak_dialog_view_;
-  std::unique_ptr<views::Widget> dialog_;
-};
-
-CredentialLeakPromptImpl::CredentialLeakPromptImpl(
-    CredentialLeakDialogController* controller,
-    content::WebContents* web_contents) {
-  credential_leak_dialog_view_ =
-      std::make_unique<CredentialLeakDialogView>(controller, web_contents);
-}
-
-void CredentialLeakPromptImpl::ShowCredentialLeakPrompt() {
-  CHECK(credential_leak_dialog_view_);
-  auto* tab_interface = tabs::TabInterface::GetFromContents(
-      credential_leak_dialog_view_->web_contents());
-  CHECK(tab_interface);
-  credential_leak_dialog_view_->InitWindow();
-  dialog_ = tab_interface->GetTabFeatures()
-                ->tab_dialog_manager()
-                ->CreateShowDialogAndBlockTabInteraction(
-                    credential_leak_dialog_view_.release());
-  dialog_->MakeCloseSynchronous(base::BindOnce(
-      &CredentialLeakPromptImpl::CloseWidget, base::Unretained(this)));
-}
-
-views::Widget* CredentialLeakPromptImpl::GetWidgetForTesting() {
-  return dialog_.get();
-}
-
-void CredentialLeakPromptImpl::CloseWidget(
-    views::Widget::ClosedReason closed_reason) {
-  auto* credential_leak_dialog_view =
-      AsViewClass<CredentialLeakDialogView>(dialog_->GetClientContentsView());
-  CHECK(credential_leak_dialog_view);
-  // Tell the controller to destroy its reference this class which will also
-  // destroy the |dialog_|.
-  credential_leak_dialog_view->controller()->ResetDialog();
-}
-
 }  // namespace
 
 CredentialLeakDialogView::CredentialLeakDialogView(
     CredentialLeakDialogController* controller,
     content::WebContents* web_contents)
     : controller_(controller), web_contents_(web_contents) {
-  CHECK(controller);
-  CHECK(web_contents);
-
-  // Set the ownership of the delegate, not the View. The View is owned by the
-  // Widget as a child view.
-  // TODO(crbug.com/338254375): Remove the following two lines once this is the
-  // default state for widgets and the delegates.
-  views::WidgetDelegate::SetOwnedByWidget(false);
-  SetOwnershipOfNewWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET);
+  DCHECK(controller);
+  DCHECK(web_contents);
 
   SetButtons(controller->ShouldShowCancelButton()
                  ? static_cast<int>(ui::mojom::DialogButton::kOk) |
@@ -133,8 +63,9 @@
   auto close_callback = [](raw_ptr<CredentialLeakDialogController>* controller,
                            ControllerClosureFn fn) {
     // Null out the controller pointer stored in the parent object, to avoid any
-    // further calls to the controller and inhibit recursive closes, and invoke
-    // the provided method on the controller.
+    // further calls to the controller and inhibit recursive closes that would
+    // otherwise happen in ControllerGone(), and invoke the provided method on
+    // the controller.
     //
     // Note that when this lambda gets bound it closes over &controller_, not
     // controller_ itself!
@@ -152,7 +83,27 @@
                      &CredentialLeakDialogController::OnCloseDialog));
 }
 
-CredentialLeakDialogView::~CredentialLeakDialogView() = default;
+CredentialLeakDialogView::~CredentialLeakDialogView() {
+  if (controller_) {
+    std::exchange(controller_, nullptr)->ResetDialog();
+  }
+}
+
+void CredentialLeakDialogView::ShowCredentialLeakPrompt() {
+  InitWindow();
+  constrained_window::ShowWebModalDialogViews(this, web_contents_);
+}
+
+void CredentialLeakDialogView::ControllerGone() {
+  // Widget::Close() synchronously calls Close() on this instance, which resets
+  // the |controller_|. The null check for |controller_| here is to avoid
+  // reentry into Close() - |controller_| might have been nulled out by the
+  // closure callbacks already, in which case the dialog is already closing. See
+  // the definition of |close_callback| in the constructor.
+  if (controller_) {
+    GetWidget()->Close();
+  }
+}
 
 void CredentialLeakDialogView::AddedToWidget() {
   // Set the header image.
@@ -188,7 +139,7 @@
 }
 
 void CredentialLeakDialogView::InitWindow() {
-  SetUseDefaultFillLayout(true);
+  SetLayoutManager(std::make_unique<views::FillLayout>());
   SetBorder(views::CreateEmptyBorder(
       views::LayoutProvider::Get()->GetDialogInsetsForContentType(
           views::DialogContentType::kControl,
@@ -206,8 +157,8 @@
 BEGIN_METADATA(CredentialLeakDialogView)
 END_METADATA
 
-std::unique_ptr<CredentialLeakPrompt> CreateCredentialLeakPromptView(
+CredentialLeakPrompt* CreateCredentialLeakPromptView(
     CredentialLeakDialogController* controller,
     content::WebContents* web_contents) {
-  return std::make_unique<CredentialLeakPromptImpl>(controller, web_contents);
+  return new CredentialLeakDialogView(controller, web_contents);
 }
diff --git a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h
index 6faf59d..89742af 100644
--- a/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h
+++ b/chrome/browser/ui/views/passwords/credential_leak_dialog_view.h
@@ -17,7 +17,8 @@
 
 class CredentialLeakDialogController;
 
-class CredentialLeakDialogView : public views::DialogDelegateView {
+class CredentialLeakDialogView : public views::DialogDelegateView,
+                                 public CredentialLeakPrompt {
   METADATA_HEADER(CredentialLeakDialogView, views::DialogDelegateView)
 
  public:
@@ -27,17 +28,18 @@
   CredentialLeakDialogView& operator=(const CredentialLeakDialogView&) = delete;
   ~CredentialLeakDialogView() override;
 
-  // Sets up the child views.
-  void InitWindow();
-
-  CredentialLeakDialogController* controller() { return controller_; }
-  content::WebContents* web_contents() { return web_contents_; }
+  // CredentialsLeakedPrompt:
+  void ShowCredentialLeakPrompt() override;
+  void ControllerGone() override;
 
  private:
   // views::DialogDelegateView:
   void AddedToWidget() override;
   std::u16string GetWindowTitle() const override;
 
+  // Sets up the child views.
+  void InitWindow();
+
   // A weak pointer to the controller.
   raw_ptr<CredentialLeakDialogController> controller_ = nullptr;
   const raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged>
diff --git a/chrome/browser/ui/views/passwords/password_change/password_change_view_factory.cc b/chrome/browser/ui/views/passwords/password_change/password_change_view_factory.cc
index 68159c9d..b1b87b8 100644
--- a/chrome/browser/ui/views/passwords/password_change/password_change_view_factory.cc
+++ b/chrome/browser/ui/views/passwords/password_change/password_change_view_factory.cc
@@ -17,6 +17,7 @@
     case PasswordChangeDelegate::State::kChangingPassword:
     case PasswordChangeDelegate::State::kPasswordSuccessfullyChanged:
     case PasswordChangeDelegate::State::kPasswordChangeFailed:
-      return new SignInCheckBubbleView(web_contents, anchor_view);
+      return new SignInCheckBubbleView(web_contents, anchor_view,
+                                       delegate->GetCurrentState());
   }
 }
diff --git a/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.cc b/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.cc
index 32e525f..4399b6c 100644
--- a/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.cc
@@ -4,15 +4,62 @@
 
 #include "chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.h"
 
-#include "chrome/browser/ui/passwords/passwords_model_delegate.h"
-#include "ui/base/metadata/metadata_impl_macros.h"
+#include <memory>
+#include <string>
 
-SignInCheckBubbleView::SignInCheckBubbleView(content::WebContents* web_contents,
-                                             views::View* anchor_view)
+#include "base/functional/bind.h"
+#include "chrome/browser/password_manager/password_change_delegate.h"
+#include "chrome/browser/ui/passwords/bubble_controllers/password_change/password_change_info_bubble_controller.h"
+#include "chrome/browser/ui/passwords/passwords_model_delegate.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/views/layout/flex_layout.h"
+
+namespace {
+std::unique_ptr<views::Label> CreateBodyText(const std::u16string& text) {
+  auto body_text = std::make_unique<views::Label>(
+      text, views::style::CONTEXT_DIALOG_BODY_TEXT);
+  body_text->SetMultiLine(true);
+  body_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  return body_text;
+}
+}  // namespace
+
+SignInCheckBubbleView::SignInCheckBubbleView(
+    content::WebContents* web_contents,
+    views::View* anchor_view,
+    PasswordChangeDelegate::State state)
     : PasswordBubbleViewBase(web_contents,
                              anchor_view,
                              /*easily_dismissable=*/true),
-      controller_(PasswordsModelDelegateFromWebContents(web_contents)) {}
+      controller_(PasswordsModelDelegateFromWebContents(web_contents), state) {
+  views::FlexLayout* flex_layout =
+      SetLayoutManager(std::make_unique<views::FlexLayout>());
+  flex_layout->SetOrientation(views::LayoutOrientation::kVertical)
+      .SetCrossAxisAlignment(views::LayoutAlignment::kStretch)
+      .SetIgnoreDefaultMainAxisMargins(true)
+      .SetCollapseMargins(true)
+      .SetDefault(
+          views::kMarginsKey,
+          gfx::Insets::VH(ChromeLayoutProvider::Get()->GetDistanceMetric(
+                              DISTANCE_CONTROL_LIST_VERTICAL),
+                          0));
+
+  // TODO(crbug.com/381053962): Get the current origit from controller.
+  AddChildView(CreateBodyText(u"demo.com"));
+  AddChildView(CreateBodyText(l10n_util::GetStringUTF16(
+      IDS_PASSWORD_MANAGER_UI_SIGN_IN_CHECK_DETAILS)));
+
+  SetButtons(static_cast<int>(ui::mojom::DialogButton::kCancel));
+  SetButtonLabel(ui::mojom::DialogButton::kCancel,
+                 l10n_util::GetStringUTF16(
+                     IDS_PASSWORD_MANAGER_UI_PASSWORD_CHANGE_CANCEL));
+  SetCancelCallback(
+      base::BindOnce(&PasswordChangeInfoBubbleController::CancelPasswordChange,
+                     base::Unretained(&controller_)));
+}
 
 SignInCheckBubbleView::~SignInCheckBubbleView() = default;
 
diff --git a/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.h b/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.h
index 670cdee..397d641 100644
--- a/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.h
+++ b/chrome/browser/ui/views/passwords/password_change/sign_in_check_bubble_view.h
@@ -15,7 +15,8 @@
 
  public:
   SignInCheckBubbleView(content::WebContents* web_contents,
-                        views::View* anchor_view);
+                        views::View* anchor_view,
+                        PasswordChangeDelegate::State state);
 
  private:
   ~SignInCheckBubbleView() override;
diff --git a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
index d306d7e..3eae9fe 100644
--- a/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/passwords/password_dialog_view_browsertest.cc
@@ -64,7 +64,7 @@
       CredentialManagerDialogController* controller) override;
   AutoSigninFirstRunPrompt* CreateAutoSigninPrompt(
       CredentialManagerDialogController* controller) override;
-  std::unique_ptr<CredentialLeakPrompt> CreateCredentialLeakPrompt(
+  CredentialLeakPrompt* CreateCredentialLeakPrompt(
       CredentialLeakDialogController* controller) override;
 
   AccountChooserDialogView* current_account_chooser() const {
@@ -76,8 +76,9 @@
         current_autosignin_prompt_);
   }
 
-  views::Widget* current_credential_leak_widget() const {
-    return current_credential_leak_prompt_->GetWidgetForTesting();
+  CredentialLeakDialogView* current_credential_leak_prompt() const {
+    return static_cast<CredentialLeakDialogView*>(
+        current_credential_leak_prompt_);
   }
 
   MOCK_METHOD(void, OnDialogClosed, (), ());
@@ -125,13 +126,12 @@
   return current_autosignin_prompt_;
 }
 
-std::unique_ptr<CredentialLeakPrompt>
+CredentialLeakPrompt*
 TestManagePasswordsUIController::CreateCredentialLeakPrompt(
     CredentialLeakDialogController* controller) {
-  auto current_credential_leak_prompt =
+  current_credential_leak_prompt_ =
       ManagePasswordsUIController::CreateCredentialLeakPrompt(controller);
-  current_credential_leak_prompt_ = current_credential_leak_prompt.get();
-  return current_credential_leak_prompt;
+  return current_credential_leak_prompt_;
 }
 
 std::unique_ptr<password_manager::PasswordFormManagerForUI> WrapFormInManager(
@@ -488,12 +488,13 @@
   controller()->OnCredentialLeak(password_manager::LeakedPasswordDetails(
       leak_type, GURL("https://example.com"), u"Eve", u"qwerty",
       /*in_account_store=*/false));
-  ASSERT_TRUE(controller()->current_credential_leak_widget());
+  ASSERT_TRUE(controller()->current_credential_leak_prompt());
   EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->GetState());
-  views::Widget* dialog = controller()->current_credential_leak_widget();
-  views::test::WidgetDestroyedWaiter bubble_observer(dialog);
+  CredentialLeakDialogView* dialog =
+      controller()->current_credential_leak_prompt();
+  views::test::WidgetDestroyedWaiter bubble_observer(dialog->GetWidget());
   ui::Accelerator esc(ui::VKEY_ESCAPE, 0);
-  EXPECT_TRUE(dialog->client_view()->AcceleratorPressed(esc));
+  EXPECT_TRUE(dialog->GetWidget()->client_view()->AcceleratorPressed(esc));
   bubble_observer.Wait();
 }
 
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc b/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc
index 05c6efc..95b1ae79 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt_interactive_uitest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_previously_denied_view.h"
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_previously_granted_view.h"
 #include "chrome/browser/ui/views/permissions/embedded_permission_prompt_show_system_prompt_view.h"
+#include "chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.h"
 #include "chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
@@ -489,6 +490,34 @@
 }
 
 IN_PROC_BROWSER_TEST_P(EmbeddedPermissionPromptInteractiveTest,
+                       TestOsPromptButtonsLabel) {
+  base::AutoReset<bool> mock_system_settings =
+      system_permission_settings::MockShowSystemSettingsForTesting();
+
+  std::u16string open_settings_label;
+
+#if BUILDFLAG(IS_MAC)
+  open_settings_label = u"Go to macOS settings";
+#elif BUILDFLAG(IS_WIN)
+  open_settings_label = u"Go to Windows settings";
+#elif BUILDFLAG(IS_CHROMEOS)
+  open_settings_label = u"Go to ChromeOS settings";
+#endif
+
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
+  RunTestSequence(
+      InstrumentTab(kWebContentsElementId),
+      NavigateWebContents(kWebContentsElementId, GetURL()),
+      ClickOnPEPCElement("camera"),
+      InAnyContext(
+          WaitForShow(EmbeddedPermissionPromptSystemSettingsView::kMainViewId)),
+      InAnyContext(CheckViewProperty(
+          EmbeddedPermissionPromptSystemSettingsView::kOpenSettingsId,
+          &views::MdTextButton::GetText, open_settings_label)));
+#endif
+}
+
+IN_PROC_BROWSER_TEST_P(EmbeddedPermissionPromptInteractiveTest,
                        TestPepcHistograms) {
   base::HistogramTester tester;
   RunTestSequence(
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.cc b/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.cc
index d28b3fc..a665573 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.cc
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.cc
@@ -12,6 +12,10 @@
 #include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
 
+DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(
+    EmbeddedPermissionPromptSystemSettingsView,
+    kOpenSettingsId);
+
 EmbeddedPermissionPromptSystemSettingsView::
     EmbeddedPermissionPromptSystemSettingsView(
         Browser* browser,
@@ -70,6 +74,8 @@
   operating_system_name = l10n_util::GetStringUTF16(IDS_MACOS_NAME_FRAGMENT);
 #elif BUILDFLAG(IS_WIN)
   operating_system_name = l10n_util::GetStringUTF16(IDS_WINDOWS_NAME_FRAGMENT);
+#elif BUILDFLAG(IS_CHROMEOS)
+  operating_system_name = l10n_util::GetStringUTF16(IDS_CHROMEOS_NAME_FRAGMENT);
 #endif
 
   // Do not show buttons if the OS is not supported.
@@ -79,5 +85,6 @@
 
   return {{l10n_util::GetStringFUTF16(IDS_EMBEDDED_PROMPT_OPEN_SYSTEM_SETTINGS,
                                       operating_system_name),
-           ButtonType::kSystemSettings, ui::ButtonStyle::kTonal}};
+           ButtonType::kSystemSettings, ui::ButtonStyle::kTonal,
+           kOpenSettingsId}};
 }
diff --git a/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.h b/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.h
index f1293bc..479e791 100644
--- a/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.h
+++ b/chrome/browser/ui/views/permissions/embedded_permission_prompt_system_settings_view.h
@@ -19,6 +19,8 @@
 class EmbeddedPermissionPromptSystemSettingsView
     : public EmbeddedPermissionPromptBaseView {
  public:
+  DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kOpenSettingsId);
+
   EmbeddedPermissionPromptSystemSettingsView(
       Browser* browser,
       base::WeakPtr<EmbeddedPermissionPromptViewDelegate> delegate);
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 1e2883de..a7a1364 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -573,7 +573,7 @@
                                            base::UTF8ToUTF16(*account_manager))
               : std::u16string();
       if (auto* icon = policy::ManagementServiceFactory::GetForProfile(profile)
-                           ->GetManagementIcon()) {
+                           ->GetManagementIconForProfile()) {
         badge_image_model = *icon;
       } else {
         gfx::ImageSkia sized_icon = gfx::CreateVectorIcon(gfx::IconDescription(
@@ -837,7 +837,7 @@
         l10n_util::GetStringUTF16(IDS_PROFILE_MENU_BROWSER_MANAGED_HEADER);
     ui::ImageModel* custom_management_image =
         policy::ManagementServiceFactory::GetForProfile(profile)
-            ->GetManagementIcon();
+            ->GetManagementIconForProfile();
     params.header_image =
         custom_management_image
             ? *custom_management_image
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index b7dff19e..7f5a8e6 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -1373,6 +1373,15 @@
          (IsOtherProfileCommand(command_id) && event.IsMouseEvent());
 }
 
+bool AppMenu::ShouldTryPositioningBesideAnchor() const {
+  // Per crbug.com/349667538, if the app menu isn't drawn with bubble borders,
+  // it should still follow what bubble borders do and *not* float beside the
+  // menu button when vertical space is constrained. Ideally, the app menu would
+  // be switched over to bubble borders on all platforms, but shadow-related
+  // hurdles need to be resolved for that.
+  return false;
+}
+
 void AppMenu::BookmarkModelChanged() {
   DCHECK(bookmark_menu_delegate_.get());
   if (!bookmark_menu_delegate_->is_mutating_model())
diff --git a/chrome/browser/ui/views/toolbar/app_menu.h b/chrome/browser/ui/views/toolbar/app_menu.h
index d1a934b..ecb3acb1 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.h
+++ b/chrome/browser/ui/views/toolbar/app_menu.h
@@ -100,6 +100,7 @@
   void OnMenuClosed(views::MenuItemView* menu) override;
   bool ShouldExecuteCommandWithoutClosingMenu(int command_id,
                                               const ui::Event& event) override;
+  bool ShouldTryPositioningBesideAnchor() const override;
 
   // bookmarks::BaseBookmarkModelObserver overrides:
   void BookmarkModelChanged() override;
diff --git a/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.cc b/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.cc
index a2ef0e6..8a601e84 100644
--- a/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.cc
+++ b/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.cc
@@ -4,14 +4,13 @@
 
 #include "chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.h"
 
-#include <openssl/base.h>
-
 #include <vector>
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/webui/certificate_viewer/certificate_viewer_webui.h"
 #include "chrome/common/pref_names.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
 
 void ShowCertificateDialog(base::WeakPtr<content::WebContents> web_contents,
                            bssl::UniquePtr<CRYPTO_BUFFER> cert) {
diff --git a/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.h b/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.h
index 631cf6b..f882eef 100644
--- a/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.h
+++ b/chrome/browser/ui/webui/certificate_manager/certificate_manager_utils.h
@@ -5,8 +5,6 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_CERTIFICATE_MANAGER_CERTIFICATE_MANAGER_UTILS_H_
 #define CHROME_BROWSER_UI_WEBUI_CERTIFICATE_MANAGER_CERTIFICATE_MANAGER_UTILS_H_
 
-#include <openssl/base.h>
-
 #include "base/functional/callback_forward.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/net/server_certificate_database.h"
@@ -14,6 +12,7 @@
 #include "chrome/browser/ui/webui/certificate_viewer/certificate_viewer_webui.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
+#include "third_party/boringssl/src/include/openssl/base.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
 // Enumeration of certificate management permissions which corresponds to
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 5c3e955a..79c9614 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1532,6 +1532,8 @@
       {"syncAdvancedPageTitle", IDS_SETTINGS_NEW_SYNC_ADVANCED_PAGE_TITLE},
       {"verifyAccount", IDS_SETTINGS_PEOPLE_VERIFY_ACCOUNT_BUTTON},
       {"signOutOfChrome", IDS_PROFILE_MENU_SIGN_OUT},
+      {"syncPausedButtonLabel",
+       IDS_SETTINGS_SIGNIN_BUTTON_LABEL_WHEN_SYNC_IS_PAUSED},
   };
 
   html_source->AddLocalizedStrings(kLocalizedStrings);
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 35505fe..87a3193c 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1734352149-254a6e7e09d2e0c94e10802bb308bf7c1cabd945-533b8266f7b0663b024c969fe7f4e011e1ad2fcf.profdata
+chrome-android64-main-1734366201-17b7b8d3c5d4c7224dee9361cd29c94733661e68-5c54d1c3646c002ecfeab2df15190031372a8057.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 1c7885d..004a06c 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1734328329-15d79d49c99c9d3ff7b6360ddba06a36a4235960-367431e83d75e6576421b305ae0b95920af8e985.profdata
+chrome-linux-main-1734350286-2eb9928898cf7341c6173741b79f1871348db20b-e207ddeef795ce2567642ecc1014d90fa683845e.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 16f9338..0b602a0 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1734343034-a87b024c0de397cc9200d4dc832cf5fc7583f2a3-473db117c65c83d1ef66f3cd29a39ba8263c8afe.profdata
+chrome-mac-arm-main-1734364700-833928fb50893743a5de890def5cc9e8aa6cc100-fc00e6bedd0297f2eb4efa233ecaaa21a3bedfef.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index cb8bbbf..6ee183e 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1734328329-ccae541b19ed657c320a1d3a2e3995e67dfbdd85-367431e83d75e6576421b305ae0b95920af8e985.profdata
+chrome-mac-main-1734350286-1c9620ca6b41fa3c92af4ae4444b927bf715ab62-e207ddeef795ce2567642ecc1014d90fa683845e.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 3b0365b..5828af5 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1734328329-9981be6b5522160a0b7a6069a38eaba031f2eed4-367431e83d75e6576421b305ae0b95920af8e985.profdata
+chrome-win-arm64-main-1734350286-353aa5eb4bed044927769b273b7bdf00f99e70b5-e207ddeef795ce2567642ecc1014d90fa683845e.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index d512dbe..221cb76 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1734317840-987b0305791881fddf0f87620beb1bfa15a6cf17-583001dd8d3f318ea2f59fe620b46b29def9b1c9.profdata
+chrome-win32-main-1734339430-bb37380d51ed3762cd755708a7aab8a80611104a-a360bb015b320de73175ef69bbc707356d2b3fa0.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 92799e7..0a26a627 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1734328329-d3e79b591e1c3f80069d1b3f563e3b5669a96ed9-367431e83d75e6576421b305ae0b95920af8e985.profdata
+chrome-win64-main-1734339430-89adc52b7aee67d26de9d9e6a7c2a5c10138f208-a360bb015b320de73175ef69bbc707356d2b3fa0.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 5d9c006..813bf44 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -919,6 +919,12 @@
 BASE_FEATURE(kPrintPreviewCrosPrimary,
              "PrintPreviewCrosPrimary",
              base::FEATURE_DISABLED_BY_DEFAULT);
+
+// If enabled, use managed per-printer print job options set via
+// DevicePrinters/PrinterBulkConfiguration policy in print preview.
+BASE_FEATURE(kUseManagedPrintJobOptionsInPrintPreview,
+             "UseManagedPrintJobOptionsInPrintPreview",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif
 
 // Enables or disables push subscriptions keeping Chrome running in the
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 68d7450..fa974eb95 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -921,6 +921,11 @@
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kUmaStorageDimensions);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_CHROMEOS)
+COMPONENT_EXPORT(CHROME_FEATURES)
+BASE_DECLARE_FEATURE(kUseManagedPrintJobOptionsInPrintPreview);
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 #if BUILDFLAG(IS_WIN)
 COMPONENT_EXPORT(CHROME_FEATURES)
 BASE_DECLARE_FEATURE(kWin10AcceleratedDefaultBrowserFlow);
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl
index 09a44d75..8517e6a 100644
--- a/chrome/common/extensions/api/autofill_private.idl
+++ b/chrome/common/extensions/api/autofill_private.idl
@@ -91,6 +91,7 @@
     ADDRESS_HOME_HOUSE_NUMBER,
     ADDRESS_HOME_SUBPREMISE,
     ADDRESS_HOME_OTHER_SUBUNIT,
+    NAME_LAST_PREFIX,
     NAME_LAST_FIRST,
     NAME_LAST_CONJUNCTION,
     NAME_LAST_SECOND,
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index fa58f81..e7a666aa 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -4258,20 +4258,22 @@
 inline constexpr char kEnterpriseBadgingTemporarySetting[] =
     "temporary_setting.enterpise_badging";
 
-// Url to an image representing the enterprise logo.
-inline constexpr char kEnterpriseLogoUrl[] = "enterprise_logo.url";
+// Url to an image representing the enterprise logo for the browser.
+// This is saved to local state, and so used for browser policies only.
+inline constexpr char kEnterpriseLogoUrlForBrowser[] =
+    "enterprise_logo.url.for_browser";
 
-// Url to an image representing the enterprise logo for ta profile.
+// Url to an image representing the enterprise logo for a profile.
 // This is used for cloud user policies only.
 inline constexpr char kEnterpriseLogoUrlForProfile[] =
     "enterprise_logo.url.for_profile";
 
-// String value of the custom label for the entity managing the profile.
-inline constexpr char kEnterpriseCustomLabel[] =
-    "enterprise_label.custom_value";
+// String value of the custom label for the entity managing the browser.
+// This is saved to local state, and so used for browser policies only.
+inline constexpr char kEnterpriseCustomLabelForBrowser[] =
+    "enterprise_label.custom_value.for_browser";
 
-// String value of the enterprise label for the entity managing the profile
-// only.
+// String value of the enterprise label for the entity managing the profile.
 // This is used for cloud user policies only.
 inline constexpr char kEnterpriseCustomLabelForProfile[] =
     "enterprise_label.custom_value.for_profile";
diff --git a/chrome/services/speech/soda/soda_client_impl_unittest.cc b/chrome/services/speech/soda/soda_client_impl_unittest.cc
index 9995233..0573b8d 100644
--- a/chrome/services/speech/soda/soda_client_impl_unittest.cc
+++ b/chrome/services/speech/soda/soda_client_impl_unittest.cc
@@ -6,6 +6,7 @@
 #include <algorithm>
 #include <memory>
 
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -92,7 +93,7 @@
   std::string buffer;
   ASSERT_TRUE(base::ReadFileToString(audio_file, &buffer));
 
-  auto handler = media::WavAudioHandler::Create(buffer);
+  auto handler = media::WavAudioHandler::Create(base::as_byte_span(buffer));
   ASSERT_TRUE(handler.get());
   ASSERT_EQ(handler->GetNumChannels(), 1);
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 7ec55cf..1d2dedb 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7441,6 +7441,8 @@
       "../browser/notifications/muted_notification_handler_unittest.cc",
       "../browser/notifications/screen_capture_notification_blocker_unittest.cc",
       "../browser/password_manager/generated_password_leak_detection_pref_unittest.cc",
+      "../browser/password_manager/password_change_delegate_mock.cc",
+      "../browser/password_manager/password_change_delegate_mock.h",
       "../browser/platform_util_unittest.cc",
       "../browser/policy/policy_path_parser_unittest.cc",
       "../browser/policy/serial_allow_usb_devices_for_urls_policy_handler_unittest.cc",
@@ -9083,6 +9085,7 @@
       deps += [
         "//components/dbus",
         "//dbus:test_support",
+        "//ui/base/accelerators/global_accelerator_listener",
         "//ui/linux:test_support",
       ]
     }
diff --git a/chrome/test/data/webui/settings/sync_account_control_test.ts b/chrome/test/data/webui/settings/sync_account_control_test.ts
index eeb8340..de857e94 100644
--- a/chrome/test/data/webui/settings/sync_account_control_test.ts
+++ b/chrome/test/data/webui/settings/sync_account_control_test.ts
@@ -375,29 +375,6 @@
       firstSetupInProgress: false,
       signedInState: SignedInState.SYNCING,
       signedInUsername: 'bar@bar.com',
-      hasError: true,
-      hasUnrecoverableError: false,
-      statusAction: StatusAction.REAUTHENTICATE,
-      disabled: false,
-    };
-    assertTrue(
-        testElement.shadowRoot!
-            .querySelector<HTMLElement>(
-                '#sync-icon-container')!.classList.contains('sync-paused'));
-    assertTrue(!!testElement.shadowRoot!.querySelector(
-        '[icon=\'settings:sync-disabled\']'));
-    displayedText =
-        userInfo.querySelector<HTMLElement>('div:not([hidden])')!.textContent!;
-    assertFalse(displayedText.includes('barName'));
-    assertFalse(displayedText.includes('fooName'));
-    assertTrue(displayedText.includes('Sync is paused'));
-    // The sync error button is shown to resolve the error.
-    assertTrue(isChildVisible(testElement, '#sync-error-button'));
-
-    testElement.syncStatus = {
-      firstSetupInProgress: false,
-      signedInState: SignedInState.SYNCING,
-      signedInUsername: 'bar@bar.com',
       statusAction: StatusAction.NO_ACTION,
       hasError: false,
       hasUnrecoverableError: false,
@@ -464,6 +441,80 @@
     assertTrue(isChildVisible(testElement, '#turn-off'));
   });
 
+  test(
+      'signed out with account awareness, kImprovedSettingsUIOnDesktop enabled',
+      function() {
+        loadTimeData.overrideValues(
+            {isImprovedSettingsUIOnDesktopEnabled: true});
+
+        testElement.syncStatus = {
+          firstSetupInProgress: false,
+          signedInState: SignedInState.SYNCING,
+          signedInUsername: 'bar@bar.com',
+          hasError: true,
+          hasUnrecoverableError: false,
+          statusAction: StatusAction.REAUTHENTICATE,
+          disabled: false,
+        };
+
+        const userInfo = testElement.shadowRoot!.querySelector('#user-info')!;
+        const displayedText =
+            userInfo.querySelector<HTMLElement>(
+                        'div:not([hidden])')!.textContent!;
+
+        assertFalse(isChildVisible(testElement, '#dropdown-arrow'));
+        assertTrue(
+            testElement.shadowRoot!
+                .querySelector<HTMLElement>(
+                    '#sync-icon-container')!.classList.contains('sync-paused'));
+        assertTrue(!!testElement.shadowRoot!.querySelector(
+            '[icon=\'settings:sync-disabled\']'));
+        assertFalse(displayedText.includes('barName'));
+        assertFalse(displayedText.includes('fooName'));
+        assertTrue(displayedText.includes('Sync is paused'));
+
+        // The sync error button is no longer shown to resolve the error.
+        assertFalse(isChildVisible(testElement, '#sync-error-button'));
+        assertTrue(isChildVisible(testElement, '#sync-paused'));
+      });
+
+
+  test(
+      'signed out with account awareness, kImprovedSettingsUIOnDesktop disabled',
+      function() {
+        loadTimeData.overrideValues(
+            {isImprovedSettingsUIOnDesktopEnabled: false});
+
+        testElement.syncStatus = {
+          firstSetupInProgress: false,
+          signedInState: SignedInState.SYNCING,
+          signedInUsername: 'bar@bar.com',
+          hasError: true,
+          hasUnrecoverableError: false,
+          statusAction: StatusAction.REAUTHENTICATE,
+          disabled: false,
+        };
+
+        const userInfo = testElement.shadowRoot!.querySelector('#user-info')!;
+        const displayedText =
+            userInfo.querySelector<HTMLElement>(
+                        'div:not([hidden])')!.textContent!;
+
+        assertTrue(
+            testElement.shadowRoot!
+                .querySelector<HTMLElement>(
+                    '#sync-icon-container')!.classList.contains('sync-paused'));
+        assertTrue(!!testElement.shadowRoot!.querySelector(
+            '[icon=\'settings:sync-disabled\']'));
+        assertFalse(displayedText.includes('barName'));
+        assertFalse(displayedText.includes('fooName'));
+        assertTrue(displayedText.includes('Sync is paused'));
+        // The sync error button is shown to resolve the error.
+        assertTrue(isChildVisible(testElement, '#sync-error-button'));
+      });
+
+
+
   test('signed in, setup in progress', function() {
     testElement.syncStatus = {
       signedInState: SignedInState.SYNCING,
diff --git a/chromecast/media/cma/backend/cplay/cplay.cc b/chromecast/media/cma/backend/cplay/cplay.cc
index 20bb7a0..d5a9b1b 100644
--- a/chromecast/media/cma/backend/cplay/cplay.cc
+++ b/chromecast/media/cma/backend/cplay/cplay.cc
@@ -87,7 +87,8 @@
  public:
   WavMixerInputSource(const Parameters& params)
       : wav_data_(ReadInputFile(params)),
-        input_handler_(::media::WavAudioHandler::Create(wav_data_)),
+        input_handler_(
+            ::media::WavAudioHandler::Create(base::as_byte_span(wav_data_))),
         device_id_(params.device_id),
         bytes_per_frame_(input_handler_->num_channels() *
                          input_handler_->bits_per_sample() / 8) {
diff --git a/chromeos/ash/components/audio/public/cpp/sounds/audio_stream_handler.cc b/chromeos/ash/components/audio/public/cpp/sounds/audio_stream_handler.cc
index 50921ee3..c6f86eb4 100644
--- a/chromeos/ash/components/audio/public/cpp/sounds/audio_stream_handler.cc
+++ b/chromeos/ash/components/audio/public/cpp/sounds/audio_stream_handler.cc
@@ -12,6 +12,7 @@
 #include <utility>
 
 #include "base/cancelable_callback.h"
+#include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
@@ -182,7 +183,8 @@
   std::unique_ptr<media::AudioHandler> audio_handler;
   switch (codec) {
     case media::AudioCodec::kPCM: {
-      audio_handler = media::WavAudioHandler::Create(audio_data);
+      audio_handler =
+          media::WavAudioHandler::Create(base::as_byte_span(audio_data));
       if (!audio_handler || !audio_handler->Initialize()) {
         LOG(ERROR) << "wav_data is not valid";
         return;
diff --git a/clank b/clank
index e27dfc9..d8f5821 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit e27dfc91559d125063c0d02b92dacd6cb92bc5f9
+Subproject commit d8f582130305674abff573b8d7a7ea738f50c427
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index a5c8485..78805f1 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -65,8 +65,6 @@
 
 static_library("browser") {
   sources = [
-    "autocomplete_history_manager.cc",
-    "autocomplete_history_manager.h",
     "autofill_address_util.cc",
     "autofill_address_util.h",
     "autofill_browser_util.cc",
@@ -327,8 +325,6 @@
     "logging/text_log_receiver.h",
     "manual_testing_import.cc",
     "manual_testing_import.h",
-    "merchant_promo_code_manager.cc",
-    "merchant_promo_code_manager.h",
     "metrics/address_data_cleaner_metrics.cc",
     "metrics/address_data_cleaner_metrics.h",
     "metrics/address_save_metrics.cc",
@@ -507,8 +503,12 @@
     "payments/webauthn_callback_types.h",
     "select_control_util.cc",
     "select_control_util.h",
-    "single_field_fill_router.cc",
-    "single_field_fill_router.h",
+    "single_field_fillers/autocomplete/autocomplete_history_manager.cc",
+    "single_field_fillers/autocomplete/autocomplete_history_manager.h",
+    "single_field_fillers/payments/merchant_promo_code_manager.cc",
+    "single_field_fillers/payments/merchant_promo_code_manager.h",
+    "single_field_fillers/single_field_fill_router.cc",
+    "single_field_fillers/single_field_fill_router.h",
     "strike_databases/address_suggestion_strike_database.cc",
     "strike_databases/address_suggestion_strike_database.h",
     "strike_databases/autofill_profile_migration_strike_database.h",
@@ -934,8 +934,6 @@
     "metrics/autofill_metrics_test_base.h",
     "metrics/ukm_metrics_test_utils.cc",
     "metrics/ukm_metrics_test_utils.h",
-    "mock_autocomplete_history_manager.cc",
-    "mock_autocomplete_history_manager.h",
     "mock_autofill_manager.cc",
     "mock_autofill_manager.h",
     "mock_autofill_manager_observer.cc",
@@ -943,10 +941,6 @@
     "mock_autofill_plus_address_delegate.cc",
     "mock_autofill_plus_address_delegate.h",
     "mock_autofill_prediction_improvements_delegate.cc",
-    "mock_merchant_promo_code_manager.cc",
-    "mock_merchant_promo_code_manager.h",
-    "mock_single_field_fill_router.cc",
-    "mock_single_field_fill_router.h",
     "payments/credit_card_access_manager_test_api.h",
     "payments/credit_card_access_manager_test_base.cc",
     "payments/credit_card_access_manager_test_base.h",
@@ -980,6 +974,12 @@
     "payments/test_payments_network_interface.h",
     "payments/test_virtual_card_enrollment_manager.cc",
     "payments/test_virtual_card_enrollment_manager.h",
+    "single_field_fillers/autocomplete/mock_autocomplete_history_manager.cc",
+    "single_field_fillers/autocomplete/mock_autocomplete_history_manager.h",
+    "single_field_fillers/mock_single_field_fill_router.cc",
+    "single_field_fillers/mock_single_field_fill_router.h",
+    "single_field_fillers/payments/mock_merchant_promo_code_manager.cc",
+    "single_field_fillers/payments/mock_merchant_promo_code_manager.h",
     "strike_databases/payments/test_credit_card_save_strike_database.cc",
     "strike_databases/payments/test_credit_card_save_strike_database.h",
     "strike_databases/payments/test_strike_database.cc",
@@ -1124,7 +1124,6 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
-    "autocomplete_history_manager_unittest.cc",
     "autofill_address_util_unittest.cc",
     "autofill_external_delegate_unittest.cc",
     "autofill_feedback_data_unittest.cc",
@@ -1225,7 +1224,6 @@
     "logging/log_router_unittest.cc",
     "logging/text_log_receiver_unittest.cc",
     "manual_testing_import_unittest.cc",
-    "merchant_promo_code_manager_unittest.cc",
     "metrics/autofill_metrics_unittest.cc",
     "metrics/autofill_metrics_utils_unittest.cc",
     "metrics/autofill_settings_metrics_unittest.cc",
@@ -1279,7 +1277,9 @@
     "payments/virtual_card_enrollment_manager_unittest.cc",
     "payments/wait_for_signal_or_timeout_unittest.cc",
     "select_control_util_unittest.cc",
-    "single_field_fill_router_unittest.cc",
+    "single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc",
+    "single_field_fillers/payments/merchant_promo_code_manager_unittest.cc",
+    "single_field_fillers/single_field_fill_router_unittest.cc",
     "strike_databases/history_clearable_strike_database_unittest.cc",
     "strike_databases/payments/virtual_card_enrollment_strike_database_unittest.cc",
     "strike_databases/simple_autofill_strike_database_unittest.cc",
diff --git a/components/autofill/core/browser/OWNERS b/components/autofill/core/browser/OWNERS
index 55694553..d8cf8cb49 100644
--- a/components/autofill/core/browser/OWNERS
+++ b/components/autofill/core/browser/OWNERS
@@ -1,6 +1 @@
-per-file autofill_optimization_guide*=file://components/autofill/core/browser/payments/OWNERS
 per-file BUILD.gn=file://components/autofill/core/browser/payments/OWNERS
-per-file mock_autofill_optimization_guide*=file://components/autofill/core/browser/payments/OWNERS
-per-file payments_data_manager*=file://components/autofill/core/browser/payments/OWNERS
-per-file payments_suggestion_generator*=file://components/autofill/core/browser/payments/OWNERS
-per-file test_payments_data_manager*=file://components/autofill/core/browser/payments/OWNERS
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 0169d31..8738293e 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -27,7 +27,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/autofill_plus_address_delegate.h"
@@ -49,6 +48,7 @@
 #include "components/autofill/core/browser/payments/credit_card_access_manager.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/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/ui/popup_open_enums.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/ui/suggestion_button_action.h"
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 21b66478..bec0b1e5 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -44,10 +44,10 @@
 #include "components/autofill/core/browser/metrics/log_event.h"
 #include "components/autofill/core/browser/metrics/suggestions_list_metrics.h"
 #include "components/autofill/core/browser/mock_autofill_plus_address_delegate.h"
-#include "components/autofill/core/browser/mock_single_field_fill_router.h"
 #include "components/autofill/core/browser/payments/mock_iban_access_manager.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/test_payments_autofill_client.h"
+#include "components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_autofill_driver.h"
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 9f6fc5ff..fbf2d6388b 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -58,7 +58,6 @@
 #include "base/time/time.h"
 #include "base/uuid.h"
 #include "build/build_config.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/autofill_browser_util.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_external_delegate.h"
@@ -107,6 +106,7 @@
 #include "components/autofill/core/browser/metrics/suggestions_list_metrics.h"
 #include "components/autofill/core/browser/payments/autofill_offer_manager.h"
 #include "components/autofill/core/browser/payments/credit_card_access_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
 #include "components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
@@ -2156,7 +2156,7 @@
 
 const gfx::Image& BrowserAutofillManager::GetCardImage(
     const CreditCard& credit_card) {
-  gfx::Image* card_art_image =
+  const gfx::Image* const card_art_image =
       client()
           .GetPersonalDataManager()
           .payments_data_manager()
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index 23572d1..8d736cd 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -22,7 +22,6 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/autofill_external_delegate.h"
@@ -48,7 +47,8 @@
 #include "components/autofill/core/browser/payments/card_unmask_delegate.h"
 #include "components/autofill/core/browser/payments/full_card_request.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/browser/studies/autofill_ablation_study.h"
 #include "components/autofill/core/browser/suggestions/suggestions_context.h"
 #include "components/autofill/core/browser/ui/suggestion_hiding_reason.h"
diff --git a/components/autofill/core/browser/browser_autofill_manager_test_api.h b/components/autofill/core/browser/browser_autofill_manager_test_api.h
index 7b6bdbd..2dd2e942 100644
--- a/components/autofill/core/browser/browser_autofill_manager_test_api.h
+++ b/components/autofill/core/browser/browser_autofill_manager_test_api.h
@@ -18,7 +18,7 @@
 #include "components/autofill/core/browser/filling/form_filler_test_api.h"
 #include "components/autofill/core/browser/filling_product.h"
 #include "components/autofill/core/browser/payments/credit_card_access_manager.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 628d4443..62d435c 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -65,13 +65,13 @@
 #include "components/autofill/core/browser/metrics/form_events/form_events.h"
 #include "components/autofill/core/browser/metrics/log_event.h"
 #include "components/autofill/core/browser/mock_autofill_plus_address_delegate.h"
-#include "components/autofill/core/browser/mock_single_field_fill_router.h"
 #include "components/autofill/core/browser/password_form_classification.h"
 #include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.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/payments/test_payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/test_payments_network_interface.h"
+#include "components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.h"
 #include "components/autofill/core/browser/strike_databases/payments/test_credit_card_save_strike_database.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
 #include "components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.h"
diff --git a/components/autofill/core/browser/data_manager/payments/DIR_METADATA b/components/autofill/core/browser/data_manager/payments/DIR_METADATA
new file mode 100644
index 0000000..202260cd
--- /dev/null
+++ b/components/autofill/core/browser/data_manager/payments/DIR_METADATA
@@ -0,0 +1,6 @@
+monorail: {
+  component: "UI>Browser>Autofill>Payments"
+}
+buganizer_public: {
+  component_id: 1457039
+}
diff --git a/components/autofill/core/browser/data_manager/payments/OWNERS b/components/autofill/core/browser/data_manager/payments/OWNERS
new file mode 100644
index 0000000..a6f6a36
--- /dev/null
+++ b/components/autofill/core/browser/data_manager/payments/OWNERS
@@ -0,0 +1 @@
+file://components/autofill/core/browser/payments/OWNERS
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 110c86f..4e62fb5 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
@@ -906,7 +906,7 @@
   return nullptr;
 }
 
-gfx::Image* PaymentsDataManager::GetCachedCardArtImageForUrl(
+const gfx::Image* PaymentsDataManager::GetCachedCardArtImageForUrl(
     const GURL& card_art_url) const {
   if (!IsAutofillWalletImportEnabled()) {
     return nullptr;
@@ -915,18 +915,12 @@
     return nullptr;
   }
 
-  auto images_iterator = credit_card_art_images_.find(card_art_url);
-
-  // If the cache contains the image, return it.
-  if (images_iterator != credit_card_art_images_.end()) {
-    gfx::Image* image = images_iterator->second.get();
-    if (!image->IsEmpty()) {
-      return image;
-    }
+  auto it = credit_card_art_images_.find(card_art_url);
+  if (it == credit_card_art_images_.end()) {
+    return nullptr;
   }
-
-  // The cache does not contain the image, return nullptr.
-  return nullptr;
+  const gfx::Image* const image = it->second.get();
+  return !image->IsEmpty() ? image : nullptr;
 }
 
 const std::vector<BnplIssuer>& PaymentsDataManager::GetUnlinkedBnplIssuers()
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 7272a1fe..71859d57 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
@@ -339,7 +339,7 @@
   // optimization for situations where a separate fetch request after trying to
   // retrieve local card art images is not needed. If the card art image is not
   // present in the cache, this function will return a nullptr.
-  gfx::Image* GetCachedCardArtImageForUrl(const GURL& card_art_url) const;
+  const gfx::Image* GetCachedCardArtImageForUrl(const GURL& card_art_url) const;
 
   // Checks if a specific card is eligible to see benefits based on its issuer
   // id.
diff --git a/components/autofill/core/browser/data_model/autofill_i18n_api.cc b/components/autofill/core/browser/data_model/autofill_i18n_api.cc
index 139c0f13..7803e499 100644
--- a/components/autofill/core/browser/data_model/autofill_i18n_api.cc
+++ b/components/autofill/core/browser/data_model/autofill_i18n_api.cc
@@ -151,6 +151,7 @@
     case NAME_MIDDLE_INITIAL:
     case NAME_FULL:
     case NAME_SUFFIX:
+    case NAME_LAST_PREFIX:
     case NAME_LAST_FIRST:
     case NAME_LAST_CONJUNCTION:
     case NAME_LAST_SECOND:
diff --git a/components/autofill/core/browser/data_model/autofill_profile.cc b/components/autofill/core/browser/data_model/autofill_profile.cc
index 5dbfd79c..0b6b93f0 100644
--- a/components/autofill/core/browser/data_model/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile.cc
@@ -517,6 +517,7 @@
                                 NAME_FIRST,
                                 NAME_MIDDLE,
                                 NAME_LAST,
+                                NAME_LAST_PREFIX,
                                 NAME_LAST_FIRST,
                                 NAME_LAST_SECOND,
                                 NAME_LAST_CONJUNCTION,
@@ -553,7 +554,7 @@
 
   // When adding field types, ensure that they don't need to be added here and
   // update the last checked value.
-  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 166,
+  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 167,
                 "New field type needs to be reviewed for inclusion in the "
                 "profile comparison logic.");
 
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
index 654f2e1..0cebc60 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
@@ -1174,26 +1174,27 @@
 
 TEST_F(AutofillProfileComparatorTest, MergeAddressesWithStructure) {
   AutofillProfile p1 = CreateProfileWithAddress(
-      "6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "ca");
+      "6543 CH BACON", "APP 3", "Barcelona", "Catalunya", "HHH999", "ES");
 
   p1.SetRawInfo(ADDRESS_HOME_STREET_NAME, u"StreetName");
   p1.SetRawInfo(ADDRESS_HOME_HOUSE_NUMBER, u"HouseNumber");
   p1.SetRawInfo(ADDRESS_HOME_SUBPREMISE, u"Subpremise");
 
-  AutofillProfile p2 = CreateProfileWithAddress(
-      "6543, Bacon Rd", "", "Montreal", "QC", "hhh 999", "CA");
+  AutofillProfile p2 = p1;
   p2.set_use_date(p1.use_date() + base::Minutes(1));
   p2.SetRawInfo(ADDRESS_HOME_STREET_NAME, u"StreetName2");
   p2.SetRawInfo(ADDRESS_HOME_HOUSE_NUMBER, u"HouseNumber2");
   p2.SetRawInfo(ADDRESS_HOME_SUBPREMISE, u"Subpremise2");
 
-  Address expected(kLegacyHierarchyCountryCode);
+  Address expected(AddressCountryCode("ES"));
   expected.SetRawInfo(ADDRESS_HOME_LINE1, u"6543 CH BACON");
   expected.SetRawInfo(ADDRESS_HOME_LINE2, u"APP 3");
-  expected.SetRawInfo(ADDRESS_HOME_CITY, u"Montreal");
-  expected.SetRawInfo(ADDRESS_HOME_STATE, u"QC");
-  expected.SetRawInfo(ADDRESS_HOME_ZIP, u"hhh 999");
-  expected.SetRawInfo(ADDRESS_HOME_COUNTRY, u"CA");
+  expected.SetRawInfo(ADDRESS_HOME_CITY, u"Barcelona");
+  expected.SetRawInfo(ADDRESS_HOME_STATE, u"Catalunya");
+  expected.SetRawInfo(ADDRESS_HOME_ZIP, u"HHH999");
+  expected.SetRawInfo(ADDRESS_HOME_STREET_NAME, u"StreetName2");
+  expected.SetRawInfo(ADDRESS_HOME_HOUSE_NUMBER, u"HouseNumber2");
+  expected.SetRawInfo(ADDRESS_HOME_SUBPREMISE, u"Subpremise2");
 
   MergeAddressesAndExpect(p1, p2, expected);
   MergeAddressesAndExpect(p2, p1, expected);
@@ -1201,27 +1202,17 @@
 
 TEST_F(AutofillProfileComparatorTest, MergeAddressesWithRewrite) {
   AutofillProfile p1 = CreateProfileWithAddress(
-      "6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "ca");
-
-  p1.SetRawInfo(ADDRESS_HOME_STREET_NAME, u"StreetName");
-  p1.SetRawInfo(ADDRESS_HOME_HOUSE_NUMBER, u"HouseNumber");
-  p1.SetRawInfo(ADDRESS_HOME_SUBPREMISE, u"Subpremise");
-
+      "6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "CA");
   AutofillProfile p2 = CreateProfileWithAddress(
       "6543, Bacon Rd", "", "Montreal", "QC", "hhh 999", "CA");
-  p2.SetRawInfo(ADDRESS_HOME_STREET_NAME, u"StreetName2");
-  p2.SetRawInfo(ADDRESS_HOME_HOUSE_NUMBER, u"HouseNumber2");
-  p2.SetRawInfo(ADDRESS_HOME_SUBPREMISE, u"Subpremise2");
-
   p2.set_use_date(p1.use_date() + base::Minutes(1));
 
-  Address expected(kLegacyHierarchyCountryCode);
+  Address expected(AddressCountryCode("CA"));
   expected.SetRawInfo(ADDRESS_HOME_LINE1, u"6543 CH BACON");
   expected.SetRawInfo(ADDRESS_HOME_LINE2, u"APP 3");
   expected.SetRawInfo(ADDRESS_HOME_CITY, u"Montreal");
   expected.SetRawInfo(ADDRESS_HOME_STATE, u"QC");
   expected.SetRawInfo(ADDRESS_HOME_ZIP, u"hhh 999");
-  expected.SetRawInfo(ADDRESS_HOME_COUNTRY, u"CA");
 
   MergeAddressesAndExpect(p1, p2, expected);
   MergeAddressesAndExpect(p2, p1, expected);
@@ -1262,23 +1253,22 @@
 
 TEST_F(AutofillProfileComparatorTest,
        MergeAddressesDependentLocalityAndSortingCode) {
-  AutofillProfile p1 = CreateProfileWithAddress(
-      "6543 CH BACON", "APP 3", "MONTRÉAL", "QUÉBEC", "HHH999", "ca");
+  AutofillProfile p1 =
+      CreateProfileWithAddress("6543 CH BACON", "APP 3", "Rio de Janeiro",
+                               "Rio de Janeiro", "HHH999", "BR");
   p1.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, u"Some String");
   p1.SetRawInfo(ADDRESS_HOME_SORTING_CODE, u"64205 Biarritz CEDEX");
-  AutofillProfile p2 = CreateProfileWithAddress(
-      "6543, Bacon Rd", "", "Montreal", "QC", "hhh 999", "CA");
+  AutofillProfile p2 = p1;
   p2.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, u"Some Other String");
   p2.SetRawInfo(ADDRESS_HOME_SORTING_CODE, u"64205 Biarritz");
   p2.set_use_date(p1.use_date() + base::Minutes(1));
 
-  Address expected(kLegacyHierarchyCountryCode);
+  Address expected(AddressCountryCode("BR"));
   expected.SetRawInfo(ADDRESS_HOME_LINE1, u"6543 CH BACON");
   expected.SetRawInfo(ADDRESS_HOME_LINE2, u"APP 3");
-  expected.SetRawInfo(ADDRESS_HOME_CITY, u"Montreal");
-  expected.SetRawInfo(ADDRESS_HOME_STATE, u"QC");
-  expected.SetRawInfo(ADDRESS_HOME_ZIP, u"hhh 999");
-  expected.SetRawInfo(ADDRESS_HOME_COUNTRY, u"CA");
+  expected.SetRawInfo(ADDRESS_HOME_CITY, u"Rio de Janeiro");
+  expected.SetRawInfo(ADDRESS_HOME_STATE, u"Rio de Janeiro");
+  expected.SetRawInfo(ADDRESS_HOME_ZIP, u"HHH999");
   expected.SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY, u"Some Other String");
   expected.SetRawInfo(ADDRESS_HOME_SORTING_CODE,
                       u"64205 Biarritz");  // Preferred by use date.
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
index cbb4e0ac..9c76b01 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component.cc
@@ -955,7 +955,6 @@
       GetValueForComparison(newer_component);
   const std::u16string newer_comparison_value =
       newer_component.GetValueForComparison(*this);
-
   // If both components are the same, there is nothing to do.
   if (SameAs(newer_component))
     return true;
diff --git a/components/autofill/core/browser/field_types.cc b/components/autofill/core/browser/field_types.cc
index f06746e..7eecd8d 100644
--- a/components/autofill/core/browser/field_types.cc
+++ b/components/autofill/core/browser/field_types.cc
@@ -105,6 +105,7 @@
          {"ADDRESS_HOME_HOUSE_NUMBER", ADDRESS_HOME_HOUSE_NUMBER},
          {"ADDRESS_HOME_SUBPREMISE", ADDRESS_HOME_SUBPREMISE},
          {"ADDRESS_HOME_OTHER_SUBUNIT", ADDRESS_HOME_OTHER_SUBUNIT},
+         {"NAME_LAST_PREFIX", NAME_LAST_PREFIX},
          {"NAME_LAST_FIRST", NAME_LAST_FIRST},
          {"NAME_LAST_CONJUNCTION", NAME_LAST_CONJUNCTION},
          {"NAME_LAST_SECOND", NAME_LAST_SECOND},
@@ -152,6 +153,7 @@
     case NAME_FIRST:
     case NAME_MIDDLE:
     case NAME_LAST:
+    case NAME_LAST_PREFIX:
     case NAME_LAST_FIRST:
     case NAME_LAST_CONJUNCTION:
     case NAME_LAST_SECOND:
@@ -346,6 +348,8 @@
       return "Middle name";
     case NAME_LAST:
       return "Last name";
+    case NAME_LAST_PREFIX:
+      return "Last name prefix";
     case NAME_LAST_FIRST:
       return "First last name";
     case NAME_LAST_CONJUNCTION:
@@ -494,6 +498,7 @@
     case NAME_FIRST:
     case NAME_MIDDLE:
     case NAME_LAST:
+    case NAME_LAST_PREFIX:
     case NAME_LAST_FIRST:
     case NAME_LAST_SECOND:
     case NAME_LAST_CONJUNCTION:
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h
index ecdd537e..87b56ac 100644
--- a/components/autofill/core/browser/field_types.h
+++ b/components/autofill/core/browser/field_types.h
@@ -464,6 +464,10 @@
   ALTERNATIVE_GIVEN_NAME = 164,
   ALTERNATIVE_FAMILY_NAME = 165,
 
+  // Prefix of the last name, e.g. "van" in the Netherlands.
+  // This is the first child of NAME_LAST.
+  NAME_LAST_PREFIX = 166,
+
   // No new types can be added without a corresponding change to the Autofill
   // server.
   // This enum must be kept in sync with FieldType from
@@ -474,7 +478,7 @@
   // If the newly added type is a storable type of AutofillProfile, update
   // AutofillProfile.StorableTypes in
   // tools/metrics/histograms/metadata/autofill/histograms.xml.
-  MAX_VALID_FIELD_TYPE = 166,
+  MAX_VALID_FIELD_TYPE = 167,
 };
 // LINT.ThenChange(//chrome/common/extensions/api/autofill_private.idl)
 
diff --git a/components/autofill/core/browser/field_types_unittest.cc b/components/autofill/core/browser/field_types_unittest.cc
index 67657e67..6806bca 100644
--- a/components/autofill/core/browser/field_types_unittest.cc
+++ b/components/autofill/core/browser/field_types_unittest.cc
@@ -97,6 +97,7 @@
       ADDRESS_HOME_HOUSE_NUMBER_AND_APT,
       ADDRESS_HOME_SUBPREMISE,
       ADDRESS_HOME_OTHER_SUBUNIT,
+      NAME_LAST_PREFIX,
       NAME_LAST_FIRST,
       NAME_LAST_CONJUNCTION,
       NAME_LAST_SECOND,
diff --git a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
index a0e3c43d..4dd8db3 100644
--- a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
+++ b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
@@ -618,6 +618,7 @@
     case NAME_MIDDLE_INITIAL:
     case NAME_FULL:
     case NAME_SUFFIX:
+    case NAME_LAST_PREFIX:
     case ALTERNATIVE_FULL_NAME:
     case ALTERNATIVE_GIVEN_NAME:
     case ALTERNATIVE_FAMILY_NAME:
diff --git a/components/autofill/core/browser/form_parsing/internal_resources b/components/autofill/core/browser/form_parsing/internal_resources
index 9efd7cc2..8572e719 160000
--- a/components/autofill/core/browser/form_parsing/internal_resources
+++ b/components/autofill/core/browser/form_parsing/internal_resources
@@ -1 +1 @@
-Subproject commit 9efd7cc2c1a908d3a67865003c25cf09bc876257
+Subproject commit 8572e719bbf714846a735cdbfc1380ff6cf6a6a7
diff --git a/components/autofill/core/browser/integrators/OWNERS b/components/autofill/core/browser/integrators/OWNERS
new file mode 100644
index 0000000..f79514c
--- /dev/null
+++ b/components/autofill/core/browser/integrators/OWNERS
@@ -0,0 +1 @@
+per-file *autofill_optimization_guide*=file://components/autofill/core/browser/payments/OWNERS
diff --git a/components/autofill/core/browser/metrics/prediction_quality_metrics.cc b/components/autofill/core/browser/metrics/prediction_quality_metrics.cc
index 9c7c0fb..2fbe1c2fe 100644
--- a/components/autofill/core/browser/metrics/prediction_quality_metrics.cc
+++ b/components/autofill/core/browser/metrics/prediction_quality_metrics.cc
@@ -292,6 +292,7 @@
         case SINGLE_USERNAME:
         case NOT_USERNAME:
         case ONE_TIME_CODE:
+        case NAME_LAST_PREFIX:
         case NAME_LAST_FIRST:
         case NAME_LAST_CONJUNCTION:
         case NAME_LAST_SECOND:
diff --git a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
index c8dc82fe..b7aae167 100644
--- a/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_cvc_authenticator_unittest.cc
@@ -26,7 +26,6 @@
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #include "components/autofill/core/browser/data_manager/test_personal_data_manager.h"
@@ -39,6 +38,7 @@
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/test_authentication_requester.h"
 #include "components/autofill/core/browser/payments/test_payments_network_interface.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/test_autofill_driver.h"
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
index 7808986..9d2c005 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
@@ -27,7 +27,6 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "build/build_config.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_manager/payments/test_payments_data_manager.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
@@ -43,6 +42,7 @@
 #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
 #include "components/autofill/core/browser/payments/test_internal_authenticator.h"
 #include "components/autofill/core/browser/payments/test_payments_network_interface.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/test_autofill_driver.h"
diff --git a/components/autofill/core/browser/payments/iban_manager.h b/components/autofill/core/browser/payments/iban_manager.h
index 59d92ea9..6d4e6e5 100644
--- a/components/autofill/core/browser/payments/iban_manager.h
+++ b/components/autofill/core/browser/payments/iban_manager.h
@@ -10,7 +10,7 @@
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #include "components/autofill/core/browser/data_model/iban.h"
 #include "components/autofill/core/browser/metrics/payments/iban_metrics.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/browser/ui/suggestion_type.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/webdata/common/web_data_service_consumer.h"
diff --git a/components/autofill/core/browser/payments/payments_autofill_client.cc b/components/autofill/core/browser/payments/payments_autofill_client.cc
index 76b886c1..8ff656a 100644
--- a/components/autofill/core/browser/payments/payments_autofill_client.cc
+++ b/components/autofill/core/browser/payments/payments_autofill_client.cc
@@ -10,11 +10,11 @@
 #include "base/functional/callback.h"
 #include "components/autofill/core/browser/autofill_progress_dialog_type.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/autofill/core/browser/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
 #include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
 #include "components/autofill/core/browser/payments/card_unmask_delegate.h"
 #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/ui/payments/bubble_show_options.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
diff --git a/components/autofill/core/browser/payments/test_payments_autofill_client.cc b/components/autofill/core/browser/payments/test_payments_autofill_client.cc
index 465bcb3..fdcc813 100644
--- a/components/autofill/core/browser/payments/test_payments_autofill_client.cc
+++ b/components/autofill/core/browser/payments/test_payments_autofill_client.cc
@@ -12,13 +12,13 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/payments/autofill_offer_manager.h"
 #include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h"
 #include "components/autofill/core/browser/payments/credit_card_otp_authenticator.h"
 #include "components/autofill/core/browser/payments/mandatory_reauth_manager.h"
 #include "components/autofill/core/browser/payments/test/mock_payments_window_manager.h"
 #include "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/ui/touch_to_fill_delegate.h"
 
diff --git a/components/autofill/core/browser/payments/test_payments_autofill_client.h b/components/autofill/core/browser/payments/test_payments_autofill_client.h
index b5e7b60..fe6ad62 100644
--- a/components/autofill/core/browser/payments/test_payments_autofill_client.h
+++ b/components/autofill/core/browser/payments/test_payments_autofill_client.h
@@ -11,7 +11,6 @@
 #include "build/build_config.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/iban.h"
-#include "components/autofill/core/browser/mock_merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
 #include "components/autofill/core/browser/payments/autofill_offer_manager.h"
 #include "components/autofill/core/browser/payments/legal_message_line.h"
@@ -21,6 +20,7 @@
 #include "components/autofill/core/browser/payments/test/mock_mandatory_reauth_manager.h"
 #include "components/autofill/core/browser/payments/test/test_credit_card_risk_based_authenticator.h"
 #include "components/autofill/core/browser/payments/test_payments_network_interface.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 
 #if !BUILDFLAG(IS_IOS)
diff --git a/components/autofill/core/browser/autocomplete_history_manager.cc b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.cc
similarity index 98%
rename from components/autofill/core/browser/autocomplete_history_manager.cc
rename to components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.cc
index 23c6d85..e986d7e9 100644
--- a/components/autofill/core/browser/autocomplete_history_manager.cc
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.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 "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 
 #include <string>
 #include <unordered_map>
@@ -118,8 +118,9 @@
     const std::u16string& field_name,
     const std::u16string& value,
     SuggestionType type) {
-  if (profile_database_)
+  if (profile_database_) {
     profile_database_->RemoveFormValueForElementName(field_name, value);
+  }
 }
 
 void AutocompleteHistoryManager::OnSingleFieldSuggestionSelected(
diff --git a/components/autofill/core/browser/autocomplete_history_manager.h b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h
similarity index 93%
rename from components/autofill/core/browser/autocomplete_history_manager.h
rename to components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h
index f25ccab..edd4981 100644
--- a/components/autofill/core/browser/autocomplete_history_manager.h
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_AUTOCOMPLETE_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_AUTOCOMPLETE_AUTOCOMPLETE_HISTORY_MANAGER_H_
 
 #include <map>
 #include <vector>
@@ -11,7 +11,7 @@
 #include "base/functional/callback.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/webdata/autocomplete/autocomplete_entry.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
@@ -152,4 +152,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_AUTOCOMPLETE_AUTOCOMPLETE_HISTORY_MANAGER_H_
diff --git a/components/autofill/core/browser/autocomplete_history_manager_unittest.cc b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc
similarity index 98%
rename from components/autofill/core/browser/autocomplete_history_manager_unittest.cc
rename to components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc
index 30c3765..8874cbe 100644
--- a/components/autofill/core/browser/autocomplete_history_manager_unittest.cc
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 
 #include <string>
 #include <vector>
@@ -477,9 +477,8 @@
 
   // Setting up mock to verify that DB response triggers a call to the handler's
   // OnSuggestionsReturned
-  EXPECT_CALL(mock_callback,
-              Run(test_field_.global_id(),
-                  testing::Truly(IsEmptySuggestionVector)));
+  EXPECT_CALL(mock_callback, Run(test_field_.global_id(),
+                                 testing::Truly(IsEmptySuggestionVector)));
 
   // Simulate response from DB.
   autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id,
@@ -650,9 +649,8 @@
       test_field_, autofill_client_, mock_callback.GetNewRef()));
 
   // Setting up mock to verify that DB response triggers a call to the handler's
-  EXPECT_CALL(mock_callback,
-              Run(test_field_.global_id(),
-                  testing::Truly(IsEmptySuggestionVector)));
+  EXPECT_CALL(mock_callback, Run(test_field_.global_id(),
+                                 testing::Truly(IsEmptySuggestionVector)));
 
   // Simulate response from DB.
   autocomplete_manager_->OnWebDataServiceRequestDone(mocked_db_query_id,
diff --git a/components/autofill/core/browser/mock_autocomplete_history_manager.cc b/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.cc
similarity index 60%
rename from components/autofill/core/browser/mock_autocomplete_history_manager.cc
rename to components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.cc
index fd7b1003..18db5908 100644
--- a/components/autofill/core/browser/mock_autocomplete_history_manager.cc
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.cc
@@ -2,9 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/mock_autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h"
 
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 
 namespace autofill {
 
diff --git a/components/autofill/core/browser/mock_autocomplete_history_manager.h b/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h
similarity index 76%
rename from components/autofill/core/browser/mock_autocomplete_history_manager.h
rename to components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h
index 1919f10..c8aa6a64 100644
--- a/components/autofill/core/browser/mock_autocomplete_history_manager.h
+++ b/components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_AUTOCOMPLETE_HISTORY_MANAGER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_AUTOCOMPLETE_MOCK_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_AUTOCOMPLETE_MOCK_AUTOCOMPLETE_HISTORY_MANAGER_H_
 
 #include "base/memory/weak_ptr.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill {
@@ -44,4 +44,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_AUTOCOMPLETE_HISTORY_MANAGER_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_AUTOCOMPLETE_MOCK_AUTOCOMPLETE_HISTORY_MANAGER_H_
diff --git a/components/autofill/core/browser/mock_single_field_fill_router.cc b/components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.cc
similarity index 86%
rename from components/autofill/core/browser/mock_single_field_fill_router.cc
rename to components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.cc
index 618212b..ff0b473 100644
--- a/components/autofill/core/browser/mock_single_field_fill_router.cc
+++ b/components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.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 "components/autofill/core/browser/mock_single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.h"
 
 namespace autofill {
 
diff --git a/components/autofill/core/browser/mock_single_field_fill_router.h b/components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.h
similarity index 80%
rename from components/autofill/core/browser/mock_single_field_fill_router.h
rename to components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.h
index aec053c77..f7c66b1 100644
--- a/components/autofill/core/browser/mock_single_field_fill_router.h
+++ b/components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_SINGLE_FIELD_FILL_ROUTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_SINGLE_FIELD_FILL_ROUTER_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_MOCK_SINGLE_FIELD_FILL_ROUTER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_MOCK_SINGLE_FIELD_FILL_ROUTER_H_
 
 #include "base/memory/weak_ptr.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill {
@@ -48,4 +48,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_SINGLE_FIELD_FILL_ROUTER_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_MOCK_SINGLE_FIELD_FILL_ROUTER_H_
diff --git a/components/autofill/core/browser/merchant_promo_code_manager.cc b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.cc
similarity index 98%
rename from components/autofill/core/browser/merchant_promo_code_manager.cc
rename to components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.cc
index 530785a..8321e33 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager.cc
+++ b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.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 "components/autofill/core/browser/merchant_promo_code_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
diff --git a/components/autofill/core/browser/merchant_promo_code_manager.h b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h
similarity index 90%
rename from components/autofill/core/browser/merchant_promo_code_manager.h
rename to components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h
index c8b98b6..6021bd1 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager.h
+++ b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_MERCHANT_PROMO_CODE_MANAGER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_MERCHANT_PROMO_CODE_MANAGER_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_PAYMENTS_MERCHANT_PROMO_CODE_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_PAYMENTS_MERCHANT_PROMO_CODE_MANAGER_H_
 
 #include <string>
 #include <vector>
 
 #include "base/gtest_prod_util.h"
 #include "components/autofill/core/browser/data_manager/personal_data_manager.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/browser/ui/suggestion_type.h"
 #include "components/autofill/core/common/unique_ids.h"
 #include "components/keyed_service/core/keyed_service.h"
@@ -103,4 +103,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_MERCHANT_PROMO_CODE_MANAGER_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_PAYMENTS_MERCHANT_PROMO_CODE_MANAGER_H_
diff --git a/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager_unittest.cc
similarity index 99%
rename from components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
rename to components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager_unittest.cc
index 2bc0a6b..41c6fda 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
+++ b/components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/merchant_promo_code_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 
 #include <list>
 #include <memory>
diff --git a/components/autofill/core/browser/mock_merchant_promo_code_manager.cc b/components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.cc
similarity index 75%
rename from components/autofill/core/browser/mock_merchant_promo_code_manager.cc
rename to components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.cc
index 7f7d5e8..69f924a 100644
--- a/components/autofill/core/browser/mock_merchant_promo_code_manager.cc
+++ b/components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.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 "components/autofill/core/browser/mock_merchant_promo_code_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.h"
 
 namespace autofill {
 
diff --git a/components/autofill/core/browser/mock_merchant_promo_code_manager.h b/components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.h
similarity index 68%
rename from components/autofill/core/browser/mock_merchant_promo_code_manager.h
rename to components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.h
index dac794e1..85cc8a9 100644
--- a/components/autofill/core/browser/mock_merchant_promo_code_manager.h
+++ b/components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_PAYMENTS_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_PAYMENTS_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
 
 #include "base/memory/weak_ptr.h"
-#include "components/autofill/core/browser/merchant_promo_code_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill {
@@ -32,4 +32,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_PAYMENTS_MOCK_MERCHANT_PROMO_CODE_MANAGER_H_
diff --git a/components/autofill/core/browser/single_field_fill_router.cc b/components/autofill/core/browser/single_field_fillers/single_field_fill_router.cc
similarity index 92%
rename from components/autofill/core/browser/single_field_fill_router.cc
rename to components/autofill/core/browser/single_field_fillers/single_field_fill_router.cc
index 78c7235..adfb773 100644
--- a/components/autofill/core/browser/single_field_fill_router.cc
+++ b/components/autofill/core/browser/single_field_fillers/single_field_fill_router.cc
@@ -2,16 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 
 #include <string>
 #include <vector>
 
 #include "base/check_deref.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
-#include "components/autofill/core/browser/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/payments/iban_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 
 namespace autofill {
 
diff --git a/components/autofill/core/browser/single_field_fill_router.h b/components/autofill/core/browser/single_field_fillers/single_field_fill_router.h
similarity index 94%
rename from components/autofill/core/browser/single_field_fill_router.h
rename to components/autofill/core/browser/single_field_fillers/single_field_fill_router.h
index c3140737..47c8cd7 100644
--- a/components/autofill/core/browser/single_field_fill_router.h
+++ b/components/autofill/core/browser/single_field_fillers/single_field_fill_router.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILL_ROUTER_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILL_ROUTER_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_SINGLE_FIELD_FILL_ROUTER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_SINGLE_FIELD_FILL_ROUTER_H_
 
 #include <string>
 #include <vector>
@@ -104,4 +104,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILL_ROUTER_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SINGLE_FIELD_FILLERS_SINGLE_FIELD_FILL_ROUTER_H_
diff --git a/components/autofill/core/browser/single_field_fill_router_unittest.cc b/components/autofill/core/browser/single_field_fillers/single_field_fill_router_unittest.cc
similarity index 97%
rename from components/autofill/core/browser/single_field_fill_router_unittest.cc
rename to components/autofill/core/browser/single_field_fillers/single_field_fill_router_unittest.cc
index f96bb91..dc779a2 100644
--- a/components/autofill/core/browser/single_field_fill_router_unittest.cc
+++ b/components/autofill/core/browser/single_field_fillers/single_field_fill_router_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 
 #include "base/functional/callback_helpers.h"
 #include "base/test/scoped_feature_list.h"
@@ -11,9 +11,9 @@
 #include "components/autofill/core/browser/data_manager/test_personal_data_manager.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/form_structure_test_api.h"
-#include "components/autofill/core/browser/mock_autocomplete_history_manager.h"
-#include "components/autofill/core/browser/mock_merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/payments/test/mock_iban_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/suggestions/suggestions_context.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/browser/webdata/mock_autofill_webdata_service.h"
diff --git a/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc b/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc
index f9f023d..e4efbd1 100644
--- a/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.cc
@@ -126,6 +126,7 @@
   return profile.GetInfo(trigger_field_type, app_locale);
 }
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 // Returns the minimum number of fields that should be returned by
 // `AutofillProfile::CreateInferredLabels()`, based on the type of the
 // triggering field.
@@ -138,6 +139,7 @@
     return 1;
   }
 }
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
 // Returns for each profile in `profiles` a differentiating label string to be
 // used as a secondary text in the corresponding suggestion bubble.
@@ -155,13 +157,16 @@
                          -> raw_ptr<const AutofillProfile, VectorExperimental> {
                        return &profile;
                      });
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   if (base::FeatureList::IsEnabled(features::kAutofillImprovedLabels)) {
     differentiating_labels = AutofillProfile::CreateInferredLabels(
         profile_ptrs, /*suggested_fields=*/std::nullopt, trigger_field_type,
         {trigger_field_type},
         GetNumberOfMinimalFieldsToShow(trigger_field_type), app_locale,
         /*use_improved_labels_order=*/true);
-  } else {
+  } else
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+  {
     differentiating_labels = AutofillProfile::CreateInferredLabels(
         profile_ptrs, field_types, /*triggering_field_type=*/std::nullopt,
         {trigger_field_type},
diff --git a/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator_unittest.cc
index a92ee6f..57c5aa40 100644
--- a/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/addresses/address_suggestion_generator_unittest.cc
@@ -48,6 +48,7 @@
 constexpr char kAddressesSuppressedHistogramName[] =
     "Autofill.AddressesSuppressedForDisuse";
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 Matcher<Suggestion> EqualLabels(
     const std::vector<std::vector<Suggestion::Text>>& suggestion_objects) {
   return Field(&Suggestion::labels, suggestion_objects);
@@ -64,6 +65,7 @@
   }
   return EqualLabels(suggestion_objects);
 }
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
 #if !BUILDFLAG(IS_IOS)
 Matcher<Suggestion> EqualsUndoAutofillSuggestion() {
@@ -783,6 +785,7 @@
   EXPECT_EQ(test_address_child.type, SuggestionType::kDevtoolsTestAddressEntry);
 }
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 // Text fixture for label generation related tests. Parameterized by triggering
 // field type since how we build labels depends highly on it.
 class AddressLabelSuggestionGeneratorTest
@@ -897,6 +900,7 @@
           AllOf(EqualLabels({{full_form_filling_label + u"United States"}})),
           AllOf(EqualLabels({{full_form_filling_label + u"Switzerland"}}))));
 }
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
 
 }  // namespace
 }  // namespace autofill
diff --git a/components/autofill/core/browser/suggestions/payments/DIR_METADATA b/components/autofill/core/browser/suggestions/payments/DIR_METADATA
new file mode 100644
index 0000000..202260cd
--- /dev/null
+++ b/components/autofill/core/browser/suggestions/payments/DIR_METADATA
@@ -0,0 +1,6 @@
+monorail: {
+  component: "UI>Browser>Autofill>Payments"
+}
+buganizer_public: {
+  component_id: 1457039
+}
diff --git a/components/autofill/core/browser/suggestions/payments/OWNERS b/components/autofill/core/browser/suggestions/payments/OWNERS
new file mode 100644
index 0000000..a6f6a36
--- /dev/null
+++ b/components/autofill/core/browser/suggestions/payments/OWNERS
@@ -0,0 +1 @@
+file://components/autofill/core/browser/payments/OWNERS
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 728334e..fb9ebabe 100644
--- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
@@ -570,9 +570,8 @@
     if constexpr (BUILDFLAG(IS_ANDROID)) {
       suggestion.custom_icon = Suggestion::CustomIconUrl(card_art_url);
     } else {
-      gfx::Image* image =
-          payments_data.GetCachedCardArtImageForUrl(card_art_url);
-      if (image) {
+      if (const gfx::Image* const image =
+              payments_data.GetCachedCardArtImageForUrl(card_art_url)) {
         suggestion.custom_icon = *image;
       }
     }
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 0b902e2a..544a815 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -38,12 +38,12 @@
 #include "components/autofill/core/browser/logging/text_log_receiver.h"
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
 #include "components/autofill/core/browser/metrics/form_interactions_ukm_logger.h"
-#include "components/autofill/core/browser/mock_autocomplete_history_manager.h"
-#include "components/autofill/core/browser/mock_merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/payments/local_card_migration_manager.h"
 #include "components/autofill/core/browser/payments/test_payments_autofill_client.h"
 #include "components/autofill/core/browser/payments/test_payments_network_interface.h"
-#include "components/autofill/core/browser/single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/payments/mock_merchant_promo_code_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #include "components/autofill/core/browser/strike_databases/payments/test_strike_database.h"
 #include "components/autofill/core/browser/studies/autofill_ablation_study.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
diff --git a/components/autofill/core/browser/test_browser_autofill_manager.cc b/components/autofill/core/browser/test_browser_autofill_manager.cc
index 7932e32..76d9db05 100644
--- a/components/autofill/core/browser/test_browser_autofill_manager.cc
+++ b/components/autofill/core/browser/test_browser_autofill_manager.cc
@@ -15,7 +15,7 @@
 #include "components/autofill/core/browser/filling/test_form_filler.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/browser/form_structure_test_api.h"
-#include "components/autofill/core/browser/mock_single_field_fill_router.h"
+#include "components/autofill/core/browser/single_field_fillers/mock_single_field_fill_router.h"
 #include "components/autofill/core/browser/test_autofill_driver.h"
 #include "components/autofill/core/browser/test_autofill_manager_waiter.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
diff --git a/components/autofill/core/browser/webdata/addresses/address_autofill_table_unittest.cc b/components/autofill/core/browser/webdata/addresses/address_autofill_table_unittest.cc
index f74ebcd..5b5b47f 100644
--- a/components/autofill/core/browser/webdata/addresses/address_autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/addresses/address_autofill_table_unittest.cc
@@ -126,7 +126,7 @@
   home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_ZIP, u"ZIP",
                                                 VerificationStatus::kObserved);
 
-  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_COUNTRY, u"DE",
+  home_profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_COUNTRY, u"ZA",
                                                 VerificationStatus::kObserved);
   home_profile.SetRawInfoWithVerificationStatus(
       ADDRESS_HOME_HOUSE_NUMBER, u"House Number",
diff --git a/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc b/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc
index 3e9f782..9a0fff6 100644
--- a/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc
+++ b/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc
@@ -604,7 +604,7 @@
   // When adding field types, ensure that they don't need to be added here and
   // update the last checked value.
   // TODO(crbug.com/359768803): Handle alternative names here.
-  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 166,
+  static_assert(FieldType::MAX_VALID_FIELD_TYPE == 167,
                 "New field type needs to be reviewed for inclusion in sync");
 
   // The profile may be in a legacy state. By calling |FinalizeAfterImport()|
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 4307a53b9..5c24382 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -363,17 +363,17 @@
 // one.
 BASE_FEATURE(kAutofillUseAUAddressModel,
              "AutofillUseAUAddressModel",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables using a custom address model for Canada, overriding the legacy one.
 BASE_FEATURE(kAutofillUseCAAddressModel,
              "AutofillUseCAAddressModel",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables using a custom address model for Germany, overriding the legacy one.
 BASE_FEATURE(kAutofillUseDEAddressModel,
              "AutofillUseDEAddressModel",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables using a custom address model for France, overriding the legacy one.
 BASE_FEATURE(kAutofillUseFRAddressModel,
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreference.java
index b0f6919..5e0567e 100644
--- a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreference.java
@@ -6,19 +6,15 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.Button;
 
+import androidx.annotation.Nullable;
 import androidx.preference.PreferenceViewHolder;
 
-/**
- * A preference with a highlighted background and prominent call to action button.
- *
- * <p>Preference.getOnPreferenceClickListener().onPreferenceClick() is called when the button is
- * clicked.
- */
-public class CardWithButtonPreference extends ChromeBasePreference implements View.OnClickListener {
-    private CharSequence mButtonText;
+/** A preference with a highlighted background and prominent call to action button. */
+public class CardWithButtonPreference extends ChromeBasePreference {
+    private @Nullable CharSequence mButtonText;
+    private @Nullable Runnable mOnButtonClick;
 
     /**
      * Constructor for CardWithButtonPreference.
@@ -34,9 +30,18 @@
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
+
+        // Only the button is clickable (https://crbug.com/382089385).
+        holder.itemView.setClickable(false);
+
         Button button = (Button) holder.findViewById(R.id.card_button);
         button.setText(mButtonText);
-        button.setOnClickListener(this);
+        button.setOnClickListener(
+                (v) -> {
+                    if (mOnButtonClick != null) {
+                        mOnButtonClick.run();
+                    }
+                });
     }
 
     /**
@@ -49,11 +54,12 @@
         notifyChanged();
     }
 
-    // OnClickListener:
-    @Override
-    public void onClick(View view) {
-        if (getOnPreferenceClickListener() != null) {
-            getOnPreferenceClickListener().onPreferenceClick(CardWithButtonPreference.this);
-        }
+    /**
+     * Sets the button on click runnable.
+     *
+     * @param onButtonClick The runnable to be invoked when the button is clicked.
+     */
+    public void setOnButtonClick(Runnable onButtonClick) {
+        mOnButtonClick = onButtonClick;
     }
 }
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreferenceTest.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreferenceTest.java
index 8c9e3a3..97bcf0b 100644
--- a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreferenceTest.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/CardWithButtonPreferenceTest.java
@@ -7,10 +7,12 @@
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.Matchers.allOf;
 
 import android.app.Activity;
@@ -63,10 +65,9 @@
         preference.setSummary(SUMMARY);
         preference.setButtonText(BUTTON_TEXT);
         CallbackHelper callback = new CallbackHelper();
-        preference.setOnPreferenceClickListener(
-                prefClicked -> {
+        preference.setOnButtonClick(
+                () -> {
                     callback.notifyCalled();
-                    return true;
                 });
 
         mPreferenceScreen.addPreference(preference);
@@ -77,8 +78,11 @@
         getSummaryView().check(matches(allOf(withText(SUMMARY), isDisplayed())));
         getButtonView().check(matches(allOf(withText(BUTTON_TEXT), isDisplayed())));
 
-        getButtonView().perform(click());
+        // Only the button is clickable.
+        getTitleView().check(matches(not(isClickable())));
+        getSummaryView().check(matches(not(isClickable())));
 
+        getButtonView().perform(click());
         Assert.assertEquals(1, callback.getCallCount());
     }
 
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 76af159f..5afe038 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@
 {
-  "version": "50.39",
-  "log_list_timestamp": "2024-12-15T12:56:13Z",
+  "version": "50.40",
+  "log_list_timestamp": "2024-12-16T12:55:42Z",
   "operators": [
     {
       "name": "Google",
diff --git a/components/content_settings/core/common/cookie_settings_base.cc b/components/content_settings/core/common/cookie_settings_base.cc
index d7a7fec..5a6d2c7 100644
--- a/components/content_settings/core/common/cookie_settings_base.cc
+++ b/components/content_settings/core/common/cookie_settings_base.cc
@@ -273,11 +273,9 @@
       domain, scheme == net::CookieSourceScheme::kSecure);
   // Pass GURL() as first_party_url since we don't know the context and
   // don't want to match against (*, exception) pattern.
-  // No overrides are given since existing ones only pertain to 3P checks.
-  ContentSetting setting =
-      GetCookieSettingInternal(origin, net::SiteForCookies::FromUrl(origin),
-                               GURL(), net::CookieSettingOverrides(), nullptr)
-          .cookie_setting();
+  SettingInfo setting_info;
+  ContentSetting setting = GetContentSetting(
+      origin, GURL(), ContentSettingsType::COOKIES, &setting_info);
   DCHECK(IsValidSetting(setting));
   if (setting == CONTENT_SETTING_ALLOW) {
     return false;
@@ -362,11 +360,9 @@
 bool CookieSettingsBase::IsCookieSessionOnly(const GURL& origin) const {
   // Pass GURL() as first_party_url since we don't know the context and
   // don't want to match against (*, exception) pattern.
-  // No overrides are given since existing ones only pertain to 3P checks.
-  ContentSetting setting =
-      GetCookieSettingInternal(origin, net::SiteForCookies::FromUrl(origin),
-                               GURL(), net::CookieSettingOverrides(), nullptr)
-          .cookie_setting();
+  SettingInfo setting_info;
+  ContentSetting setting = GetContentSetting(
+      origin, GURL(), ContentSettingsType::COOKIES, &setting_info);
   DCHECK(IsValidSetting(setting));
   return setting == CONTENT_SETTING_SESSION_ONLY;
 }
diff --git a/components/cronet/gn2bp/gen_android_bp.py b/components/cronet/gn2bp/gen_android_bp.py
index a6419f3..b2745d6e 100755
--- a/components/cronet/gn2bp/gen_android_bp.py
+++ b/components/cronet/gn2bp/gen_android_bp.py
@@ -286,12 +286,12 @@
     [('export_include_dirs', {
         "base/allocator/partition_allocator/src/",
     })],
-    'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_partition_alloc_buildflags':
+    'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_buildflags':
     [('export_include_dirs', {
         ".",
         "base/allocator/partition_allocator/src/",
     })],
-    'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_partition_alloc_buildflags__testing':
+    'cronet_aml_base_allocator_partition_allocator_src_partition_alloc_buildflags__testing':
     [('export_include_dirs', {
         ".",
         "base/allocator/partition_allocator/src/",
@@ -1053,7 +1053,7 @@
   return label_to_module_name(protoc_gn_target_name)
 
 
-def create_rust_cxx_module(blueprint, target):
+def create_rust_cxx_modules(blueprint, target):
   """Generate genrules for a CXX GN target
 
     GN actions are used to dynamically generate files during the build. The
@@ -1068,7 +1068,7 @@
         target: gn_utils.Target object.
 
     Returns:
-        The source_genrule module.
+        The source and headers genrule modules.
   """
   header_genrule = Module("cc_genrule",
                           label_to_module_name(target.name) + "_header",
@@ -1094,9 +1094,7 @@
       [f"{gn_utils.label_to_path(out)}.cc" for out in target.sources])
 
   cc_genrule.genrule_headers.add(header_genrule.name)
-  blueprint.add_module(cc_genrule)
-  blueprint.add_module(header_genrule)
-  return cc_genrule
+  return (header_genrule, cc_genrule)
 
 
 def create_proto_modules(blueprint, gn, target):
@@ -2162,7 +2160,8 @@
   """Generate module(s) for a given GN target.
 
     Given a GN target name, generate one or more corresponding modules into a
-    blueprint. The only case when this generates >1 module is proto libraries.
+    blueprint. Most of the time this will only generate one module, with some
+    exceptions such as protos and rust cxxbridge generation.
 
     Args:
         blueprint: Blueprint instance which is being generated.
@@ -2196,7 +2195,7 @@
       bp_module_name += "__FFI"
 
   if bp_module_name in blueprint.modules:
-    return blueprint.modules[bp_module_name]
+    return (blueprint.modules[bp_module_name], )
 
   log.info('create modules for %s (%s)', target.name, target.type)
 
@@ -2206,9 +2205,9 @@
     else:
       # Can be used for both host and device targets.
       module_type = 'cc_binary'
-    module = Module(module_type, bp_module_name, gn_target_name)
+    modules = (Module(module_type, bp_module_name, gn_target_name), )
   elif target.type == 'rust_executable':
-    module = Module("rust_binary", bp_module_name, gn_target_name)
+    modules = (Module("rust_binary", bp_module_name, gn_target_name), )
   elif target.type == "rust_library":
     _type = "rust_library_rlib"
     if parent_gn_type in ["static_library", "shared_library"]:
@@ -2216,40 +2215,42 @@
       # necessary static library that can be linked.
       _type = "rust_ffi_static"
     # Chromium only uses rlibs.
-    module = Module(_type, bp_module_name, gn_target_name)
+    modules = (Module(_type, bp_module_name, gn_target_name), )
   elif target.type == "rust_proc_macro":
-    module = Module("rust_proc_macro", bp_module_name, gn_target_name)
+    modules = (Module("rust_proc_macro", bp_module_name, gn_target_name), )
   elif target.type in ['static_library', 'source_set']:
-    module = Module('cc_library_static', bp_module_name, gn_target_name)
+    modules = (Module('cc_library_static', bp_module_name, gn_target_name), )
   elif target.type == 'shared_library':
-    module = Module('cc_library_shared', bp_module_name, gn_target_name)
+    modules = (Module('cc_library_shared', bp_module_name, gn_target_name), )
   elif target.type == 'group':
     # "group" targets are resolved recursively by gn_utils.get_target().
     # There's nothing we need to do at this level for them.
-    return None
+    return ()
   elif target.type == 'proto_library':
+    # TODO: change create_proto_modules() to return both modules.
     module = create_proto_modules(blueprint, gn, target)
     if module is None:
-      return None
+      return ()
+    modules = (module, )
   elif target.type == "rust_bindgen":
-    module = create_bindgen_module(blueprint, target, bp_module_name)
+    modules = (create_bindgen_module(blueprint, target, bp_module_name), )
   elif target.type == 'action':
-    module = create_action_module(
+    modules = (create_action_module(
         blueprint, gn, target,
         'java_genrule' if parent_gn_type == "java_library" else 'cc_genrule',
-        is_test_target)
+        is_test_target), )
   elif target.type == 'action_foreach':
     if target.script == "//third_party/rust/cxx/chromium_integration/run_cxxbridge.py":
-      module = create_rust_cxx_module(blueprint, target)
+      modules = create_rust_cxx_modules(blueprint, target)
     else:
-      module = create_action_foreach_modules(blueprint, gn, target,
-                                             is_test_target)
+      modules = (create_action_foreach_modules(blueprint, gn, target,
+                                               is_test_target), )
   elif target.type == 'copy':
     # TODO: careful now! copy targets are not supported yet, but this will stop
     # traversing the dependency tree. For //base:base, this is not a big
     # problem as libicu contains the only copy target which happens to be a
     # leaf node.
-    return None
+    return ()
   elif target.type == 'java_library':
     if target.jar_path:
       module = Module('java_import', bp_module_name, gn_target_name)
@@ -2269,231 +2270,230 @@
       module.sdk_version = target.sdk_version
     else:
       module.defaults.add(java_framework_defaults_module)
+    modules = (module, )
   else:
     raise Exception('Unknown target %s (%s)' % (target.name, target.type))
 
-  blueprint.add_module(module)
-  if target.type not in ['action', 'action_foreach']:
-    # Actions should get their srcs from their corresponding ActionSanitizer as actionSanitizer
-    # filters srcs differently according to the type of the action.
-    module.srcs.update(
-        gn_utils.label_to_path(src) for src in target.sources
-        if is_supported_source_file(src))
+  for module in modules:
+    blueprint.add_module(module)
+    if target.type not in ['action', 'action_foreach']:
+      # Actions should get their srcs from their corresponding ActionSanitizer as actionSanitizer
+      # filters srcs differently according to the type of the action.
+      module.srcs.update(
+          gn_utils.label_to_path(src) for src in target.sources
+          if is_supported_source_file(src))
 
-  # Add arch-specific properties
-  for arch_name, arch in target.get_archs().items():
-    module.target[arch_name].srcs.update(
-        gn_utils.label_to_path(src) for src in arch.sources
-        if is_supported_source_file(src))
-
-  module.rtti = target.rtti
-
-  if target.type in gn_utils.LINKER_UNIT_TYPES:
-    set_module_flags(module, module.type, target.cflags, target.defines,
-                     target.ldflags, target.libs)
-    set_module_include_dirs(module, target.cflags, target.include_dirs)
-    # TODO: set_module_xxx is confusing, apply similar function to module and target in better way.
+    # Add arch-specific properties
     for arch_name, arch in target.get_archs().items():
-      # TODO(aymanm): Make libs arch-specific.
-      set_module_flags(module.target[arch_name], module.type, arch.cflags,
-                       arch.defines, arch.ldflags, [])
-      # -Xclang -target-feature -Xclang +mte are used to enable MTE (Memory Tagging Extensions).
-      # Flags which does not start with '-' could not be in the cflags so enabling MTE by
-      # -march and -mcpu Feature Modifiers. MTE is only available on arm64. This is needed for
-      # building //base/allocator/partition_allocator:partition_alloc for arm64.
-      if '+mte' in arch.cflags and arch_name == 'android_arm64':
-        module.target[arch_name].cflags.add('-march=armv8-a+memtag')
-      set_module_include_dirs(module.target[arch_name], arch.cflags,
-                              arch.include_dirs)
+      module.target[arch_name].srcs.update(
+          gn_utils.label_to_path(src) for src in arch.sources
+          if is_supported_source_file(src))
 
-  if not module.type == "rust_proc_macro":
-    # rust_proc_macro modules does not support the fields of `host_supported`
-    # or `device_supported`. In a different world, we would have classes for
-    # each different module that specifies what it can support to avoid
-    # those kind of conditions.
-    #
-    # See go/android.bp for additional information.
-    module.host_supported = target.host_supported()
-    module.device_supported = target.device_supported()
+    module.rtti = target.rtti
 
-  module.gn_type = target.type
-  module.build_file_path = target.build_file_path
-  # Chromium does not use visibility at all, in order to avoid visibility issues
-  # in AOSP. Make every module visible to any module in external/cronet.
-  module.visibility = {"//external/cronet:__subpackages__"}
+    if target.type in gn_utils.LINKER_UNIT_TYPES:
+      set_module_flags(module, module.type, target.cflags, target.defines,
+                       target.ldflags, target.libs)
+      set_module_include_dirs(module, target.cflags, target.include_dirs)
+      # TODO: set_module_xxx is confusing, apply similar function to module and target in better way.
+      for arch_name, arch in target.get_archs().items():
+        # TODO(aymanm): Make libs arch-specific.
+        set_module_flags(module.target[arch_name], module.type, arch.cflags,
+                         arch.defines, arch.ldflags, [])
+        # -Xclang -target-feature -Xclang +mte are used to enable MTE (Memory Tagging Extensions).
+        # Flags which does not start with '-' could not be in the cflags so enabling MTE by
+        # -march and -mcpu Feature Modifiers. MTE is only available on arm64. This is needed for
+        # building //base/allocator/partition_allocator:partition_alloc for arm64.
+        if '+mte' in arch.cflags and arch_name == 'android_arm64':
+          module.target[arch_name].cflags.add('-march=armv8-a+memtag')
+        set_module_include_dirs(module.target[arch_name], arch.cflags,
+                                arch.include_dirs)
 
-  if module.type in [
-      "rust_library_rlib", "rust_proc_macro", "rust_binary", "rust_ffi_static"
-  ]:
-    module.crate_name = target.crate_name
-    module.crate_root = gn_utils.label_to_path(target.crate_root)
-    module.min_sdk_version = 30
-    module.apex_available = [tethering_apex]
-    for arch_name, arch in target.get_archs().items():
-      _set_rust_flags(module.target[arch_name], arch.rust_flags, arch_name)
+    if not module.type == "rust_proc_macro":
+      # rust_proc_macro modules does not support the fields of `host_supported`
+      # or `device_supported`. In a different world, we would have classes for
+      # each different module that specifies what it can support to avoid
+      # those kind of conditions.
+      #
+      # See go/android.bp for additional information.
+      module.host_supported = target.host_supported()
+      module.device_supported = target.device_supported()
 
-  if module.is_genrule():
-    module.apex_available.add(tethering_apex)
+    module.gn_type = target.type
+    module.build_file_path = target.build_file_path
+    # Chromium does not use visibility at all, in order to avoid visibility issues
+    # in AOSP. Make every module visible to any module in external/cronet.
+    module.visibility = {"//external/cronet:__subpackages__"}
 
-  if module.type == "java_library":
-    if gn_utils.contains_aidl(target.sources):
-      # frameworks/base/core/java includes the source files that are used to compile framework.aidl.
-      # framework.aidl is added implicitly as a dependency to every AIDL GN action, this can be
-      # identified by third_party/android_sdk/public/platforms/android-34/framework.aidl.
-      module.aidl["include_dirs"] = {"frameworks/base/core/java/"}
-      module.aidl["local_include_dirs"] = target.local_aidl_includes
+    if module.type in [
+        "rust_library_rlib", "rust_proc_macro", "rust_binary", "rust_ffi_static"
+    ]:
+      module.crate_name = target.crate_name
+      module.crate_root = gn_utils.label_to_path(target.crate_root)
+      module.min_sdk_version = 30
+      module.apex_available = [tethering_apex]
+      for arch_name, arch in target.get_archs().items():
+        _set_rust_flags(module.target[arch_name], arch.rust_flags, arch_name)
 
-  if (module.is_compiled() and not module.type.startswith("java")
-      and not module.type.startswith("rust")):
-    # Don't try to inject library/source dependencies into genrules or
-    # filegroups because they are not compiled in the traditional sense.
-    module.defaults = [cc_defaults_module]
-    for lib in target.libs:
-      # Generally library names should be mangled as 'libXXX', unless they
-      # are HAL libraries (e.g., android.hardware.health@2.0) or AIDL c++ / NDK
-      # libraries (e.g. "android.hardware.power.stats-V1-cpp")
-      android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \
-        else 'lib' + lib
-      if lib in shared_library_allowlist:
-        module.add_android_shared_lib(android_lib)
+    if module.is_genrule():
+      module.apex_available.add(tethering_apex)
 
-  # If the module is a static library, export all the generated headers.
-  if module.type == 'cc_library_static':
-    module.export_generated_headers = module.generated_headers
+    if module.type == "java_library":
+      if gn_utils.contains_aidl(target.sources):
+        # frameworks/base/core/java includes the source files that are used to compile framework.aidl.
+        # framework.aidl is added implicitly as a dependency to every AIDL GN action, this can be
+        # identified by third_party/android_sdk/public/platforms/android-34/framework.aidl.
+        module.aidl["include_dirs"] = {"frameworks/base/core/java/"}
+        module.aidl["local_include_dirs"] = target.local_aidl_includes
 
-  if module.name in [
-      'cronet_aml_components_cronet_android_cronet',
-      'cronet_aml_components_cronet_android_cronet' + gn_utils.TESTING_SUFFIX
-  ]:
-    if target.output_name is None:
-      raise Exception('Failed to get output_name for libcronet name')
-    # .so file name needs to match with CronetLibraryLoader.java (e.g. libcronet.109.0.5386.0.so)
-    # So setting the output name based on the output_name from the desc.json
-    module.stem = 'libmainline' + target.output_name
-  elif module.is_test() and module.type == 'cc_library_shared':
-    if target.output_name:
-      # If we have an output name already declared, use it.
-      module.stem = 'lib' + target.output_name
-    else:
-      # Tests output should be a shared library in the format of 'lib[module_name]'
-      module.stem = 'lib' + target.get_target_name()[:target.get_target_name(
-      ).find(gn_utils.TESTING_SUFFIX)]
+    if (module.is_compiled() and not module.type.startswith("java")
+        and not module.type.startswith("rust")):
+      # Don't try to inject library/source dependencies into genrules or
+      # filegroups because they are not compiled in the traditional sense.
+      module.defaults = [cc_defaults_module]
+      for lib in target.libs:
+        # Generally library names should be mangled as 'libXXX', unless they
+        # are HAL libraries (e.g., android.hardware.health@2.0) or AIDL c++ / NDK
+        # libraries (e.g. "android.hardware.power.stats-V1-cpp")
+        android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \
+          else 'lib' + lib
+        if lib in shared_library_allowlist:
+          module.add_android_shared_lib(android_lib)
 
-  # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
-  all_deps = [(dep_name, 'common') for dep_name in target.proto_deps]
-  for arch_name, arch in target.arch.items():
-    all_deps += [(dep_name, arch_name) for dep_name in arch.deps]
+    # If the module is a static library, export all the generated headers.
+    if module.type == 'cc_library_static':
+      module.export_generated_headers = module.generated_headers
 
-  # Sort deps before iteration to make result deterministic.
-  for (dep_name, arch_name) in sorted(all_deps):
-    module_target = module.target[arch_name] if arch_name != 'common' else module
-    # |builtin_deps| override GN deps with Android-specific ones. See the
-    # config in the top of this file.
-    if dep_name in builtin_deps:
-      builtin_deps[dep_name](module, arch_name)
-      continue
-
-    # This is like the builtin_deps with always_disable except that it matches
-    # a string.
-    if "_build_script" in dep_name:
-      continue
-
-    dep_module = create_modules_from_target(blueprint, gn, dep_name,
-                                            target.type, is_test_target)
-
-    if dep_module is None:
-      continue
-
-    # TODO: Proper dependency check for genrule.
-    # Currently, only propagating genrule dependencies.
-    # Also, currently, all the dependencies are propagated upwards.
-    # in gn, public_deps should be propagated but deps should not.
-    # Not sure this information is available in the desc.json.
-    # Following rule works for adding android_runtime_jni_headers to base:base.
-    # If this doesn't work for other target, hardcoding for specific target
-    # might be better.
-    if module.is_genrule() and dep_module.is_genrule():
-      if module_target.gn_type != "proto_library":
-        # proto_library are treated differently because each proto action
-        # is split into two different targets, a cpp target and a header target.
-        # the cpp target is used as the entry point to the proto action, hence
-        # it should not be propagated as a genrule header because it generates
-        # cpp files only.
-        module_target.genrule_headers.add(dep_module.name)
-      module_target.genrule_headers.update(dep_module.genrule_headers)
-
-    # For filegroups, and genrule, recurse but don't apply the
-    # deps.
-    if not module.is_compiled() or module.is_genrule():
-      continue
-
-    # Drop compiled modules that doesn't provide any benefit. This is mostly
-    # applicable to source_sets when converted to cc_static_library, sometimes
-    # the source set only has header files which are dropped so the module becomes empty.
-    # is_compiled is there to prevent dropping of genrules.
-    if dep_module.is_compiled() and not dep_module.has_input_files():
-      continue
-
-    if dep_module.type == 'cc_library_shared':
-      module_target.shared_libs.add(dep_module.name)
-    elif dep_module.type == 'cc_library_static':
-      if module.type in ['cc_library_shared', 'cc_binary']:
-        module_target.whole_static_libs.add(dep_module.name)
-      elif module.type == 'cc_library_static':
-        module_target.generated_headers.update(dep_module.generated_headers)
-        module_target.shared_libs.update(dep_module.shared_libs)
-        module_target.header_libs.update(dep_module.header_libs)
-      elif module.type in [
-          "rust_library_rlib", "rust_binary", "rust_ffi_static"
-      ]:
-        module_target.static_libs.add(dep_module.name)
+    if module.name in [
+        'cronet_aml_components_cronet_android_cronet',
+        'cronet_aml_components_cronet_android_cronet' + gn_utils.TESTING_SUFFIX
+    ]:
+      if target.output_name is None:
+        raise Exception('Failed to get output_name for libcronet name')
+      # .so file name needs to match with CronetLibraryLoader.java (e.g. libcronet.109.0.5386.0.so)
+      # So setting the output name based on the output_name from the desc.json
+      module.stem = 'libmainline' + target.output_name
+    elif module.is_test() and module.type == 'cc_library_shared':
+      if target.output_name:
+        # If we have an output name already declared, use it.
+        module.stem = 'lib' + target.output_name
       else:
-        raise Exception(
-            f"Trying to add an unknown type {dep_module.type} to a type of {module.type}"
-        )
-    elif dep_module.type == "rust_bindgen":
-      module.srcs.add(":" + dep_module.name)
-      if module_target.type == "cc_library_static":
-        # rust_bindgen generates a .c / .cc file which has include
-        # defined from the root of the android tree.
-        module_target.include_dirs.append(".")
-        # The rust_bindgen has to know the name of the cc library which is going to
-        # consume it. We don't know that until we add the `rust_bindgen` as a dep.
-        dep_module.static_inline_library = module.name
-    elif dep_module.type == "rust_library_rlib":
-      module_target.rustlibs.add(dep_module.name)
-    elif dep_module.type == "rust_ffi_static":
-      assert module.type in [
-          "cc_library_static", "cc_library_shared"
-      ], "Only CC libraries can depend on rust_ffi_static"
-      # CPP libraries must not depend on rust_library_rlib, they must depend
-      # on rust_ffi_rlib as per aosp/3094614 and go/android-made-to-order-rust-staticlibs.
-      module_target.static_libs.add(dep_module.name)
-    elif dep_module.type == "rust_proc_macro":
-      module_target.proc_macros.add(dep_module.name)
-    elif dep_module.type == 'cc_genrule':
-      module_target.generated_headers.update(dep_module.genrule_headers)
-      module_target.srcs.update(dep_module.genrule_srcs)
-      module_target.shared_libs.update(dep_module.genrule_shared_libs)
-      module_target.header_libs.update(dep_module.genrule_header_libs)
-    elif dep_module.type in ['java_library', 'java_import']:
-      # A module depending on a module with system_current sdk version should also compile against
-      # the system sdk. This is because a module's SDK API surface should be >= its deps SDK API surface.
-      # And system_current has a larger API surface than current or module_current.
-      if dep_module.sdk_version == 'system_current':
-        module.sdk_version = 'system_current'
-      if module.type not in ["cc_library_static"]:
-        # This is needed to go around the case where `url` component depends
-        # on `url_java`.
-        # TODO(aymanm): Remove the if condition once crrev/4902547 has been imported downstream
-        module_target.static_libs.add(dep_module.name)
-    elif dep_module.type in ['genrule', 'java_genrule']:
-      module_target.srcs.add(":" + dep_module.name)
-    else:
-      raise Exception(
-          'Unsupported arch-specific dependency %s of target %s with type %s' %
-          (dep_module.name, target.name, dep_module.type))
-  return module
+        # Tests output should be a shared library in the format of 'lib[module_name]'
+        module.stem = 'lib' + target.get_target_name()[:target.get_target_name(
+        ).find(gn_utils.TESTING_SUFFIX)]
+
+    # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
+    all_deps = [(dep_name, 'common') for dep_name in target.proto_deps]
+    for arch_name, arch in target.arch.items():
+      all_deps += [(dep_name, arch_name) for dep_name in arch.deps]
+
+    # Sort deps before iteration to make result deterministic.
+    for (dep_name, arch_name) in sorted(all_deps):
+      module_target = module.target[
+          arch_name] if arch_name != 'common' else module
+      # |builtin_deps| override GN deps with Android-specific ones. See the
+      # config in the top of this file.
+      if dep_name in builtin_deps:
+        builtin_deps[dep_name](module, arch_name)
+        continue
+
+      # This is like the builtin_deps with always_disable except that it matches
+      # a string.
+      if "_build_script" in dep_name:
+        continue
+
+      for dep_module in create_modules_from_target(blueprint, gn, dep_name,
+                                                   target.type, is_test_target):
+        # TODO: Proper dependency check for genrule.
+        # Currently, only propagating genrule dependencies.
+        # Also, currently, all the dependencies are propagated upwards.
+        # in gn, public_deps should be propagated but deps should not.
+        # Not sure this information is available in the desc.json.
+        # Following rule works for adding android_runtime_jni_headers to base:base.
+        # If this doesn't work for other target, hardcoding for specific target
+        # might be better.
+        if module.is_genrule() and dep_module.is_genrule():
+          if module_target.gn_type != "proto_library":
+            # proto_library are treated differently because each proto action
+            # is split into two different targets, a cpp target and a header target.
+            # the cpp target is used as the entry point to the proto action, hence
+            # it should not be propagated as a genrule header because it generates
+            # cpp files only.
+            module_target.genrule_headers.add(dep_module.name)
+          module_target.genrule_headers.update(dep_module.genrule_headers)
+
+        # For filegroups, and genrule, recurse but don't apply the
+        # deps.
+        if not module.is_compiled() or module.is_genrule():
+          continue
+
+        # Drop compiled modules that doesn't provide any benefit. This is mostly
+        # applicable to source_sets when converted to cc_static_library, sometimes
+        # the source set only has header files which are dropped so the module becomes empty.
+        # is_compiled is there to prevent dropping of genrules.
+        if dep_module.is_compiled() and not dep_module.has_input_files():
+          continue
+
+        if dep_module.type == 'cc_library_shared':
+          module_target.shared_libs.add(dep_module.name)
+        elif dep_module.type == 'cc_library_static':
+          if module.type in ['cc_library_shared', 'cc_binary']:
+            module_target.whole_static_libs.add(dep_module.name)
+          elif module.type == 'cc_library_static':
+            module_target.generated_headers.update(dep_module.generated_headers)
+            module_target.shared_libs.update(dep_module.shared_libs)
+            module_target.header_libs.update(dep_module.header_libs)
+          elif module.type in [
+              "rust_library_rlib", "rust_binary", "rust_ffi_static"
+          ]:
+            module_target.static_libs.add(dep_module.name)
+          else:
+            raise Exception(
+                f"Trying to add an unknown type {dep_module.type} to a type of {module.type}"
+            )
+        elif dep_module.type == "rust_bindgen":
+          module.srcs.add(":" + dep_module.name)
+          if module_target.type == "cc_library_static":
+            # rust_bindgen generates a .c / .cc file which has include
+            # defined from the root of the android tree.
+            module_target.include_dirs.append(".")
+            # The rust_bindgen has to know the name of the cc library which is going to
+            # consume it. We don't know that until we add the `rust_bindgen` as a dep.
+            dep_module.static_inline_library = module.name
+        elif dep_module.type == "rust_library_rlib":
+          module_target.rustlibs.add(dep_module.name)
+        elif dep_module.type == "rust_ffi_static":
+          assert module.type in [
+              "cc_library_static", "cc_library_shared"
+          ], "Only CC libraries can depend on rust_ffi_static"
+          # CPP libraries must not depend on rust_library_rlib, they must depend
+          # on rust_ffi_rlib as per aosp/3094614 and go/android-made-to-order-rust-staticlibs.
+          module_target.static_libs.add(dep_module.name)
+        elif dep_module.type == "rust_proc_macro":
+          module_target.proc_macros.add(dep_module.name)
+        elif dep_module.type == 'cc_genrule':
+          module_target.generated_headers.update(dep_module.genrule_headers)
+          module_target.srcs.update(dep_module.genrule_srcs)
+          module_target.shared_libs.update(dep_module.genrule_shared_libs)
+          module_target.header_libs.update(dep_module.genrule_header_libs)
+        elif dep_module.type in ['java_library', 'java_import']:
+          # A module depending on a module with system_current sdk version should also compile against
+          # the system sdk. This is because a module's SDK API surface should be >= its deps SDK API surface.
+          # And system_current has a larger API surface than current or module_current.
+          if dep_module.sdk_version == 'system_current':
+            module.sdk_version = 'system_current'
+          if module.type not in ["cc_library_static"]:
+            # This is needed to go around the case where `url` component depends
+            # on `url_java`.
+            # TODO(aymanm): Remove the if condition once crrev/4902547 has been imported downstream
+            module_target.static_libs.add(dep_module.name)
+        elif dep_module.type in ['genrule', 'java_genrule']:
+          module_target.srcs.add(":" + dep_module.name)
+        else:
+          raise Exception(
+              'Unsupported arch-specific dependency %s of target %s with type %s'
+              % (dep_module.name, target.name, dep_module.type))
+  return modules
 
 
 def turn_off_allocator_shim_for_musl(module):
@@ -2571,21 +2571,21 @@
   blueprint.add_module(create_cc_defaults_module())
 
   for target in targets:
-    module = create_modules_from_target(blueprint,
-                                        gn,
-                                        target,
-                                        parent_gn_type=None,
-                                        is_test_target=False)
-    if module:
+    modules = create_modules_from_target(blueprint,
+                                         gn,
+                                         target,
+                                         parent_gn_type=None,
+                                         is_test_target=False)
+    for module in modules:
       module.visibility.update(root_modules_visibility)
 
   for test_target in test_targets:
-    module = create_modules_from_target(blueprint,
-                                        gn,
-                                        test_target + gn_utils.TESTING_SUFFIX,
-                                        parent_gn_type=None,
-                                        is_test_target=True)
-    if module:
+    modules = create_modules_from_target(blueprint,
+                                         gn,
+                                         test_target + gn_utils.TESTING_SUFFIX,
+                                         parent_gn_type=None,
+                                         is_test_target=True)
+    for module in modules:
       module.visibility.update(root_modules_visibility)
 
   # Merge in additional hardcoded arguments.
diff --git a/components/facilitated_payments/core/browser/ewallet_manager.cc b/components/facilitated_payments/core/browser/ewallet_manager.cc
index e20ccf9c..1cf878c5 100644
--- a/components/facilitated_payments/core/browser/ewallet_manager.cc
+++ b/components/facilitated_payments/core/browser/ewallet_manager.cc
@@ -152,10 +152,15 @@
   initiate_payment_request_details_->instrument_id_ = selected_instrument_id;
 
   client_->LoadRiskData(base::BindOnce(&EwalletManager::OnRiskDataLoaded,
-                                       weak_ptr_factory_.GetWeakPtr()));
+                                       weak_ptr_factory_.GetWeakPtr(),
+                                       base::TimeTicks::Now()));
 }
 
-void EwalletManager::OnRiskDataLoaded(const std::string& risk_data) {
+void EwalletManager::OnRiskDataLoaded(base::TimeTicks start_time,
+                                      const std::string& risk_data) {
+  LogLoadRiskDataResultAndLatency(kPaymentsType,
+                                  /*was_successful=*/!risk_data.empty(),
+                                  base::TimeTicks::Now() - start_time, scheme_);
   if (risk_data.empty()) {
     client_->ShowErrorScreen();
     return;
diff --git a/components/facilitated_payments/core/browser/ewallet_manager.h b/components/facilitated_payments/core/browser/ewallet_manager.h
index f31593c..507fbc7 100644
--- a/components/facilitated_payments/core/browser/ewallet_manager.h
+++ b/components/facilitated_payments/core/browser/ewallet_manager.h
@@ -80,8 +80,10 @@
   void OnEwalletPaymentPromptResult(bool is_prompt_accepted,
                                     int64_t selected_instrument_id);
 
-  // Invoked after risk data is fetched. `risk_data` is the fetched risk data.
-  void OnRiskDataLoaded(const std::string& risk_data);
+  // Invoked when risk data is fetched. The call to fetch the risk data was
+  // made at `start_time`. `risk_data` is the fetched risk data.
+  void OnRiskDataLoaded(base::TimeTicks start_time,
+                        const std::string& risk_data);
 
   // Called after retrieving the client token from the facilitated payment API.
   // If not empty, the client token can be used for initiating payment.
diff --git a/components/facilitated_payments/core/browser/ewallet_manager_test_api.h b/components/facilitated_payments/core/browser/ewallet_manager_test_api.h
index 243de4e..7541ade 100644
--- a/components/facilitated_payments/core/browser/ewallet_manager_test_api.h
+++ b/components/facilitated_payments/core/browser/ewallet_manager_test_api.h
@@ -49,8 +49,9 @@
                                                    selected_instrument_id);
   }
 
-  void OnRiskDataLoaded(const std::string& risk_data) {
-    ewallet_manager_->OnRiskDataLoaded(risk_data);
+  void OnRiskDataLoaded(base::TimeTicks start_time,
+                        const std::string& risk_data) {
+    ewallet_manager_->OnRiskDataLoaded(start_time, risk_data);
   }
 
   void OnGetClientToken(std::vector<uint8_t> client_token) {
diff --git a/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc b/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc
index 9fe55547..493471d 100644
--- a/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc
@@ -313,18 +313,44 @@
 // from the facilitated payments API client.
 TEST_F(EwalletManagerTest,
        RiskDataEmpty_GetClientTokenNotCalled_ErrorScreenShown) {
+  base::HistogramTester histogram_tester;
+
   EXPECT_CALL(GetApiClient(), GetClientToken(testing::_)).Times(0);
   EXPECT_CALL(client_, ShowErrorScreen);
 
-  test_api(*ewallet_manager_).OnRiskDataLoaded(/*risk_data=*/"");
+  test_api(*ewallet_manager_)
+      .OnRiskDataLoaded(base::TimeTicks::Now() - base::Seconds(2),
+                        /*risk_data=*/"");
+
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Ewallet.LoadRiskData.Failure.Latency",
+      /*sample=*/2000,
+      /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Ewallet.LoadRiskData.Failure.Latency.ShopeePay",
+      /*sample=*/2000,
+      /*expected_bucket_count=*/1);
 }
 
 // If the risk data is not empty, then the manager retrieves a client token from
 // the facilitated payments API client.
 TEST_F(EwalletManagerTest, RiskDataNotEmpty_GetClientTokenCalled) {
+  base::HistogramTester histogram_tester;
+
   EXPECT_CALL(GetApiClient(), GetClientToken(testing::_));
 
-  test_api(*ewallet_manager_).OnRiskDataLoaded(/*risk_data=*/"fake risk data");
+  test_api(*ewallet_manager_)
+      .OnRiskDataLoaded(base::TimeTicks::Now() - base::Seconds(2),
+                        /*risk_data=*/"fake risk data");
+
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Ewallet.LoadRiskData.Success.Latency",
+      /*sample=*/2000,
+      /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Ewallet.LoadRiskData.Success.Latency.ShopeePay",
+      /*sample=*/2000,
+      /*expected_bucket_count=*/1);
 }
 
 // If the client token is empty, an error screen will be shown.
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
index ba9889b..bfc4266 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.cc
@@ -212,7 +212,8 @@
 void FacilitatedPaymentsManager::OnRiskDataLoaded(
     base::TimeTicks start_time,
     const std::string& risk_data) {
-  LogLoadRiskDataResultAndLatency(/*was_successful=*/!risk_data.empty(),
+  LogLoadRiskDataResultAndLatency(kPaymentsType,
+                                  /*was_successful=*/!risk_data.empty(),
                                   base::TimeTicks::Now() - start_time);
   if (risk_data.empty()) {
     ShowErrorScreen();
@@ -307,9 +308,12 @@
     PurchaseActionResult result) {
   switch (result) {
     case PurchaseActionResult::kCouldNotInvoke:
+      LogPayflowExitedReason(
+          PayflowExitedReason::kPurchaseActionCouldNotBeInvoked);
       ShowErrorScreen();
       break;
-    case PurchaseActionResult::kResultOk:  // Intentional fallthrough.
+    case PurchaseActionResult::kResultOk:
+      [[fallthrough]];  // Intentional fallthrough.
     case PurchaseActionResult::kResultCanceled:
       DismissPrompt();
       break;
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager.h b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
index aab5d7a1e..4e51ed3 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager.h
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager.h
@@ -72,9 +72,6 @@
                            ApiClientInitializedLazily);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            ApiClientTriggeredAfterPixCodeValidation);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      CopyTriggerHappenedBeforeDOMSearch_ApiClientIsAvailableCalledOnlyOnce);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            CopyTrigger_UrlInAllowlist_PixValidationTriggered);
   FRIEND_TEST_ALL_PREFIXES(
@@ -83,27 +80,9 @@
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest, DismissPrompt);
   FRIEND_TEST_ALL_PREFIXES(
       FacilitatedPaymentsManagerTest,
-      DOMSearchHappenedBeforeCopyTrigger_ApiClientIsAvailableCalledOnlyOnce);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      DOMSearch_CheckAllowlistResultLongDelay_UrlInAllowlist_PixCodeDetectionNotTriggered);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      DOMSearch_CheckAllowlistResultShortDelay_UrlInAllowlist_PixCodeDetectionTriggered);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      DOMSearch_CheckAllowlistResultShortDelay_UrlNotInAllowlist_PixCodeDetectionNotTriggered);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      DOMSearch_CheckAllowlistResultUnknown_PixCodeDetectionNotTriggered);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
       ErrorScreenNotAutoDismissedAfterInvokingPurchaseAction);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            HandlesFailureToLazilyInitializeApiClient);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      InvalidPixCodeDetectionResultDoesNotTriggerApiClient);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            LogApiAvailabilityCheckResultAndLatency);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
@@ -116,11 +95,6 @@
                            NoPaymentsDataManager_NoApiClientTriggered);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            NoPixAccounts_NoApiClientTriggered);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest, NoPixCode_NoUkm);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
-                           NoPixCode_PixCodeNotFoundLoggedAfterMaxAttempts);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
-                           NoPixCode_PixCodeNotFoundLoggedAfterMaxAttempts);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            NoPixPaymentPromptWhenApiClientNotAvailable);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
@@ -198,21 +172,6 @@
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest, ShowProgressScreen);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
                            ShowsPixPaymentPromptWhenApiClientAvailable);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
-                           ShowsPixPaymentPrompt_HistogramLogged);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      TriggerPixDetectionOnDomContentLoadedExpDisabled_Ukm);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
-                           TriggerPixDetectionOnDomContentLoadedExpEnabled_Ukm);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      ValidPixCodeDetectionResult_HasPixAccounts_ApiClientTriggered);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTest,
-      ValidPixCodeDetectionResult_InvalidPixCodeString_ApiClientNotTriggered);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTest,
-                           ValidPixDetectionResultToPixPaymentPromptShown);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTestForUiScreens,
                            NewScreenCouldNotBeShown);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTestForUiScreens,
@@ -225,14 +184,6 @@
                            PayflowExitedReason_LandscapeScreenOrientation);
   FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTestInLandscapeMode,
                            PixPayflowBlockedWhenFlagDisabled);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTestWhenPixCodeExists,
-      LongPageLoadDelay_PixCodeNotFoundLoggedAfterMaxAttempts);
-  FRIEND_TEST_ALL_PREFIXES(
-      FacilitatedPaymentsManagerTestWhenPixCodeExists,
-      LongPageLoadDelay_PixCodeNotFoundLoggedAfterMaxAttempts);
-  FRIEND_TEST_ALL_PREFIXES(FacilitatedPaymentsManagerTestWhenPixCodeExists,
-                           Ukm);
 
   // Register optimization guide deciders for PIX. It is an allowlist of URLs
   // where we attempt PIX code detection.
diff --git a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
index f1cfbdf..ad38a75 100644
--- a/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/facilitated_payments_manager_unittest.cc
@@ -829,9 +829,16 @@
 // shown.
 TEST_F(FacilitatedPaymentsManagerTest,
        OnPurchaseActionResult_CouldNotInvoke_ErrorScreenShown) {
+  base::HistogramTester histogram_tester;
+
   EXPECT_CALL(*client_, ShowErrorScreen);
 
   manager_->OnPurchaseActionResult(PurchaseActionResult::kCouldNotInvoke);
+
+  histogram_tester.ExpectUniqueSample(
+      "FacilitatedPayments.Pix.PayflowExitedReason",
+      /*sample=*/PayflowExitedReason::kPurchaseActionCouldNotBeInvoked,
+      /*expected_bucket_count=*/1);
 }
 
 // Test that when Chrome is successful in invoking the purchase action, the UI
diff --git a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc
index 8683b530..11153b6 100644
--- a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc
+++ b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.cc
@@ -123,12 +123,25 @@
   }
 }
 
-void LogLoadRiskDataResultAndLatency(bool was_successful,
-                                     base::TimeDelta duration) {
+void LogLoadRiskDataResultAndLatency(
+    FacilitatedPaymentsType payment_type,
+    bool was_successful,
+    base::TimeDelta duration,
+    std::optional<PaymentLinkValidator::Scheme> scheme) {
   base::UmaHistogramLongTimes(
-      base::StrCat({"FacilitatedPayments.Pix.LoadRiskData.",
-                    was_successful ? "Success" : "Failure", ".Latency"}),
+      base::StrCat({"FacilitatedPayments.", PaymentTypeToString(payment_type),
+                    ".LoadRiskData.", ResultToString(was_successful),
+                    ".Latency"}),
       duration);
+  if (payment_type == FacilitatedPaymentsType::kEwallet) {
+    CHECK(scheme.has_value());
+    CHECK_NE(PaymentLinkValidator::Scheme::kInvalid, *scheme);
+    base::UmaHistogramLongTimes(
+        base::StrCat({"FacilitatedPayments.Ewallet.LoadRiskData.",
+                      ResultToString(was_successful), ".Latency.",
+                      SchemeToString(*scheme)}),
+        duration);
+  }
 }
 
 void LogGetClientTokenResultAndLatency(bool result, base::TimeDelta duration) {
diff --git a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
index bf5b6db..91b33ef 100644
--- a/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
+++ b/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h
@@ -28,8 +28,11 @@
 // Reasons for why the payflow was exited early. These only include the reasons
 // after the renderer has detected a valid code and sent the signal to the
 // browser process.
+//
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
+//
+// LINT.IfChange(PayflowExitedReason)
 enum class PayflowExitedReason {
   // The code validator encountered an error.
   kCodeValidatorFailed = 0,
@@ -59,8 +62,11 @@
   // The FOP selector was dismissed by a user action e.g., swiping down, tapping
   // on the webpage behind the FOP selector, or tapping on the omnibox.
   kFopSelectorClosedByUser = 12,
-  kMaxValue = kFopSelectorClosedByUser
+  // Chrome attempted, but was unable to invoke purchase action.
+  kPurchaseActionCouldNotBeInvoked = 13,
+  kMaxValue = kPurchaseActionCouldNotBeInvoked
 };
+// LINT.ThenChange(/tools/metrics/histograms/metadata/facilitated_payments/enums.xml:FacilitatedPayments.PayFlowExitedReason)
 
 // Log when a Pix code is copied to the clippboard on an allowlisted merchant
 // website.
@@ -97,8 +103,14 @@
 // Logs the result and latency for fetching the risk data. If the risk data was
 // fetched successfully, `was_successful` is true. The call took `duration` to
 // complete.
-void LogLoadRiskDataResultAndLatency(bool was_successful,
-                                     base::TimeDelta duration);
+// `payment_type` must be either `kEwallet` or `kPix`.
+// The `scheme` parameter is required for the 'kEwallet' payment type and should
+// not be `kInvalid`.
+void LogLoadRiskDataResultAndLatency(
+    FacilitatedPaymentsType payment_type,
+    bool was_successful,
+    base::TimeDelta duration,
+    std::optional<PaymentLinkValidator::Scheme> scheme = std::nullopt);
 
 // Log the result and the latency of the GetClientToken call made to api client.
 void LogGetClientTokenResultAndLatency(bool result, base::TimeDelta duration);
diff --git a/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc b/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc
index b2309b3..41450821 100644
--- a/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc
+++ b/components/facilitated_payments/core/metrics/facilitated_payments_metrics_unittest.cc
@@ -77,18 +77,6 @@
       /*expected_bucket_count=*/1);
 }
 
-TEST(FacilitatedPaymentsMetricsTest, LogLoadRiskDataResult) {
-  base::HistogramTester histogram_tester;
-
-  LogLoadRiskDataResultAndLatency(/*was_successful=*/true,
-                                  base::Milliseconds(10));
-
-  histogram_tester.ExpectUniqueSample(
-      "FacilitatedPayments.Pix.LoadRiskData.Success.Latency",
-      /*sample=*/10,
-      /*expected_bucket_count=*/1);
-}
-
 TEST(FacilitatedPaymentsMetricsTest, LogGetClientTokenResultAndLatency) {
   base::HistogramTester histogram_tester;
 
@@ -205,7 +193,8 @@
                     PayflowExitedReason::kActionTokenNotAvailable,
                     PayflowExitedReason::kUserLoggedOut,
                     PayflowExitedReason::kFopSelectorClosedNotByUser,
-                    PayflowExitedReason::kFopSelectorClosedByUser));
+                    PayflowExitedReason::kFopSelectorClosedByUser,
+                    PayflowExitedReason::kPurchaseActionCouldNotBeInvoked));
 
 class FacilitatedPaymentsMetricsUkmTest : public testing::Test {
  public:
@@ -359,6 +348,50 @@
           : 0);
 }
 
+TEST_P(FacilitatedPaymentsMetricsParameterizedTest,
+       LogLoadRiskDataResult_Success) {
+  base::HistogramTester histogram_tester;
+
+  LogLoadRiskDataResultAndLatency(payment_type(), /*was_successful=*/true,
+                                  base::Milliseconds(10), scheme());
+
+  histogram_tester.ExpectUniqueSample(
+      base::StrCat({"FacilitatedPayments.", GetFacilitatedPaymentsTypeString(),
+                    ".LoadRiskData.Success.Latency"}),
+      /*sample=*/10,
+      /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      base::StrCat({"FacilitatedPayments.Ewallet.LoadRiskData.Success.Latency.",
+                    GetSchemeString()}),
+      /*sample=*/10,
+      /*expected_bucket_count=*/payment_type() ==
+              FacilitatedPaymentsType::kEwallet
+          ? 1
+          : 0);
+}
+
+TEST_P(FacilitatedPaymentsMetricsParameterizedTest,
+       LogLoadRiskDataResult_Failure) {
+  base::HistogramTester histogram_tester;
+
+  LogLoadRiskDataResultAndLatency(payment_type(), /*was_successful=*/false,
+                                  base::Milliseconds(10), scheme());
+
+  histogram_tester.ExpectUniqueSample(
+      base::StrCat({"FacilitatedPayments.", GetFacilitatedPaymentsTypeString(),
+                    ".LoadRiskData.Failure.Latency"}),
+      /*sample=*/10,
+      /*expected_bucket_count=*/1);
+  histogram_tester.ExpectUniqueSample(
+      base::StrCat({"FacilitatedPayments.Ewallet.LoadRiskData.Failure.Latency.",
+                    GetSchemeString()}),
+      /*sample=*/10,
+      /*expected_bucket_count=*/payment_type() ==
+              FacilitatedPaymentsType::kEwallet
+          ? 1
+          : 0);
+}
+
 class FacilitatedPaymentsMetricsTestForUiScreens
     : public testing::TestWithParam<UiState> {
  public:
diff --git a/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc b/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
index 32a2af96fd..98062f9 100644
--- a/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
+++ b/components/page_load_metrics/browser/page_load_metrics_update_dispatcher.cc
@@ -459,9 +459,11 @@
     UpdateSoftNavigation(std::move(*soft_navigation_metrics));
   } else {
     if (!render_frame_host->GetParentOrOuterDocument()) {
-      // TODO(crbug.com/40065854): `client_->IsPageMainFrame()` didn't return
-      // the correct status.
-      base::debug::DumpWithoutCrashing();
+      // TODO(crbug.com/40065854): This can be removed once
+      // PageLoadMetricsUpdateDispatcher::IsPageMainFrame() is made consistent
+      // with the main-frame status reported by the RenderFrameHost.
+      LOG(ERROR) << "IsPageMainFrame() did not correctly identify the "
+                    "RenderFrameHost as a main frame.";
       return;
     }
 
diff --git a/components/password_manager/core/browser/export/BUILD.gn b/components/password_manager/core/browser/export/BUILD.gn
index 1716e75..327c0a4 100644
--- a/components/password_manager/core/browser/export/BUILD.gn
+++ b/components/password_manager/core/browser/export/BUILD.gn
@@ -27,6 +27,8 @@
     sources += [
       "login_db_deprecation_password_exporter.cc",
       "login_db_deprecation_password_exporter.h",
+      "login_db_deprecation_runner.cc",
+      "login_db_deprecation_runner.h",
     ]
     deps += [ "//components/password_manager/core/browser/password_store:password_store_interface" ]
   }
diff --git a/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.cc b/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.cc
index ac31b7d..916b536 100644
--- a/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.cc
+++ b/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.cc
@@ -13,7 +13,6 @@
 
 namespace {
 constexpr std::string_view kExportedPasswordsFileName = "ChromePasswords.csv";
-
 }  // namespace
 
 LoginDbDeprecationPasswordExporter::LoginDbDeprecationPasswordExporter(
@@ -23,7 +22,9 @@
     default;
 
 void LoginDbDeprecationPasswordExporter::Start(
-    PasswordStoreInterface* password_store) {
+    scoped_refptr<PasswordStoreInterface> password_store,
+    base::OnceClosure export_cleanup_calback) {
+  export_cleanup_callback_ = std::move(export_cleanup_calback);
   password_store->GetAutofillableLogins(weak_factory_.GetWeakPtr());
 }
 
@@ -36,6 +37,7 @@
     PasswordStoreInterface* store,
     LoginsResultOrError logins_or_error) {
   if (absl::holds_alternative<PasswordStoreBackendError>(logins_or_error)) {
+    OnExportComplete();
     return;
   }
 
@@ -47,15 +49,24 @@
     passwords_.emplace_back(password_form);
   }
   if (passwords_.empty()) {
+    OnExportComplete();
     return;
   }
 
-  // TODO(crbug.com/378650395): Pass the callbacks into the constructor.
-  exporter_ = std::make_unique<PasswordManagerExporter>(this, base::DoNothing(),
-                                                        base::DoNothing());
+  // TODO(crbug.com/378650395): Pass the all the callbacks in the constructor.
+  exporter_ = std::make_unique<PasswordManagerExporter>(
+      this, base::DoNothing(),
+      base::BindOnce(&LoginDbDeprecationPasswordExporter::OnExportComplete,
+                     weak_factory_.GetWeakPtr()));
   exporter_->PreparePasswordsForExport();
   exporter_->SetDestination(
       export_dir_path_.Append(FILE_PATH_LITERAL(kExportedPasswordsFileName)));
 }
 
+void LoginDbDeprecationPasswordExporter::OnExportComplete() {
+  // TODO(crbug.com/378650395): Handle success/failure.
+  std::move(export_cleanup_callback_).Run();
+  // The callback above destroys `this`.
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.h b/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.h
index f19b3c7..9ac3e1c 100644
--- a/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.h
+++ b/components/password_manager/core/browser/export/login_db_deprecation_password_exporter.h
@@ -27,7 +27,8 @@
       const LoginDbDeprecationPasswordExporter&) = delete;
   ~LoginDbDeprecationPasswordExporter() override;
 
-  void Start(PasswordStoreInterface* password_store);
+  void Start(scoped_refptr<PasswordStoreInterface> password_store,
+             base::OnceClosure export_cleanup_calback);
 
   // Allows the `PasswordManagerExporter` to retrieve the saved credentials
   // after `this` receives them. Not a necessary pattern for this use-case
@@ -40,6 +41,14 @@
       PasswordStoreInterface* store,
       LoginsResultOrError results_or_error) override;
 
+  // Called when the `exporter_` completes all the export operations,
+  // irrespective of whether the export succeeded.
+  void OnExportComplete();
+
+  // Callback to invoke when ALL the export operations finished. It will clean
+  // up `this`.
+  base::OnceClosure export_cleanup_callback_;
+
   // Serializes the passwords and writes them to a CSV file.
   std::unique_ptr<PasswordManagerExporter> exporter_;
 
diff --git a/components/password_manager/core/browser/export/login_db_deprecation_password_exporter_unittest.cc b/components/password_manager/core/browser/export/login_db_deprecation_password_exporter_unittest.cc
index 0638702..63c7abd 100644
--- a/components/password_manager/core/browser/export/login_db_deprecation_password_exporter_unittest.cc
+++ b/components/password_manager/core/browser/export/login_db_deprecation_password_exporter_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/test/bind.h"
+#include "base/test/mock_callback.h"
 #include "base/test/run_until.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
@@ -24,8 +25,10 @@
 namespace password_manager {
 
 namespace {
+
 const std::string kLineEnding = "\n";
-}
+
+}  // namespace
 
 class LoginDbDeprecationPasswordExporterTest : public testing::Test {
  public:
@@ -48,7 +51,7 @@
   const base::FilePath& export_dir() { return temp_dir_.GetPath(); }
 
   LoginDbDeprecationPasswordExporter* exporter() { return exporter_.get(); }
-  PasswordStoreInterface* password_store() { return password_store_.get(); }
+  scoped_refptr<TestPasswordStore> password_store() { return password_store_; }
 
  protected:
   base::test::TaskEnvironment task_env_;
@@ -62,9 +65,8 @@
 
 TEST_F(LoginDbDeprecationPasswordExporterTest, DoesntExportIfNoPasswords) {
   // No passwords in the backend.
-  exporter()->Start(password_store());
-
-  task_env_.RunUntilIdle();
+  exporter()->Start(password_store(), task_env_.QuitClosure());
+  task_env_.RunUntilQuit();
 
   base::FilePath expected_file_path =
       export_dir().Append(FILE_PATH_LITERAL("ChromePasswords.csv"));
@@ -81,17 +83,15 @@
   form.password_value = u"Secret";
   form.notes = {PasswordNote(kNoteValue, base::Time::Now())};
 
-  base::test::TestFuture<PasswordChangesOrError> future;
   password_store()->AddLogin(form, task_env_.QuitClosure());
   task_env_.RunUntilQuit();
 
-  exporter()->Start(password_store());
+  exporter()->Start(password_store(), task_env_.QuitClosure());
+  task_env_.RunUntilQuit();
+
   base::FilePath expected_file_path =
       export_dir().Append(FILE_PATH_LITERAL("ChromePasswords.csv"));
-  ASSERT_TRUE(base::test::RunUntil([&expected_file_path]() {
-    return base::PathExists(expected_file_path);
-  }));
-
+  EXPECT_TRUE(base::PathExists(expected_file_path));
   std::string contents;
   const std::string kExpectedContents =
       "name,url,username,password,note" + kLineEnding +
diff --git a/components/password_manager/core/browser/export/login_db_deprecation_runner.cc b/components/password_manager/core/browser/export/login_db_deprecation_runner.cc
new file mode 100644
index 0000000..17e4764
--- /dev/null
+++ b/components/password_manager/core/browser/export/login_db_deprecation_runner.cc
@@ -0,0 +1,31 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/export/login_db_deprecation_runner.h"
+
+#include "base/memory/scoped_refptr.h"
+
+namespace password_manager {
+
+LoginDbDeprecationRunner::LoginDbDeprecationRunner(base::FilePath export_dir)
+    : exporter_(
+          std::make_unique<LoginDbDeprecationPasswordExporter>(export_dir)) {}
+
+LoginDbDeprecationRunner::~LoginDbDeprecationRunner() = default;
+
+void LoginDbDeprecationRunner::StartExport(
+    scoped_refptr<PasswordStoreInterface> password_store) {
+  // TODO(crbug.com/378650395): Delay the export by a configurable amount
+  // of time.
+  CHECK(exporter_);
+  exporter_->Start(password_store,
+                   base::BindOnce(&LoginDbDeprecationRunner::ExportFinished,
+                                  weak_ptr_factory_.GetWeakPtr()));
+}
+
+void LoginDbDeprecationRunner::ExportFinished() {
+  exporter_.reset();
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/export/login_db_deprecation_runner.h b/components/password_manager/core/browser/export/login_db_deprecation_runner.h
new file mode 100644
index 0000000..4b1398af
--- /dev/null
+++ b/components/password_manager/core/browser/export/login_db_deprecation_runner.h
@@ -0,0 +1,37 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_EXPORT_LOGIN_DB_DEPRECATION_RUNNER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_EXPORT_LOGIN_DB_DEPRECATION_RUNNER_H_
+
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/password_manager/core/browser/export/login_db_deprecation_password_exporter.h"
+#include "components/password_manager/core/browser/password_store/password_store_interface.h"
+
+namespace password_manager {
+
+// Owns and runs the pre-deprecation password export on Android.
+// Once the export is done it will destroy the exporter and clear the memory.
+// The service will only be instantiated for clients whose passwords couldn't
+// be migrated to GMS Core.
+class LoginDbDeprecationRunner : public KeyedService {
+ public:
+  explicit LoginDbDeprecationRunner(base::FilePath export_dir);
+  LoginDbDeprecationRunner(const LoginDbDeprecationRunner&) = delete;
+  LoginDbDeprecationRunner& operator=(const LoginDbDeprecationRunner&) = delete;
+  ~LoginDbDeprecationRunner() override;
+
+  void StartExport(scoped_refptr<PasswordStoreInterface> password_store);
+
+ private:
+  void ExportFinished();
+
+  std::unique_ptr<LoginDbDeprecationPasswordExporter> exporter_;
+
+  base::WeakPtrFactory<LoginDbDeprecationRunner> weak_ptr_factory_{this};
+};
+
+}  // namespace password_manager
+
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_EXPORT_LOGIN_DB_DEPRECATION_RUNNER_H_
diff --git a/components/permissions_strings.grdp b/components/permissions_strings.grdp
index a0cc4a3..4e5b801 100644
--- a/components/permissions_strings.grdp
+++ b/components/permissions_strings.grdp
@@ -517,6 +517,9 @@
   <message name="IDS_WINDOWS_NAME_FRAGMENT" desc="Text fragment identifying the Windows">
     Windows
   </message>
+  <message name="IDS_CHROMEOS_NAME_FRAGMENT" desc="Text fragment identifying the ChromeOS">
+    ChromeOS
+  </message>
   <!--Embedded permission prompts: button labels-->
   <message name="IDS_EMBEDDED_PROMPT_OK_LABEL" desc="Label used on a button that allows the user to acknowledge and close an informational dialog">
     OK
diff --git a/components/permissions_strings_grdp/IDS_CHROMEOS_NAME_FRAGMENT.png.sha1 b/components/permissions_strings_grdp/IDS_CHROMEOS_NAME_FRAGMENT.png.sha1
new file mode 100644
index 0000000..2411ead
--- /dev/null
+++ b/components/permissions_strings_grdp/IDS_CHROMEOS_NAME_FRAGMENT.png.sha1
@@ -0,0 +1 @@
+1e1b4666da1ec3de0baa577626e54472c4c25467
\ No newline at end of file
diff --git a/components/policy/core/common/management/management_service.cc b/components/policy/core/common/management/management_service.cc
index 824e285..7954778 100644
--- a/components/policy/core/common/management/management_service.cc
+++ b/components/policy/core/common/management/management_service.cc
@@ -112,7 +112,7 @@
   }
 }
 
-ui::ImageModel* ManagementService::GetManagementIcon() {
+ui::ImageModel* ManagementService::GetManagementIconForProfile() {
   return nullptr;
 }
 
diff --git a/components/policy/core/common/management/management_service.h b/components/policy/core/common/management/management_service.h
index c5701110..12cb4e1 100644
--- a/components/policy/core/common/management/management_service.h
+++ b/components/policy/core/common/management/management_service.h
@@ -111,7 +111,7 @@
   // until `callback` is called.
   virtual void RefreshCache(CacheRefreshCallback callback);
 
-  virtual ui::ImageModel* GetManagementIcon();
+  virtual ui::ImageModel* GetManagementIconForProfile();
 
   // Returns true if `authority` is are actively managed.
   bool HasManagementAuthority(EnterpriseManagementAuthority authority);
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index edfb84d..9648fcf 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -1114,6 +1114,7 @@
     SESSION_KIOSK = 3;
     SESSION_ARC_KIOSK = 4;
     SESSION_WEB_KIOSK = 5;
+    SESSION_IWA_KIOSK = 6;
   }
 
   optional SessionType session_type = 4;
diff --git a/components/policy/resources/templates/policy_definitions/DefaultSearchProvider/DefaultSearchProviderEnabled.yaml b/components/policy/resources/templates/policy_definitions/DefaultSearchProvider/DefaultSearchProviderEnabled.yaml
index 0fd2fff..fcb99848 100644
--- a/components/policy/resources/templates/policy_definitions/DefaultSearchProvider/DefaultSearchProviderEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/DefaultSearchProvider/DefaultSearchProviderEnabled.yaml
@@ -5,9 +5,9 @@
 
   If you set the policy, users can't change it in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>. If not set, the default search provider is on, and users can set the search provider list.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: true
 features:
   can_be_recommended: true
diff --git a/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml b/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml
index aaf96b0..9530639 100644
--- a/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml
+++ b/components/policy/resources/templates/policy_definitions/Extensions/ExtensionInstallForcelist.yaml
@@ -14,9 +14,9 @@
 
   Each list item of the policy is a string that contains an extension ID and, optionally, an update URL separated by a semicolon (;). The extension ID is the 32-letter string found, for example, on chrome://extensions when in Developer mode. If specified, the update URL should point to an Update Manifest XML document ( https://developer.chrome.com/extensions/autoupdate ). The update URL should use one of the following schemes: <ph name="HTTP_SCHEME">http</ph>, <ph name="HTTPS_SCHEME">https</ph> or <ph name="FILE_SCHEME">file</ph>. By default, the Chrome Web Store's update URL is used. The update URL set in this policy is only used for the initial installation; subsequent updates of the extension use the update URL in the extension's manifest. The update url for subsequent updates can be overridden using the <ph name="EXTENSION_SETTINGS_POLICY_NAME">ExtensionSettings</ph> policy, see http://support.google.com/chrome/a?p=Configure_ExtensionSettings_policy.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph> instances, apps and extensions from outside the Chrome Web Store can only be forced installed if the instance is joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph> instances, apps and extensions from outside the Chrome Web Store can only be forced installed if the instance is joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph> instances, apps and extensions from outside the Chrome Web Store can only be force installed if the instance is managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph> instances, apps and extensions from outside the Chrome Web Store can only be force installed if the instance is managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
   Note: This policy doesn't apply to Incognito mode. Read about hosting extensions ( https://developer.chrome.com/extensions/hosting ).
 example_value:
diff --git a/components/policy/resources/templates/policy_definitions/Extensions/ExtensionSettings.yaml b/components/policy/resources/templates/policy_definitions/Extensions/ExtensionSettings.yaml
index 3dd3ec1..56762b7 100644
--- a/components/policy/resources/templates/policy_definitions/Extensions/ExtensionSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/Extensions/ExtensionSettings.yaml
@@ -4,9 +4,9 @@
 
   This policy maps an extension ID or an update URL to its specific setting only. A default configuration can be set for the special ID <ph name="DEFAULT_SCOPE">"*"</ph>, which applies to all extensions without a custom configuration in this policy. With an update URL, configuration applies to extensions with the exact update URL stated in the extension manifest ( http://support.google.com/chrome/a?p=Configure_ExtensionSettings_policy ). If the 'override_update_url' flag is set to true, the extension is installed and updated using the "update" URL specified in the <ph name="EXTENSION_INSTALL_FORCELIST_POLICY_NAME">ExtensionInstallForcelist</ph> policy or in 'update_url' field in this policy. The flag 'override_update_url' is ignored if the 'update_url' is a Chrome Web Store url.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph> instances, apps and extensions from outside the Chrome Web Store can only be forced installed if the instance is joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph> instances, apps and extensions from outside the Chrome Web Store can only be forced installed if the instance is joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph> instances, apps and extensions from outside the Chrome Web Store can only be force installed if the instance is managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph> instances, apps and extensions from outside the Chrome Web Store can only be force installed if the instance is managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value:
   '*':
     allowed_types:
diff --git a/components/policy/resources/templates/policy_definitions/FirstPartySets/FirstPartySetsOverrides.yaml b/components/policy/resources/templates/policy_definitions/FirstPartySets/FirstPartySetsOverrides.yaml
index 5bd54b2..743bd48 100644
--- a/components/policy/resources/templates/policy_definitions/FirstPartySets/FirstPartySetsOverrides.yaml
+++ b/components/policy/resources/templates/policy_definitions/FirstPartySets/FirstPartySetsOverrides.yaml
@@ -33,9 +33,9 @@
   All sets provided by the policy must be valid First-Party Sets, if they aren't then an
   appropriate error will be outputted.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
   This is the equivalent of the <ph name="RELATED_WEBSITE_SETS_OVERRIDES_POLICY_NAME">RelatedWebsiteSetsOverrides</ph> policy.
   Either policy may be used, but this one will be deprecated soon so the <ph name="RELATED_WEBSITE_SETS_OVERRIDES_POLICY_NAME">RelatedWebsiteSetsOverrides</ph> policy is preferred.
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/AutoOpenFileTypes.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/AutoOpenFileTypes.yaml
index 26db68a..0d4b4c3 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/AutoOpenFileTypes.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/AutoOpenFileTypes.yaml
@@ -6,7 +6,7 @@
 
   If this policy isn't set, only file types that a user has already specified to automatically be opened will do so when downloaded.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value:
 - exe
 - txt
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupEnabled.yaml
index 0437305..7f3a51a4 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupEnabled.yaml
@@ -6,7 +6,7 @@
 
   Setting the policy to Disabled means Chrome Cleanup won't periodically scan and manual triggering is disabled.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: true
 features:
   dynamic_refresh: false
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupReportingEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupReportingEnabled.yaml
index 24c979e2..9a45833 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupReportingEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/ChromeCleanupReportingEnabled.yaml
@@ -8,7 +8,7 @@
 
   Leaving the policy unset means <ph name="CHROME_CLEANUP_NAME">Chrome Cleanup</ph> may, in line with policy set by <ph name="SAFE_BROWSING_EXTENDED_REPORTING_ENABLED_POLICY_NAME">SafeBrowsingExtendedReportingEnabled</ph>, report about scans for detecting unwanted software to Google. <ph name="CHROME_CLEANUP_NAME">Chrome Cleanup</ph> asks users if they want the cleanup and to share the results with Google to help with future unwanted software detection. These results have file metadata, automatically installed extensions, and registry keys, as described by the Chrome Privacy Whitepaper.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: true
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/CommandLineFlagSecurityWarningsEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/CommandLineFlagSecurityWarningsEnabled.yaml
index 4bd2e6b7..85af401 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/CommandLineFlagSecurityWarningsEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/CommandLineFlagSecurityWarningsEnabled.yaml
@@ -5,9 +5,9 @@
 
   Setting the policy to Disabled prevents security warnings from appearing when Chrome is launched with potentially dangerous command-line flags.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: true
 features:
   dynamic_refresh: false
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/EnterpriseSearchAggregatorSettings.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/EnterpriseSearchAggregatorSettings.yaml
index e06b32fa..fb746f7 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/EnterpriseSearchAggregatorSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/EnterpriseSearchAggregatorSettings.yaml
@@ -14,9 +14,9 @@
 
   The <ph name="ICON_URL_SEARCH_AGGREGATOR_SETTINGS_FIELD">icon_url</ph> field specifies the URL to an image that will be used on the search suggestions. A default icon will be used when this field is not set. It's recommended to use a favicon (example <ph name="ICON_URL_EXAMPLE">https://www.google.com/favicon.ico</ph>).
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
 example_value:
   name: My Search Aggregator
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/MetricsReportingEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/MetricsReportingEnabled.yaml
index 3aa9171..e854605 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/MetricsReportingEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/MetricsReportingEnabled.yaml
@@ -7,9 +7,9 @@
 
   When this policy is not set, users can choose the anonymous reporting behavior at installation or first run, and can change this setting later.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
   (For <ph name="PRODUCT_OS_NAME">$2<ex>Google ChromeOS</ex></ph>, see <ph name="DEVICE_METRICS_REPORTING_ENABLED_POLICY_NAME">DeviceMetricsReportingEnabled</ph>.)
 example_value: true
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingForTrustedSourcesEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingForTrustedSourcesEnabled.yaml
index 6db8b12c..7c00d88 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingForTrustedSourcesEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingForTrustedSourcesEnabled.yaml
@@ -7,7 +7,7 @@
 
   These restrictions apply to downloads triggered from webpage content, as well as the Download link menu option. These restrictions don't apply to the save or download of the currently displayed page or to saving as PDF from the printing options.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: false
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingWhitelistDomains.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingWhitelistDomains.yaml
index e057c15f..6d34677 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingWhitelistDomains.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/SafeBrowsingWhitelistDomains.yaml
@@ -7,7 +7,7 @@
 
         Setting the policy to Disabled or leaving it unset means default Safe Browsing protection applies to all resources.
 
-         On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this functionality is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, running on Windows 10 Pro, or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>. On <ph name="MAC_OS_NAME">macOS</ph>, this functionality is only available on instances that are managed via MDM, or joined to a domain via MCX.
+         On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this functionality is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, running on Windows 10 Pro, or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>. On <ph name="MAC_OS_NAME">macOS</ph>, this functionality is only available on instances that are managed via MDM, or joined to a domain via MCX.
 example_value:
 - mydomain.com
 - myuniversity.edu
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/SiteSearchSettings.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/SiteSearchSettings.yaml
index d224977..239f10a0ef 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/SiteSearchSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/SiteSearchSettings.yaml
@@ -16,9 +16,9 @@
 
   In case of a conflict with a shortcut previously created by the user, the user setting takes precedence. However, users can still trigger the option created by the policy by typing "@" in the search bar. For example, if the user already defined "work" as a shortcut to URL1 and the policy defines "work" as a shortcut to URL2, then typing "work" in the search bar will trigger a search to URL1, but typing "@work" in the search bar will trigger a search to URL2.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
 example_value:
 - featured: true
diff --git a/components/policy/resources/templates/policy_definitions/RelatedWebsiteSets/RelatedWebsiteSetsOverrides.yaml b/components/policy/resources/templates/policy_definitions/RelatedWebsiteSets/RelatedWebsiteSetsOverrides.yaml
index 4469c2b1..82bcfce5 100644
--- a/components/policy/resources/templates/policy_definitions/RelatedWebsiteSets/RelatedWebsiteSetsOverrides.yaml
+++ b/components/policy/resources/templates/policy_definitions/RelatedWebsiteSets/RelatedWebsiteSetsOverrides.yaml
@@ -35,9 +35,9 @@
   All sets provided by the policy must be valid Related Website Sets, if they aren't then an
   appropriate error will be outputted.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value:
   additions:
   - associatedSites:
diff --git a/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionChangePasswordURL.yaml b/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionChangePasswordURL.yaml
index 615d737..77debe61 100644
--- a/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionChangePasswordURL.yaml
+++ b/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionChangePasswordURL.yaml
@@ -4,9 +4,9 @@
 
   Turning the policy off or leaving it unset means the service sends users to https://myaccount.google.com to change their password.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: https://mydomain.com/change_password.html
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionLoginURLs.yaml b/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionLoginURLs.yaml
index fb806f39..072dfdd 100644
--- a/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionLoginURLs.yaml
+++ b/components/policy/resources/templates/policy_definitions/SafeBrowsing/PasswordProtectionLoginURLs.yaml
@@ -5,9 +5,9 @@
 
   Turning this setting off or leaving it unset means the password protection service only captures the password salted hashes on https://accounts.google.com.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value:
 - https://mydomain.com/login.html
 - https://login.mydomain.com
diff --git a/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingAllowlistDomains.yaml b/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingAllowlistDomains.yaml
index ec102d1..84ce3d850 100644
--- a/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingAllowlistDomains.yaml
+++ b/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingAllowlistDomains.yaml
@@ -6,9 +6,9 @@
 
   This policy does not support regular expressions; however, subdomains of a given domain are allowlisted. Fully qualified domain names (FQDNs) are not required.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value:
 - mydomain.com
 - myuniversity.edu
diff --git a/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingEnabled.yaml b/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingEnabled.yaml
index c21bca3..82923200 100644
--- a/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/SafeBrowsing/SafeBrowsingEnabled.yaml
@@ -11,9 +11,9 @@
 
   If the policy <ph name="SAFE_BROWSING_PROTECTION_LEVEL_POLICY_NAME">SafeBrowsingProtectionLevel</ph> is set, the value of the policy <ph name="SAFE_BROWSING_ENABLED_POLICY_NAME">SafeBrowsingEnabled</ph> is ignored.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: true
 features:
   can_be_recommended: true
diff --git a/components/policy/resources/templates/policy_definitions/Startup/HomepageIsNewTabPage.yaml b/components/policy/resources/templates/policy_definitions/Startup/HomepageIsNewTabPage.yaml
index 648ba2f..7e64ce2 100644
--- a/components/policy/resources/templates/policy_definitions/Startup/HomepageIsNewTabPage.yaml
+++ b/components/policy/resources/templates/policy_definitions/Startup/HomepageIsNewTabPage.yaml
@@ -5,9 +5,9 @@
 
   If you set the policy, users can't change their homepage type in <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph>. If not set, the user decides whether or not the New Tab page is their homepage.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: true
 features:
   can_be_recommended: true
diff --git a/components/policy/resources/templates/policy_definitions/Startup/HomepageLocation.yaml b/components/policy/resources/templates/policy_definitions/Startup/HomepageLocation.yaml
index 6c2508b..ec5ecb94 100644
--- a/components/policy/resources/templates/policy_definitions/Startup/HomepageLocation.yaml
+++ b/components/policy/resources/templates/policy_definitions/Startup/HomepageLocation.yaml
@@ -8,9 +8,9 @@
 
   Leaving both <ph name="HOMEPAGE_LOCATION_POLICY_NAME">HomepageLocation</ph> and <ph name="HOMEPAGE_IS_NEW_TAB_PAGE_POLICY_NAME">HomepageIsNewTabPage</ph> unset lets users choose their homepage.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: https://www.chromium.org
 features:
   can_be_recommended: true
diff --git a/components/policy/resources/templates/policy_definitions/Startup/NewTabPageLocation.yaml b/components/policy/resources/templates/policy_definitions/Startup/NewTabPageLocation.yaml
index 54ed8241..87f49a99 100644
--- a/components/policy/resources/templates/policy_definitions/Startup/NewTabPageLocation.yaml
+++ b/components/policy/resources/templates/policy_definitions/Startup/NewTabPageLocation.yaml
@@ -10,9 +10,9 @@
 
   Leaving the policy unset or empty puts the default New Tab page in use.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: https://www.chromium.org
 features:
   dynamic_refresh: true
diff --git a/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartup.yaml b/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartup.yaml
index 6f806bba..997712f5 100644
--- a/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartup.yaml
+++ b/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartup.yaml
@@ -8,9 +8,9 @@
 
   If this policy is set to <ph name="POLICY_ENUM_RESTOREONSTARTUP_RESTOREONSTARTUPISLASTSESSIONANDURLS">RestoreOnStartupIsLastSessionAndURLs</ph>, browser will restore previous session and open a separate window to show URLs that are set from <ph name="RESTORE_ON_STARTUP_URLS_POLICY_NAME">RestoreOnStartupURLs</ph>. Note that users can choose to keep those URLs open and they will also be restored in the future session.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 
-  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MAC_OS_NAME">macOS</ph>, this policy is only available on instances that are managed via MDM, joined to a domain via MCX or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value: 4
 features:
   can_be_recommended: true
diff --git a/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartupURLs.yaml b/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartupURLs.yaml
index d490421..fdd2d56 100644
--- a/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartupURLs.yaml
+++ b/components/policy/resources/templates/policy_definitions/Startup/RestoreOnStartupURLs.yaml
@@ -4,7 +4,7 @@
 
   If not set, the New Tab page opens on start up.
 
-  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_BROWSER_CLOUD_MANAGEMENT_NAME">Chrome Browser Cloud Management</ph>.
+  On <ph name="MS_WIN_NAME">Microsoft® Windows®</ph>, this policy is only available on instances that are joined to a <ph name="MS_AD_NAME">Microsoft® Active Directory®</ph> domain, joined to <ph name="MS_AAD_NAME">Microsoft® Azure® Active Directory®</ph> or enrolled in <ph name="CHROME_ENTERPRISE_CORE_NAME">Chrome Enterprise Core</ph>.
 example_value:
 - https://example.com
 - https://www.chromium.org
diff --git a/components/policy/test/data/pref_mapping/EnterpriseCustomLabel.json b/components/policy/test/data/pref_mapping/EnterpriseCustomLabel.json
index 4820de7..6af8d9b 100644
--- a/components/policy/test/data/pref_mapping/EnterpriseCustomLabel.json
+++ b/components/policy/test/data/pref_mapping/EnterpriseCustomLabel.json
@@ -5,16 +5,21 @@
       "linux",
       "mac"
     ],
-    "simple_policy_pref_mapping_test": {
-      "pref_name": "enterprise_label.custom_value",
-      "default_value": "",
-      "values_to_test": [
-        "Custom label",
-        "Another label"
-      ]
-    },
     "policy_pref_mapping_tests": [
       {
+        "policies": {},
+        "prefs": {
+          "enterprise_label.custom_value.for_browser": {
+            "location": "local_state",
+            "default_value": ""
+          },
+          "enterprise_label.custom_value.for_profile": {
+            "location": "user_profile",
+            "default_value": ""
+          }
+        }
+      },
+      {
         "policies": {
           "EnterpriseCustomLabel": "Label"
         },
@@ -25,7 +30,7 @@
           }
         },
         "prefs": {
-          "enterprise_label.custom_value": {
+          "enterprise_label.custom_value.for_browser": {
             "location": "local_state",
             "value": "Label"
           },
@@ -46,10 +51,6 @@
           }
         },
         "prefs": {
-          "enterprise_label.custom_value": {
-            "location": "user_profile",
-            "value": "Label"
-          },
           "enterprise_label.custom_value.for_profile": {
             "location": "user_profile",
             "value": "Label"
diff --git a/components/policy/test/data/pref_mapping/EnterpriseLogoUrl.json b/components/policy/test/data/pref_mapping/EnterpriseLogoUrl.json
index b08d1cc..74b791f 100644
--- a/components/policy/test/data/pref_mapping/EnterpriseLogoUrl.json
+++ b/components/policy/test/data/pref_mapping/EnterpriseLogoUrl.json
@@ -9,18 +9,13 @@
       {
         "policies": {},
         "prefs": {
-          "enterprise_logo.url": {
+          "enterprise_logo.url.for_browser": {
+            "location": "local_state",
             "default_value": ""
-          }
-        }
-      },
-      {
-        "policies": {
-          "EnterpriseLogoUrl": "http://www.example.com"
-        },
-        "prefs": {
-          "enterprise_logo.url": {
-            "value": "http://www.example.com"
+          },
+          "enterprise_logo.url.for_profile": {
+            "location": "user_profile",
+            "default_value": ""
           }
         }
       },
@@ -29,7 +24,12 @@
           "EnterpriseLogoUrl": "not_a_url"
         },
         "prefs": {
-          "enterprise_logo.url": {
+          "enterprise_logo.url.for_browser": {
+            "location": "local_state",
+            "default_value": ""
+          },
+          "enterprise_logo.url.for_profile": {
+            "location": "user_profile",
             "default_value": ""
           }
         }
@@ -45,7 +45,7 @@
           }
         },
         "prefs": {
-          "enterprise_logo.url": {
+          "enterprise_logo.url.for_browser": {
             "location": "local_state",
             "value": "http://www.example.com"
           },
@@ -66,10 +66,6 @@
           }
         },
         "prefs": {
-          "enterprise_logo.url": {
-            "location": "user_profile",
-            "value": "http://www.example.com"
-          },
           "enterprise_logo.url.for_profile": {
             "location": "user_profile",
             "value": "http://www.example.com"
diff --git a/components/safe_browsing/core/browser/ping_manager.cc b/components/safe_browsing/core/browser/ping_manager.cc
index 76704874..7a5059c3 100644
--- a/components/safe_browsing/core/browser/ping_manager.cc
+++ b/components/safe_browsing/core/browser/ping_manager.cc
@@ -250,8 +250,21 @@
   auto it = safebrowsing_reports_.find(source);
   CHECK(it != safebrowsing_reports_.end(), base::NotFatalUntil::M130);
   safebrowsing_reports_.erase(it);
+  if (!on_url_loader_complete_callback_.is_null()) {
+    std::move(on_url_loader_complete_callback_).Run();
+  }
 }
 
+void PingManager::OnSafeBrowsingHitURLLoaderComplete(
+    network::SimpleURLLoader* source,
+    std::unique_ptr<std::string> response_body) {
+  int response_code = source->ResponseInfo() && source->ResponseInfo()->headers
+                          ? source->ResponseInfo()->headers->response_code()
+                          : 0;
+  RecordHttpResponseOrErrorCode("SafeBrowsing.HitReport.NetworkResult",
+                                source->NetError(), response_code);
+  OnURLLoaderComplete(source, std::move(response_body));
+}
 void PingManager::OnThreatDetailsReportURLLoaderComplete(
     network::SimpleURLLoader* source,
     bool has_access_token,
@@ -259,8 +272,11 @@
   int response_code = source->ResponseInfo() && source->ResponseInfo()->headers
                           ? source->ResponseInfo()->headers->response_code()
                           : 0;
-  std::string metric = "SafeBrowsing.ClientSafeBrowsingReport.NetworkResult.";
-  std::string suffix = (has_access_token ? "YesAccessToken" : "NoAccessToken");
+  std::string metric = "SafeBrowsing.ClientSafeBrowsingReport.NetworkResult";
+  std::string suffix =
+      (has_access_token ? ".YesAccessToken" : ".NoAccessToken");
+  RecordHttpResponseOrErrorCode(metric.c_str(), source->NetError(),
+                                response_code);
   RecordHttpResponseOrErrorCode((metric + suffix).c_str(), source->NetError(),
                                 response_code);
   OnURLLoaderComplete(source, std::move(response_body));
@@ -290,8 +306,8 @@
 
   report_ptr->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
       url_loader_factory_.get(),
-      base::BindOnce(&PingManager::OnURLLoaderComplete, base::Unretained(this),
-                     report_ptr.get()));
+      base::BindOnce(&PingManager::OnSafeBrowsingHitURLLoaderComplete,
+                     base::Unretained(this), report_ptr.get()));
   safebrowsing_reports_.insert(std::move(report_ptr));
 
   // The following is to log this HitReport on any open chrome://safe-browsing
@@ -604,4 +620,9 @@
   hats_delegate_ = std::move(hats_delegate);
 }
 
+void PingManager::SetOnURLLoaderCompleteCallbackForTesting(
+    base::OnceCallback<void()> callback) {
+  on_url_loader_complete_callback_ = std::move(callback);
+}
+
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/core/browser/ping_manager.h b/components/safe_browsing/core/browser/ping_manager.h
index ae30e13..2adaa03 100644
--- a/components/safe_browsing/core/browser/ping_manager.h
+++ b/components/safe_browsing/core/browser/ping_manager.h
@@ -13,6 +13,7 @@
 
 #include "base/containers/unique_ptr_adapters.h"
 #include "base/files/file_util.h"
+#include "base/functional/callback_forward.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/task/sequenced_task_runner.h"
@@ -140,6 +141,9 @@
 
   void OnURLLoaderComplete(network::SimpleURLLoader* source,
                            std::unique_ptr<std::string> response_body);
+  void OnSafeBrowsingHitURLLoaderComplete(
+      network::SimpleURLLoader* source,
+      std::unique_ptr<std::string> response_body);
   void OnThreatDetailsReportURLLoaderComplete(
       network::SimpleURLLoader* source,
       bool has_access_token,
@@ -174,6 +178,8 @@
       std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher);
   void SetHatsDelegateForTesting(
       std::unique_ptr<SafeBrowsingHatsDelegate> hats_delegate);
+  void SetOnURLLoaderCompleteCallbackForTesting(
+      base::OnceCallback<void()> callback);
 
  protected:
   friend class PingManagerTest;
@@ -252,6 +258,9 @@
   // Determines whether the user has opted in to send persisted reports.
   base::RepeatingCallback<bool()> get_should_send_persisted_report_;
 
+  // If populated, called once the URL loader completes.
+  base::OnceCallback<void()> on_url_loader_complete_callback_;
+
   base::WeakPtrFactory<PingManager> weak_factory_{this};
 };
 
diff --git a/components/safe_browsing/core/browser/ping_manager_unittest.cc b/components/safe_browsing/core/browser/ping_manager_unittest.cc
index be24765..7b4f872 100644
--- a/components/safe_browsing/core/browser/ping_manager_unittest.cc
+++ b/components/safe_browsing/core/browser/ping_manager_unittest.cc
@@ -219,8 +219,10 @@
 
   std::string access_token = "testing_access_token";
   network::TestURLLoaderFactory test_url_loader_factory;
+  bool interceptor_called = false;
   test_url_loader_factory.SetInterceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        interceptor_called = true;
         EXPECT_EQ(GetUploadData(request), expected_report_content);
         EXPECT_THAT(
             request.headers.GetHeader(net::HttpRequestHeaders::kAuthorization),
@@ -239,8 +241,15 @@
   ping_manager()->SetURLLoaderFactoryForTesting(
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &test_url_loader_factory));
-
+  test_url_loader_factory.AddResponse(
+      "https://safebrowsing.google.com/safebrowsing/clientreport/"
+      "malware?client=unittest&appver=1.0&pver=4.0" +
+          key_param_,
+      "");
   EXPECT_CALL(*webui_delegate_.get(), AddToCSBRRsSent(_)).Times(1);
+  base::RunLoop run_loop;
+  ping_manager()->SetOnURLLoaderCompleteCallbackForTesting(
+      run_loop.QuitClosure());
   PingManager::ReportThreatDetailsResult result =
       ping_manager()->ReportThreatDetails(std::move(report));
   EXPECT_EQ(result, PingManager::ReportThreatDetailsResult::SUCCESS);
@@ -248,6 +257,25 @@
   if (expect_access_token) {
     raw_token_fetcher->RunAccessTokenCallback(access_token);
   }
+  run_loop.Run();
+  EXPECT_TRUE(interceptor_called);
+  histogram_tester.ExpectUniqueSample(
+      /*name=*/"SafeBrowsing.ClientSafeBrowsingReport.NetworkResult",
+      /*sample=*/200,
+      /*expected_bucket_count=*/1);
+  if (expect_access_token) {
+    histogram_tester.ExpectUniqueSample(
+        /*name=*/
+        "SafeBrowsing.ClientSafeBrowsingReport.NetworkResult.YesAccessToken",
+        /*sample=*/200,
+        /*expected_bucket_count=*/1);
+  } else {
+    histogram_tester.ExpectUniqueSample(
+        /*name=*/
+        "SafeBrowsing.ClientSafeBrowsingReport.NetworkResult.NoAccessToken",
+        /*sample=*/200,
+        /*expected_bucket_count=*/1);
+  }
 }
 
 TEST_F(PingManagerTest, TestSafeBrowsingHitUrl) {
@@ -736,6 +764,7 @@
 }
 
 TEST_F(PingManagerTest, ReportSafeBrowsingHit) {
+  base::HistogramTester histogram_tester;
   std::unique_ptr<HitReport> hit_report = std::make_unique<HitReport>();
   std::string post_data = "testing_hit_report_post_data";
   hit_report->post_data = post_data;
@@ -749,16 +778,32 @@
   hit_report->is_enhanced_protection = false;
 
   network::TestURLLoaderFactory test_url_loader_factory;
+  bool interceptor_called = false;
   test_url_loader_factory.SetInterceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
+        interceptor_called = true;
         EXPECT_EQ(GetUploadData(request), post_data);
       }));
   ping_manager()->SetURLLoaderFactoryForTesting(
       base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
           &test_url_loader_factory));
-
+  test_url_loader_factory.AddResponse(
+      "https://safebrowsing.google.com/safebrowsing/"
+      "report?client=unittest&appver=1.0&pver=4.0" +
+          key_param_ +
+          "&ext=2&evts=phishblhit&evtd=&evtr=&evhr=&evtb=0&src=l4&m=0",
+      "");
   EXPECT_CALL(*webui_delegate_.get(), AddToHitReportsSent(_)).Times(1);
+  base::RunLoop run_loop;
+  ping_manager()->SetOnURLLoaderCompleteCallbackForTesting(
+      run_loop.QuitClosure());
   ping_manager()->ReportSafeBrowsingHit(std::move(hit_report));
+  run_loop.Run();
+  EXPECT_TRUE(interceptor_called);
+  histogram_tester.ExpectUniqueSample(
+      /*name=*/"SafeBrowsing.HitReport.NetworkResult",
+      /*sample=*/200,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(PingManagerTest, AttachThreatDetailsAndLaunchSurvey) {
diff --git a/components/saved_tab_groups/internal/saved_tab_group_proto_conversion_unittest.cc b/components/saved_tab_groups/internal/saved_tab_group_proto_conversion_unittest.cc
index e06fabb..33d9313 100644
--- a/components/saved_tab_groups/internal/saved_tab_group_proto_conversion_unittest.cc
+++ b/components/saved_tab_groups/internal/saved_tab_group_proto_conversion_unittest.cc
@@ -113,7 +113,10 @@
       "last_updater_cache_guid_1",  // last_updater_cache_guid
       /*created_before_syncing_tab_groups=*/true,
       creation_time_windows_epoch_micros, update_time_windows_epoch_micros);
+  const base::Uuid kOriginatingSavedTabGroupGuid =
+      base::Uuid::GenerateRandomV4();
   group.SetLastUserInteractionTime(time_);
+  group.SetOriginatingTabGroupGuid(kOriginatingSavedTabGroupGuid);
 
   proto::SavedTabGroupData proto =
       SavedTabGroupSyncBridge::SavedTabGroupToDataForTest(group);
diff --git a/components/saved_tab_groups/internal/saved_tab_group_proto_conversions.cc b/components/saved_tab_groups/internal/saved_tab_group_proto_conversions.cc
index 8444fa2..3a2a3e06 100644
--- a/components/saved_tab_groups/internal/saved_tab_group_proto_conversions.cc
+++ b/components/saved_tab_groups/internal/saved_tab_group_proto_conversions.cc
@@ -170,12 +170,17 @@
 
   bool created_before_syncing_tab_groups = false;
   base::Time last_user_interaction_time;
+  base::Uuid originating_tab_group_guid;
   if (data.has_local_tab_group_data()) {
     created_before_syncing_tab_groups =
         data.local_tab_group_data().created_before_syncing_tab_groups();
     last_user_interaction_time = TimeFromWindowsEpochMicros(
         data.local_tab_group_data()
             .last_user_interaction_time_windows_epoch_micros());
+    if (data.local_tab_group_data().has_originating_tab_group_guid()) {
+      originating_tab_group_guid = base::Uuid::ParseLowercase(
+          data.local_tab_group_data().originating_tab_group_guid());
+    }
   }
 
   SavedTabGroup group = SavedTabGroup(
@@ -184,6 +189,9 @@
       created_before_syncing_tab_groups, creation_time);
   group.SetUpdateTimeWindowsEpochMicros(update_time);
   group.SetLastUserInteractionTime(last_user_interaction_time);
+  if (originating_tab_group_guid.is_valid()) {
+    group.SetOriginatingTabGroupGuid(std::move(originating_tab_group_guid));
+  }
 
   return group;
 }
@@ -236,11 +244,15 @@
 
   pb_data.mutable_local_tab_group_data()->set_created_before_syncing_tab_groups(
       group.created_before_syncing_tab_groups());
-  pb_data.mutable_local_tab_group_data()
-      ->set_last_user_interaction_time_windows_epoch_micros(
-          group.last_user_interaction_time()
-              .ToDeltaSinceWindowsEpoch()
-              .InMicroseconds());
+  proto::LocalTabGroupData* local_data = pb_data.mutable_local_tab_group_data();
+  local_data->set_last_user_interaction_time_windows_epoch_micros(
+      group.last_user_interaction_time()
+          .ToDeltaSinceWindowsEpoch()
+          .InMicroseconds());
+  if (group.originating_tab_group_guid().has_value()) {
+    local_data->set_originating_tab_group_guid(
+        group.originating_tab_group_guid().value().AsLowercaseString());
+  }
 
   pb_data.set_version(kCurrentSchemaVersion);
 
@@ -310,8 +322,7 @@
   // Note: When adding a new syncable field, also update IsSyncEquivalent().
 
   pb_data.set_version(kCurrentSchemaVersion);
-  pb_data.mutable_local_tab_group_data()->set_is_tab_pending_sanitization(
-      tab.is_pending_sanitization());
+
   return pb_data;
 }
 
diff --git a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc
index 4665a40..e9c97ce3 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge.cc
@@ -122,9 +122,9 @@
   sync_pb::SharedTabGroup* pb_group = pb_specifics.mutable_tab_group();
   pb_group->set_color(TabGroupColorToSyncColor(group.color()));
   pb_group->set_title(base::UTF16ToUTF8(group.title()));
-  if (group.originating_saved_tab_group_guid().has_value()) {
+  if (group.originating_tab_group_guid().has_value()) {
     pb_group->set_originating_tab_group_guid(
-        group.originating_saved_tab_group_guid().value().AsLowercaseString());
+        group.originating_tab_group_guid().value().AsLowercaseString());
   }
   return pb_specifics;
 }
@@ -140,9 +140,9 @@
       SyncColorToTabGroupColor(specifics.tab_group().color());
   std::u16string title = base::UTF8ToUTF16(specifics.tab_group().title());
   base::Uuid guid = base::Uuid::ParseLowercase(specifics.guid());
-  base::Uuid originating_saved_tab_group_guid;
+  base::Uuid originating_tab_group_guid;
   if (specifics.tab_group().has_originating_tab_group_guid()) {
-    originating_saved_tab_group_guid = base::Uuid::ParseLowercase(
+    originating_tab_group_guid = base::Uuid::ParseLowercase(
         specifics.tab_group().originating_tab_group_guid());
   }
 
@@ -162,9 +162,8 @@
       CollaborationId(collaboration_metadata.collaboration_id()));
   group.SetCreatedByAttribution(collaboration_metadata.created_by());
   group.SetUpdatedByAttribution(collaboration_metadata.last_updated_by());
-  if (originating_saved_tab_group_guid.is_valid()) {
-    group.SetOriginatingSavedTabGroupGuid(
-        std::move(originating_saved_tab_group_guid));
+  if (originating_tab_group_guid.is_valid()) {
+    group.SetOriginatingTabGroupGuid(std::move(originating_tab_group_guid));
   }
 
   // Set the remote update time explicitly because the setters above could have
diff --git a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
index 5fefe14..0361a57 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
@@ -942,7 +942,7 @@
   SavedTabGroup group(u"title", tab_groups::TabGroupColorId::kGrey,
                       /*urls=*/{}, /*position=*/std::nullopt);
   group.SetCollaborationId(CollaborationId("collaboration"));
-  group.SetOriginatingSavedTabGroupGuid(kOriginatingSavedTabGroupGuid);
+  group.SetOriginatingTabGroupGuid(kOriginatingSavedTabGroupGuid);
   SavedTabGroupTab tab1 = test::CreateSavedTabGroupTab(
       "http://google.com/1", u"tab 1", group.saved_guid(), /*position=*/0);
   SavedTabGroupTab tab2 = test::CreateSavedTabGroupTab(
diff --git a/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc b/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc
index e917a4f..aca2b41d 100644
--- a/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc
+++ b/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc
@@ -591,8 +591,30 @@
 }
 
 void TabGroupSyncServiceImpl::AboutToUnShareTabGroup(
-    const LocalTabGroupID& local_group_id) {
+    const LocalTabGroupID& local_group_id,
+    base::OnceClosure on_complete_callback) {
   model_->SetIsTransitioningToSaved(local_group_id, true);
+  std::move(on_complete_callback).Run();
+}
+
+void TabGroupSyncServiceImpl::OnTabGroupUnShareComplete(
+    const LocalTabGroupID& local_group_id,
+    bool success) {
+  const SavedTabGroup* saved_group = model_->Get(local_group_id);
+  CHECK(saved_group);
+  CHECK(saved_group->is_transitioning_to_saved());
+
+  if (!success) {
+    return;
+  }
+
+  // Make a deep copy of shared tab group.
+  SavedTabGroup cloned_group = saved_group->CloneAsSavedTabGroup();
+  cloned_group.SetCreatedBeforeSyncingTabGroups(
+      !sync_bridge_mediator_->IsSavedBridgeSyncing());
+  cloned_group.SetCreatorCacheGuid(
+      sync_bridge_mediator_->GetLocalCacheGuidForSavedBridge());
+  model_->AddedLocally(std::move(cloned_group));
 }
 
 void TabGroupSyncServiceImpl::MakeTabGroupSharedForTesting(
@@ -984,7 +1006,7 @@
 
   // Saved tab group should be transitions to shared before notifying observers
   // because the new group may be opened automatically on some platforms.
-  bool group_migrated =
+  bool group_migrated_to_shared =
       TransitionSavedToSharedTabGroupIfNeeded(*saved_tab_group);
 
   // Update the list even if the group wasn't transitioned. This is needed if
@@ -992,7 +1014,10 @@
   // version).
   UpdateTransitionedSavedTabGroupsList();
 
-  if (group_migrated) {
+  bool group_migrated_to_saved =
+      TransitionSharedToSavedTabGroupIfNeeded(*saved_tab_group);
+
+  if (group_migrated_to_shared || group_migrated_to_saved) {
     NotifyTabGroupMigrated(saved_tab_group->saved_guid(), source);
 
     // Simulate tab group update after the transition to notify observers which
@@ -1027,11 +1052,10 @@
   const SavedTabGroup* new_group = model_->Get(new_group_guid);
   CHECK(new_group);
   // Originating saved tab group must exist if it was transitioned.
-  CHECK(new_group->originating_saved_tab_group_guid().has_value());
+  CHECK(new_group->originating_tab_group_guid().has_value());
   for (TabGroupSyncService::Observer& observer : observers_) {
     observer.OnTabGroupMigrated(
-        *new_group, new_group->originating_saved_tab_group_guid().value(),
-        source);
+        *new_group, new_group->originating_tab_group_guid().value(), source);
   }
 }
 
@@ -1326,39 +1350,56 @@
 
 bool TabGroupSyncServiceImpl::TransitionSavedToSharedTabGroupIfNeeded(
     const SavedTabGroup& shared_group) {
-  if (!shared_group.originating_saved_tab_group_guid().has_value()) {
+  return TransitionOriginatingTabGroupToNewGroupIfNeeded(
+      shared_group, OpeningSource::kConnectOnGroupShare,
+      ClosingSource::kDisconnectOnGroupShared);
+}
+
+bool TabGroupSyncServiceImpl::TransitionSharedToSavedTabGroupIfNeeded(
+    const SavedTabGroup& saved_group) {
+  // TODO(crbug.com/370746008): After replacing the originating group here,
+  // it needs to be deleted.
+  return TransitionOriginatingTabGroupToNewGroupIfNeeded(
+      saved_group, OpeningSource::kConnectOnGroupUnShare,
+      ClosingSource::kDisconnectOnGroupUnShared);
+}
+
+bool TabGroupSyncServiceImpl::TransitionOriginatingTabGroupToNewGroupIfNeeded(
+    const SavedTabGroup& tab_group,
+    OpeningSource opening_source,
+    ClosingSource closing_source) {
+  if (!tab_group.originating_tab_group_guid().has_value()) {
     return false;
   }
 
-  const SavedTabGroup* originating_saved_group =
-      model_->Get(shared_group.originating_saved_tab_group_guid().value());
-  if (!originating_saved_group) {
+  const SavedTabGroup* originating_tab_group =
+      model_->Get(tab_group.originating_tab_group_guid().value());
+  if (!originating_tab_group) {
     // Originating group doesn't exist in the model and hence it wasn't
     // transitioned. The group may not exist if it was deleted from the current
     // device before the remote shared tab group was downloaded.
     return false;
   }
 
-  if (originating_saved_group->local_group_id().has_value()) {
+  if (originating_tab_group->local_group_id().has_value()) {
     // The group is open in the tab strip and needs to be transitioned with all
     // local IDs.
 
     // Make a copy because both groups will be updated.
     const LocalTabGroupID local_group_id =
-        originating_saved_group->local_group_id().value();
+        originating_tab_group->local_group_id().value();
 
     // First, remove the local tab group mapping and then disconnect the local
     // tab group. Note that on some platforms the coordinator may call
     // RemoveLocalTabGroupMapping() but it should be a no-op.
-    RemoveLocalTabGroupMapping(local_group_id,
-                               ClosingSource::kDisconnectOnGroupShared);
+    RemoveLocalTabGroupMapping(local_group_id, closing_source);
     coordinator_->DisconnectLocalTabGroup(local_group_id);
 
     // Connect the shared tab group to the local group: update the local tab
     // group mapping on all platforms, and update the mapping for session
     // restore.
-    ConnectLocalTabGroup(shared_group.saved_guid(), local_group_id,
-                         OpeningSource::kConnectOnGroupShare);
+    ConnectLocalTabGroup(tab_group.saved_guid(), local_group_id,
+                         opening_source);
   }
 
   return true;
@@ -1422,9 +1463,9 @@
   // collaboration. For such groups, the originating saved tab group should
   // still be accessible, so they need to be excluded.
   for (const SavedTabGroup& group : GetAllGroups()) {
-    if (group.originating_saved_tab_group_guid().has_value()) {
+    if (group.originating_tab_group_guid().has_value()) {
       transitioned_saved_tab_groups_.insert(
-          group.originating_saved_tab_group_guid().value());
+          group.originating_tab_group_guid().value());
     }
   }
 }
diff --git a/components/saved_tab_groups/internal/tab_group_sync_service_impl.h b/components/saved_tab_groups/internal/tab_group_sync_service_impl.h
index 7d27cf3..747e58d 100644
--- a/components/saved_tab_groups/internal/tab_group_sync_service_impl.h
+++ b/components/saved_tab_groups/internal/tab_group_sync_service_impl.h
@@ -103,7 +103,10 @@
   void MakeTabGroupSharedForTesting(const LocalTabGroupID& local_group_id,
                                     std::string_view collaboration_id);
 
-  void AboutToUnShareTabGroup(const LocalTabGroupID& local_group_id) override;
+  void AboutToUnShareTabGroup(const LocalTabGroupID& local_group_id,
+                              base::OnceClosure on_complete_callback) override;
+  void OnTabGroupUnShareComplete(const LocalTabGroupID& local_group_id,
+                                 bool success) override;
 
   std::vector<SavedTabGroup> GetAllGroups() const override;
   std::optional<SavedTabGroup> GetGroup(const base::Uuid& guid) const override;
@@ -264,6 +267,23 @@
   bool TransitionSavedToSharedTabGroupIfNeeded(
       const SavedTabGroup& shared_group);
 
+  // Transitions the originating shared tab group to the given saved tab group.
+  // Returns true if the group is transitioned.
+  bool TransitionSharedToSavedTabGroupIfNeeded(
+      const SavedTabGroup& saved_group);
+
+  // Transitions a originating tab group to a new tab group. Called when
+  // either a saved tab group is becoming shared, or when a shared tab group is
+  // becoming private. This call will find the orignating tab group from the
+  // `tab_group`'s originating group guid, and replace it with `tab_group`.
+  // `opening_source` is the reason for adding the new group, and
+  // `closing_source` is the reason for removing the originating tab group.
+  // Returns true if the group is transitioned.
+  bool TransitionOriginatingTabGroupToNewGroupIfNeeded(
+      const SavedTabGroup& tab_group,
+      OpeningSource opening_source,
+      ClosingSource closing_source);
+
   // Helper method called by NavigateTab() when UrlRestriction is retrieved.
   void NavigateTabInternal(
       const LocalTabGroupID& group_id,
diff --git a/components/saved_tab_groups/internal/tab_group_sync_service_unittest.cc b/components/saved_tab_groups/internal/tab_group_sync_service_unittest.cc
index 73e5298e..ee21eb8 100644
--- a/components/saved_tab_groups/internal/tab_group_sync_service_unittest.cc
+++ b/components/saved_tab_groups/internal/tab_group_sync_service_unittest.cc
@@ -1814,7 +1814,7 @@
   EXPECT_NE(shared_group->saved_guid(), group_1_.saved_guid());
   EXPECT_TRUE(shared_group->saved_guid().is_valid());
   EXPECT_EQ(shared_group->collaboration_id(), CollaborationId("collaboration"));
-  EXPECT_EQ(shared_group->originating_saved_tab_group_guid(),
+  EXPECT_EQ(shared_group->originating_tab_group_guid(),
             originating_group->saved_guid());
   EXPECT_EQ(shared_group->local_group_id(), local_group_id_1_);
 
@@ -1882,60 +1882,133 @@
   ASSERT_TRUE(shared_group->is_shared_tab_group());
   ASSERT_FALSE(shared_group->is_transitioning_to_saved());
 
-  tab_group_sync_service_->AboutToUnShareTabGroup(local_group_id_1_);
+  tab_group_sync_service_->AboutToUnShareTabGroup(local_group_id_1_,
+                                                  base::DoNothing());
   shared_group = tab_group_sync_service_->GetGroup(local_group_id_1_);
   ASSERT_TRUE(shared_group->is_shared_tab_group());
   ASSERT_TRUE(shared_group->is_transitioning_to_saved());
 }
 
-TEST_F(TabGroupSyncServiceTest, ShouldNotReturnOriginatingTabGroup) {
-  ON_CALL(*collaboration_finder_, IsCollaborationAvailable)
-      .WillByDefault(Return(false));
-
+TEST_F(TabGroupSyncServiceTest, OnTabGroupUnShareFailed) {
+  std::optional<SavedTabGroup> group =
+      tab_group_sync_service_->GetGroup(local_group_id_1_);
   tab_group_sync_service_->MakeTabGroupShared(local_group_id_1_,
                                               "collaboration");
+
+  // The new group replaces the originating one asynchronously.
   WaitForPostedTasks();
 
+  // Unshare the tab group and fail it.
+  tab_group_sync_service_->AboutToUnShareTabGroup(local_group_id_1_,
+                                                  base::DoNothing());
+  std::optional<SavedTabGroup> shared_group =
+      tab_group_sync_service_->GetGroup(local_group_id_1_);
+  ASSERT_TRUE(shared_group->is_shared_tab_group());
+  ASSERT_TRUE(shared_group->is_transitioning_to_saved());
+
+  tab_group_sync_service_->OnTabGroupUnShareComplete(local_group_id_1_, false);
+  shared_group = tab_group_sync_service_->GetGroup(local_group_id_1_);
+  ASSERT_TRUE(shared_group->is_shared_tab_group());
+  ASSERT_TRUE(shared_group->is_transitioning_to_saved());
+}
+
+TEST_F(TabGroupSyncServiceTest, OnTabGroupUnShareSucceeded) {
+  std::optional<SavedTabGroup> group =
+      tab_group_sync_service_->GetGroup(local_group_id_1_);
+  tab_group_sync_service_->MakeTabGroupShared(local_group_id_1_,
+                                              "collaboration");
+
+  // The new group replaces the originating one asynchronously.
+  WaitForPostedTasks();
+
+  // Unshare the tab group.
+  tab_group_sync_service_->AboutToUnShareTabGroup(local_group_id_1_,
+                                                  base::DoNothing());
+  std::optional<SavedTabGroup> shared_group =
+      tab_group_sync_service_->GetGroup(local_group_id_1_);
+  ASSERT_TRUE(shared_group->is_shared_tab_group());
+  ASSERT_TRUE(shared_group->is_transitioning_to_saved());
+
+  // Transition the shared tab group to a saved tab group.
+  Sequence s;
+  EXPECT_CALL(*coordinator_, DisconnectLocalTabGroup(local_group_id_1_))
+      .InSequence(s);
+  EXPECT_CALL(*coordinator_, ConnectLocalTabGroup(_, local_group_id_1_))
+      .InSequence(s);
+  EXPECT_CALL(*observer_, OnTabGroupMigrated(_, shared_group->saved_guid(),
+                                             TriggerSource::LOCAL));
+
+  tab_group_sync_service_->OnTabGroupUnShareComplete(local_group_id_1_, true);
+  shared_group = tab_group_sync_service_->GetGroup(local_group_id_1_);
+  ASSERT_TRUE(shared_group->is_shared_tab_group());
+  ASSERT_TRUE(shared_group->is_transitioning_to_saved());
+
+  // The new group replaces the originating one asynchronously.
+  WaitForPostedTasks();
+
+  // The originating group should have empty local group id now.
+  std::optional<SavedTabGroup> originating_group =
+      tab_group_sync_service_->GetGroup(shared_group->saved_guid());
+  ASSERT_TRUE(originating_group.has_value());
+  EXPECT_TRUE(originating_group->is_shared_tab_group());
+  EXPECT_EQ(originating_group->local_group_id(), std::nullopt);
+
   std::optional<SavedTabGroup> saved_group =
       tab_group_sync_service_->GetGroup(local_group_id_1_);
-  ASSERT_TRUE(saved_group.has_value());
+  // Verify that both groups have the same fields.
+  EXPECT_EQ(shared_group->title(), saved_group->title());
+  EXPECT_EQ(shared_group->color(), saved_group->color());
 
-  // The group is still saved because it shouldn't be transitioned while it's
-  // not available.
+  // Verify that the shared group has updated fields.
   ASSERT_FALSE(saved_group->is_shared_tab_group());
+  EXPECT_GT(saved_group->creation_time_windows_epoch_micros(),
+            shared_group->creation_time_windows_epoch_micros());
+  EXPECT_GT(saved_group->update_time_windows_epoch_micros(),
+            shared_group->update_time_windows_epoch_micros());
+  EXPECT_EQ(saved_group->creator_cache_guid(), kTestCacheGuid);
+  EXPECT_EQ(saved_group->last_updater_cache_guid(), std::nullopt);
+  EXPECT_EQ(saved_group->position(), std::nullopt);
+  EXPECT_TRUE(saved_group->last_user_interaction_time().is_null());
 
-  // The saved tab group should be present in GetAllGroups() while the shared
-  // one is not accessible.
-  EXPECT_THAT(tab_group_sync_service_->GetAllGroups(),
-              Contains(HasGuid(saved_group->saved_guid())));
-  EXPECT_THAT(tab_group_sync_service_->GetAllGroups(),
-              Not(Contains(IsSharedGroup())));
+  // Verify shared tab group fields.
+  EXPECT_NE(saved_group->saved_guid(), shared_group->saved_guid());
+  EXPECT_TRUE(saved_group->saved_guid().is_valid());
+  EXPECT_EQ(saved_group->originating_tab_group_guid(),
+            shared_group->saved_guid());
+  EXPECT_EQ(saved_group->local_group_id(), local_group_id_1_);
 
-  // Simulate collaboration availability.
-  ON_CALL(*collaboration_finder_, IsCollaborationAvailable)
-      .WillByDefault(Return(true));
-  tab_group_sync_service_->OnCollaborationAvailable("collaboration");
-  WaitForPostedTasks();
+  // Verify group tabs.
+  ASSERT_EQ(shared_group->saved_tabs().size(), group_1_.saved_tabs().size());
+  EXPECT_FALSE(shared_group->saved_tabs().empty());
+  for (size_t i = 0; i < shared_group->saved_tabs().size(); ++i) {
+    const SavedTabGroupTab& saved_tab = saved_group->saved_tabs()[i];
+    const SavedTabGroupTab& shared_tab = originating_group->saved_tabs()[i];
 
-  // The group is expected to be transitioned after the collaboration became
-  // available.
-  std::optional<SavedTabGroup> shared_group =
-      tab_group_sync_service_->GetGroup(local_group_id_1_);
-  ASSERT_TRUE(shared_group.has_value());
-  ASSERT_TRUE(shared_group->is_shared_tab_group());
+    // Verify the same fields.
+    EXPECT_EQ(saved_tab.url(), shared_tab.url());
+    EXPECT_EQ(saved_tab.title(), shared_tab.title());
+    EXPECT_EQ(saved_tab.favicon(), shared_tab.favicon());
+    EXPECT_EQ(saved_tab.saved_group_guid(), saved_group->saved_guid());
 
-  // Only shared tab group should be returned.
-  EXPECT_THAT(tab_group_sync_service_->GetAllGroups(),
-              Not(Contains(HasGuid(saved_group->saved_guid()))));
-  EXPECT_THAT(tab_group_sync_service_->GetAllGroups(),
-              Contains(HasGuid(shared_group->saved_guid())));
+    // Verify updated fields.
+    EXPECT_NE(saved_tab.saved_tab_guid(), shared_tab.saved_tab_guid());
+    EXPECT_GT(saved_group->creation_time_windows_epoch_micros(),
+              shared_tab.creation_time_windows_epoch_micros());
+    EXPECT_GT(saved_tab.update_time_windows_epoch_micros(),
+              shared_tab.update_time_windows_epoch_micros());
+
+    // Do not verify the position of the original tab because its meaning
+    // differs for shared tab groups: it's the index of the tab in the shared
+    // group.
+    EXPECT_EQ(saved_tab.position(), i);
+  }
 }
 
 TEST_F(TabGroupSyncServiceTest, ShouldNotReturnOriginatingTabGroupOnRemoteAdd) {
   // Simulate remote transition to shared tab group from `group_1_`.
   SavedTabGroup shared_group = test::CreateTestSavedTabGroupWithNoTabs();
   shared_group.SetCollaborationId(CollaborationId("collaboration"));
-  shared_group.SetOriginatingSavedTabGroupGuid(group_1_.saved_guid());
+  shared_group.SetOriginatingTabGroupGuid(group_1_.saved_guid());
 
   model_->AddedFromSync(shared_group);
   WaitForPostedTasks();
diff --git a/components/saved_tab_groups/proto/local_tab_group_data.proto b/components/saved_tab_groups/proto/local_tab_group_data.proto
index 6e4e735..e76cd14e 100644
--- a/components/saved_tab_groups/proto/local_tab_group_data.proto
+++ b/components/saved_tab_groups/proto/local_tab_group_data.proto
@@ -26,4 +26,7 @@
   // Indicates whether a tab is pending sanitization. This field is only
   // applicable to tabs, instead of tab groups.
   optional bool is_tab_pending_sanitization = 5;
+
+  // Shared tab group GUID from which this saved tab group was created.
+  optional string originating_tab_group_guid = 6;
 }
diff --git a/components/saved_tab_groups/public/saved_tab_group.cc b/components/saved_tab_groups/public/saved_tab_group.cc
index 41db3a1..405e634 100644
--- a/components/saved_tab_groups/public/saved_tab_group.cc
+++ b/components/saved_tab_groups/public/saved_tab_group.cc
@@ -221,10 +221,9 @@
   return *this;
 }
 
-SavedTabGroup& SavedTabGroup::SetOriginatingSavedTabGroupGuid(
-    std::optional<base::Uuid> originating_saved_tab_group_guid) {
-  originating_saved_tab_group_guid_ =
-      std::move(originating_saved_tab_group_guid);
+SavedTabGroup& SavedTabGroup::SetOriginatingTabGroupGuid(
+    std::optional<base::Uuid> originating_tab_group_guid) {
+  originating_tab_group_guid_ = std::move(originating_tab_group_guid);
   return *this;
 }
 
@@ -410,23 +409,19 @@
 
 SavedTabGroup SavedTabGroup::CloneAsSharedTabGroup(
     CollaborationId collaboration_id) const {
-  SavedTabGroup shared_group(title(), color(), /*urls=*/{});
+  SavedTabGroup shared_group = CopyBaseFieldsWithTabs();
   shared_group.SetCollaborationId(std::move(collaboration_id));
-  shared_group.SetOriginatingSavedTabGroupGuid(saved_guid());
-
-  for (size_t i = 0; i < saved_tabs().size(); ++i) {
-    const SavedTabGroupTab& tab = saved_tabs()[i];
-
-    // Use tab's index as a position for shared tabs because shared tab groups
-    // use unique positions for syncing tabs.
-    SavedTabGroupTab shared_tab(tab.url(), tab.title(),
-                                shared_group.saved_guid(), /*position=*/i);
-    shared_tab.SetFavicon(tab.favicon());
-    shared_group.AddTabLocally(std::move(shared_tab));
-  }
+  shared_group.SetOriginatingTabGroupGuid(saved_guid());
   return shared_group;
 }
 
+SavedTabGroup SavedTabGroup::CloneAsSavedTabGroup() const {
+  DCHECK(is_shared_tab_group());
+  SavedTabGroup saved_group = CopyBaseFieldsWithTabs();
+  saved_group.SetOriginatingTabGroupGuid(saved_guid());
+  return saved_group;
+}
+
 bool SavedTabGroup::IsPendingSanitization() const {
   for (const auto& tab : saved_tabs()) {
     if (tab.is_pending_sanitization()) {
@@ -451,4 +446,20 @@
         base::NotFatalUntil::M135);
 }
 
+SavedTabGroup SavedTabGroup::CopyBaseFieldsWithTabs() const {
+  SavedTabGroup cloned_group(title(), color(), /*urls=*/{});
+
+  for (size_t i = 0; i < saved_tabs().size(); ++i) {
+    const SavedTabGroupTab& tab = saved_tabs()[i];
+
+    // Use tab's index as position for the copied tab as tabs are
+    // displayed in the same order.
+    SavedTabGroupTab cloned_tab(tab.url(), tab.title(),
+                                cloned_group.saved_guid(), /*position=*/i);
+    cloned_tab.SetFavicon(tab.favicon());
+    cloned_group.AddTabLocally(std::move(cloned_tab));
+  }
+  return cloned_group;
+}
+
 }  // namespace tab_groups
diff --git a/components/saved_tab_groups/public/saved_tab_group.h b/components/saved_tab_groups/public/saved_tab_group.h
index cd627df..8748c8e 100644
--- a/components/saved_tab_groups/public/saved_tab_group.h
+++ b/components/saved_tab_groups/public/saved_tab_group.h
@@ -80,8 +80,8 @@
   const std::optional<CollaborationId>& collaboration_id() const {
     return collaboration_id_;
   }
-  std::optional<base::Uuid> originating_saved_tab_group_guid() const {
-    return originating_saved_tab_group_guid_;
+  std::optional<base::Uuid> originating_tab_group_guid() const {
+    return originating_tab_group_guid_;
   }
   const SharedAttribution& shared_attribution() const {
     return shared_attribution_;
@@ -127,8 +127,8 @@
   SavedTabGroup& SetPinned(bool pinned);
   SavedTabGroup& SetCollaborationId(
       std::optional<CollaborationId> collaboration_id);
-  SavedTabGroup& SetOriginatingSavedTabGroupGuid(
-      std::optional<base::Uuid> originating_saved_tab_group_guid);
+  SavedTabGroup& SetOriginatingTabGroupGuid(
+      std::optional<base::Uuid> originating_tab_group_guid);
   SavedTabGroup& SetIsTransitioningToSaved(bool is_transitioning_to_saved);
 
   // Sets the updater of the tab group, and also the creator if it's the first
@@ -197,6 +197,11 @@
   // copied.
   SavedTabGroup CloneAsSharedTabGroup(CollaborationId collaboration_id) const;
 
+  // Creates a copy of this group and converts it to a saved tab group. The new
+  // group and new tabs will have new UUIDs. Local tab and group IDs are not
+  // copied. This method should only be called on shared tab groups.
+  SavedTabGroup CloneAsSavedTabGroup() const;
+
   // Whether the TabGroup is pending sanitization.
   bool IsPendingSanitization() const;
 
@@ -220,6 +225,10 @@
   void RemoveTabImpl(const base::Uuid& saved_tab_guid,
                      bool ignore_empty_groups_for_testing = false);
 
+  // Make a copy the saved tab group, keeping fields like title, color, favicon
+  // and all the tabs. UUID and local tab and group IDs are not copied.
+  SavedTabGroup CopyBaseFieldsWithTabs() const;
+
   // The ID used to represent the group in sync.
   base::Uuid saved_guid_;
 
@@ -271,8 +280,9 @@
   std::optional<CollaborationId> collaboration_id_;
 
   // The saved guid of the group that this group was created from. Used for
-  // shared tab groups only.
-  std::optional<base::Uuid> originating_saved_tab_group_guid_;
+  // both shared and saved tab groups when they are converted from the other
+  // type.
+  std::optional<base::Uuid> originating_tab_group_guid_;
 
   // Atribution data for the shared tab group.
   SharedAttribution shared_attribution_;
diff --git a/components/saved_tab_groups/public/tab_group_sync_service.h b/components/saved_tab_groups/public/tab_group_sync_service.h
index 5220f91..b6bd4fdd4 100644
--- a/components/saved_tab_groups/public/tab_group_sync_service.h
+++ b/components/saved_tab_groups/public/tab_group_sync_service.h
@@ -202,9 +202,15 @@
   // Starts the process of converting a shared tab group to saved tab group. Due
   // to network, Chrome will need to wait for server confirmation before the
   // conversion completes successfully. The tab group must be shared when
-  // calling this.
+  // calling this. `on_complete_callback` will be called on completion.
   virtual void AboutToUnShareTabGroup(
-      const LocalTabGroupID& local_group_id) = 0;
+      const LocalTabGroupID& local_group_id,
+      base::OnceClosure on_complete_callback) = 0;
+
+  // Called when server confirms that the shared tab group has become private
+  // or when unshare fails due to some errors.
+  virtual void OnTabGroupUnShareComplete(const LocalTabGroupID& local_group_id,
+                                         bool success) = 0;
 
   // Accessor methods.
   virtual std::vector<SavedTabGroup> GetAllGroups() const = 0;
diff --git a/components/saved_tab_groups/public/types.h b/components/saved_tab_groups/public/types.h
index 2326a2b..f03d965 100644
--- a/components/saved_tab_groups/public/types.h
+++ b/components/saved_tab_groups/public/types.h
@@ -91,8 +91,10 @@
   kAutoSaveOnSessionRestoreForV1Group = 7,
   // The group was connected as a part of sharing a group.
   kConnectOnGroupShare = 8,
+  // The group was connected as a part of un-sharing a group.
+  kConnectOnGroupUnShare = 9,
 
-  kMaxValue = kConnectOnGroupShare,
+  kMaxValue = kConnectOnGroupUnShare,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/tab/enums.xml:GroupOpeningSource)
 
@@ -123,7 +125,10 @@
   // The local group was disconnected from its sync group because the group was
   // shared.
   kDisconnectOnGroupShared = 9,
-  kMaxValue = kDisconnectOnGroupShared,
+  // The local group was disconnected from its sync group because the group was
+  // un-shared.
+  kDisconnectOnGroupUnShared = 10,
+  kMaxValue = kDisconnectOnGroupUnShared,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/tab/enums.xml:GroupClosingSource)
 
diff --git a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc
index 978448d..d253742c 100644
--- a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc
+++ b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc
@@ -246,12 +246,28 @@
 }
 
 void FakeTabGroupSyncService::AboutToUnShareTabGroup(
-    const LocalTabGroupID& local_group_id) {
+    const LocalTabGroupID& local_group_id,
+    base::OnceClosure on_complete_callback) {
   std::optional<int> index = GetIndexOf(local_group_id);
   CHECK(index.has_value());
   SavedTabGroup& group = groups_[index.value()];
   group.SetIsTransitioningToSaved(true);
   NotifyObserversOfTabGroupUpdated(group);
+  std::move(on_complete_callback).Run();
+}
+
+void FakeTabGroupSyncService::OnTabGroupUnShareComplete(
+    const LocalTabGroupID& local_group_id,
+    bool success) {
+  std::optional<int> index = GetIndexOf(local_group_id);
+  CHECK(index.has_value());
+  SavedTabGroup& group = groups_[index.value()];
+  if (!success) {
+    group.SetIsTransitioningToSaved(false);
+  } else {
+    group.SetCollaborationId(std::nullopt);
+  }
+  NotifyObserversOfTabGroupShared(group);
 }
 
 std::vector<SavedTabGroup> FakeTabGroupSyncService::GetAllGroups() const {
diff --git a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h
index 00b1f21..29c087f3 100644
--- a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h
+++ b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h
@@ -57,7 +57,10 @@
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
   void MakeTabGroupShared(const LocalTabGroupID& local_group_id,
                           std::string_view collaboration_id) override;
-  void AboutToUnShareTabGroup(const LocalTabGroupID& local_group_id) override;
+  void AboutToUnShareTabGroup(const LocalTabGroupID& local_group_id,
+                              base::OnceClosure on_complete_callback) override;
+  void OnTabGroupUnShareComplete(const LocalTabGroupID& local_group_id,
+                                 bool success) override;
   std::vector<SavedTabGroup> GetAllGroups() const override;
   std::optional<SavedTabGroup> GetGroup(const base::Uuid& guid) const override;
   std::optional<SavedTabGroup> GetGroup(
diff --git a/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h b/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h
index b6a279837..d5d171e 100644
--- a/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h
+++ b/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h
@@ -62,7 +62,10 @@
   MOCK_METHOD(void,
               MakeTabGroupShared,
               (const LocalTabGroupID&, std::string_view));
-  MOCK_METHOD(void, AboutToUnShareTabGroup, (const LocalTabGroupID&));
+  MOCK_METHOD(void,
+              AboutToUnShareTabGroup,
+              (const LocalTabGroupID&, base::OnceClosure));
+  MOCK_METHOD(void, OnTabGroupUnShareComplete, (const LocalTabGroupID&, bool));
 
   MOCK_METHOD(std::vector<SavedTabGroup>, GetAllGroups, (), (const));
   MOCK_METHOD(std::optional<SavedTabGroup>,
diff --git a/components/sync/protocol/collaboration_group_version.proto b/components/sync/protocol/collaboration_group_version.proto
new file mode 100644
index 0000000..206306d
--- /dev/null
+++ b/components/sync/protocol/collaboration_group_version.proto
@@ -0,0 +1,23 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_package = "org.chromium.components.sync.protocol";
+
+option optimize_for = LITE_RUNTIME;
+
+package sync_pb;
+
+// This proto contains metadata about the version of a specific collaboration.
+// Each CollaborationGroupVersion should be mapped to a specific
+// CollaborationGroup, though the mapping happens outside of Chrome Sync.
+message CollaborationGroupVersion {
+  // The current version of a collaboration group. This is used for ensuring
+  // that old and new clients can enable or disable functionality based on:
+  // (1) The value of this field.
+  // (2) What that specific version of Chrome supports.
+  optional int64 version = 1;
+}
diff --git a/components/sync/protocol/protocol_sources.gni b/components/sync/protocol/protocol_sources.gni
index 3b51a36..42139df 100644
--- a/components/sync/protocol/protocol_sources.gni
+++ b/components/sync/protocol/protocol_sources.gni
@@ -16,6 +16,7 @@
   "client_commands.proto",
   "client_debug_info.proto",
   "collaboration_group_specifics.proto",
+  "collaboration_group_version.proto",
   "contact_info_specifics.proto",
   "cookie_specifics.proto",
   "data_type_progress_marker.proto",
diff --git a/components/test/data/autofill/heuristics-json/internal b/components/test/data/autofill/heuristics-json/internal
index 9209bec..e219405 160000
--- a/components/test/data/autofill/heuristics-json/internal
+++ b/components/test/data/autofill/heuristics-json/internal
@@ -1 +1 @@
-Subproject commit 9209becc24ddfd96a485da2298d78d2b27479fef
+Subproject commit e2194054cb0a44ecb7a11bc71a09d0600f3d2a2d
diff --git a/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java b/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java
index f7dd6563..7195c91 100644
--- a/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java
+++ b/components/url_formatter/android/java/src/org/chromium/components/url_formatter/UrlFormatter.java
@@ -11,20 +11,24 @@
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.url.GURL;
 import org.chromium.url.Origin;
 
 /** Wrapper for utilities in url_formatter. */
 @JNINamespace("url_formatter::android")
+@NullMarked
 public final class UrlFormatter {
     /**
      * Refer to url_formatter::FixupURL.
      *
+     * <pre>
      * Given a URL-like string, returns a possibly-invalid GURL. For example:
      *  - "google.com" -> "http://google.com/"
      *  - "about:" -> "chrome://version/"
      *  - "//mail.google.com:/" -> "file:///mail.google.com:/"
      *  - "0x100.0" -> "http://0x100.0/" (invalid)
+     * </pre>
      */
     public static GURL fixupUrl(String uri) {
         if (TextUtils.isEmpty(uri)) return GURL.emptyGURL();
diff --git a/content/browser/android/content_feature_map.cc b/content/browser/android/content_feature_map.cc
index 01afb00..b794c3c1 100644
--- a/content/browser/android/content_feature_map.cc
+++ b/content/browser/android/content_feature_map.cc
@@ -40,7 +40,6 @@
     &features::kProcessSharingWithStrictSiteInstances,
     &features::kReduceGpuPriorityOnBackground,
     &features::kContinueGestureOnLosingFocus,
-    &features::kSelectionMenuItemModification,
     &features::kSmartZoom,
     &features::kTouchDragAndContextMenu,
     &features::kWebBluetoothNewPermissionsBackend,
diff --git a/content/browser/interest_group/additional_bids_util.cc b/content/browser/interest_group/additional_bids_util.cc
index 55abdab1..3c30aaf8 100644
--- a/content/browser/interest_group/additional_bids_util.cc
+++ b/content/browser/interest_group/additional_bids_util.cc
@@ -336,6 +336,21 @@
     }
   }
 
+  std::optional<std::string> aggregate_win_signals;
+  const base::Value* aggregate_win_signals_val =
+      bid_dict->Find("aggregateWinSignals");
+  if (aggregate_win_signals_val) {
+    std::optional<std::string> serialized_aggregate_win_signals =
+        base::WriteJson(*aggregate_win_signals_val);
+    if (serialized_aggregate_win_signals) {
+      aggregate_win_signals =
+          std::move(serialized_aggregate_win_signals).value();
+    } else {
+      return base::unexpected(base::StrCat(
+          {"Additional bid on auction with seller '", seller.Serialize(),
+           "' rejected due to invalid aggregateWinSignals."}));
+    }
+  }
   std::vector<blink::AdDescriptor> ad_components;
   const base::Value* ad_components_val = bid_dict->Find("adComponents");
   if (ad_components_val) {
@@ -455,6 +470,7 @@
       /*ad_component_descriptors=*/std::move(ad_components),
       /*modeling_signals=*/
       static_cast<std::optional<uint16_t>>(modeling_signals),
+      /*aggregate_wins_signals=*/std::move(aggregate_win_signals),
       /*bid_duration=*/base::TimeDelta(),
       /*bidding_signals_data_version=*/std::nullopt, bid_ad,
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
diff --git a/content/browser/interest_group/additional_bids_util_unittest.cc b/content/browser/interest_group/additional_bids_util_unittest.cc
index 789f441..f755ed2a 100644
--- a/content/browser/interest_group/additional_bids_util_unittest.cc
+++ b/content/browser/interest_group/additional_bids_util_unittest.cc
@@ -669,6 +669,7 @@
             bid->ad_descriptor);
   EXPECT_EQ(0u, bid->ad_component_descriptors.size());
   EXPECT_EQ(std::nullopt, bid->modeling_signals);
+  EXPECT_EQ(std::nullopt, bid->aggregate_win_signals);
   EXPECT_EQ(&bid_state->bidder->interest_group, bid->interest_group);
   EXPECT_EQ(&bid_state->bidder->interest_group.ads.value()[0], bid->bid_ad);
   EXPECT_EQ(bid_state, bid->bid_state);
@@ -715,6 +716,7 @@
             bid->ad_descriptor);
   EXPECT_EQ(0u, bid->ad_component_descriptors.size());
   EXPECT_EQ(std::nullopt, bid->modeling_signals);
+  EXPECT_EQ(std::nullopt, bid->aggregate_win_signals);
   EXPECT_EQ(&bid_state->bidder->interest_group, bid->interest_group);
   EXPECT_EQ(&bid_state->bidder->interest_group.ads.value()[0], bid->bid_ad);
   EXPECT_EQ(bid_state, bid->bid_state);
@@ -845,6 +847,64 @@
       result.error());
 }
 
+TEST_F(AdditionalBidsUtilTest, ValidAggregateWinSignals) {
+  base::Value::Dict additional_bid_dict = MakeMinimalValid();
+  base::Value::Dict aggregate_win_signals_dict;
+  aggregate_win_signals_dict.Set("test_string", "hello");
+  aggregate_win_signals_dict.Set("test_number", 1.0);
+  base::Value::List test_array;
+  test_array.Append(1);
+  test_array.Append(2);
+  test_array.Append(3);
+  aggregate_win_signals_dict.Set("test_array", std::move(test_array));
+  additional_bid_dict.SetByDottedPath("bid.aggregateWinSignals",
+                                      std::move(aggregate_win_signals_dict));
+
+  base::Value input(std::move(additional_bid_dict));
+  auto result = DecodeAdditionalBid(
+      /*auction=*/nullptr, input, kAuctionNonce, /*seller_nonce=*/std::nullopt,
+      kInterestGroupBuyers, kSeller,
+      base::optional_ref<const url::Origin>(kTopSeller));
+  ASSERT_TRUE(result.has_value()) << result.error();
+  ASSERT_TRUE(result->bid);
+  ASSERT_TRUE(result->bid->aggregate_win_signals);
+  EXPECT_EQ(
+      *result->bid->aggregate_win_signals,
+      R"({"test_array":[1,2,3],"test_number":1.0,"test_string":"hello"})");
+}
+
+TEST_F(AdditionalBidsUtilTest, InvalidAggregateWinSignals) {
+  base::Value::Dict additional_bid_dict = MakeMinimalValid();
+  base::Value::Dict aggregate_win_signals_dict;
+
+  // Create a deeply nested list that exceeds the maximum depth
+  // for JSON serialization.
+  const size_t kMaxDepth = 200;
+  base::Value::List deep_list;
+  for (size_t i = 0; i < kMaxDepth + 1; ++i) {
+    base::Value::List new_top_list;
+    new_top_list.Append(std::move(deep_list));
+    deep_list = std::move(new_top_list);
+  }
+  aggregate_win_signals_dict.Set("deeply_nested", std::move(deep_list));
+
+  additional_bid_dict.SetByDottedPath("bid.aggregateWinSignals",
+                                      std::move(aggregate_win_signals_dict));
+
+  base::Value input(std::move(additional_bid_dict));
+  auto result = DecodeAdditionalBid(
+      /*auction=*/nullptr, input, kAuctionNonce, /*seller_nonce=*/std::nullopt,
+      kInterestGroupBuyers, kSeller,
+      base::optional_ref<const url::Origin>(kTopSeller));
+
+  // Expect the decoding to fail due to exceeding max depth
+  EXPECT_FALSE(result.has_value());
+  EXPECT_EQ(result.error(),
+            base::StrCat({"Additional bid on auction with seller '",
+                          kSeller.Serialize(),
+                          "' rejected due to invalid aggregateWinSignals."}));
+}
+
 TEST_F(AdditionalBidsUtilTest, ValidModelingSignals) {
   base::Value::Dict additional_bid_dict = MakeMinimalValid();
   additional_bid_dict.SetByDottedPath("bid.modelingSignals", 0);
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index c1b05c94..6bc91d4 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -25329,7 +25329,8 @@
       /*ad_cost=*/std::nullopt, blink::AdDescriptor(kKAnonUrl),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_urls=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   auto enforced_bid = auction_worklet::mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kEnforcedKAnon, "ad", 5.0,
@@ -25337,7 +25338,8 @@
       /*ad_cost=*/std::nullopt, blink::AdDescriptor(kKAnonUrl),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_urls=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   auto non_kanon_bid = auction_worklet::mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kUnenforcedKAnon, "ad", 5.0,
@@ -25345,7 +25347,8 @@
       /*ad_cost=*/std::nullopt, blink::AdDescriptor(kNonKAnonUrl),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_urls=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   const struct TestCase {
     std::set<KAnonMode> run_in_modes;
diff --git a/content/browser/interest_group/auction_worklet_manager_unittest.cc b/content/browser/interest_group/auction_worklet_manager_unittest.cc
index 5b42a2fcd..812685d 100644
--- a/content/browser/interest_group/auction_worklet_manager_unittest.cc
+++ b/content/browser/interest_group/auction_worklet_manager_unittest.cc
@@ -364,6 +364,7 @@
       const std::optional<url::Origin>& browser_signal_top_level_seller_origin,
       const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
       std::optional<uint32_t> bidding_signals_data_version,
+      const std::optional<std::string>& aggregate_win_signals,
       uint64_t trace_id,
       ReportWinCallback report_win_callback) override {
     NOTREACHED();
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index ab77169..64feb273 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -1285,6 +1285,7 @@
     blink::AdDescriptor ad_descriptor,
     std::vector<blink::AdDescriptor> ad_component_descriptors,
     std::optional<uint16_t> modeling_signals,
+    std::optional<std::string> aggregate_win_signals,
     base::TimeDelta bid_duration,
     std::optional<uint32_t> bidding_signals_data_version,
     const blink::InterestGroup::Ad* bid_ad,
@@ -1299,6 +1300,7 @@
       ad_descriptor(std::move(ad_descriptor)),
       ad_component_descriptors(std::move(ad_component_descriptors)),
       modeling_signals(modeling_signals),
+      aggregate_win_signals(std::move(aggregate_win_signals)),
       bid_duration(bid_duration),
       bidding_signals_data_version(bidding_signals_data_version),
       selected_buyer_and_seller_reporting_id(
@@ -2033,6 +2035,7 @@
         /*ad_cost=*/std::nullopt, std::move(ad_descriptor),
         std::move(ad_component_descriptors),
         /*modeling_signals=*/std::nullopt,
+        /*aggregate_win_signals=*/std::nullopt,
         /*bid_duration=*/base::Seconds(0),
         /*bidding_signals_data_version=*/std::nullopt, matching_ad,
         selected_buyer_and_seller_reporting_id, bid_state, auction_);
@@ -2862,7 +2865,8 @@
         mojo_bid->bid_role, std::move(mojo_bid->ad), mojo_bid->bid,
         std::move(mojo_bid->bid_currency), mojo_bid->ad_cost,
         std::move(ad_descriptor), std::move(ad_component_descriptors),
-        std::move(mojo_bid->modeling_signals), mojo_bid->bid_duration,
+        std::move(mojo_bid->modeling_signals),
+        std::move(mojo_bid->aggregate_win_signals), mojo_bid->bid_duration,
         bidding_signals_data_version, matching_ad,
         std::move(mojo_bid->selected_buyer_and_seller_reporting_id), &bid_state,
         auction_);
@@ -3479,6 +3483,7 @@
       bidder_bid->bid_state->additional_bid_buyer.has_value();
   winning_bid_info.ad_cost = bidder_bid->ad_cost;
   winning_bid_info.modeling_signals = bidder_bid->modeling_signals;
+  winning_bid_info.aggregate_win_signals = bidder_bid->aggregate_win_signals;
   winning_bid_info.bid_duration = winner->bid->bid_duration;
   winning_bid_info.bidding_signals_data_version =
       winner->bid->bidding_signals_data_version;
@@ -5373,8 +5378,8 @@
                                            : component_bid->bid_currency,
       component_bid->ad_cost, component_bid->ad_descriptor,
       component_bid->ad_component_descriptors, component_bid->modeling_signals,
-      component_bid->bid_duration, component_bid->bidding_signals_data_version,
-      component_bid->bid_ad,
+      component_bid->aggregate_win_signals, component_bid->bid_duration,
+      component_bid->bidding_signals_data_version, component_bid->bid_ad,
       component_bid->selected_buyer_and_seller_reporting_id,
       component_bid->bid_state, component_bid->auction);
 }
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h
index 5ef033e1..027a0ba 100644
--- a/content/browser/interest_group/interest_group_auction.h
+++ b/content/browser/interest_group/interest_group_auction.h
@@ -361,6 +361,7 @@
         blink::AdDescriptor ad_descriptor,
         std::vector<blink::AdDescriptor> ad_component_descriptors,
         std::optional<uint16_t> modeling_signals,
+        std::optional<std::string> aggregate_win_signals,
         base::TimeDelta bid_duration,
         std::optional<uint32_t> bidding_signals_data_version,
         const blink::InterestGroup::Ad* bid_ad,
@@ -403,6 +404,7 @@
     const blink::AdDescriptor ad_descriptor;
     const std::vector<blink::AdDescriptor> ad_component_descriptors;
     const std::optional<uint16_t> modeling_signals;
+    const std::optional<std::string> aggregate_win_signals;
     const base::TimeDelta bid_duration;
     const std::optional<uint32_t> bidding_signals_data_version;
 
diff --git a/content/browser/interest_group/interest_group_auction_reporter.cc b/content/browser/interest_group/interest_group_auction_reporter.cc
index 1a1397f..ee683d1 100644
--- a/content/browser/interest_group/interest_group_auction_reporter.cc
+++ b/content/browser/interest_group/interest_group_auction_reporter.cc
@@ -926,6 +926,7 @@
           : std::optional<url::Origin>(),
       auction_config->non_shared_params.reporting_timeout,
       winning_bid_info_.bidding_signals_data_version,
+      /*aggregate_win_signals=*/winning_bid_info_.aggregate_win_signals,
       top_level_seller_winning_bid_info_.trace_id,
       base::BindOnce(&InterestGroupAuctionReporter::OnBidderReportWinComplete,
                      weak_ptr_factory_.GetWeakPtr(),
diff --git a/content/browser/interest_group/interest_group_auction_reporter.h b/content/browser/interest_group/interest_group_auction_reporter.h
index 0d57151..d65374e 100644
--- a/content/browser/interest_group/interest_group_auction_reporter.h
+++ b/content/browser/interest_group/interest_group_auction_reporter.h
@@ -183,6 +183,9 @@
     // Modeling signals returned by the bidder.
     std::optional<uint16_t> modeling_signals;
 
+    // Arbitrary data passed from generateBid to use in `reportAggregateWin()`.
+    std::optional<std::string> aggregate_win_signals;
+
     // How long it took to generate the bid.
     base::TimeDelta bid_duration;
 
diff --git a/content/browser/interest_group/interest_group_storage.cc b/content/browser/interest_group/interest_group_storage.cc
index 7d9cb26..4d21f059 100644
--- a/content/browser/interest_group/interest_group_storage.cc
+++ b/content/browser/interest_group/interest_group_storage.cc
@@ -5460,8 +5460,6 @@
       max_owner_storage_size_(MaxOwnerStorageSize()),
       max_ops_before_maintenance_(
           blink::features::kInterestGroupStorageMaxOpsBeforeMaintenance.Get()),
-      db_(std::make_unique<sql::Database>(GetDatabaseOptions(),
-                                          "InterestGroups")),
       db_maintenance_timer_(FROM_HERE,
                             kIdlePeriod,
                             this,
diff --git a/content/browser/interest_group/mock_auction_process_manager.cc b/content/browser/interest_group/mock_auction_process_manager.cc
index 84908b4a3..0638e76 100644
--- a/content/browser/interest_group/mock_auction_process_manager.cc
+++ b/content/browser/interest_group/mock_auction_process_manager.cc
@@ -159,6 +159,7 @@
     const std::optional<url::Origin>& browser_signal_top_level_seller_origin,
     const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
     std::optional<uint32_t> bidding_signals_data_version,
+    const std::optional<std::string>& aggregate_win_signals,
     uint64_t trace_id,
     ReportWinCallback report_win_callback) {
   // While the real BidderWorklet implementation supports multiple pending
@@ -306,9 +307,10 @@
 
   bids.push_back(auction_worklet::mojom::BidderWorkletBid::New(
       bid_role, "ad", *bid, bid_currency, /*ad_cost=*/std::nullopt,
-      std::move(ad_descriptor),
-      selected_buyer_and_seller_reporting_id_, ad_component_descriptors,
-      /*modeling_signals=*/std::nullopt, duration));
+      std::move(ad_descriptor), selected_buyer_and_seller_reporting_id_,
+      ad_component_descriptors,
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      duration));
   bids.insert(bids.end(), std::make_move_iterator(further_bids.begin()),
               std::make_move_iterator(further_bids.end()));
 
diff --git a/content/browser/interest_group/mock_auction_process_manager.h b/content/browser/interest_group/mock_auction_process_manager.h
index 426a822..ac4dc57c 100644
--- a/content/browser/interest_group/mock_auction_process_manager.h
+++ b/content/browser/interest_group/mock_auction_process_manager.h
@@ -116,6 +116,7 @@
       const std::optional<url::Origin>& browser_signal_top_level_seller_origin,
       const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
       std::optional<uint32_t> bidding_signals_data_version,
+      const std::optional<std::string>& aggregate_win_signals,
       uint64_t trace_id,
       ReportWinCallback report_win_callback) override;
   void ConnectDevToolsAgent(
diff --git a/content/browser/largest_contentful_paint_browsertests.cc b/content/browser/largest_contentful_paint_browsertests.cc
index 36df070b..63487e47 100644
--- a/content/browser/largest_contentful_paint_browsertests.cc
+++ b/content/browser/largest_contentful_paint_browsertests.cc
@@ -36,11 +36,6 @@
     return web_contents()->GetPrimaryFrameTree().root()->current_frame_host();
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    "ExposeRenderTimeNonTaoDelayedImage");
-  }
-
   EvalJsResult GetStartTime(std::string type) const {
     std::string script = content::JsReplace("getStartTime($1);", type);
     return EvalJs(shell(), script);
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 8127b79..978bf92 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -45,6 +45,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/service_worker_context_observer.h"
 #include "content/public/browser/service_worker_running_info.h"
 #include "content/public/browser/storage_usage_info.h"
@@ -127,13 +128,15 @@
           std::move(callback)));
 }
 
-void DidStartWorker(scoped_refptr<ServiceWorkerVersion> version,
-                    ServiceWorkerContext::StartWorkerCallback info_callback,
-                    ServiceWorkerContext::StatusCodeCallback failure_callback,
-                    blink::ServiceWorkerStatusCode start_worker_status) {
+void DidStartWorker(
+    scoped_refptr<ServiceWorkerVersion> version,
+    ServiceWorkerContext::StartWorkerCallback info_callback,
+    ServiceWorkerContext::StatusCodeResponseCallback failure_callback,
+    blink::ServiceWorkerStatusCode start_worker_status) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
-    std::move(failure_callback).Run(start_worker_status);
+    std::move(failure_callback)
+        .Run(StatusCodeResponse{.status_code = start_worker_status});
     return;
   }
   EmbeddedWorkerInstance* instance = version->embedded_worker();
@@ -144,12 +147,13 @@
 
 void FoundRegistrationForStartWorker(
     ServiceWorkerContext::StartWorkerCallback info_callback,
-    ServiceWorkerContext::StatusCodeCallback failure_callback,
+    ServiceWorkerContext::StatusCodeResponseCallback failure_callback,
     blink::ServiceWorkerStatusCode service_worker_status,
     scoped_refptr<ServiceWorkerRegistration> registration) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
-    std::move(failure_callback).Run(service_worker_status);
+    std::move(failure_callback)
+        .Run(StatusCodeResponse{.status_code = service_worker_status});
     return;
   }
 
@@ -165,7 +169,8 @@
   // However, if the installation is rejected, the installing version can go
   // away by the time we reach here from DidFindRegistrationForFindImpl.
   if (!version_ptr) {
-    std::move(failure_callback).Run(service_worker_status);
+    std::move(failure_callback)
+        .Run(StatusCodeResponse{.status_code = service_worker_status});
     return;
   }
 
@@ -808,7 +813,7 @@
     const GURL& scope,
     const blink::StorageKey& key,
     StartWorkerCallback info_callback,
-    StatusCodeCallback failure_callback) {
+    StatusCodeResponseCallback failure_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   FindRegistrationForScopeImpl(
       scope, key,
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index bc354c51..2516a79 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -220,10 +220,11 @@
                              CheckHasServiceWorkerCallback callback) override;
 
   void ClearAllServiceWorkersForTest(base::OnceClosure callback) override;
-  void StartWorkerForScope(const GURL& scope,
-                           const blink::StorageKey& key,
-                           StartWorkerCallback info_callback,
-                           StatusCodeCallback failure_callback) override;
+  void StartWorkerForScope(
+      const GURL& scope,
+      const blink::StorageKey& key,
+      StartWorkerCallback info_callback,
+      StatusCodeResponseCallback failure_callback) override;
   void StartServiceWorkerAndDispatchMessage(
       const GURL& scope,
       const blink::StorageKey& key,
diff --git a/content/browser/service_worker/service_worker_process_browsertest.cc b/content/browser/service_worker/service_worker_process_browsertest.cc
index 1fc3ac04..9dfe5f9 100644
--- a/content/browser/service_worker/service_worker_process_browsertest.cc
+++ b/content/browser/service_worker/service_worker_process_browsertest.cc
@@ -232,11 +232,10 @@
             worker_process_id = process_id;
             loop.Quit();
           }),
-      base::BindLambdaForTesting(
-          [&loop](blink::ServiceWorkerStatusCode status_code) {
-            ASSERT_FALSE(true) << "start worker failed";
-            loop.Quit();
-          }));
+      base::BindLambdaForTesting([&loop](StatusCodeResponse status) {
+        ASSERT_FALSE(true) << "start worker failed";
+        loop.Quit();
+      }));
   loop.Run();
 
   // The page and service worker are in different processes. (This is not
diff --git a/content/browser/web_contents/file_chooser_impl.cc b/content/browser/web_contents/file_chooser_impl.cc
index a85b27a..64a4910 100644
--- a/content/browser/web_contents/file_chooser_impl.cc
+++ b/content/browser/web_contents/file_chooser_impl.cc
@@ -209,6 +209,7 @@
   listener_impl_ = nullptr;
   if (!render_frame_host()) {
     std::move(callback_).Run(nullptr);
+    weak_factory_.InvalidateWeakPtrs();
     return;
   }
   storage::FileSystemContext* file_system_context = nullptr;
@@ -235,11 +236,14 @@
     }
   }
   std::move(callback_).Run(FileChooserResult::New(std::move(files), base_dir));
+  weak_factory_.InvalidateWeakPtrs();
 }
 
 void FileChooserImpl::FileSelectionCanceled() {
   listener_impl_ = nullptr;
   std::move(callback_).Run(nullptr);
+
+  weak_factory_.InvalidateWeakPtrs();
 }
 
 RenderFrameHostImpl* FileChooserImpl::render_frame_host() {
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index a8577b4..c2f0752 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -11614,6 +11614,12 @@
   return target_network_;
 }
 
+void WebContentsImpl::DisconnectFileSelectListenerIfAny() {
+  if (active_file_chooser_) {
+    active_file_chooser_->FileSelectionCanceled();
+  }
+}
+
 // static
 void WebContentsImpl::UpdateAttributionSupportAllRenderers() {
   for (WebContentsImpl* web_contents : GetAllWebContents()) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 8213039..22d9e683 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1002,6 +1002,7 @@
   BackForwardTransitionAnimationManager*
   GetBackForwardTransitionAnimationManager() override;
   net::handles::NetworkHandle GetTargetNetwork() override;
+  void DisconnectFileSelectListenerIfAny() override;
 
   void GetMediaCaptureRawDeviceIdsOpened(
       blink::mojom::MediaStreamType type,
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 4197d93..58ed054 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -3190,6 +3190,44 @@
 }
 
 IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
+                       DisconnectFileChooserListener) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  shell()->set_hold_file_chooser();
+
+  GURL url = embedded_test_server()->GetURL("/click-noreferrer-links.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url));
+
+  WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
+  auto [chooser, remote] =
+      FileChooserImpl::CreateForTesting(wc->GetPrimaryMainFrame());
+  base::WeakPtr<FileChooserImpl> chooser_weak_ptr = chooser->GetWeakPtr();
+
+  // Request file chooser.
+  base::RunLoop run_loop;
+  base::OnceClosure quit_closure = run_loop.QuitClosure();
+  blink::mojom::FileChooserResultPtr result_received;
+  remote->OpenFileChooser(blink::mojom::FileChooserParams::New(),
+                          base::BindLambdaForTesting(
+                              [&](blink::mojom::FileChooserResultPtr result) {
+                                result_received = std::move(result);
+                                std::move(quit_closure).Run();
+                              }));
+  remote.FlushForTesting();
+  EXPECT_EQ(shell()->run_file_chooser_count(), 1u);
+
+  // Disconnect listener.
+  wc->DisconnectFileSelectListenerIfAny();
+
+  // Send result from listener, which now should be ignored.
+  shell()->held_file_chooser_listener()->FileSelected(
+      {}, base::FilePath(), blink::mojom::FileChooserParams::Mode::kOpen);
+  run_loop.Run();
+
+  // Check that request was cancelled, ie returned null result.
+  EXPECT_FALSE(result_received);
+}
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
                        FrameDetachInCopyDoesNotCrash) {
   ASSERT_TRUE(embedded_test_server()->Start());
   EXPECT_TRUE(NavigateToURL(
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java
index d7382882..2f607fd 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectActionMenuHelper.java
@@ -31,13 +31,11 @@
 import org.chromium.base.PackageManagerUtils;
 import org.chromium.base.StrictModeContext;
 import org.chromium.content.R;
-import org.chromium.content_public.browser.ContentFeatureMap;
 import org.chromium.content_public.browser.SelectionClient;
 import org.chromium.content_public.browser.SelectionClient.Result;
 import org.chromium.content_public.browser.SelectionMenuGroup;
 import org.chromium.content_public.browser.SelectionMenuItem;
 import org.chromium.content_public.browser.selection.SelectionActionMenuDelegate;
-import org.chromium.content_public.common.ContentFeatures;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -233,8 +231,7 @@
         menuItemBuilders.add(selectAll(delegate.canSelectAll()));
         menuItemBuilders.add(webSearch(context, delegate.canWebSearch()));
         menuItemBuilders.add(pasteAsPlainText(context, delegate.canPasteAsPlainText()));
-        if (ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION)
-                && selectionActionMenuDelegate != null) {
+        if (selectionActionMenuDelegate != null) {
             selectionActionMenuDelegate.modifyDefaultMenuItems(
                     menuItemBuilders, isSelectionPassword, isSelectionReadOnly, selectedText);
         }
@@ -323,8 +320,7 @@
         }
         List<ResolveInfo> supportedActivities =
                 PackageManagerUtils.queryIntentActivities(createProcessTextIntent(), 0);
-        if (ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION) &&
-                selectionActionMenuDelegate != null) {
+        if (selectionActionMenuDelegate != null) {
             supportedActivities =
                     selectionActionMenuDelegate.filterTextProcessingActivities(supportedActivities);
         }
@@ -361,8 +357,7 @@
     private static void addAdditionalTextProcessingItems(
             SelectionMenuGroup textProcessingItems,
             SelectionActionMenuDelegate selectionActionMenuDelegate) {
-        if (ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION)
-                && selectionActionMenuDelegate != null) {
+        if (selectionActionMenuDelegate != null) {
             textProcessingItems.addItems(
                     selectionActionMenuDelegate.getAdditionalTextProcessingItems());
         }
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
index b6cd3605..aba04f42 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -58,7 +58,6 @@
 import org.chromium.content.browser.webcontents.WebContentsImpl.UserDataFactory;
 import org.chromium.content_public.browser.ActionModeCallback;
 import org.chromium.content_public.browser.ActionModeCallbackHelper;
-import org.chromium.content_public.browser.ContentFeatureMap;
 import org.chromium.content_public.browser.ImeEventObserver;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.SelectAroundCaretResult;
@@ -69,7 +68,6 @@
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.content_public.browser.selection.SelectionActionMenuDelegate;
 import org.chromium.content_public.browser.selection.SelectionDropdownMenuDelegate;
-import org.chromium.content_public.common.ContentFeatures;
 import org.chromium.ui.base.Clipboard;
 import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.ViewAndroidDelegate;
@@ -986,16 +984,13 @@
             Menu menu,
             Map<MenuItem, View.OnClickListener> customMenuItemClickListeners,
             @Nullable MenuItem.OnMenuItemClickListener additionalMenuItemClickListener) {
-        boolean isSelectionMenuOrderCorrectionEnabled =
-                ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION);
         for (SelectionMenuGroup group : menuGroups) {
             addMenuItemsToActionMenu(
                     context,
                     group,
                     menu,
                     customMenuItemClickListeners,
-                    additionalMenuItemClickListener,
-                    isSelectionMenuOrderCorrectionEnabled);
+                    additionalMenuItemClickListener);
         }
     }
 
@@ -1009,8 +1004,7 @@
             SelectionMenuGroup group,
             Menu menu,
             Map<MenuItem, View.OnClickListener> customMenuItemClickListeners,
-            @Nullable MenuItem.OnMenuItemClickListener additionalMenuItemClickListener,
-            boolean isSelectionMenuOrderCorrectionEnabled) {
+            @Nullable MenuItem.OnMenuItemClickListener additionalMenuItemClickListener) {
         // All menu items and groups are sorted already at this point, so this is just passing
         // 1-indexed value as order.
         int menuItemCount = menu.size();
@@ -1021,10 +1015,7 @@
                 continue;
             }
             MenuItem menuItem =
-                    menu.add(group.id, item.id, isSelectionMenuOrderCorrectionEnabled
-                                            ? ++menuItemCount
-                                            : item.orderInCategory,
-                                    item.getTitle(context))
+                    menu.add(group.id, item.id, ++menuItemCount, item.getTitle(context))
                             .setShowAsActionFlags(item.showAsActionFlags);
             @Nullable Drawable icon = item.getIcon(context);
             if (icon != null) {
@@ -1117,11 +1108,8 @@
                 SelectActionMenuHelper.getTextProcessingItems(
                         mContext, false, false, this::processText, mSelectionActionMenuDelegate);
         if (!textProcessingItems.items.isEmpty()) {
-            boolean isSelectionMenuOrderCorrectionEnabled =
-                    ContentFeatureMap.isEnabled(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION);
             addMenuItemsToActionMenu(
-                    mContext, textProcessingItems, menu, mCustomActionMenuItemClickListeners, null,
-                    isSelectionMenuOrderCorrectionEnabled);
+                    mContext, textProcessingItems, menu, mCustomActionMenuItemClickListeners, null);
         }
     }
 
diff --git a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java
index 85f6214..292defe9 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectActionMenuHelperTest.java
@@ -28,12 +28,10 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features;
 import org.chromium.content.R;
 import org.chromium.content_public.browser.SelectionMenuGroup;
 import org.chromium.content_public.browser.SelectionMenuItem;
 import org.chromium.content_public.browser.selection.SelectionActionMenuDelegate;
-import org.chromium.content_public.common.ContentFeatures;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -124,7 +122,6 @@
 
     @Test
     @Feature({"TextInput"})
-    @Features.EnableFeatures({ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION})
     public void testDefaultMenuItemsOrder() {
         SelectionMenuGroup menuGroup =
                 SelectActionMenuHelper.getDefaultItems(
@@ -147,7 +144,6 @@
 
     @Test
     @Feature({"TextInput"})
-    @Features.EnableFeatures({ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION})
     public void testDefaultMenuItemsOrderUsingSelectionActionMenuDelegate() {
         SelectionActionMenuDelegate selectionActionMenuDelegate =
                 new TestSelectionActionMenuDelegate();
@@ -172,7 +168,6 @@
 
     @Test
     @Feature({"TextInput"})
-    @Features.EnableFeatures({ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION})
     public void testGetTextProcessingItems() {
         ContextUtils.initApplicationContextForTests(mContext);
         List<ResolveInfo> list2 = new ArrayList();
diff --git a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java
index 0645b8bf..035aa4de 100644
--- a/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java
+++ b/content/public/android/junit/src/org/chromium/content/browser/selection/SelectionPopupControllerTest.java
@@ -66,7 +66,6 @@
 import org.chromium.content_public.browser.selection.SelectionActionMenuDelegate;
 import org.chromium.content_public.browser.selection.SelectionDropdownMenuDelegate;
 import org.chromium.content_public.browser.test.util.TestSelectionDropdownMenuDelegate;
-import org.chromium.content_public.common.ContentFeatures;
 import org.chromium.ui.base.ViewAndroidDelegate;
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.mojom.MenuSourceType;
@@ -177,7 +176,6 @@
 
         mTestValues = new FeatureList.TestValues();
         setDropdownMenuFeatureEnabled(false);
-        mTestValues.addFeatureFlagOverride(ContentFeatures.SELECTION_MENU_ITEM_MODIFICATION, true);
         FeatureList.setTestValues(mTestValues);
 
         SelectionPopupControllerImpl.setDisableMagnifierForTesting(true);
diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h
index 8461bd4b..1639cbb 100644
--- a/content/public/browser/service_worker_context.h
+++ b/content/public/browser/service_worker_context.h
@@ -20,6 +20,7 @@
 #include "content/public/browser/service_worker_running_info.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/messaging/transferable_message.h"
+#include "third_party/blink/public/common/service_worker/extended_service_worker_status_code.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-forward.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom-forward.h"
@@ -50,6 +51,12 @@
 struct ServiceWorkerRunningInfo;
 struct StorageUsageInfo;
 
+struct StatusCodeResponse {
+  blink::ServiceWorkerStatusCode status_code;
+  std::optional<blink::ExtendedServiceWorkerStatusCode> extended_status_code =
+      std::nullopt;
+};
+
 enum class ServiceWorkerCapability {
   NO_SERVICE_WORKER,
   SERVICE_WORKER_NO_FETCH_HANDLER,
@@ -120,6 +127,9 @@
   using StatusCodeCallback =
       base::OnceCallback<void(blink::ServiceWorkerStatusCode status_code)>;
 
+  using StatusCodeResponseCallback =
+      base::OnceCallback<void(StatusCodeResponse)>;
+
   using StartServiceWorkerForNavigationHintCallback = base::OnceCallback<void(
       StartServiceWorkerForNavigationHintResult result)>;
 
@@ -254,10 +264,11 @@
   //
   // There is no guarantee about whether the callback is called synchronously or
   // asynchronously.
-  virtual void StartWorkerForScope(const GURL& scope,
-                                   const blink::StorageKey& key,
-                                   StartWorkerCallback info_callback,
-                                   StatusCodeCallback failure_callback) = 0;
+  virtual void StartWorkerForScope(
+      const GURL& scope,
+      const blink::StorageKey& key,
+      StartWorkerCallback info_callback,
+      StatusCodeResponseCallback failure_callback) = 0;
 
   // Starts the active worker of the registration for the given `scope` and
   // `key` and dispatches the given `message` to the service worker.
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index 0de63e4..e9c6a19 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -1644,6 +1644,10 @@
   // be bound.
   virtual net::handles::NetworkHandle GetTargetNetwork() = 0;
 
+  // Disconnect any outstanding `FileSelectListener` so that any calls will be
+  // no-op.
+  virtual void DisconnectFileSelectListenerIfAny() = 0;
+
  private:
   // This interface should only be implemented inside content.
   friend class WebContentsImpl;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 48f0f1e..c58d247c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -1364,13 +1364,6 @@
              "ReduceGpuPriorityOnBackground",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Apply text selection menu order correction logic for Android.
-// TODO(crbug.com/40947146) This is a kill switch landed in M122.
-// Please remove after M124.
-BASE_FEATURE(kSelectionMenuItemModification,
-             "SelectionMenuItemModification",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Screen Capture API support for Android.
 // This should not be enabled unless ENABLE_SCREEN_CAPTURE is on, otherwise
 // it won't work.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 4fe18a5..1588b471 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -302,7 +302,6 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(
     kGinJavaBridgeMojoSkipClearObjectsOnMainDocumentReady);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kReduceGpuPriorityOnBackground);
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kSelectionMenuItemModification);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSmartZoom);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kUserMediaScreenCapturing);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kWebViewSuppressTapDuringFling);
diff --git a/content/public/test/fake_service_worker_context.cc b/content/public/test/fake_service_worker_context.cc
index 7673e97..aa3da06 100644
--- a/content/public/test/fake_service_worker_context.cc
+++ b/content/public/test/fake_service_worker_context.cc
@@ -97,7 +97,7 @@
     const GURL& scope,
     const blink::StorageKey& key,
     ServiceWorkerContext::StartWorkerCallback info_callback,
-    ServiceWorkerContext::StatusCodeCallback failure_callback) {
+    ServiceWorkerContext::StatusCodeResponseCallback failure_callback) {
   NOTREACHED();
 }
 
diff --git a/content/public/test/fake_service_worker_context.h b/content/public/test/fake_service_worker_context.h
index ad1fad7e..cb058158 100644
--- a/content/public/test/fake_service_worker_context.h
+++ b/content/public/test/fake_service_worker_context.h
@@ -77,7 +77,8 @@
       const GURL& scope,
       const blink::StorageKey& key,
       ServiceWorkerContext::StartWorkerCallback info_callback,
-      ServiceWorkerContext::StatusCodeCallback failure_callback) override;
+      ServiceWorkerContext::StatusCodeResponseCallback failure_callback)
+      override;
   bool IsLiveStartingServiceWorker(int64_t service_worker_version_id) override;
   bool IsLiveRunningServiceWorker(int64_t service_worker_version_id) override;
   service_manager::InterfaceProvider& GetRemoteInterfaces(
diff --git a/content/services/auction_worklet/auction_v8_helper_unittest.cc b/content/services/auction_worklet/auction_v8_helper_unittest.cc
index a39cabc..406752d2 100644
--- a/content/services/auction_worklet/auction_v8_helper_unittest.cc
+++ b/content/services/auction_worklet/auction_v8_helper_unittest.cc
@@ -123,6 +123,7 @@
       const std::optional<url::Origin>& browser_signal_top_level_seller_origin,
       const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
       std::optional<uint32_t> bidding_data_version,
+      const std::optional<std::string>& aggregate_win_signals,
       uint64_t trace_id,
       ReportWinCallback report_win_callback) override {
     ADD_FAILURE() << "ReportWin shouldn't be called on DebugConnector";
diff --git a/content/services/auction_worklet/bidder_worklet.cc b/content/services/auction_worklet/bidder_worklet.cc
index 7573913..83a1a3d 100644
--- a/content/services/auction_worklet/bidder_worklet.cc
+++ b/content/services/auction_worklet/bidder_worklet.cc
@@ -631,6 +631,7 @@
     const std::optional<url::Origin>& browser_signal_top_level_seller_origin,
     const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
     std::optional<uint32_t> bidding_signals_data_version,
+    const std::optional<std::string>& aggregate_win_signals,
     uint64_t trace_id,
     ReportWinCallback report_win_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(user_sequence_checker_);
@@ -674,6 +675,7 @@
   report_win_task->browser_signal_reporting_timeout =
       browser_signal_reporting_timeout;
   report_win_task->bidding_signals_data_version = bidding_signals_data_version;
+  report_win_task->aggregate_win_signals = aggregate_win_signals;
   report_win_task->trace_id = trace_id;
   report_win_task->callback = std::move(report_win_callback);
 
@@ -890,6 +892,7 @@
     const std::optional<url::Origin>& browser_signal_top_level_seller_origin,
     const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
     const std::optional<uint32_t>& bidding_signals_data_version,
+    const std::optional<std::string>& aggregate_win_signals,
     uint64_t trace_id,
     ReportWinCallbackInternal callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
@@ -2615,7 +2618,8 @@
           std::move(task->browser_signal_seller_origin),
           std::move(task->browser_signal_top_level_seller_origin),
           std::move(task->browser_signal_reporting_timeout),
-          std::move(task->bidding_signals_data_version), task->trace_id,
+          std::move(task->bidding_signals_data_version),
+          std::move(task->aggregate_win_signals), task->trace_id,
           base::BindOnce(&BidderWorklet::DeliverReportWinOnUserThread,
                          weak_ptr_factory_.GetWeakPtr(), task)));
 }
diff --git a/content/services/auction_worklet/bidder_worklet.h b/content/services/auction_worklet/bidder_worklet.h
index 120676eb..dc61a4ed 100644
--- a/content/services/auction_worklet/bidder_worklet.h
+++ b/content/services/auction_worklet/bidder_worklet.h
@@ -221,6 +221,7 @@
       const std::optional<url::Origin>& browser_signal_top_level_seller_origin,
       const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
       std::optional<uint32_t> bidding_signals_data_version,
+      const std::optional<std::string>& aggregate_win_signals,
       uint64_t trace_id,
       ReportWinCallback report_win_callback) override;
   void ConnectDevToolsAgent(
@@ -360,6 +361,7 @@
     bool browser_signal_made_highest_scoring_other_bid;
     std::optional<double> browser_signal_ad_cost;
     std::optional<uint16_t> browser_signal_modeling_signals;
+    std::optional<std::string> aggregate_win_signals;
     uint8_t browser_signal_join_count;
     uint8_t browser_signal_recency;
     url::Origin browser_signal_seller_origin;
@@ -526,6 +528,7 @@
             browser_signal_top_level_seller_origin,
         const std::optional<base::TimeDelta> browser_signal_reporting_timeout,
         const std::optional<uint32_t>& bidding_signals_data_version,
+        const std::optional<std::string>& aggregate_win_signals,
         uint64_t trace_id,
         ReportWinCallbackInternal callback);
 
diff --git a/content/services/auction_worklet/bidder_worklet_unittest.cc b/content/services/auction_worklet/bidder_worklet_unittest.cc
index 0a3e356..dd65d71 100644
--- a/content/services/auction_worklet/bidder_worklet_unittest.cc
+++ b/content/services/auction_worklet/bidder_worklet_unittest.cc
@@ -407,6 +407,7 @@
     browser_signal_made_highest_scoring_other_bid_ = false;
     browser_signal_ad_cost_.reset();
     browser_signal_modeling_signals_.reset();
+    aggregate_win_signals_.reset();
     browser_signal_join_count_ = 2;
     browser_signal_recency_report_win_ = 5;
     data_version_.reset();
@@ -441,7 +442,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         expected_data_version);
   }
 
@@ -641,6 +643,7 @@
         browser_signal_recency_report_win_, browser_signal_seller_origin_,
         browser_signal_top_level_seller_origin_,
         browser_signal_reporting_timeout_, data_version_,
+        aggregate_win_signals_,
 
         /*trace_id=*/1,
         base::BindOnce(
@@ -1069,6 +1072,7 @@
   bool browser_signal_made_highest_scoring_other_bid_;
   std::optional<double> browser_signal_ad_cost_;
   std::optional<uint16_t> browser_signal_modeling_signals_;
+  std::optional<std::string> aggregate_win_signals_;
   uint8_t browser_signal_recency_report_win_;
 
   // Used for reportWin();
@@ -1256,7 +1260,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Wait until idle to ensure all requests have been observed within the
   // `auction_network_events_handler_`.
@@ -1276,7 +1281,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Make sure "ad" can be of a variety of JS object types.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -1288,7 +1294,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1298,7 +1305,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: {a:1,b:null}, bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1308,7 +1316,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: [2.5,[]], bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1318,7 +1327,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: -5, bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1327,7 +1337,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   // Some values that can't be represented in JSON become null.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: 0/0, bid:1, render:"https://response.test/"})",
@@ -1337,7 +1348,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: [globalThis.not_defined], bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1347,7 +1359,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: [function() {return 1;}], bid:1, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1357,7 +1370,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Other values JSON can't represent result in failing instead of null.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -1417,7 +1431,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_FALSE(generate_bid_metrics_->script_timed_out);
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid:1.5, render:"https://response.test/"})",
@@ -1428,7 +1443,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid:2, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1439,7 +1455,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid:0.001, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1450,7 +1467,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Bids <= 0.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -1494,7 +1512,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:true, render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1504,7 +1523,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:[1], render:"https://response.test/"})",
       mojom::BidderWorkletBid::New(
@@ -1514,7 +1534,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: ["ad"], bid:[1,2], render:"https://response.test/"})",
       /*expected_bids=*/mojom::BidderWorkletBidPtr(),
@@ -1567,7 +1588,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(reject_reason_, mojom::RejectReason::kNotAvailable);
 
   // Not specifying currency explicitly results in nullopt tag.
@@ -1581,7 +1603,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(reject_reason_, mojom::RejectReason::kNotAvailable);
 
   // Trying to explicitly specify kUnspecifiedAdCurrency fails.
@@ -1633,7 +1656,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(reject_reason_, mojom::RejectReason::kNotAvailable);
 
   // Explicitly specified incorrect one.
@@ -1658,7 +1682,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(reject_reason_, mojom::RejectReason::kNotAvailable);
 }
 
@@ -1688,7 +1713,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Missing value with bid <= 0 is considered a valid no-bid case.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -1896,7 +1922,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad",
         bid:1,
@@ -1925,7 +1952,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Auction should fail if adComponents in return value is an unexpected type.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -2008,7 +2036,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           std::move(expected_descriptors),
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Results with 41 or more values are rejected.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -2133,7 +2162,8 @@
               blink::AdDescriptor(GURL("https://ad_component.test/")) /* 24 */,
               blink::AdDescriptor(GURL("https://ad_component.test/")) /* 25 */,
           },
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Results with 26 or more values are rejected.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -2289,7 +2319,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/123u, base::TimeDelta()));
+          /*modeling_signals=*/123u, /*aggregate_win_signals=*/std::nullopt,
+          base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsNaN) {
@@ -2304,7 +2335,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsInfinity) {
@@ -2319,7 +2351,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsNegative) {
@@ -2334,7 +2367,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsNegativeInfinity) {
@@ -2349,7 +2383,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsNegativeZero) {
@@ -2364,7 +2399,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsPositiveZero) {
@@ -2379,7 +2415,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/0u, base::TimeDelta()));
+          /*modeling_signals=*/0u, /*aggregate_win_signals=*/std::nullopt,
+          base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsNonInteger) {
@@ -2394,7 +2431,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/123u, base::TimeDelta()));
+          /*modeling_signals=*/123u, /*aggregate_win_signals=*/std::nullopt,
+          base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsTooLarge) {
@@ -2409,7 +2447,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidModelingSignalsAlmostTooLarge) {
@@ -2424,7 +2463,88 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/4095, base::TimeDelta()));
+          /*modeling_signals=*/4095, /*aggregate_win_signals=*/std::nullopt,
+          base::TimeDelta()));
+}
+
+class PrivateModelTrainingEnabledTest : public BidderWorkletTest {
+ public:
+  PrivateModelTrainingEnabledTest() {
+    feature_list_.InitAndEnableFeature(
+        blink::features::kFledgePrivateModelTraining);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(PrivateModelTrainingEnabledTest, GenerateBidAggregateWinSignals) {
+  const std::string kGenerateBidBody =
+      R"({bid:1, render:"https://response.test/",
+         aggregateWinSignals: {
+           "test_array":[1,2,3],
+           "test_number":1.0,
+           "test_string":"hello"}
+         })";
+
+  RunGenerateBidWithReturnValueExpectingResult(
+      kGenerateBidBody,
+      mojom::BidderWorkletBid::New(
+          auction_worklet::mojom::BidRole::kUnenforcedKAnon, "null", 1,
+          /*bid_currency=*/std::nullopt, /*ad_cost=*/std::nullopt,
+          blink::AdDescriptor(GURL("https://response.test/")),
+          /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
+          /*ad_component_descriptors=*/std::nullopt,
+          /*modeling_signals=*/
+          std::nullopt,
+          /*aggregate_win_signals=*/
+          R"({"test_array":[1,2,3],"test_number":1.0,"test_string":"hello"})",
+          base::TimeDelta()));
+}
+
+TEST_F(PrivateModelTrainingEnabledTest, GenerateBidInvalidAggregateWinSignals) {
+  RunGenerateBidWithJavascriptExpectingResult(
+      R"(function generateBid() {
+         return {bid:1, render:"https://response.test/",
+         aggregateWinSignals: function() {}};
+       })",
+      /*expected_bids=*/mojom::BidderWorkletBidPtr(),
+      /*expected_data_version=*/std::nullopt,
+      {"https://url.test/ generateBid() bid has invalid aggregateWinSignals "
+       "value."});
+}
+
+class PrivateModelTrainingDisabledTest : public BidderWorkletTest {
+ public:
+  PrivateModelTrainingDisabledTest() {
+    feature_list_.InitAndDisableFeature(
+        blink::features::kFledgePrivateModelTraining);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(PrivateModelTrainingDisabledTest,
+       GenerateBidModelingSignalsNotAffected) {
+  const std::string kGenerateBidBody =
+      R"({bid:1, render:"https://response.test/",
+         aggregateWinSignals: {
+           "test_array":[1,2,3],
+           "test_number":1.0,
+           "test_string":"hello"}
+         })";
+
+  RunGenerateBidWithReturnValueExpectingResult(
+      kGenerateBidBody,
+      mojom::BidderWorkletBid::New(
+          auction_worklet::mojom::BidRole::kUnenforcedKAnon, "null", 1,
+          /*bid_currency=*/std::nullopt, /*ad_cost=*/std::nullopt,
+          blink::AdDescriptor(GURL("https://response.test/")),
+          /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
+          /*ad_component_descriptors=*/std::nullopt,
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidReturnValueInvalid) {
@@ -2826,7 +2946,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       {"https://url.test/:10 Uncaught something."});
 }
@@ -2895,7 +3016,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: "ad", bid: 5,
@@ -3070,7 +3192,8 @@
               blink::AdDescriptor(GURL("https://ad_component.test/")),
               blink::AdDescriptor(GURL("https://ad_component2.test/")),
               blink::AdDescriptor(GURL("https://ad_component3.test/"))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Can't say that more is mandatory than target.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -3148,7 +3271,8 @@
           std::vector<blink::AdDescriptor>{
               blink::AdDescriptor(GURL("https://ad_component.test/")),
               blink::AdDescriptor(GURL("https://ad_component2.test/"))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, TargetNumAdComponentsKAnon) {
@@ -3175,7 +3299,8 @@
       std::vector<blink::AdDescriptor>{
           blink::AdDescriptor(GURL("https://ad_component.test/")),
           blink::AdDescriptor(GURL("https://ad_component2.test/"))},
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   interest_group_ad_components_->emplace_back(
       GURL("https://ad_component2.test/"), /*metadata=*/std::nullopt);
@@ -3233,7 +3358,8 @@
         std::vector<blink::AdDescriptor>{
             blink::AdDescriptor(GURL("https://ad_component2.test/")),
             blink::AdDescriptor(GURL("https://ad_component4.test/"))},
-        /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+        /*modeling_signals=*/std::nullopt,
+        /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
     expected.push_back(non_k_anon_bid->Clone());
     RunGenerateBidWithReturnValueExpectingResult(kBid, std::move(expected));
   }
@@ -3253,7 +3379,8 @@
                 std::vector<blink::AdDescriptor>{
                     blink::AdDescriptor(GURL("https://ad_component.test/")),
                     blink::AdDescriptor(GURL("https://ad_component2.test/"))},
-                /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+                /*modeling_signals=*/std::nullopt,
+                /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, TargetAndMandatoryAdComponentsKAnon) {
@@ -3281,7 +3408,8 @@
       std::vector<blink::AdDescriptor>{
           blink::AdDescriptor(GURL("https://ad_component.test/")),
           blink::AdDescriptor(GURL("https://ad_component2.test/"))},
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   interest_group_ad_components_->emplace_back(
       GURL("https://ad_component2.test/"), /*metadata=*/std::nullopt);
@@ -3350,7 +3478,8 @@
         std::vector<blink::AdDescriptor>{
             blink::AdDescriptor(GURL("https://ad_component.test/")),
             blink::AdDescriptor(GURL("https://ad_component3.test/"))},
-        /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+        /*modeling_signals=*/std::nullopt,
+        /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
     expected.push_back(non_k_anon_bid->Clone());
     RunGenerateBidWithReturnValueExpectingResult(kBid, std::move(expected));
   }
@@ -3370,7 +3499,8 @@
                 std::vector<blink::AdDescriptor>{
                     blink::AdDescriptor(GURL("https://ad_component.test/")),
                     blink::AdDescriptor(GURL("https://ad_component2.test/"))},
-                /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+                /*modeling_signals=*/std::nullopt,
+                /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidMultiBid) {
@@ -3383,7 +3513,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   // Empty array means no-bid.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -3564,7 +3695,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   auto expected_bid3 = mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kUnenforcedKAnon, "\"lad\"", 8,
@@ -3573,7 +3705,8 @@
       blink::AdDescriptor(GURL("https://response3.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   // Can set an array with one entry.
   RunGenerateBidWithJavascriptExpectingResult(
@@ -3690,7 +3823,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_bidding_url_ = GURL("https://[::1]:40000/");
   RunGenerateBidWithReturnValueExpectingResult(
@@ -3702,7 +3836,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidInterestGroupName) {
@@ -3719,7 +3854,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_name_ = R"("foo")";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -3731,7 +3867,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_name_ = "[1]";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -3743,7 +3880,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest,
@@ -3885,7 +4023,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_bidding_url_ = GURL("https://url.test/foo");
   RunGenerateBidWithReturnValueExpectingResult(
@@ -3897,7 +4036,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 // Check that accessing `biddingLogicUrl` displays a warning.
@@ -3987,7 +4127,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_wasm_url_ = GURL(kWasmUrl);
   AddResponse(&url_loader_factory_, GURL(kWasmUrl), kWasmMimeType,
@@ -4002,7 +4143,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 // Check that accessing `biddingWasmHelperUrl` displays a warning.
@@ -4110,7 +4252,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       kGenerateBidBodyUsingDeprecatedDailyUpdateUrl,
       mojom::BidderWorkletBid::New(
@@ -4120,7 +4263,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   update_url_ = GURL("https://url.test/daily_update");
   RunGenerateBidWithReturnValueExpectingResult(
@@ -4133,7 +4277,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   RunGenerateBidWithReturnValueExpectingResult(
       kGenerateBidBodyUsingDeprecatedDailyUpdateUrl,
       mojom::BidderWorkletBid::New(
@@ -4144,7 +4289,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 // Check that accessing `updateUrl` displays a warning.
@@ -4280,7 +4426,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_trusted_bidding_signals_url_ =
       GURL("https://signals.test/foo.json");
@@ -4301,7 +4448,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 // Check that accessing `trustedBiddingSignalsUrl` displays a warning.
@@ -4409,7 +4557,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // 0-length but non-null key list.
   interest_group_trusted_bidding_signals_keys_.emplace();
@@ -4421,7 +4570,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_trusted_bidding_signals_keys_->push_back("2");
   interest_group_trusted_bidding_signals_keys_->push_back("1");
@@ -4435,7 +4585,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidInterestGroupUserBiddingSignals) {
@@ -4455,7 +4606,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_user_bidding_signals_ = R"("foo")";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -4467,7 +4619,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_user_bidding_signals_ = "[1]";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -4478,7 +4631,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   interest_group_user_bidding_signals_ = std::nullopt;
   RunGenerateBidWithReturnValueExpectingResult(
@@ -4489,7 +4643,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 // Test multiple GenerateBid calls on a single worklet, in parallel. Do this
@@ -5305,7 +5460,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   auction_signals_ = "[1]";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -5316,7 +5472,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidPerBuyerSignals) {
@@ -5339,7 +5496,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   per_buyer_signals_ = "[1]";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -5350,7 +5508,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   per_buyer_signals_ = std::nullopt;
   RunGenerateBidWithReturnValueExpectingResult(
@@ -5361,7 +5520,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest,
@@ -5380,7 +5540,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   direct_from_seller_auction_signals_header_ad_slot_ = "[1]";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -5391,7 +5552,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest,
@@ -5410,7 +5572,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   direct_from_seller_per_buyer_signals_header_ad_slot_ = "[1]";
   RunGenerateBidWithReturnValueExpectingResult(
@@ -5421,7 +5584,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidBrowserSignalSellerOrigin) {
@@ -5436,7 +5600,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   browser_signal_seller_origin_ =
       url::Origin::Create(GURL("https://[::1]:40000/"));
@@ -5449,7 +5614,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidBrowserSignalsAdComponentsLimit) {
@@ -5492,7 +5658,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Need to set `allowComponentAuction` to true for a bid to be created when
   // topLevelSeller is non-null.
@@ -5508,7 +5675,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidBrowserSignalTopWindowOrigin) {
@@ -5522,7 +5690,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidBrowserSignalJoinCountBidCount) {
@@ -5549,7 +5718,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
     *test_case.value_ptr = 10;
     RunGenerateBidWithReturnValueExpectingResult(
@@ -5562,7 +5732,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
     SetDefaultParameters();
   }
 }
@@ -5605,7 +5776,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Make sure `metadata` is treated as an object, instead of a raw string.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -5617,7 +5789,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 // Verify generateBid can see the reporting Ids when
@@ -5646,7 +5819,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Ad with only selectable reporting ids.
   interest_group_ads_.clear();
@@ -5669,7 +5843,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest,
@@ -5700,7 +5875,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidReturnsSelectedReportingId) {
@@ -5727,7 +5903,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/"selectable_id1",
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidWithInvalidSelectedReportingId) {
@@ -5772,7 +5949,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidAdComponents) {
@@ -5786,7 +5964,8 @@
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           std::vector<blink::AdDescriptor>{
               blink::AdDescriptor(GURL("https://ad_component.test/"))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   // Empty, but non-null, adComponents field.
   RunGenerateBidWithReturnValueExpectingResult(
       R"({ad: 0, bid:1, render:"https://response.test/", adComponents:[]})",
@@ -5796,7 +5975,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           std::vector<blink::AdDescriptor>(),
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // An adComponent URL that's not in the InterestGroup's adComponents list
   // should fail.
@@ -5830,7 +6010,8 @@
           std::vector<blink::AdDescriptor>{
               blink::AdDescriptor(GURL("https://ad_component.test/")),
               blink::AdDescriptor(GURL("https://ad_component2.test/"))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 // Test behavior of the `allowComponentAuction` output field, which can block
@@ -5843,7 +6024,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   // Use a null `topLevelSellerOrigin`. `allowComponentAuction` value should be
   // ignored.
@@ -5954,15 +6136,17 @@
               /*charset=*/std::nullopt, ToyWasm());
 
   RunGenerateBidWithJavascriptExpectingResult(
-      bid_script, mojom::BidderWorkletBid::New(
-                      auction_worklet::mojom::BidRole::kUnenforcedKAnon,
-                      R"([{"name":"test_const","kind":"global"}])", 1,
-                      /*bid_currency=*/std::nullopt,
-                      /*ad_cost=*/std::nullopt,
-                      blink::AdDescriptor(GURL("https://response.test/")),
-                      /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
-                      /*ad_component_descriptors=*/std::nullopt,
-                      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      bid_script,
+      mojom::BidderWorkletBid::New(
+          auction_worklet::mojom::BidRole::kUnenforcedKAnon,
+          R"([{"name":"test_const","kind":"global"}])", 1,
+          /*bid_currency=*/std::nullopt,
+          /*ad_cost=*/std::nullopt,
+          blink::AdDescriptor(GURL("https://response.test/")),
+          /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
+          /*ad_component_descriptors=*/std::nullopt,
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, WasmReportWin) {
@@ -5998,7 +6182,7 @@
       browser_signal_modeling_signals_, browser_signal_join_count_,
       browser_signal_recency_report_win_, browser_signal_seller_origin_,
       browser_signal_top_level_seller_origin_,
-      browser_signal_reporting_timeout_, data_version_,
+      browser_signal_reporting_timeout_, data_version_, aggregate_win_signals_,
       /*trace_id=*/1,
       base::BindLambdaForTesting(
           [&run_loop](
@@ -6271,7 +6455,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   }
 }
 
@@ -6306,7 +6491,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(observed_requests += 1, url_loader_factory_.total_requests());
 
   // Request with TrustedBiddingSignals keys and null URL. No request should be
@@ -6321,7 +6507,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(observed_requests += 1, url_loader_factory_.total_requests());
 
   // Request with TrustedBiddingSignals URL and null keys. Request should be
@@ -6338,7 +6525,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(observed_requests += 2, url_loader_factory_.total_requests());
 
   // Request with TrustedBiddingSignals URL and empty keys. Request should be
@@ -6353,7 +6541,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(observed_requests += 2, url_loader_factory_.total_requests());
   url_loader_factory_.ClearResponses();
 
@@ -6379,7 +6568,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       {"Failed to load "
        "https://signals.test/"
@@ -6405,7 +6595,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
   EXPECT_EQ(observed_requests += 2, url_loader_factory_.total_requests());
 }
 
@@ -6440,7 +6631,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/
       {"Bidding signals URL https://signals.test/ is using outdated bidding "
@@ -6898,7 +7090,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/7u);
 }
 
@@ -6920,7 +7113,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/7u);
 }
 
@@ -6945,7 +7139,8 @@
           blink::AdDescriptor(GURL("https://response.test/replacement")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Test the no-bid version as well.
   RunGenerateBidWithJavascriptExpectingResult(
@@ -6988,7 +7183,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, GenerateBidTimedOut) {
@@ -7060,7 +7256,9 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, AuctionV8Helper::kScriptTimeout),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt,
+          AuctionV8Helper::kScriptTimeout),
       /*expected_data_version=*/std::nullopt,
       {"https://url.test/ execution of `generateBid` timed out."});
   EXPECT_TRUE(generate_bid_metrics_->script_timed_out);
@@ -7119,7 +7317,9 @@
           blink::AdDescriptor(GURL("https://response.test/replacement")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, AuctionV8Helper::kScriptTimeout),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt,
+          AuctionV8Helper::kScriptTimeout),
       /*expected_data_version=*/std::nullopt,
       {"https://url.test/ execution of `generateBid` timed out."});
   EXPECT_TRUE(generate_bid_metrics_->script_timed_out);
@@ -7186,7 +7386,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       {"https://url.test/ execution of `generateBid` timed out."});
 }
@@ -7252,7 +7453,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7326,7 +7528,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7396,7 +7599,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7417,7 +7621,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7438,7 +7643,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7459,7 +7665,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7483,7 +7690,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7506,7 +7714,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -7807,7 +8016,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   RunGenerateBidWithJavascriptExpectingResult(
       CreateBasicGenerateBidScriptWithExtraCode(
@@ -7819,7 +8029,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 TEST_F(BidderWorkletTest, DeleteBeforeReportWinCallback) {
@@ -7846,7 +8057,7 @@
       browser_signal_modeling_signals_, browser_signal_join_count_,
       browser_signal_recency_report_win_, browser_signal_seller_origin_,
       browser_signal_top_level_seller_origin_,
-      browser_signal_reporting_timeout_, data_version_,
+      browser_signal_reporting_timeout_, data_version_, aggregate_win_signals_,
       /*trace_id=*/1,
       base::BindOnce(
           [](const std::optional<GURL>& report_url,
@@ -7905,6 +8116,7 @@
           browser_signal_seller_origin_,
           browser_signal_top_level_seller_origin_,
           browser_signal_reporting_timeout_, data_version_,
+          aggregate_win_signals_,
           /*trace_id=*/1,
           base::BindLambdaForTesting(
               [&run_loop, &num_report_win_calls, i](
@@ -7962,6 +8174,7 @@
         browser_signal_recency_report_win_, browser_signal_seller_origin_,
         browser_signal_top_level_seller_origin_,
         browser_signal_reporting_timeout_, data_version_,
+        aggregate_win_signals_,
         /*trace_id=*/1,
         base::BindOnce(
             [](const std::optional<GURL>& report_url,
@@ -8558,7 +8771,8 @@
         browser_signal_modeling_signals_, browser_signal_join_count_,
         browser_signal_recency_report_win_, browser_signal_seller_origin_,
         browser_signal_top_level_seller_origin_,
-        browser_signal_reporting_timeout_, data_version_, /*trace_id=*/1,
+        browser_signal_reporting_timeout_, data_version_,
+        aggregate_win_signals_, /*trace_id=*/1,
         base::BindLambdaForTesting(
             [&run_loop](
                 const std::optional<GURL>& report_url,
@@ -10243,7 +10457,7 @@
       browser_signal_modeling_signals_, browser_signal_join_count_,
       browser_signal_recency_report_win_, browser_signal_seller_origin_,
       browser_signal_top_level_seller_origin_,
-      browser_signal_reporting_timeout_, data_version_,
+      browser_signal_reporting_timeout_, data_version_, aggregate_win_signals_,
       /*trace_id=*/1,
       base::BindOnce(
           [](const std::optional<GURL>& report_url,
@@ -10317,7 +10531,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{}, GURL("https://loss.url"),
       GURL("https://win.url"));
@@ -10333,7 +10548,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{}, GURL("https://loss.url"),
       /*expected_debug_win_report_url=*/std::nullopt);
@@ -10347,7 +10563,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{}, /*expected_debug_loss_report_url=*/std::nullopt,
       GURL("https://win.url"));
@@ -10925,7 +11142,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11109,7 +11327,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11187,7 +11406,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11388,7 +11608,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11487,7 +11708,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11517,7 +11739,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11549,7 +11772,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11604,7 +11828,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11642,7 +11867,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11680,7 +11906,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11750,7 +11977,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11793,7 +12021,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11865,7 +12094,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -11929,7 +12159,8 @@
             blink::AdDescriptor(GURL("https://response.test/")),
             /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
             /*ad_component_descriptors=*/std::nullopt,
-            /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+            /*modeling_signals=*/std::nullopt,
+            /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
         /*expected_data_version=*/std::nullopt,
         /*expected_errors=*/{},
         /*expected_debug_loss_report_url=*/std::nullopt,
@@ -12262,7 +12493,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -12285,7 +12517,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -12307,7 +12540,8 @@
         blink::AdDescriptor(GURL("https://response2.test/")),
         /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
         /*ad_component_descriptors=*/std::nullopt,
-        /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+        /*modeling_signals=*/std::nullopt,
+        /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
     expected.push_back(expected[0]->Clone());
     // k-anon-enforced bid will be 1.
     expected[1]->bid_role = auction_worklet::mojom::BidRole::kEnforcedKAnon;
@@ -12345,7 +12579,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -12379,7 +12614,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -12402,7 +12638,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -12424,7 +12661,8 @@
       blink::AdDescriptor(GURL("https://response2.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
   expected.push_back(mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kEnforcedKAnon, R"(["ad"])", 1,
       /*bid_currency=*/std::nullopt,
@@ -12432,7 +12670,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
 
   RunGenerateBidWithJavascriptExpectingResult(
       CreateGenerateBidScript(
@@ -12463,7 +12702,8 @@
           blink::AdDescriptor(GURL("https://response2.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{},
       /*expected_debug_loss_report_url=*/std::nullopt,
@@ -12496,7 +12736,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta());
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta());
 
   // 3 bids none of which are k-anon. This triggers a re-run, which is skipped
   // since there are no k-anon ads.
@@ -12588,7 +12829,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
   expected.push_back(mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kUnenforcedKAnon, "null", 9,
       /*bid_currency=*/std::nullopt,
@@ -12596,7 +12838,8 @@
       blink::AdDescriptor(GURL("https://response2.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
   expected.push_back(mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kUnenforcedKAnon, "null", 8,
       /*bid_currency=*/std::nullopt,
@@ -12604,7 +12847,8 @@
       blink::AdDescriptor(GURL("https://response3.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
   expected.push_back(mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kEnforcedKAnon, "null", 7,
       /*bid_currency=*/std::nullopt,
@@ -12612,7 +12856,8 @@
       blink::AdDescriptor(GURL("https://response4.test/")),
       /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
 
   RunGenerateBidWithJavascriptExpectingResult(kScript, std::move(expected));
 }
@@ -12651,7 +12896,8 @@
         blink::AdDescriptor(GURL("https://response2.test/")),
         /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
         /*ad_component_descriptors=*/std::nullopt,
-        /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+        /*modeling_signals=*/std::nullopt,
+        /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
     expected.push_back(mojom::BidderWorkletBid::New(
         auction_worklet::mojom::BidRole::kEnforcedKAnon, R"(["ad"])", 2,
         /*bid_currency=*/std::nullopt,
@@ -12659,7 +12905,8 @@
         blink::AdDescriptor(GURL("https://response.test/")),
         /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
         /*ad_component_descriptors=*/std::nullopt,
-        /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+        /*modeling_signals=*/std::nullopt,
+        /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
     RunGenerateBidWithJavascriptExpectingResult(kScript, std::move(expected));
   }
 }
@@ -12716,7 +12963,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/"non-k-anon-selected-id",
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
   expected.push_back(mojom::BidderWorkletBid::New(
       auction_worklet::mojom::BidRole::kEnforcedKAnon, R"(["ad"])", 1,
       /*bid_currency=*/std::nullopt,
@@ -12724,7 +12972,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/"k-anon-selected-id",
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
   RunGenerateBidWithJavascriptExpectingResult(kScript, std::move(expected));
 }
 
@@ -12783,7 +13032,8 @@
       blink::AdDescriptor(GURL("https://response.test/")),
       /*selected_buyer_and_seller_reporting_id=*/"non-k-anon-selected-id",
       /*ad_component_descriptors=*/std::nullopt,
-      /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+      /*modeling_signals=*/std::nullopt, /*aggregate_win_signals=*/std::nullopt,
+      base::TimeDelta()));
   RunGenerateBidWithJavascriptExpectingResult(
       kScript, std::move(expected),
       /*expected_data_version=*/std::nullopt,
@@ -13274,7 +13524,7 @@
       browser_signal_modeling_signals_, browser_signal_join_count_,
       browser_signal_recency_report_win_, browser_signal_seller_origin_,
       browser_signal_top_level_seller_origin_,
-      browser_signal_reporting_timeout_, data_version_,
+      browser_signal_reporting_timeout_, data_version_, aggregate_win_signals_,
 
       /*trace_id=*/1,
       base::BindOnce(
@@ -13410,7 +13660,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'render' field corresponds to an object that contains the url string
   // and size with units.
@@ -13434,7 +13685,8 @@
                             blink::AdSize::LengthUnit::kPixels)),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'render' field corresponds to an object that contains the url string
   // and size without units.
@@ -13458,7 +13710,8 @@
                             blink::AdSize::LengthUnit::kPixels)),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'render' field corresponds to an object with an invalid field.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -13491,7 +13744,8 @@
                             blink::AdSize::LengthUnit::kPixels)),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'render' field corresponds to an empty object.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -13531,7 +13785,8 @@
                             blink::AdSize::LengthUnit::kPixels)),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // Size is not convertible to string.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -13674,7 +13929,8 @@
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           std::vector<blink::AdDescriptor>{
               blink::AdDescriptor(GURL("https://ad_component.test/"))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'adComponents' field corresponds to an array of object that has the url
   // string and size with units.
@@ -13700,7 +13956,8 @@
               GURL("https://ad_component.test/"),
               blink::AdSize(100, blink::AdSize::LengthUnit::kScreenWidth, 50,
                             blink::AdSize::LengthUnit::kPixels))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'adComponents' field corresponds to an array of object that has the url
   // string and size without units.
@@ -13726,7 +13983,8 @@
               GURL("https://ad_component.test/"),
               blink::AdSize(100, blink::AdSize::LengthUnit::kPixels, 50,
                             blink::AdSize::LengthUnit::kPixels))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'adComponents' field corresponds to an array of objects.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -13756,7 +14014,8 @@
                   blink::AdSize(100, blink::AdSize::LengthUnit::kScreenWidth,
                                 50, blink::AdSize::LengthUnit::kPixels)),
               blink::AdDescriptor(GURL("https://ad_component.test/"))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'adComponents' field corresponds to an array of url string and objects.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -13788,7 +14047,8 @@
                                 50, blink::AdSize::LengthUnit::kPixels)),
               blink::AdDescriptor(GURL("https://ad_component.test/")),
               blink::AdDescriptor(GURL("https://ad_component.test/"))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'adComponents' field corresponds to an array of an object that has an
   // invalid field.
@@ -13833,7 +14093,8 @@
               GURL("https://ad_component.test/"),
               blink::AdSize(100, blink::AdSize::LengthUnit::kScreenWidth, 50,
                             blink::AdSize::LengthUnit::kPixels))},
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 
   // The 'adComponents' field corresponds to an array of an empty object.
   RunGenerateBidWithReturnValueExpectingResult(
@@ -14325,7 +14586,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{}, std::nullopt, std::nullopt,
       /*expected_set_priority=*/std::nullopt,
@@ -14427,7 +14689,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{}, std::nullopt, std::nullopt,
       /*expected_set_priority=*/std::nullopt,
@@ -14493,7 +14756,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       {"Failed to load https://signals.test/"
        "?hostname=top.window.test&keys=key1,key2&interestGroupNames=Fred HTTP "
@@ -14598,7 +14862,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()),
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()),
       /*expected_data_version=*/std::nullopt,
       /*expected_errors=*/{}, std::nullopt, std::nullopt,
       /*expected_set_priority=*/std::nullopt,
@@ -14926,7 +15191,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta());
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta());
 
       auto actual_bid = std::move(bids_[0]);
       EXPECT_EQ(expected_bid->bid_role, actual_bid->bid_role);
@@ -15232,7 +15498,8 @@
           blink::AdDescriptor(GURL("https://response.test/")),
           /*selected_buyer_and_seller_reporting_id=*/std::nullopt,
           /*ad_component_descriptors=*/std::nullopt,
-          /*modeling_signals=*/std::nullopt, base::TimeDelta()));
+          /*modeling_signals=*/std::nullopt,
+          /*aggregate_win_signals=*/std::nullopt, base::TimeDelta()));
 }
 
 }  // namespace
diff --git a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
index 7e1ef73..b4338d1 100644
--- a/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
+++ b/content/services/auction_worklet/public/mojom/bidder_worklet.mojom
@@ -101,6 +101,11 @@
   // `modelingSignals` returned by generateBid, if any.
   uint16? modeling_signals;
 
+  // `aggregateWinSignals` returned by `generateBid`, if present. This field
+  // allows the bidder to pass arbitrary JSON-serializable data that can be
+  // accessed within the `reportAggregateWin()` javascript function.
+  string? aggregate_win_signals;
+
   // How long it took to run the generateBid() script.
   mojo_base.mojom.TimeDelta bid_duration;
 };
@@ -596,6 +601,10 @@
   // `bidding_signals_data_version` The value of the Data-Version header served
   //  with the trusted bidding signals.
   //
+  // `aggregate_win_signals`:  The value of a JSON serializable object for
+  // `reportAggregateWin()`. Note that this is passed along with `reportWin()`,
+  // but is not a visible argument within the JavaScript function.
+  //
   // `trace_id` ID of a nestable asynchronous trace event of category `fledge`
   //  to use with tracing calls.
   //
@@ -649,6 +658,7 @@
       url.mojom.Origin? browser_signal_top_level_seller_origin,
       mojo_base.mojom.TimeDelta? browser_signal_reporting_timeout,
       uint32? bidding_signals_data_version,
+      string? aggregate_win_signals,
       uint64 trace_id) => (
           url.mojom.Url? report_url,
           map<string, url.mojom.Url> ad_beacon_map,
diff --git a/content/services/auction_worklet/seller_worklet.cc b/content/services/auction_worklet/seller_worklet.cc
index f838fd4..cc73a4f6 100644
--- a/content/services/auction_worklet/seller_worklet.cc
+++ b/content/services/auction_worklet/seller_worklet.cc
@@ -1856,6 +1856,17 @@
 
 SellerWorklet::V8State::~V8State() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(v8_sequence_checker_);
+
+  AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper_.get());
+  v8::Isolate* isolate = v8_helper_->isolate();
+  v8::HeapStatistics heap_statistics;
+  isolate->GetHeapStatistics(&heap_statistics);
+  base::UmaHistogramCounts100000(
+      "Ads.InterestGroup.Auction.SellerWorkletIsolateUsedHeapSizeKilobytes",
+      heap_statistics.used_heap_size() / 1024);
+  base::UmaHistogramCounts100000(
+      "Ads.InterestGroup.Auction.SellerWorkletIsolateTotalHeapSizeKilobytes",
+      heap_statistics.total_heap_size() / 1024);
 }
 
 void SellerWorklet::V8State::FinishInit(
diff --git a/content/services/auction_worklet/seller_worklet_unittest.cc b/content/services/auction_worklet/seller_worklet_unittest.cc
index 1a6d02af..6ed0ac0 100644
--- a/content/services/auction_worklet/seller_worklet_unittest.cc
+++ b/content/services/auction_worklet/seller_worklet_unittest.cc
@@ -1018,9 +1018,18 @@
 // Test the case the SellerWorklet pipe is closed before any of its methods are
 // invoked. Nothing should happen.
 TEST_F(SellerWorkletTest, PipeClosed) {
-  auto sellet_worklet = CreateWorklet();
-  sellet_worklet.reset();
-  base::RunLoop().RunUntilIdle();
+  base::HistogramTester histogram_tester;
+  auto seller_worklet = CreateWorklet();
+  seller_worklet.reset();
+  task_environment_.RunUntilIdle();
+  EXPECT_TRUE(seller_worklets_.empty());
+
+  // These metrics should get recorded when a seller worklet is destroyed.
+  histogram_tester.ExpectTotalCount(
+      "Ads.InterestGroup.Auction.SellerWorkletIsolateUsedHeapSizeKilobytes", 1);
+  histogram_tester.ExpectTotalCount(
+      "Ads.InterestGroup.Auction.SellerWorkletIsolateTotalHeapSizeKilobytes",
+      1);
 }
 
 TEST_F(SellerWorkletTest, NetworkError) {
diff --git a/content/services/auction_worklet/set_bid_bindings.cc b/content/services/auction_worklet/set_bid_bindings.cc
index d905550..742abfc5 100644
--- a/content/services/auction_worklet/set_bid_bindings.cc
+++ b/content/services/auction_worklet/set_bid_bindings.cc
@@ -201,6 +201,7 @@
   std::optional<std::vector<AdRender>> ad_components;
   std::optional<double> ad_cost;
   std::optional<UnrestrictedDouble> modeling_signals;
+  std::optional<v8::Local<v8::Value>> aggregate_win_signals;
   std::optional<std::string> selected_buyer_and_seller_reporting_id;
   bool allow_component_auction = false;
   uint32_t num_mandatory_ad_components = 0;
@@ -400,6 +401,11 @@
   convert_set_bid.GetOptionalSequence(
       "adComponents", std::move(components_exist), collect_components);
   convert_set_bid.GetOptional("adCost", idl.ad_cost);
+  if (base::FeatureList::IsEnabled(
+          blink::features::kFledgePrivateModelTraining)) {
+    convert_set_bid.GetOptional("aggregateWinSignals",
+                                idl.aggregate_win_signals);
+  }
   std::optional<bool> maybe_allow_component_auction;
   convert_set_bid.GetOptional("allowComponentAuction",
                               maybe_allow_component_auction);
@@ -407,7 +413,6 @@
   convert_set_bid.GetOptional("bid", idl.bid);
   convert_set_bid.GetOptional("bidCurrency", idl.bid_currency);
   convert_set_bid.GetOptional("modelingSignals", idl.modeling_signals);
-
   std::optional<uint32_t> maybe_num_mandatory_ad_components;
   if (support_multi_bid_) {
     convert_set_bid.GetOptional("numMandatoryAdComponents",
@@ -520,6 +525,24 @@
     modeling_signals = idl.modeling_signals->number;
   }
 
+  std::optional<std::string> aggregate_win_signals;
+  if (idl.aggregate_win_signals.has_value()) {
+    // Don't need a `script_timeout` here since one is already active.
+    std::string aggregate_win_signals_str;
+    AuctionV8Helper::Result json_result = v8_helper_->ExtractJson(
+        context, *idl.aggregate_win_signals, /*script_timeout=*/nullptr,
+        &aggregate_win_signals_str);
+    if (json_result == AuctionV8Helper::Result::kFailure) {
+      return base::unexpected(IdlConvert::Status::MakeErrorMessage(base::StrCat(
+          {error_prefix, "bid has invalid aggregateWinSignals value."})));
+    } else if (json_result == AuctionV8Helper::Result::kTimeout) {
+      return base::unexpected(IdlConvert::Status::MakeTimeout(base::StrCat(
+          {error_prefix,
+           "serializing bid 'aggregateWinSignals' value to JSON timed out."})));
+    }
+    aggregate_win_signals = std::move(aggregate_win_signals_str);
+  }
+
   std::string render_url_string;
   std::optional<blink::AdSize> render_size = std::nullopt;
   std::string error_msg;
@@ -667,6 +690,7 @@
       std::move(idl.selected_buyer_and_seller_reporting_id),
       std::move(ad_component_descriptors),
       static_cast<std::optional<uint16_t>>(modeling_signals),
+      /*aggregate_win_signals*/ std::move(aggregate_win_signals),
       /*bid_duration=*/base::TimeDelta());
   return bid_and_worklet_only_metadata;
 }
diff --git a/content/shell/browser/shell.h b/content/shell/browser/shell.h
index a9e656c..8d0aba9 100644
--- a/content/shell/browser/shell.h
+++ b/content/shell/browser/shell.h
@@ -203,6 +203,10 @@
   // Counts both RunFileChooser and EnumerateDirectory.
   size_t run_file_chooser_count() const { return run_file_chooser_count_; }
 
+  FileSelectListener* held_file_chooser_listener() const {
+    return held_file_chooser_listener_.get();
+  }
+
  private:
   class DevToolsWebContentsObserver;
 
diff --git a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
index 4af7ed2..2294fe5 100644
--- a/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
+++ b/device/bluetooth/bluez/bluetooth_adapter_bluez.cc
@@ -381,17 +381,6 @@
   }
   initialized_ = true;
 
-#if BUILDFLAG(IS_CHROMEOS)
-  bluez::BluezDBusManager::Get()
-      ->GetBluetoothDebugManagerClient()
-      ->SetDevCoredump(
-          base::FeatureList::IsEnabled(
-              chromeos::bluetooth::features::kBluetoothCoredump),
-          base::BindOnce(&BluetoothAdapterBlueZ::OnSetDevCoredumpSuccess,
-                         weak_ptr_factory_.GetWeakPtr()),
-          base::BindOnce(&BluetoothAdapterBlueZ::OnSetDevCoredumpError,
-                         weak_ptr_factory_.GetWeakPtr()));
-#endif // BUILDFLAG(IS_CHROMEOS)
   std::move(init_callback_).Run();
 }
 
@@ -399,21 +388,6 @@
   Shutdown();
 }
 
-#if BUILDFLAG(IS_CHROMEOS)
-void BluetoothAdapterBlueZ::OnSetDevCoredumpSuccess() {
-  bool flag = base::FeatureList::IsEnabled(
-      chromeos::bluetooth::features::kBluetoothCoredump);
-  BLUETOOTH_LOG(DEBUG) << "Bluetooth devcoredump state set to " << flag;
-}
-
-void BluetoothAdapterBlueZ::OnSetDevCoredumpError(
-    const std::string& error_name,
-    const std::string& error_message) {
-  BLUETOOTH_LOG(ERROR) << "Failed to update bluetooth devcoredump state: "
-                       << error_name << ": " << error_message;
-}
-#endif // BUILDFLAG(IS_CHROMEOS)
-
 std::string BluetoothAdapterBlueZ::GetAddress() const {
   if (!IsPresent())
     return std::string();
diff --git a/device/bluetooth/chromeos_platform_features.cc b/device/bluetooth/chromeos_platform_features.cc
index 2832ab9..b9e2897 100644
--- a/device/bluetooth/chromeos_platform_features.cc
+++ b/device/bluetooth/chromeos_platform_features.cc
@@ -6,14 +6,6 @@
 
 namespace chromeos::bluetooth::features {
 
-BASE_FEATURE(kBluetoothCoredump,
-             "BluetoothCoredump",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-BASE_FEATURE(kBluetoothFlossCoredump,
-             "BluetoothFlossCoredump",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kBluetoothFlossTelephony,
              "BluetoothFlossTelephony",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/device/bluetooth/chromeos_platform_features.h b/device/bluetooth/chromeos_platform_features.h
index c42479f..587115eb 100644
--- a/device/bluetooth/chromeos_platform_features.h
+++ b/device/bluetooth/chromeos_platform_features.h
@@ -10,12 +10,6 @@
 
 namespace chromeos::bluetooth::features {
 
-// Enables/disables the bluetooth devcoredump feature
-DEVICE_BLUETOOTH_EXPORT BASE_DECLARE_FEATURE(kBluetoothCoredump);
-
-// Enables/disables the bluetooth devcoredump feature for floss
-DEVICE_BLUETOOTH_EXPORT BASE_DECLARE_FEATURE(kBluetoothFlossCoredump);
-
 // Enables/disables the bluetooth telephony feature for floss
 DEVICE_BLUETOOTH_EXPORT BASE_DECLARE_FEATURE(kBluetoothFlossTelephony);
 
diff --git a/device/bluetooth/dbus/bluetooth_debug_manager_client.cc b/device/bluetooth/dbus/bluetooth_debug_manager_client.cc
index 6737ade..38162d0 100644
--- a/device/bluetooth/dbus/bluetooth_debug_manager_client.cc
+++ b/device/bluetooth/dbus/bluetooth_debug_manager_client.cc
@@ -44,27 +44,6 @@
   ~BluetoothDebugManagerClientImpl() override = default;
 
   // BluetoothDebugManagerClient override.
-  void SetDevCoredump(const bool enable,
-                      base::OnceClosure callback,
-                      ErrorCallback error_callback) override {
-    constexpr char kDevCoredump[] = "SetDevCoredump";
-
-    dbus::MethodCall method_call(bluetooth_debug::kBluetoothDebugInterface,
-                                 kDevCoredump);
-
-    dbus::MessageWriter writer(&method_call);
-    writer.AppendBool(enable);
-
-    object_proxy_->CallMethodWithErrorCallback(
-        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
-        base::BindOnce(&BluetoothDebugManagerClientImpl::OnSuccess,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
-        base::BindOnce(&BluetoothDebugManagerClientImpl::OnError,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       std::move(error_callback)));
-  }
-
-  // BluetoothDebugManagerClient override.
   void SetLLPrivacy(const bool enable,
                     base::OnceClosure callback,
                     ErrorCallback error_callback) override {
diff --git a/device/bluetooth/dbus/bluetooth_debug_manager_client.h b/device/bluetooth/dbus/bluetooth_debug_manager_client.h
index faa5b92..defb5f6 100644
--- a/device/bluetooth/dbus/bluetooth_debug_manager_client.h
+++ b/device/bluetooth/dbus/bluetooth_debug_manager_client.h
@@ -33,11 +33,6 @@
                                   const std::string& error_message)>
       ErrorCallback;
 
-  // Invoke D-Bus API to update bluetooth devcoredump state.
-  virtual void SetDevCoredump(const bool enable,
-                              base::OnceClosure callback,
-                              ErrorCallback error_callback) = 0;
-
   // Invoke D-Bus API to enable or disable LL privacy.
   virtual void SetLLPrivacy(const bool enable,
                             base::OnceClosure callback,
diff --git a/device/bluetooth/dbus/fake_bluetooth_debug_manager_client.h b/device/bluetooth/dbus/fake_bluetooth_debug_manager_client.h
index d5038b6..63814c05 100644
--- a/device/bluetooth/dbus/fake_bluetooth_debug_manager_client.h
+++ b/device/bluetooth/dbus/fake_bluetooth_debug_manager_client.h
@@ -30,10 +30,6 @@
                     base::OnceClosure callback,
                     ErrorCallback error_callback) override;
 
-  void SetDevCoredump(const bool enable,
-                      base::OnceClosure callback,
-                      ErrorCallback error_callback) override {}
-
   void SetLLPrivacy(const bool enable,
                     base::OnceClosure callback,
                     ErrorCallback error_callback) override {}
diff --git a/device/bluetooth/floss/floss_dbus_client.h b/device/bluetooth/floss/floss_dbus_client.h
index 4ceb00a8..2a15dea 100644
--- a/device/bluetooth/floss/floss_dbus_client.h
+++ b/device/bluetooth/floss/floss_dbus_client.h
@@ -353,7 +353,6 @@
 
 namespace experimental {
 inline constexpr char kSetLLPrivacy[] = "SetLLPrivacy";
-inline constexpr char kSetDevCoredump[] = "SetDevCoredump";
 }  // namespace experimental
 
 // BluetoothDevice structure for DBus apis.
diff --git a/device/bluetooth/floss/floss_manager_client.cc b/device/bluetooth/floss/floss_manager_client.cc
index c401501..459547a4 100644
--- a/device/bluetooth/floss/floss_manager_client.cc
+++ b/device/bluetooth/floss/floss_manager_client.cc
@@ -195,12 +195,6 @@
                                enable);
 }
 
-void FlossManagerClient::SetDevCoredump(ResponseCallback<Void> callback,
-                                        const bool enable) {
-  CallExperimentalMethod<Void>(std::move(callback),
-                               experimental::kSetDevCoredump, enable);
-}
-
 // Register manager client against manager.
 void FlossManagerClient::RegisterWithManager() {
   DCHECK(!manager_available_);
@@ -306,14 +300,6 @@
                   base::BindOnce(&FlossManagerClient::CompleteSetFlossEnabled,
                                  weak_ptr_factory_.GetWeakPtr()));
 
-  SetDevCoredump(base::BindOnce([](DBusResult<Void> ret) {
-                   if (!ret.has_value()) {
-                     LOG(ERROR) << "Fail to set devcoredump.\n";
-                   }
-                 }),
-                 base::FeatureList::IsEnabled(
-                     chromeos::bluetooth::features::kBluetoothFlossCoredump));
-
   if (floss::features::IsLLPrivacyAvailable()) {
     SetLLPrivacy(
         base::BindOnce([](DBusResult<bool> ret) {
diff --git a/device/bluetooth/floss/floss_manager_client.h b/device/bluetooth/floss/floss_manager_client.h
index 532dc4d..9daf5d7 100644
--- a/device/bluetooth/floss/floss_manager_client.h
+++ b/device/bluetooth/floss/floss_manager_client.h
@@ -130,10 +130,6 @@
   // Invoke D-Bus API to enable or disable LL privacy.
   virtual void SetLLPrivacy(ResponseCallback<bool> callback, const bool enable);
 
-  // Invoke D-Bus API to enable or disable devcoredump.
-  virtual void SetDevCoredump(ResponseCallback<Void> callback,
-                              const bool enable);
-
   // Initializes the manager client.
   void Init(dbus::Bus* bus,
             const std::string& service_name,
diff --git a/extensions/browser/service_worker/service_worker_task_queue.cc b/extensions/browser/service_worker/service_worker_task_queue.cc
index ec9f89a..281eb16 100644
--- a/extensions/browser/service_worker/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker/service_worker_task_queue.cc
@@ -175,7 +175,7 @@
 void ServiceWorkerTaskQueue::DidStartWorkerFail(
     const SequencedContextId& context_id,
     base::Time start_time,
-    blink::ServiceWorkerStatusCode status_code) {
+    content::StatusCodeResponse status) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   if (!IsCurrentActivation(context_id.extension_id, context_id.token)) {
     // This can happen is when the registration got unregistered right before we
@@ -184,27 +184,27 @@
     return;
   }
 
-  if (IsStartWorkerFailureUnexpected(status_code)) {
+  if (IsStartWorkerFailureUnexpected(status.status_code)) {
     base::UmaHistogramBoolean(
         "Extensions.ServiceWorkerBackground.StartWorkerStatus", false);
     base::UmaHistogramEnumeration(
         "Extensions.ServiceWorkerBackground.StartWorker_FailStatus",
-        status_code);
+        status.status_code);
     base::UmaHistogramTimes(
         "Extensions.ServiceWorkerBackground.StartWorkerTime_Fail",
         base::Time::Now() - start_time);
     LOG(ERROR)
         << "DidStartWorkerFail " << context_id.extension_id << ": "
         << static_cast<std::underlying_type_t<blink::ServiceWorkerStatusCode>>(
-               status_code);
+               status.status_code);
   }
 
   WorkerState* worker_state = GetWorkerState(context_id);
   DCHECK(worker_state);
   if (g_test_observer) {
     std::vector<PendingTask>* tasks = pending_tasks(context_id);
-    g_test_observer->DidStartWorkerFail(context_id.extension_id,
-                                        tasks ? tasks->size() : 0, status_code);
+    g_test_observer->DidStartWorkerFail(
+        context_id.extension_id, tasks ? tasks->size() : 0, status.status_code);
   }
   DeleteAllPendingTasks(context_id);
   // TODO(https://crbug/1062936): Needs more thought: extension would be in
diff --git a/extensions/browser/service_worker/service_worker_task_queue.h b/extensions/browser/service_worker/service_worker_task_queue.h
index df8ce3d..4599ee9 100644
--- a/extensions/browser/service_worker/service_worker_task_queue.h
+++ b/extensions/browser/service_worker/service_worker_task_queue.h
@@ -432,7 +432,7 @@
                               int thread_id);
   void DidStartWorkerFail(const SequencedContextId& context_id,
                           base::Time start_time,
-                          blink::ServiceWorkerStatusCode status_code);
+                          content::StatusCodeResponse status);
 
   bool IsStartWorkerFailureUnexpected(
       blink::ServiceWorkerStatusCode status_code);
diff --git a/gpu/vulkan/vulkan_instance.cc b/gpu/vulkan/vulkan_instance.cc
index b405961f..f0a3a4a5 100644
--- a/gpu/vulkan/vulkan_instance.cc
+++ b/gpu/vulkan/vulkan_instance.cc
@@ -28,6 +28,10 @@
 #include <sys/sysmacros.h>
 #endif
 
+#if BUILDFLAG(IS_ANDROID)
+#include "base/android/build_info.h"
+#endif
+
 namespace gpu {
 
 namespace {
@@ -231,7 +235,13 @@
   VkResult result =
       vkCreateInstance(&instance_create_info, nullptr, &owned_vk_instance_);
   if (VK_SUCCESS != result) {
-    LOG(ERROR) << "vkCreateInstance() failed: " << result;
+#if BUILDFLAG(IS_ANDROID)
+    // TODO(crbug.com/381535049): Remove deqp level printing after figuring out
+    // device initialization discrepancies.
+    const auto* build_info = base::android::BuildInfo::GetInstance();
+    LOG(ERROR) << "deqp_level=" << build_info->vulkan_deqp_level();
+#endif
+    LOG(ERROR) << "vkCreateInstance() failed: " << static_cast<int>(result);
     return false;
   }
   vk_instance_ = owned_vk_instance_;
diff --git a/infra/config/generated/builder-owners/chrome-build-team@google.com.txt b/infra/config/generated/builder-owners/chrome-build-team@google.com.txt
index 04cf523..0066db0 100644
--- a/infra/config/generated/builder-owners/chrome-build-team@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-build-team@google.com.txt
@@ -21,8 +21,10 @@
 build/win-build-perf-siso
 ci/Deterministic Linux
 ci/Deterministic Linux (dbg)
+ci/linux-modules-compile-fyi-rel
 ci/linux-win-cross-rel
 try/ios-simulator-exp
 try/linux-full-remote-rel
 try/linux-full-remote-rel-compilator
+try/linux-modules-compile-fyi-rel
 try/linux-win-cross-rel
\ No newline at end of file
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 f9d0c198..347cb2f 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
@@ -179,6 +179,7 @@
 try/dawn-win10-x64-deps-rel
 try/dawn-win10-x86-deps-rel
 try/dawn-win11-arm64-deps-rel
+try/gpu-fyi-try-android-a13-32
 try/gpu-fyi-try-android-m-nexus-5x-64
 try/gpu-fyi-try-android-nvidia-shield-tv
 try/gpu-fyi-try-android-p-pixel-2-32
diff --git "a/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/properties.json" "b/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/properties.json"
index 9d7b8fb..39eef9a 100644
--- "a/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/properties.json"
+++ "b/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/properties.json"
@@ -66,6 +66,12 @@
           "project": "chromium"
         }
       ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "gpu-fyi-try-android-a13-32",
+          "group": "tryserver.chromium.android"
+        }
+      ],
       "targets_spec_directory": "src/infra/config/generated/builders/ci/Android FYI Release (Samsung A13)/targets"
     }
   },
diff --git "a/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/targets/chromium.gpu.fyi.json"
index 87b6f96..2a3629f 100644
--- "a/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Android FYI Release \050Samsung A13\051/targets/chromium.gpu.fyi.json"
@@ -16,9 +16,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -49,9 +49,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -82,9 +82,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -118,9 +118,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -148,9 +148,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -189,9 +189,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -238,9 +238,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -278,9 +278,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -318,9 +318,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -362,9 +362,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -411,9 +411,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -452,9 +452,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -492,9 +492,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -533,9 +533,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
diff --git a/infra/config/generated/builders/ci/GPU FYI Android arm Builder/properties.json b/infra/config/generated/builders/ci/GPU FYI Android arm Builder/properties.json
index ec82d3ce..da3e3c9 100644
--- a/infra/config/generated/builders/ci/GPU FYI Android arm Builder/properties.json
+++ b/infra/config/generated/builders/ci/GPU FYI Android arm Builder/properties.json
@@ -216,6 +216,10 @@
       ],
       "mirroring_builder_group_and_names": [
         {
+          "builder": "gpu-fyi-try-android-a13-32",
+          "group": "tryserver.chromium.android"
+        },
+        {
           "builder": "gpu-fyi-try-android-nvidia-shield-tv",
           "group": "tryserver.chromium.android"
         },
diff --git a/infra/config/generated/builders/ci/GPU FYI Android arm Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Android arm Builder/targets/chromium.gpu.fyi.json
index 8080f8e2..ebe99142 100644
--- a/infra/config/generated/builders/ci/GPU FYI Android arm Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Android arm Builder/targets/chromium.gpu.fyi.json
@@ -1788,9 +1788,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -1821,9 +1821,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -1854,9 +1854,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -1890,9 +1890,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -1920,9 +1920,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -1961,9 +1961,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2010,9 +2010,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2050,9 +2050,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2090,9 +2090,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2134,9 +2134,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2183,9 +2183,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2224,9 +2224,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2264,9 +2264,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
@@ -2305,9 +2305,9 @@
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
-            "device_os": "S",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
-            "device_type": "a13",
+            "device_type": "a13ve",
             "os": "Android",
             "pool": "chromium.tests.gpu"
           },
diff --git a/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json b/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json
index 6438104..292cf01 100644
--- a/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json
+++ b/infra/config/generated/builders/ci/Linux ASan LSan Builder/targets/chromium.memory.json
@@ -1334,7 +1334,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 6
         },
         "test": "unit_tests",
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
diff --git "a/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json" "b/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json"
index 6438104..292cf01 100644
--- "a/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json"
+++ "b/infra/config/generated/builders/ci/Linux ASan LSan Tests \0501\051/targets/chromium.memory.json"
@@ -1334,7 +1334,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 6
         },
         "test": "unit_tests",
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
diff --git a/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json b/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json
index 8c7dc58..618c2bf 100644
--- a/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json
+++ b/infra/config/generated/builders/ci/Linux Builder/targets/chromium.linux.json
@@ -1913,7 +1913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 3
         },
         "test": "blink_wpt_tests",
         "test_id_prefix": "ninja://:blink_wpt_tests/"
diff --git a/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json b/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json
index c2ed78bd..24326a1 100644
--- a/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json
+++ b/infra/config/generated/builders/ci/Linux Tests/targets/chromium.linux.json
@@ -1882,7 +1882,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 3
         },
         "test": "blink_wpt_tests",
         "test_id_prefix": "ninja://:blink_wpt_tests/"
diff --git a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
index ed49004c..5e24353 100644
--- a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
@@ -1994,7 +1994,7 @@
             "os": "Windows-10-19045"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 18
+          "shards": 20
         },
         "test": "headless_shell_wpt",
         "test_id_prefix": "ninja://:headless_shell_wpt/"
diff --git a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
index adc6026..caa8b02a 100644
--- a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
@@ -1971,7 +1971,7 @@
             "os": "Windows-10-19045"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 18
+          "shards": 20
         },
         "test": "headless_shell_wpt",
         "test_id_prefix": "ninja://:headless_shell_wpt/"
diff --git a/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/gn-args.json b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/gn-args.json
new file mode 100644
index 0000000..2c6187e
--- /dev/null
+++ b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/gn-args.json
@@ -0,0 +1,12 @@
+{
+  "gn_args": {
+    "clang_use_chrome_plugins": false,
+    "dcheck_always_on": false,
+    "is_component_build": false,
+    "is_debug": false,
+    "target_cpu": "x64",
+    "target_os": "linux",
+    "use_libcxx_modules": true,
+    "use_siso": false
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/properties.json b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/properties.json
new file mode 100644
index 0000000..dd0c517
--- /dev/null
+++ b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/properties.json
@@ -0,0 +1,76 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "linux-modules-compile-fyi-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-linux-archive",
+              "builder_group": "chromium.linux",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "linux-modules-compile-fyi-rel",
+          "project": "chromium"
+        }
+      ],
+      "mirroring_builder_group_and_names": [
+        {
+          "builder": "linux-modules-compile-fyi-rel",
+          "group": "tryserver.chromium.linux"
+        }
+      ],
+      "retry_failed_shards": true,
+      "targets_spec_directory": "src/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/targets"
+    }
+  },
+  "$build/reclient": {
+    "instance": "rbe-chromium-trusted",
+    "metrics_project": "chromium-reclient-metrics",
+    "scandeps_server": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder"
+    ],
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "project": "rbe-chromium-trusted",
+    "remote_jobs": 500
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.linux",
+  "recipe": "chromium"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/shadow-properties.json b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/shadow-properties.json
new file mode 100644
index 0000000..4325ef4
--- /dev/null
+++ b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/shadow-properties.json
@@ -0,0 +1,17 @@
+{
+  "$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": 500
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/targets/chromium.linux.json b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/targets/chromium.linux.json
new file mode 100644
index 0000000..e28033d
--- /dev/null
+++ b/infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/targets/chromium.linux.json
@@ -0,0 +1,7 @@
+{
+  "linux-modules-compile-fyi-rel": {
+    "additional_compile_targets": [
+      "all"
+    ]
+  }
+}
\ 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 a003a8d..c188780 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -388,6 +388,7 @@
     "linux-cast-x64-rel": "ci/linux-cast-x64-rel/gn-args.json",
     "linux-extended-tracing-rel": "ci/linux-extended-tracing-rel/gn-args.json",
     "linux-gcc-rel": "ci/linux-gcc-rel/gn-args.json",
+    "linux-modules-compile-fyi-rel": "ci/linux-modules-compile-fyi-rel/gn-args.json",
     "linux-v4l2-codec-rel": "ci/linux-v4l2-codec-rel/gn-args.json"
   },
   "chromium.mac": {
@@ -643,6 +644,7 @@
     "android_compile_x86_dbg": "try/android_compile_x86_dbg/gn-args.json",
     "android_optional_gpu_tests_rel": "try/android_optional_gpu_tests_rel/gn-args.json",
     "gpu-fyi-cq-android-arm64": "try/gpu-fyi-cq-android-arm64/gn-args.json",
+    "gpu-fyi-try-android-a13-32": "try/gpu-fyi-try-android-a13-32/gn-args.json",
     "gpu-fyi-try-android-m-nexus-5x-64": "try/gpu-fyi-try-android-m-nexus-5x-64/gn-args.json",
     "gpu-fyi-try-android-nvidia-shield-tv": "try/gpu-fyi-try-android-nvidia-shield-tv/gn-args.json",
     "gpu-fyi-try-android-p-pixel-2-32": "try/gpu-fyi-try-android-p-pixel-2-32/gn-args.json",
@@ -816,6 +818,7 @@
     "linux-js-coverage-rel": "try/linux-js-coverage-rel/gn-args.json",
     "linux-layout-tests-edit-ng": "try/linux-layout-tests-edit-ng/gn-args.json",
     "linux-libfuzzer-asan-rel": "try/linux-libfuzzer-asan-rel/gn-args.json",
+    "linux-modules-compile-fyi-rel": "try/linux-modules-compile-fyi-rel/gn-args.json",
     "linux-multiscreen-fyi-rel": "try/linux-multiscreen-fyi-rel/gn-args.json",
     "linux-perfetto-rel": "try/linux-perfetto-rel/gn-args.json",
     "linux-rel": "try/linux-rel/gn-args.json",
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/gn-args.json b/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/gn-args.json
new file mode 100644
index 0000000..a9ed216
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/gn-args.json
@@ -0,0 +1,16 @@
+{
+  "gn_args": {
+    "dcheck_always_on": true,
+    "debuggable_apks": false,
+    "ffmpeg_branding": "Chrome",
+    "is_component_build": false,
+    "is_debug": false,
+    "proprietary_codecs": true,
+    "symbol_level": 1,
+    "target_cpu": "arm",
+    "target_os": "android",
+    "use_remoteexec": true,
+    "use_siso": true,
+    "use_static_angle": true
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/properties.json b/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/properties.json
new file mode 100644
index 0000000..eb2a5dd
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/properties.json
@@ -0,0 +1,106 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "Android FYI Release (Samsung A13)",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "TEST",
+              "legacy_android_config": {
+                "config": "main_builder_rel_mb"
+              },
+              "legacy_chromium_config": {
+                "config": "android",
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android"
+                ],
+                "config": "chromium"
+              },
+              "parent": {
+                "bucket": "ci",
+                "builder": "GPU FYI Android arm Builder",
+                "project": "chromium"
+              },
+              "run_tests_serially": true
+            }
+          },
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "GPU FYI Android arm Builder",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.gpu.fyi",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_android_config": {
+                "config": "main_builder_rel_mb"
+              },
+              "legacy_chromium_config": {
+                "config": "android",
+                "target_platform": "android"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "android"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "GPU FYI Android arm Builder",
+          "project": "chromium"
+        }
+      ],
+      "builder_ids_in_scope_for_testing": [
+        {
+          "bucket": "ci",
+          "builder": "Android FYI Release (Samsung A13)",
+          "project": "chromium"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/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.android",
+  "recipe": "chromium_trybot"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/targets/chromium.gpu.fyi.json
new file mode 100644
index 0000000..35138c9f
--- /dev/null
+++ b/infra/config/generated/builders/try/gpu-fyi-try-android-a13-32/targets/chromium.gpu.fyi.json
@@ -0,0 +1,555 @@
+{
+  "Android FYI Release (Samsung A13)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "-v"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "angle_unittests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=validating",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.samsung_a13.gl_tests.filter",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_validating",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--git-revision=${got_revision}",
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "args": [
+          "--gs-results-bucket=chromium-result-details",
+          "--recover-devices"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gpu_unittests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating",
+          "--enforce-browser-version",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "expected_color",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force-online-connection-state-for-indicator",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "expected_color_pixel_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "gpu_process",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "gpu_process_launch_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "hardware_accelerated_feature_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--expected-vendor-id",
+          "0",
+          "--expected-device-id",
+          "0",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "info_collection_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force-online-connection-state-for-indicator",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--git-revision=${got_revision}",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_validating_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force-online-connection-state-for-indicator",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "trace_test",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_android_runtimes.json",
+          "--jobs=1",
+          "--compatibility-mode=dont-require-rooted-device",
+          "--initial-find-device-attempts=3"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_validating_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "device_os": "TP1A.220624.014",
+            "device_os_type": "user",
+            "device_type": "a13ve",
+            "os": "Android",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 6
+        },
+        "test": "telemetry_gpu_integration_test_android_chrome",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test_android_chrome/"
+      }
+    ]
+  },
+  "GPU FYI Android arm Builder": {}
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json
index 8c7dc58..618c2bf 100644
--- a/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux-dcheck-off-rel/targets/chromium.linux.json
@@ -1913,7 +1913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 3
         },
         "test": "blink_wpt_tests",
         "test_id_prefix": "ninja://:blink_wpt_tests/"
diff --git a/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json
index 8c7dc58..618c2bf 100644
--- a/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux-full-remote-rel/targets/chromium.linux.json
@@ -1913,7 +1913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 3
         },
         "test": "blink_wpt_tests",
         "test_id_prefix": "ninja://:blink_wpt_tests/"
diff --git a/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/gn-args.json b/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/gn-args.json
new file mode 100644
index 0000000..2e27819a
--- /dev/null
+++ b/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/gn-args.json
@@ -0,0 +1,13 @@
+{
+  "gn_args": {
+    "clang_use_chrome_plugins": false,
+    "dcheck_always_on": false,
+    "is_component_build": false,
+    "is_debug": false,
+    "target_cpu": "x64",
+    "target_os": "linux",
+    "use_libcxx_modules": true,
+    "use_reclient": false,
+    "use_siso": false
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/properties.json b/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/properties.json
new file mode 100644
index 0000000..2b475b5
--- /dev/null
+++ b/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/properties.json
@@ -0,0 +1,67 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/try/linux-modules-compile-fyi-rel/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "ci",
+              "builder": "linux-modules-compile-fyi-rel",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "build_gs_bucket": "chromium-linux-archive",
+              "builder_group": "chromium.linux",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "build_config": "Release",
+                "config": "chromium",
+                "target_bits": 64,
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "ci",
+          "builder": "linux-modules-compile-fyi-rel",
+          "project": "chromium"
+        }
+      ],
+      "targets_spec_directory": "src/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/targets"
+    }
+  },
+  "$build/siso": {
+    "configs": [
+      "builder",
+      "remote-link"
+    ],
+    "enable_cloud_monitoring": true,
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [],
+    "metrics_project": "chromium-reclient-metrics",
+    "output_local_strategy": "minimum",
+    "project": "rbe-chromium-untrusted"
+  },
+  "$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/linux-modules-compile-fyi-rel/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/targets/chromium.linux.json
new file mode 100644
index 0000000..e28033d
--- /dev/null
+++ b/infra/config/generated/builders/try/linux-modules-compile-fyi-rel/targets/chromium.linux.json
@@ -0,0 +1,7 @@
+{
+  "linux-modules-compile-fyi-rel": {
+    "additional_compile_targets": [
+      "all"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json b/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json
index 8c7dc58..618c2bf 100644
--- a/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux-rel/targets/chromium.linux.json
@@ -1913,7 +1913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 3
         },
         "test": "blink_wpt_tests",
         "test_id_prefix": "ninja://:blink_wpt_tests/"
diff --git a/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json b/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json
index 6438104..292cf01 100644
--- a/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json
+++ b/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/targets/chromium.memory.json
@@ -1334,7 +1334,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 6
         },
         "test": "unit_tests",
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
diff --git a/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json b/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json
index 8c7dc58..618c2bf 100644
--- a/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json
+++ b/infra/config/generated/builders/try/linux_chromium_compile_rel_ng/targets/chromium.linux.json
@@ -1913,7 +1913,7 @@
             "os": "Ubuntu-22.04"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 3
         },
         "test": "blink_wpt_tests",
         "test_id_prefix": "ninja://:blink_wpt_tests/"
diff --git a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
index 3a94cac..9702b4f 100644
--- a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
+++ b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
@@ -1994,7 +1994,7 @@
             "os": "Windows-10-19045"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 18
+          "shards": 20
         },
         "test": "headless_shell_wpt",
         "test_id_prefix": "ninja://:headless_shell_wpt/"
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index a6a9c7b..482f5b5 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -10299,6 +10299,27 @@
           }
         ]
       },
+      "linux-modules-compile-fyi-rel": {
+        "contact_team_email": "chrome-build-team@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-multiscreen-fyi-rel": {
         "contact_team_email": "web-windowing-team@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/commit-queue.cfg b/infra/config/generated/luci/commit-queue.cfg
index 21297e82..d8464576 100644
--- a/infra/config/generated/luci/commit-queue.cfg
+++ b/infra/config/generated/luci/commit-queue.cfg
@@ -3775,6 +3775,10 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
+        name: "chromium/try/gpu-fyi-try-android-a13-32"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/gpu-fyi-try-android-m-nexus-5x-64"
         includable_only: true
       }
@@ -4885,6 +4889,10 @@
         mode_allowlist: "FULL_RUN"
       }
       builders {
+        name: "chromium/try/linux-modules-compile-fyi-rel"
+        includable_only: true
+      }
+      builders {
         name: "chromium/try/linux-msan-chained-origins-rel"
         includable_only: true
       }
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 64b3a0c..9f716c8 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -3356,6 +3356,7 @@
           use_invocation_timestamp: true
         }
       }
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-a13-32\">gpu-fyi-try-android-a13-32</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -15497,7 +15498,7 @@
           use_invocation_timestamp: true
         }
       }
-      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-nvidia-shield-tv\">gpu-fyi-try-android-nvidia-shield-tv</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-p-pixel-2-32\">gpu-fyi-try-android-p-pixel-2-32</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-r-pixel-4-32\">gpu-fyi-try-android-r-pixel-4-32</a></li></ul>"
+      description_html: "This builder is mirrored by any of the following try builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-a13-32\">gpu-fyi-try-android-a13-32</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-nvidia-shield-tv\">gpu-fyi-try-android-nvidia-shield-tv</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-p-pixel-2-32\">gpu-fyi-try-android-p-pixel-2-32</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/try/gpu-fyi-try-android-r-pixel-4-32\">gpu-fyi-try-android-r-pixel-4-32</a></li></ul>"
       shadow_builder_adjustments {
         service_account: "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com"
         pool: "luci.chromium.try"
@@ -57906,6 +57907,117 @@
       }
     }
     builders {
+      name: "linux-modules-compile-fyi-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:32"
+      dimensions: "cpu:x86-64"
+      dimensions: "free_space:standard"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.ci"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/properties.json",'
+        '    "shadow_properties_file": "infra/config/generated/builders/ci/linux-modules-compile-fyi-rel/shadow-properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.linux",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
+        '}'
+      execution_timeout_secs: 21600
+      build_numbers: YES
+      service_account: "chromium-ci-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: "Experimental compile with use_libcxx_modules=true.<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/linux-modules-compile-fyi-rel\">linux-modules-compile-fyi-rel</a></li></ul>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+        pool: "luci.chromium.try"
+        dimensions: "free_space:"
+        dimensions: "pool:luci.chromium.try"
+      }
+      contact_team_email: "chrome-build-team@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-multiscreen-fyi-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -98040,6 +98152,114 @@
       }
     }
     builders {
+      name: "gpu-fyi-try-android-a13-32"
+      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-android-a13-32/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "tryserver.chromium.android",'
+        '  "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: "Runs GPU tests on Samsung A13 phones<br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/Android FYI Release (Samsung A13)\">Android FYI Release (Samsung A13)</a></li><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/GPU FYI Android arm Builder\">GPU FYI Android arm Builder</a></li></ul>"
+      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-android-m-nexus-5x-64"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
@@ -111487,6 +111707,117 @@
       }
     }
     builders {
+      name: "linux-modules-compile-fyi-rel"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "cores:32"
+      dimensions: "cpu:x86-64"
+      dimensions: "os:Ubuntu-22.04"
+      dimensions: "pool:luci.chromium.try"
+      dimensions: "ssd:1"
+      exe {
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
+      }
+      properties:
+        '{'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
+        '  },'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/try/linux-modules-compile-fyi-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
+      grace_period {
+        seconds: 120
+      }
+      build_numbers: YES
+      service_account: "chromium-try-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: "<br>Experimental compile with use_libcxx_modules=true.<br/><br/>This builder mirrors the following CI builders:<br/><ul><li><a href=\"https://ci.chromium.org/p/chromium/builders/ci/linux-modules-compile-fyi-rel\">linux-modules-compile-fyi-rel</a></li></ul>"
+      contact_team_email: "chrome-build-team@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-msan-chained-origins-rel"
       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 5ee191e..efb9fed 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -10116,6 +10116,11 @@
     short_name: "lk"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/linux-modules-compile-fyi-rel"
+    category: "linux"
+    short_name: "mod"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/linux-rr-orchestrator-fyi"
     category: "linux"
     short_name: "rr"
@@ -17216,6 +17221,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-cq-android-arm64"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-a13-32"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-m-nexus-5x-64"
   }
   builders {
@@ -17579,6 +17587,9 @@
     name: "buildbucket/luci.chromium.try/linux-libfuzzer-asan-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-modules-compile-fyi-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-msan-chained-origins-rel"
   }
   builders {
@@ -18496,6 +18507,9 @@
     name: "buildbucket/luci.chromium.try/gpu-fyi-cq-android-arm64"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-a13-32"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/gpu-fyi-try-android-m-nexus-5x-64"
   }
   builders {
@@ -19049,6 +19063,9 @@
     name: "buildbucket/luci.chromium.try/linux-libfuzzer-asan-rel"
   }
   builders {
+    name: "buildbucket/luci.chromium.try/linux-modules-compile-fyi-rel"
+  }
+  builders {
     name: "buildbucket/luci.chromium.try/linux-multiscreen-fyi-rel"
   }
   builders {
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index e35c549..6f40d36 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5134,6 +5134,15 @@
   }
 }
 job {
+  id: "linux-modules-compile-fyi-rel"
+  realm: "ci"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "ci"
+    builder: "linux-modules-compile-fyi-rel"
+  }
+}
+job {
   id: "linux-multiscreen-fyi-rel"
   realm: "ci"
   schedule: "with 5h interval"
@@ -6806,6 +6815,7 @@
   triggers: "linux-fieldtrial-rel"
   triggers: "linux-gcc-rel"
   triggers: "linux-headless-shell-rel"
+  triggers: "linux-modules-compile-fyi-rel"
   triggers: "linux-official"
   triggers: "linux-perfetto-rel"
   triggers: "linux-presubmit"
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index 02b2245..76560427 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -271,8 +271,8 @@
     'swarming': {
       'dimensions': {
         'os': 'Android',
-        'device_type': 'a13',
-        'device_os': 'S',
+        'device_type': 'a13ve',
+        'device_os': 'TP1A.220624.014',
         'device_os_type': 'user',
         'pool': 'chromium.tests.gpu',
       },
diff --git a/infra/config/gn_args/gn_args.star b/infra/config/gn_args/gn_args.star
index 656a6083..a79de59 100644
--- a/infra/config/gn_args/gn_args.star
+++ b/infra/config/gn_args/gn_args.star
@@ -822,6 +822,17 @@
     ],
 )
 
+# Do not use this for non-FYI builders.
+gn_args.config(
+    name = "libcxx_modules",
+    args = {
+        # TODO: crbug.com/351909443 - remove once performance of plugins is
+        # improved.
+        "clang_use_chrome_plugins": False,
+        "use_libcxx_modules": True,
+    },
+)
+
 gn_args.config(
     name = "libfuzzer",
     args = {
diff --git a/infra/config/lib/targets-internal/magic_args.star b/infra/config/lib/targets-internal/magic_args.star
index ebb99d9..05d3def1 100644
--- a/infra/config/lib/targets-internal/magic_args.star
+++ b/infra/config/lib/targets-internal/magic_args.star
@@ -291,6 +291,7 @@
 
     unrooted_devices = (
         "a13",
+        "a13ve",
         "a23",
         "dm1q",  # Samsung S23.
         "devonn",  # Motorola Moto G Power 5G.
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index 9911574..c1902d8 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -473,7 +473,6 @@
     list_view = "chromium.gpu.experimental",
 )
 
-# TODO(crbug.com/40282670): Add a trybot for this builder when there's capacity.
 ci.thin_tester(
     name = "Android FYI Release (Samsung A13)",
     triggered_by = ["GPU FYI Android arm Builder"],
diff --git a/infra/config/subprojects/chromium/ci/chromium.linux.star b/infra/config/subprojects/chromium/ci/chromium.linux.star
index 83899f6..89dac30 100644
--- a/infra/config/subprojects/chromium/ci/chromium.linux.star
+++ b/infra/config/subprojects/chromium/ci/chromium.linux.star
@@ -927,6 +927,50 @@
 )
 
 ci.builder(
+    name = "linux-modules-compile-fyi-rel",
+    branch_selector = branches.selector.MAIN,
+    description_html = "Experimental compile with use_libcxx_modules=true.",
+    builder_spec = builder_config.builder_spec(
+        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,
+        ),
+        build_gs_bucket = "chromium-linux-archive",
+    ),
+    gn_args = gn_args.config(
+        configs = [
+            "libcxx_modules",
+            "linux",
+            "no_siso",
+            "release_builder",
+            "x64",
+        ],
+    ),
+    targets = targets.bundle(
+        additional_compile_targets = [
+            "all",
+        ],
+    ),
+    cores = 32,
+    ssd = True,
+    gardener_rotations = args.ignore_default(None),
+    tree_closing = False,
+    console_view_entry = consoles.console_view_entry(
+        console_view = "chromium.fyi",
+        category = "linux",
+        short_name = "mod",
+    ),
+    main_console_view = None,
+    contact_team_email = "chrome-build-team@google.com",
+    execution_timeout = 6 * time.hour,
+    notifies = args.ignore_default([]),
+)
+
+ci.builder(
     name = "linux-v4l2-codec-rel",
     branch_selector = branches.selector.MAIN,
     builder_spec = builder_config.builder_spec(
diff --git a/infra/config/subprojects/chromium/gpu.try.star b/infra/config/subprojects/chromium/gpu.try.star
index 5b20687..12e0aaf 100644
--- a/infra/config/subprojects/chromium/gpu.try.star
+++ b/infra/config/subprojects/chromium/gpu.try.star
@@ -108,6 +108,16 @@
 )
 
 gpu_android_builder(
+    name = "gpu-fyi-try-android-a13-32",
+    description_html = "Runs GPU tests on Samsung A13 phones",
+    mirrors = [
+        "ci/GPU FYI Android arm Builder",
+        "ci/Android FYI Release (Samsung A13)",
+    ],
+    gn_args = "ci/GPU FYI Android arm Builder",
+)
+
+gpu_android_builder(
     name = "gpu-fyi-try-android-s23-64",
     description_html = "Runs GPU tests on Samsung S23 phones",
     mirrors = [
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index 11faa4b..c05596f4 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -857,6 +857,18 @@
 )
 
 try_.builder(
+    name = "linux-modules-compile-fyi-rel",
+    mirrors = [
+        "ci/linux-modules-compile-fyi-rel",
+    ],
+    gn_args = "ci/linux-modules-compile-fyi-rel",
+    cores = 32,
+    ssd = True,
+    contact_team_email = "chrome-build-team@google.com",
+    execution_timeout = 6 * time.hour,
+)
+
+try_.builder(
     name = "linux_upload_clang",
     executable = "recipe:chromium_toolchain/package_clang",
     gn_args = gn_args.config(
diff --git a/infra/config/targets/autoshard_exceptions.json b/infra/config/targets/autoshard_exceptions.json
index ba3818e2..61c5b73 100644
--- a/infra/config/targets/autoshard_exceptions.json
+++ b/infra/config/targets/autoshard_exceptions.json
@@ -440,18 +440,18 @@
             },
             "not_site_per_process_blink_wpt_tests": {
                 "debug": {
-                    "avg_num_builds_per_peak_hour": 90,
-                    "estimated_bot_hour_delta": -15.8,
-                    "prev_avg_pending_time_sec": 17.8,
-                    "prev_p50_pending_time_sec": 1.0,
-                    "prev_p90_pending_time_sec": 59.0,
-                    "prev_percentile_duration_minutes": 8.69,
-                    "prev_shard_count": 9,
-                    "simulated_max_shard_duration": 13.54,
-                    "test_overhead_min": 2.6333333333333333,
+                    "avg_num_builds_per_peak_hour": 103,
+                    "estimated_bot_hour_delta": -4.92,
+                    "prev_avg_pending_time_sec": 19.0,
+                    "prev_p50_pending_time_sec": 0.0,
+                    "prev_p90_pending_time_sec": 70.0,
+                    "prev_percentile_duration_minutes": 6.97,
+                    "prev_shard_count": 5,
+                    "simulated_max_shard_duration": 10.66,
+                    "test_overhead_min": 1.4333333333333333,
                     "try_builder": "linux-rel"
                 },
-                "shards": 5
+                "shards": 3
             },
             "not_site_per_process_headless_shell_wpt_tests": {
                 "debug": {
@@ -564,18 +564,18 @@
             },
             "unit_tests": {
                 "debug": {
-                    "avg_num_builds_per_peak_hour": 94,
-                    "estimated_bot_hour_delta": 2.82,
-                    "prev_avg_pending_time_sec": 17.7,
+                    "avg_num_builds_per_peak_hour": 101,
+                    "estimated_bot_hour_delta": 1.88,
+                    "prev_avg_pending_time_sec": 22.9,
                     "prev_p50_pending_time_sec": 0.0,
-                    "prev_p90_pending_time_sec": 70.0,
-                    "prev_percentile_duration_minutes": 18.17,
-                    "prev_shard_count": 4,
-                    "simulated_max_shard_duration": 14.9,
-                    "test_overhead_min": 1.8,
+                    "prev_p90_pending_time_sec": 89.0,
+                    "prev_percentile_duration_minutes": 16.09,
+                    "prev_shard_count": 5,
+                    "simulated_max_shard_duration": 13.59,
+                    "test_overhead_min": 1.1166666666666667,
                     "try_builder": "linux_chromium_asan_rel_ng"
                 },
-                "shards": 5
+                "shards": 6
             },
             "webkit_unit_tests_v2": {
                 "debug": {
@@ -733,6 +733,21 @@
                 },
                 "shards": 10
             },
+            "headless_shell_wpt_tests": {
+                "debug": {
+                    "avg_num_builds_per_peak_hour": 96,
+                    "estimated_bot_hour_delta": 12.53,
+                    "prev_avg_pending_time_sec": 155.3,
+                    "prev_p50_pending_time_sec": 5.0,
+                    "prev_p90_pending_time_sec": 582.0,
+                    "prev_percentile_duration_minutes": 16.1,
+                    "prev_shard_count": 18,
+                    "simulated_max_shard_duration": 14.88,
+                    "test_overhead_min": 3.9166666666666665,
+                    "try_builder": "win-rel"
+                },
+                "shards": 20
+            },
             "pixel_browser_tests": {
                 "debug": {
                     "avg_num_builds_per_peak_hour": 98,
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index 9ab8af9..58066415 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -872,8 +872,8 @@
     swarming = targets.swarming(
         dimensions = {
             "os": "Android",
-            "device_type": "a13",
-            "device_os": "S",
+            "device_type": "a13ve",
+            "device_os": "TP1A.220624.014",
             "device_os_type": "user",
             "pool": "chromium.tests.gpu",
         },
diff --git a/ios/chrome/browser/autofill/model/autocomplete_history_manager_factory.mm b/ios/chrome/browser/autofill/model/autocomplete_history_manager_factory.mm
index bd3ee877..0264aa4 100644
--- a/ios/chrome/browser/autofill/model/autocomplete_history_manager_factory.mm
+++ b/ios/chrome/browser/autofill/model/autocomplete_history_manager_factory.mm
@@ -7,7 +7,7 @@
 #import <utility>
 
 #import "base/no_destructor.h"
-#import "components/autofill/core/browser/autocomplete_history_manager.h"
+#import "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #import "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #import "components/keyed_service/core/service_access_type.h"
 #import "ios/chrome/browser/history/model/history_service_factory.h"
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 2f0a04a8..a9bf887 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
@@ -409,9 +409,9 @@
   GURL cardArtURL =
       _personalDataManager->payments_data_manager().GetCardArtURL(*creditCard);
   if (!cardArtURL.is_empty() && cardArtURL.is_valid()) {
-    gfx::Image* image = _personalDataManager->payments_data_manager()
-                            .GetCachedCardArtImageForUrl(cardArtURL);
-    if (image) {
+    if (const gfx::Image* const image =
+            _personalDataManager->payments_data_manager()
+                .GetCachedCardArtImageForUrl(cardArtURL)) {
       return image->ToUIImage();
     }
   }
diff --git a/ios/chrome/browser/autofill/ui_bundled/card_unmask_prompt_view_bridge.mm b/ios/chrome/browser/autofill/ui_bundled/card_unmask_prompt_view_bridge.mm
index f9c039b4..10261f5 100644
--- a/ios/chrome/browser/autofill/ui_bundled/card_unmask_prompt_view_bridge.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/card_unmask_prompt_view_bridge.mm
@@ -101,10 +101,9 @@
 UIImage* CardUnmaskPromptViewBridge::GetCardIcon() {
   // Firstly check if card art image is available.
   const CreditCard& credit_card = GetController()->GetCreditCard();
-  gfx::Image* image =
-      personal_data_manager_->payments_data_manager()
-          .GetCachedCardArtImageForUrl(credit_card.card_art_url());
-  if (image) {
+  if (const gfx::Image* const image =
+          personal_data_manager_->payments_data_manager()
+              .GetCachedCardArtImageForUrl(credit_card.card_art_url())) {
     return image->ToUIImage();
   }
 
diff --git a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h
index 611e38d..07302ee 100644
--- a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h
+++ b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.h
@@ -12,7 +12,6 @@
 
 #import "base/memory/raw_ptr.h"
 #import "base/memory/weak_ptr.h"
-#import "components/autofill/core/browser/autocomplete_history_manager.h"
 #import "components/autofill/core/browser/autofill_client.h"
 #import "components/autofill/core/browser/country_type.h"
 #import "components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_manager.h"
@@ -21,6 +20,7 @@
 #import "components/autofill/core/browser/metrics/form_interactions_ukm_logger.h"
 #import "components/autofill/core/browser/password_form_classification.h"
 #import "components/autofill/core/browser/payments/card_unmask_delegate.h"
+#import "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #import "components/autofill/core/browser/strike_databases/strike_database.h"
 #import "components/autofill/core/browser/studies/autofill_ablation_study.h"
 #import "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
diff --git a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm
index 73b0d8e..8cf8094 100644
--- a/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/chrome_autofill_client_ios.mm
@@ -26,7 +26,7 @@
 #import "components/autofill/core/browser/logging/log_manager.h"
 #import "components/autofill/core/browser/logging/log_router.h"
 #import "components/autofill/core/browser/payments/payments_network_interface.h"
-#import "components/autofill/core/browser/single_field_fill_router.h"
+#import "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #import "components/autofill/core/browser/ui/suggestion_type.h"
 #import "components/autofill/core/common/autofill_features.h"
 #import "components/autofill/core/common/autofill_prefs.h"
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 af922865..b9abe59 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
@@ -296,9 +296,9 @@
       _personalDataManager->payments_data_manager().GetCardArtURL(creditCard);
   if (IsKeyboardAccessoryUpgradeEnabled() && !cardArtURL.is_empty() &&
       cardArtURL.is_valid()) {
-    gfx::Image* image = _personalDataManager->payments_data_manager()
-                            .GetCachedCardArtImageForUrl(cardArtURL);
-    if (image) {
+    if (const gfx::Image* const image =
+            _personalDataManager->payments_data_manager()
+                .GetCachedCardArtImageForUrl(cardArtURL)) {
       return image->ToUIImage();
     }
   }
diff --git a/ios/chrome/browser/browser_view/ui_bundled/DEPS b/ios/chrome/browser/browser_view/ui_bundled/DEPS
index d4d373ce..7f09115 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/DEPS
+++ b/ios/chrome/browser/browser_view/ui_bundled/DEPS
@@ -39,6 +39,7 @@
   "+ios/chrome/browser/lens/ui_bundled",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h",
   "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h",
+  "+ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h",
   "+ios/chrome/browser/lens_overlay/model/lens_overlay_tab_helper.h",
   "+ios/chrome/browser/main_content/ui_bundled",
   "+ios/chrome/browser/metrics/model",
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
index 23150d3..5dc33eb 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -102,6 +102,7 @@
 #import "ios/chrome/browser/lens/ui_bundled/lens_coordinator.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
 #import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.h"
+#import "ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h"
 #import "ios/chrome/browser/lens_overlay/model/lens_overlay_tab_helper.h"
 #import "ios/chrome/browser/metrics/model/tab_usage_recorder_browser_agent.h"
 #import "ios/chrome/browser/mini_map/ui_bundled/mini_map_coordinator.h"
@@ -575,6 +576,7 @@
   BubbleViewControllerPresenter* _contextualPanelEntrypointHelpPresenter;
   ToolbarAccessoryPresenter* _toolbarAccessoryPresenter;
   LensCoordinator* _lensCoordinator;
+  LensViewFinderCoordinator* _lensViewFinderCoordinator;
   LensOverlayCoordinator* _lensOverlayCoordinator;
   ToolbarCoordinator* _toolbarCoordinator;
   TabStripCoordinator* _tabStripCoordinator;
@@ -1115,7 +1117,12 @@
   _NTPCoordinator.toolbarDelegate = _toolbarCoordinator;
   self.tabLifecycleMediator.NTPCoordinator = _NTPCoordinator;
 
-  _lensCoordinator = [[LensCoordinator alloc] initWithBrowser:self.browser];
+  if (IsLVFUnifiedExperienceEnabled()) {
+    _lensViewFinderCoordinator =
+        [[LensViewFinderCoordinator alloc] initWithBrowser:self.browser];
+  } else {
+    _lensCoordinator = [[LensCoordinator alloc] initWithBrowser:self.browser];
+  }
 
   _safeAreaProvider = [[SafeAreaProvider alloc] initWithBrowser:self.browser];
 
@@ -1170,9 +1177,14 @@
   // The Lens coordinator needs to be started before the primary toolbar
   // coordinator so that the LensCommands dispatcher is correctly registered in
   // time.
-  _lensCoordinator.baseViewController = viewController;
-  _lensCoordinator.delegate = viewController;
-  [_lensCoordinator start];
+  if (IsLVFUnifiedExperienceEnabled()) {
+    _lensViewFinderCoordinator.baseViewController = viewController;
+    [_lensViewFinderCoordinator start];
+  } else {
+    _lensCoordinator.baseViewController = viewController;
+    _lensCoordinator.delegate = viewController;
+    [_lensCoordinator start];
+  }
 
   _toolbarCoordinator.baseViewController = viewController;
   _toolbarCoordinator.omniboxFocusDelegate = viewController;
@@ -1256,8 +1268,13 @@
   [_lensCoordinator stop];
   _lensCoordinator = nil;
 
-  [_lensOverlayCoordinator stop];
-  _lensOverlayCoordinator = nil;
+  if (IsLVFUnifiedExperienceEnabled()) {
+    [_lensViewFinderCoordinator stop];
+    _lensViewFinderCoordinator = nil;
+  } else {
+    [_lensOverlayCoordinator stop];
+    _lensOverlayCoordinator = nil;
+  }
 
   [self.downloadManagerCoordinator stop];
   self.downloadManagerCoordinator = nil;
diff --git a/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm b/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm
index 8d1101f..4dbb419 100644
--- a/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm
+++ b/ios/chrome/browser/data_sharing/model/data_sharing_sdk_delegate_ios.mm
@@ -39,21 +39,17 @@
     base::OnceCallback<void(
         const base::expected<data_sharing_pb::ReadGroupsResult, absl::Status>&)>
         callback) {
-  NSMutableArray<NSString*>* ids = [NSMutableArray array];
   NSMutableArray<ShareKitReadGroupParamConfiguration*>* groupsParam =
       [NSMutableArray array];
   for (auto group_param : params.group_params()) {
-    NSString* group_id = base::SysUTF8ToNSString(group_param.group_id());
-    [ids addObject:group_id];
     ShareKitReadGroupParamConfiguration* param =
         [[ShareKitReadGroupParamConfiguration alloc] init];
-    param.groupID = group_id;
+    param.groupID = base::SysUTF8ToNSString(group_param.group_id());
     param.consistencyToken =
         base::SysUTF8ToNSString(group_param.consistency_token());
     [groupsParam addObject:param];
   }
   ShareKitReadConfiguration* config = [[ShareKitReadConfiguration alloc] init];
-  config.collabIDs = ids;
   config.groupsParam = groupsParam;
   config.callback = base::CallbackToBlock(std::move(callback));
   share_kit_service_->ReadGroups(config);
diff --git a/ios/chrome/browser/lens/ui_bundled/BUILD.gn b/ios/chrome/browser/lens/ui_bundled/BUILD.gn
index b3ab5dc..b36d0b0 100644
--- a/ios/chrome/browser/lens/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/lens/ui_bundled/BUILD.gn
@@ -64,6 +64,7 @@
     ":lens_entrypoint",
     "//base",
     "//components/prefs",
+    "//ios/chrome/browser/lens_overlay/coordinator:lens_overlay_availability",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/public/features:features",
diff --git a/ios/chrome/browser/lens/ui_bundled/DEPS b/ios/chrome/browser/lens/ui_bundled/DEPS
index 094176b9..bae725c0 100644
--- a/ios/chrome/browser/lens/ui_bundled/DEPS
+++ b/ios/chrome/browser/lens/ui_bundled/DEPS
@@ -8,4 +8,5 @@
     "+ios/chrome/browser/url_loading/model",
     "+ios/chrome/browser/web/model/web_navigation_util.h",
     "+ios/chrome/browser/web_state_list/model/web_state_dependency_installer_bridge.h",
+    "+ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h",
 ]
diff --git a/ios/chrome/browser/lens/ui_bundled/lens_availability.h b/ios/chrome/browser/lens/ui_bundled/lens_availability.h
index 9b03c69..8a0cd37 100644
--- a/ios/chrome/browser/lens/ui_bundled/lens_availability.h
+++ b/ios/chrome/browser/lens/ui_bundled/lens_availability.h
@@ -35,6 +35,9 @@
 bool CheckAndLogAvailabilityForLensEntryPoint(
     LensEntrypoint entry_point,
     bool is_google_default_search_engine);
+
+// Checks whether the context menu unified experience in enabled;
+bool IsLensContextMenuUnifiedExperienceEnabled();
 }  // namespace lens_availability
 
 #endif  // IOS_CHROME_BROWSER_LENS_UI_BUNDLED_LENS_AVAILABILITY_H_
diff --git a/ios/chrome/browser/lens/ui_bundled/lens_availability.mm b/ios/chrome/browser/lens/ui_bundled/lens_availability.mm
index 7a48080..96515a3 100644
--- a/ios/chrome/browser/lens/ui_bundled/lens_availability.mm
+++ b/ios/chrome/browser/lens/ui_bundled/lens_availability.mm
@@ -7,6 +7,7 @@
 #import "base/metrics/histogram_functions.h"
 #import "base/notreached.h"
 #import "components/prefs/pref_service.h"
+#import "ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
@@ -78,4 +79,11 @@
 
   return lens_support_status == LensSupportStatus::LensSearchSupported;
 }
+
+bool IsLensContextMenuUnifiedExperienceEnabled() {
+  bool featureEnabled =
+      base::FeatureList::IsEnabled(kEnableLensContextMenuUnifiedExperience);
+  return IsLensOverlayAvailable() && featureEnabled;
+}
+
 }  // namespace lens_availability
diff --git a/ios/chrome/browser/lens/ui_bundled/lens_coordinator.mm b/ios/chrome/browser/lens/ui_bundled/lens_coordinator.mm
index 1cd7bd40..70602ce 100644
--- a/ios/chrome/browser/lens/ui_bundled/lens_coordinator.mm
+++ b/ios/chrome/browser/lens/ui_bundled/lens_coordinator.mm
@@ -29,6 +29,7 @@
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h"
 #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
 #import "ios/chrome/browser/shared/public/commands/lens_commands.h"
+#import "ios/chrome/browser/shared/public/commands/lens_overlay_commands.h"
 #import "ios/chrome/browser/shared/public/commands/new_tab_page_commands.h"
 #import "ios/chrome/browser/shared/public/commands/omnibox_commands.h"
 #import "ios/chrome/browser/shared/public/commands/open_lens_input_selection_command.h"
@@ -187,6 +188,15 @@
 #pragma mark - Commands
 
 - (void)searchImageWithLens:(SearchImageWithLensCommand*)command {
+  if (lens_availability::IsLensContextMenuUnifiedExperienceEnabled()) {
+    id<LensOverlayCommands> handler = HandlerForProtocol(
+        self.browser->GetCommandDispatcher(), LensOverlayCommands);
+    [handler
+        searchImageWithLens:command.image
+                 entrypoint:LensOverlayEntrypoint::kSearchImageContextMenu];
+    return;
+  }
+
   const bool isIncognito = self.browser->GetProfile()->IsOffTheRecord();
   __weak LensCoordinator* weakSelf = self;
 
diff --git a/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn b/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
index 5bf9752..21c6602 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
+++ b/ios/chrome/browser/lens_overlay/coordinator/BUILD.gn
@@ -31,6 +31,8 @@
     "lens_result_page_mediator.h",
     "lens_result_page_mediator.mm",
     "lens_result_page_mediator_delegate.h",
+    "lens_view_finder_coordinator.h",
+    "lens_view_finder_coordinator.mm",
     "lens_web_provider.h",
   ]
   deps = [
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h
index 44cff32..f38a081 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.h
@@ -12,4 +12,7 @@
 // instead of new tab.
 bool IsLensOverlaySameTabNavigationEnabled();
 
+// Returns whether LVF unified experience is enabled.
+bool IsLVFUnifiedExperienceEnabled();
+
 #endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_OVERLAY_AVAILABILITY_H_
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.mm
index d1679b8..7a96af3 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_availability.mm
@@ -32,3 +32,7 @@
 bool IsLensOverlaySameTabNavigationEnabled() {
   return base::FeatureList::IsEnabled(kLensOverlayEnableSameTabNavigation);
 }
+
+bool IsLVFUnifiedExperienceEnabled() {
+  return base::FeatureList::IsEnabled(kEnableLensViewFinderUnifiedExperience);
+}
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
index 16c95f7..6d66648 100644
--- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -142,6 +142,9 @@
 
   /// Presenter for the lens container.
   LensOverlayContainerPresenter* _containerPresenter;
+
+  /// Whether the image should be repositioned when exiting.
+  BOOL _shouldResetSelectionToInitialPositionOnExit;
 }
 
 #pragma mark - public
@@ -180,9 +183,6 @@
       initWithBaseViewController:_containerViewController];
   _networkIssueAlertPresenter.delegate = self;
 
-  if ([self termsOfServiceAccepted]) {
-    [_selectionViewController start];
-  }
   return YES;
 }
 
@@ -268,46 +268,39 @@
 
 #pragma mark - LensOverlayCommands
 
+- (void)searchImageWithLens:(UIImage*)image
+                 entrypoint:(LensOverlayEntrypoint)entrypoint {
+  [self prepareOverlayWithEntrypoint:entrypoint];
+  // Even if the image is already prepared at this point, the snapshotting
+  // infrastructure still needs to be built to allow the restoration window to
+  // be displayed when exiting and re-entering the experience.
+  [self prepareSnapshotCapturingInfrastructure];
+  _shouldResetSelectionToInitialPositionOnExit = NO;
+  [self handleOverlayImageCaptured:image
+                        entrypoint:entrypoint
+                          animated:YES
+                        completion:nil];
+}
+
 - (void)createAndShowLensUI:(BOOL)animated
                  entrypoint:(LensOverlayEntrypoint)entrypoint
                  completion:(void (^)(BOOL))completion {
-  if (self.isUICreated) {
-    // The UI is probably associated with the non-active tab. Destroy it with no
-    // animation.
-    [self destroyLensUI:NO
-                 reason:lens::LensOverlayDismissalSource::kNewLensInvocation];
-  }
-
-  [[NSNotificationCenter defaultCenter]
-      addObserver:self
-         selector:@selector(lowMemoryWarningReceived)
-             name:UIApplicationDidReceiveMemoryWarningNotification
-           object:nil];
-
-  _associatedTabHelper = [self activeTabHelper];
-
-  _metricsRecorder = [[LensOverlayMetricsRecorder alloc]
-      initWithEntrypoint:entrypoint
-      associatedWebState:_associatedTabHelper->GetWebState()];
-
-  // The instance that creates the Lens UI designates itself as the command
-  // handler for the associated tab.
-  _associatedTabHelper->SetLensOverlayCommandsHandler(self);
-  _associatedTabHelper->SetLensOverlayUIAttachedAndAlive(true);
-
+  [self prepareOverlayWithEntrypoint:entrypoint];
+  _shouldResetSelectionToInitialPositionOnExit = YES;
   __weak __typeof(self) weakSelf = self;
   [self captureSnapshotWithCompletion:^(UIImage* snapshot) {
-    [weakSelf onSnapshotCaptured:snapshot
-                      entrypoint:entrypoint
-                        animated:animated
-                      completion:completion];
+    [weakSelf handleOverlayImageCaptured:snapshot
+                              entrypoint:entrypoint
+                                animated:animated
+                              completion:completion];
   }];
 }
 
-- (void)onSnapshotCaptured:(UIImage*)snapshot
-                entrypoint:(LensOverlayEntrypoint)entrypoint
-                  animated:(BOOL)animated
-                completion:(void (^)(BOOL))completion {
+// Handles presenting the base image to be used in the overlay.
+- (void)handleOverlayImageCaptured:(UIImage*)snapshot
+                        entrypoint:(LensOverlayEntrypoint)entrypoint
+                          animated:(BOOL)animated
+                        completion:(void (^)(BOOL))completion {
   if (!snapshot) {
     if (completion) {
       completion(NO);
@@ -354,6 +347,12 @@
 }
 
 - (void)onContainerViewControllerPresented {
+  // Start the selection UI only when the container is presented. This avoids
+  // results being reported before the container is fully shown.
+  if ([self termsOfServiceAccepted]) {
+    [_selectionViewController start];
+  }
+
   if (self.shouldShowConsentFlow) {
     if (self.isResultsBottomSheetOpen) {
       [self stopResultPage];
@@ -491,14 +490,25 @@
   __weak __typeof(self) weakSelf = self;
   __weak LensOverlayContainerPresenter* weakContainerPresenter =
       _containerPresenter;
-  [_selectionViewController resetSelectionAreaToInitialPosition:^{
+
+  void (^onSelectionExitPositionSettled)() = ^{
     [weakSelf exitFullscreenAnimated:YES];
     if (!weakContainerPresenter) {
-      completion();
+      if (completion) {
+        completion();
+      }
+
       return;
     }
     [weakContainerPresenter fadeSelectionUIWithCompletion:completion];
-  }];
+  };
+
+  if (_shouldResetSelectionToInitialPositionOnExit) {
+    [_selectionViewController
+        resetSelectionAreaToInitialPosition:onSelectionExitPositionSettled];
+  } else {
+    onSelectionExitPositionSettled();
+  }
 }
 
 - (void)dismissLensOverlayWithCompletion:(void (^)())completion {
@@ -653,6 +663,32 @@
 
 #pragma mark - private
 
+- (void)prepareOverlayWithEntrypoint:(LensOverlayEntrypoint)entrypoint {
+  if (self.isUICreated) {
+    // The UI is probably associated with the non-active tab. Destroy it with no
+    // animation.
+    [self destroyLensUI:NO
+                 reason:lens::LensOverlayDismissalSource::kNewLensInvocation];
+  }
+
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector(lowMemoryWarningReceived)
+             name:UIApplicationDidReceiveMemoryWarningNotification
+           object:nil];
+
+  _associatedTabHelper = [self activeTabHelper];
+
+  _metricsRecorder = [[LensOverlayMetricsRecorder alloc]
+      initWithEntrypoint:entrypoint
+      associatedWebState:_associatedTabHelper->GetWebState()];
+
+  // The instance that creates the Lens UI designates itself as the command
+  // handler for the associated tab.
+  _associatedTabHelper->SetLensOverlayCommandsHandler(self);
+  _associatedTabHelper->SetLensOverlayUIAttachedAndAlive(true);
+}
+
 - (void)openURLInNewTab:(GURL)URL {
   OpenNewTabCommand* command = [OpenNewTabCommand
       commandWithURLFromChrome:URL
@@ -816,28 +852,24 @@
   return tabHelper;
 }
 
-// Captures a screenshot of the active web state.
-- (void)captureSnapshotWithCompletion:(void (^)(UIImage*))completion {
+// Sets up the necessary utilities for turning on fullscreen as well as
+// capturing a snapshot of the base window.
+- (BOOL)prepareSnapshotCapturingInfrastructure {
   Browser* browser = self.browser;
   if (!browser) {
-    completion(nil);
-    return;
+    return NO;
   }
 
   web::WebState* activeWebState =
       browser->GetWebStateList()->GetActiveWebState();
 
   if (!activeWebState) {
-    completion(nil);
-    return;
+    return NO;
   }
 
-  CHECK(_associatedTabHelper, kLensOverlayNotFatalUntil);
-
-  UIWindow* sceneWindow = self.browser->GetSceneState().window;
+  UIWindow* sceneWindow = browser->GetSceneState().window;
   if (!sceneWindow) {
-    completion(nil);
-    return;
+    return NO;
   }
 
   _associatedTabHelper->SetSnapshotController(
@@ -846,6 +878,20 @@
           FullscreenController::FromBrowser(browser), sceneWindow,
           IsCurrentLayoutBottomOmnibox(browser)));
 
+  return YES;
+}
+
+// Captures a screenshot of the active web state.
+- (void)captureSnapshotWithCompletion:(void (^)(UIImage*))completion {
+  BOOL success = [self prepareSnapshotCapturingInfrastructure];
+  if (!success) {
+    if (completion) {
+      completion(nil);
+    }
+
+    return;
+  }
+
   _associatedTabHelper->CaptureFullscreenSnapshot(base::BindOnce(completion));
 }
 
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h b/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h
new file mode 100644
index 0000000..c5a3a8f5
--- /dev/null
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h
@@ -0,0 +1,27 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_VIEW_FINDER_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_VIEW_FINDER_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
+
+// Coordinates the Lens View Finder experience.
+@interface LensViewFinderCoordinator : ChromeCoordinator
+
+// Initializes this Coordinator with its `browser` and a nil base view
+// controller.
+- (instancetype)initWithBrowser:(Browser*)browser NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+// The base view controller for this coordinator.
+@property(weak, nonatomic, readwrite) UIViewController* baseViewController;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_COORDINATOR_LENS_VIEW_FINDER_COORDINATOR_H_
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.mm
new file mode 100644
index 0000000..0b280ab
--- /dev/null
+++ b/ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.mm
@@ -0,0 +1,126 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/lens_overlay/coordinator/lens_view_finder_coordinator.h"
+
+#import "ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.h"
+#import "ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.h"
+#import "ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.h"
+#import "ios/chrome/browser/shared/model/browser/browser.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
+#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
+#import "ios/chrome/browser/shared/public/commands/lens_commands.h"
+#import "ios/chrome/browser/shared/public/commands/lens_overlay_commands.h"
+#import "ios/chrome/browser/shared/public/commands/open_lens_input_selection_command.h"
+#import "ios/chrome/browser/shared/public/commands/search_image_with_lens_command.h"
+#import "ios/public/provider/chrome/browser/lens/lens_api.h"
+
+namespace {
+
+// Maps the presentation style to transition type.
+LensViewFinderTransition TransitionFromPresentationStyle(
+    LensInputSelectionPresentationStyle style) {
+  switch (style) {
+    case LensInputSelectionPresentationStyle::SlideFromLeft:
+      return LensViewFinderTransitionSlideFromLeft;
+    case LensInputSelectionPresentationStyle::SlideFromRight:
+      return LensViewFinderTransitionSlideFromRight;
+  }
+}
+
+}  // namespace
+
+@interface LensViewFinderCoordinator () <LensCommands,
+                                         ChromeLensControllerDelegate>
+@end
+
+@implementation LensViewFinderCoordinator {
+  // Controls the lens view finder experience.
+  id<ChromeLensController> _lensController;
+
+  // The user interface to be presented.
+  __weak UIViewController* _lensViewController;
+
+  // Manages the presenting & dismissal of the LVF user interface.
+  LensViewFinderTransitionManager* _transitionManager;
+}
+
+@synthesize baseViewController = _baseViewController;
+
+- (instancetype)initWithBrowser:(Browser*)browser {
+  return [super initWithBaseViewController:nil browser:browser];
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  [super start];
+  [self.browser->GetCommandDispatcher()
+      startDispatchingToTarget:self
+                   forProtocol:@protocol(LensCommands)];
+}
+
+- (void)stop {
+  [self.browser->GetCommandDispatcher() stopDispatchingToTarget:self];
+  [super stop];
+}
+
+#pragma mark - LensCommands
+
+- (void)searchImageWithLens:(SearchImageWithLensCommand*)command {
+  id<LensOverlayCommands> _lensOverlayCommands = HandlerForProtocol(
+      self.browser->GetCommandDispatcher(), LensOverlayCommands);
+  [_lensOverlayCommands
+      searchImageWithLens:command.image
+               entrypoint:LensOverlayEntrypoint::kSearchImageContextMenu];
+}
+
+- (void)openLensInputSelection:(OpenLensInputSelectionCommand*)command {
+  LensOverlayConfigurationFactory* configurationFactory =
+      [[LensOverlayConfigurationFactory alloc] init];
+  LensConfiguration* configuration = [configurationFactory
+      configurationForLensEntrypoint:command.entryPoint
+                             profile:self.browser->GetProfile()];
+
+  _transitionManager = [[LensViewFinderTransitionManager alloc]
+      initWithLVFTransitionType:TransitionFromPresentationStyle(
+                                    command.presentationStyle)];
+
+  _lensController = ios::provider::NewChromeLensController(configuration);
+  _lensController.delegate = self;
+
+  _lensViewController = _lensController.inputSelectionViewController;
+  _lensViewController.transitioningDelegate = _transitionManager;
+  _lensViewController.modalPresentationStyle =
+      UIModalPresentationOverCurrentContext;
+  _lensViewController.modalTransitionStyle =
+      UIModalTransitionStyleCrossDissolve;
+  [self.baseViewController presentViewController:_lensViewController
+                                        animated:YES
+                                      completion:nil];
+}
+
+#pragma mark - ChromeLensControllerDelegate
+
+- (void)lensControllerDidGenerateImage:(UIImage*)image {
+}
+
+- (void)lensControllerDidGenerateLoadParams:
+    (const web::NavigationManager::WebLoadParams&)params {
+}
+
+- (void)lensControllerDidSelectURL:(NSURL*)url {
+}
+
+- (void)lensControllerDidTapDismissButton {
+  if (self.baseViewController.presentedViewController == _lensViewController) {
+    [self.baseViewController dismissViewControllerAnimated:YES completion:nil];
+  }
+}
+
+- (CGRect)webContentFrame {
+  return [UIScreen mainScreen].bounds;
+}
+
+@end
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.h b/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.h
index bae1251..6255bd1 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.h
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.h
@@ -9,6 +9,7 @@
 
 @class LensConfiguration;
 enum class LensOverlayEntrypoint;
+enum class LensEntrypoint;
 class ProfileIOS;
 
 // A factory class to create configuration objects.
@@ -19,6 +20,10 @@
                           (LensOverlayEntrypoint)entrypoint
                                          profile:(ProfileIOS*)profile;
 
+// Creates a configuration object for the given entrypoint and profile.
+- (LensConfiguration*)configurationForLensEntrypoint:(LensEntrypoint)entrypoint
+                                             profile:(ProfileIOS*)profile;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_MODEL_LENS_OVERLAY_CONFIGURATION_FACTORY_H_
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.mm b/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.mm
index 121fd145..829a2cc1 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.mm
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_configuration_factory.mm
@@ -23,6 +23,8 @@
       return LensEntrypoint::LensOverlayLocationBar;
     case LensOverlayEntrypoint::kOverflowMenu:
       return LensEntrypoint::LensOverlayOverflowMenu;
+    case LensOverlayEntrypoint::kSearchImageContextMenu:
+      return LensEntrypoint::ContextMenu;
   }
 }
 
@@ -33,6 +35,13 @@
 - (LensConfiguration*)configurationForEntrypoint:
                           (LensOverlayEntrypoint)entrypoint
                                          profile:(ProfileIOS*)profile {
+  LensEntrypoint lensEntrypoint =
+      LensEntrypointFromOverlayEntrypoint(entrypoint);
+  return [self configurationForLensEntrypoint:lensEntrypoint profile:profile];
+}
+
+- (LensConfiguration*)configurationForLensEntrypoint:(LensEntrypoint)entrypoint
+                                             profile:(ProfileIOS*)profile {
   CHECK(profile, kLensOverlayNotFatalUntil);
   // Lens needs to have visibility into the user's identity and whether the
   // search should be incognito or not.
@@ -41,7 +50,7 @@
   configuration.isIncognito = isIncognito;
   configuration.singleSignOnService =
       GetApplicationContext()->GetSingleSignOnService();
-  configuration.entrypoint = LensEntrypointFromOverlayEntrypoint(entrypoint);
+  configuration.entrypoint = entrypoint;
 
   if (!isIncognito) {
     AuthenticationService* authenticationService =
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.h b/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.h
index cf12b60..d8eb358 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.h
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.h
@@ -13,6 +13,8 @@
   kLocationBar,
   // 3-dots overflow menu.
   kOverflowMenu,
+  // Search image button from context menu.
+  kSearchImageContextMenu,
 };
 
 namespace lens {
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.mm b/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.mm
index f5a1b092..9aa59a4f 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.mm
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_entrypoint.mm
@@ -12,6 +12,7 @@
     case LensOverlayEntrypoint::kLocationBar:
       return LensOverlayInvocationSource::kOmnibox;
     case LensOverlayEntrypoint::kOverflowMenu:
+    case LensOverlayEntrypoint::kSearchImageContextMenu:
       return LensOverlayInvocationSource::kAppMenu;
   }
 }
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_snapshot_controller.h b/ios/chrome/browser/lens_overlay/model/lens_overlay_snapshot_controller.h
index cb985ebc..084f092 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_snapshot_controller.h
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_snapshot_controller.h
@@ -18,6 +18,7 @@
 class FullscreenController;
 enum class FullscreenAnimatorStyle : short;
 
+// Manages the flow of capturing the snapshot of a given base window.
 class LensOverlaySnapshotController final
     : public FullscreenControllerObserver {
  public:
diff --git a/ios/chrome/browser/lens_overlay/ui/BUILD.gn b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
index 7a77791..e11fce4 100644
--- a/ios/chrome/browser/lens_overlay/ui/BUILD.gn
+++ b/ios/chrome/browser/lens_overlay/ui/BUILD.gn
@@ -8,6 +8,7 @@
     ":accessibility_identifier_constants",
     ":consent_presenter",
     ":container_presenter",
+    ":lvf_transition_manager",
     ":network_issue_alert_presenter",
     ":progress_bar",
     ":protocols",
@@ -170,6 +171,14 @@
   ]
 }
 
+source_set("lvf_transition_manager") {
+  sources = [
+    "lens_view_finder_transition_manager.h",
+    "lens_view_finder_transition_manager.mm",
+  ]
+  deps = []
+}
+
 source_set("eg2_tests") {
   configs += [ "//build/config/ios:xctest_config" ]
   testonly = true
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.h b/ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.h
new file mode 100644
index 0000000..cd7690c
--- /dev/null
+++ b/ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.h
@@ -0,0 +1,25 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_VIEW_FINDER_TRANSITION_MANAGER_H_
+#define IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_VIEW_FINDER_TRANSITION_MANAGER_H_
+
+#import <UIKit/UIKit.h>
+
+// Supported transition for LVF presentation.
+typedef NS_ENUM(NSInteger, LensViewFinderTransition) {
+  LensViewFinderTransitionSlideFromLeft = 0,
+  LensViewFinderTransitionSlideFromRight,
+};
+
+@interface LensViewFinderTransitionManager
+    : NSObject <UIViewControllerTransitioningDelegate,
+                UIViewControllerAnimatedTransitioning>
+
+- (instancetype)initWithLVFTransitionType:
+    (LensViewFinderTransition)transitionType;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_LENS_OVERLAY_UI_LENS_VIEW_FINDER_TRANSITION_MANAGER_H_
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.mm b/ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.mm
new file mode 100644
index 0000000..cc205838
--- /dev/null
+++ b/ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.mm
@@ -0,0 +1,165 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/lens_overlay/ui/lens_view_finder_transition_manager.h"
+
+namespace {
+
+// The time it takes for the presenting animation to complete.
+const CGFloat kPresentingTransitionAnimationDuration = .4;
+
+// The time it takes for the dismissal animation to complete.
+const CGFloat kDismissalTransitionAnimationDuration = .3;
+
+// Motion curves for the presentation animation.
+static const CGFloat kPresentationAnimationCurve0 = 0.05;
+static const CGFloat kPresentationAnimationCurve1 = 0.7;
+static const CGFloat kPresentationAnimationCurve2 = 0.1;
+static const CGFloat kPresentationAnimationCurve3 = 1.;
+
+}  // namespace
+
+@implementation LensViewFinderTransitionManager {
+  LensViewFinderTransition _transitionType;
+}
+
+- (instancetype)initWithLVFTransitionType:
+    (LensViewFinderTransition)transitionType {
+  self = [super init];
+  if (self) {
+    _transitionType = transitionType;
+  }
+
+  return self;
+}
+
+#pragma mark - UIViewControllerTransitioningDelegate
+
+- (id<UIViewControllerInteractiveTransitioning>)
+    interactionControllerForPresentation:
+        (id<UIViewControllerAnimatedTransitioning>)animator {
+  return nil;
+}
+
+- (id<UIViewControllerInteractiveTransitioning>)
+    interactionControllerForDismissal:
+        (id<UIViewControllerAnimatedTransitioning>)animator {
+  return nil;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+    animationControllerForPresentedController:(UIViewController*)presented
+                         presentingController:(UIViewController*)presenting
+                             sourceController:(UIViewController*)source {
+  return self;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+    animationControllerForDismissedController:(UIViewController*)dismissed {
+  return self;
+}
+
+#pragma mark - UIViewControllerAnimatedTransitioning
+
+- (NSTimeInterval)transitionDuration:
+    (id<UIViewControllerContextTransitioning>)transitionContext {
+  return [self isPresentingFromContext:transitionContext]
+             ? kPresentingTransitionAnimationDuration
+             : kDismissalTransitionAnimationDuration;
+}
+
+- (void)animateTransition:
+    (id<UIViewControllerContextTransitioning>)transitionContext {
+  BOOL presenting = [self isPresentingFromContext:transitionContext];
+  if (presenting) {
+    [self animatePresenting:transitionContext];
+  } else {
+    [self animateDismissal:transitionContext];
+  }
+}
+
+#pragma mark - Private methods
+
+- (void)animatePresenting:
+    (id<UIViewControllerContextTransitioning>)transitionContext {
+  UIView* containerView = [transitionContext containerView];
+  UIView* lensView =
+      [transitionContext viewForKey:UITransitionContextToViewKey];
+  UIViewController* lensViewController = [transitionContext
+      viewControllerForKey:UITransitionContextToViewControllerKey];
+
+  [containerView addSubview:lensView];
+  [containerView bringSubviewToFront:lensView];
+  CGRect startingFrame = containerView.bounds;
+  BOOL isLeftSlide = _transitionType == LensViewFinderTransitionSlideFromLeft;
+  if (isLeftSlide) {
+    startingFrame.origin.x = -startingFrame.size.width;
+  } else {
+    startingFrame.origin.x = containerView.bounds.size.width;
+  }
+
+  lensView.frame = startingFrame;
+
+  UIViewPropertyAnimator* animator = [[UIViewPropertyAnimator alloc]
+      initWithDuration:[self transitionDuration:transitionContext]
+         controlPoint1:CGPointMake(kPresentationAnimationCurve0,
+                                   kPresentationAnimationCurve1)
+         controlPoint2:CGPointMake(kPresentationAnimationCurve2,
+                                   kPresentationAnimationCurve3)
+            animations:^{
+              lensView.frame = [transitionContext
+                  finalFrameForViewController:lensViewController];
+            }];
+
+  [animator addCompletion:^(UIViewAnimatingPosition finalPosition) {
+    BOOL success = ![transitionContext transitionWasCancelled];
+    [transitionContext completeTransition:success];
+  }];
+
+  [animator startAnimation];
+}
+
+- (void)animateDismissal:
+    (id<UIViewControllerContextTransitioning>)transitionContext {
+  UIView* containerView = [transitionContext containerView];
+  UIView* toView = [transitionContext viewForKey:UITransitionContextToViewKey];
+  UIView* lensView =
+      [transitionContext viewForKey:UITransitionContextFromViewKey];
+
+  // Create and add a Lens view snapshot.
+  UIView* lensViewSnapshot = [lensView snapshotViewAfterScreenUpdates:YES];
+
+  [containerView addSubview:toView];
+  [containerView addSubview:lensViewSnapshot];
+  [lensView removeFromSuperview];
+  [containerView bringSubviewToFront:lensViewSnapshot];
+
+  lensViewSnapshot.alpha = 1.0;
+
+  [UIView animateWithDuration:[self transitionDuration:transitionContext]
+      animations:^{
+        lensViewSnapshot.alpha = 0.0;
+      }
+      completion:^(BOOL finished) {
+        BOOL success = ![transitionContext transitionWasCancelled];
+        if (success) {
+          [lensViewSnapshot removeFromSuperview];
+        }
+
+        [transitionContext completeTransition:success];
+      }];
+}
+
+- (BOOL)isPresentingFromContext:
+    (id<UIViewControllerContextTransitioning>)transitionContext {
+  UIViewController* fromViewController = [transitionContext
+      viewControllerForKey:UITransitionContextFromViewControllerKey];
+  UIViewController* toViewController = [transitionContext
+      viewControllerForKey:UITransitionContextToViewControllerKey];
+  UIViewController* toPresentingViewController =
+      toViewController.presentingViewController;
+  return toPresentingViewController == fromViewController;
+}
+
+@end
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl.h b/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
index 05dfe7e..1fc74e2 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
@@ -46,6 +46,7 @@
   bool HasProfileWithName(std::string_view name) const override;
   bool CanCreateProfileWithName(std::string_view name) const override;
   std::string ReserveNewProfileName() override;
+  bool CanDeleteProfileWithName(std::string_view name) const override;
   bool LoadProfileAsync(std::string_view name,
                         ProfileLoadedCallback initialized_callback,
                         ProfileLoadedCallback created_callback) override;
@@ -56,6 +57,7 @@
   ProfileIOS* CreateProfile(std::string_view name) override;
   void UnloadProfile(std::string_view name) override;
   void UnloadAllProfiles() override;
+  void MarkProfileForDeletion(std::string_view name) override;
   ProfileAttributesStorageIOS* GetProfileAttributesStorage() override;
 
   // ProfileIOS::Delegate:
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
index bc973f5..6b9ee3c 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
@@ -21,6 +21,7 @@
 #import "base/threading/scoped_blocking_call.h"
 #import "base/uuid.h"
 #import "components/prefs/pref_service.h"
+#import "components/prefs/scoped_user_pref_update.h"
 #import "components/signin/public/identity_manager/identity_manager.h"
 #import "ios/chrome/browser/profile/model/off_the_record_profile_ios_impl.h"
 #import "ios/chrome/browser/profile/model/profile_ios_impl.h"
@@ -215,6 +216,7 @@
 bool ProfileManagerIOSImpl::CanCreateProfileWithName(
     std::string_view name) const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
   // Cannot create a profile with the same name as an existing profile.
   if (HasProfileWithName(name)) {
     return false;
@@ -225,9 +227,12 @@
     return false;
   }
 
-  // TODO(crbug.com/335630301): check whether there is a Profile with that name
-  // whose deletion is pending, and return false if this is the case (to avoid
-  // recovering its state).
+  const base::Value::List& profiles_to_remove =
+      local_state_->GetList(prefs::kProfilesToRemove);
+  if (base::Contains(profiles_to_remove, name)) {
+    return false;
+  }
+
   return true;
 }
 
@@ -245,6 +250,19 @@
   return profile_name;
 }
 
+bool ProfileManagerIOSImpl::CanDeleteProfileWithName(
+    std::string_view name) const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!HasProfileWithName(name)) {
+    return false;
+  }
+  // Cannot delete the personal profile.
+  if (name == profile_attributes_storage_.GetPersonalProfileName()) {
+    return false;
+  }
+  return true;
+}
+
 bool ProfileManagerIOSImpl::LoadProfileAsync(
     std::string_view name,
     ProfileLoadedCallback initialized_callback,
@@ -326,6 +344,22 @@
   }
 }
 
+void ProfileManagerIOSImpl::MarkProfileForDeletion(std::string_view name) {
+  DCHECK(CanDeleteProfileWithName(name));
+
+  ScopedListPrefUpdate update(local_state_, prefs::kProfilesToRemove);
+  update->Append(base::Value(name));
+
+  auto iter = profiles_map_.find(name);
+  if (iter != profiles_map_.end() && iter->second.is_loaded()) {
+    ProfileIOS* profile = iter->second.profile();
+    DCHECK(profile);
+    for (auto& observer : observers_) {
+      observer.OnProfileMarkedForPermanentDeletion(this, profile);
+    }
+  }
+}
+
 ProfileAttributesStorageIOS*
 ProfileManagerIOSImpl::GetProfileAttributesStorage() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -432,9 +466,15 @@
     can_create &= CanCreateProfileWithName(name);
   }
 
+  // Profile creation is forbiden for profiles that have been marked for
+  // deletion.
+  const base::Value::List& profiles_to_remove =
+      local_state_->GetList(prefs::kProfilesToRemove);
+  const bool marked_for_deletion = base::Contains(profiles_to_remove, name);
+
   auto iter = profiles_map_.find(name);
   if (iter == profiles_map_.end()) {
-    if (is_new_profile && !can_create) {
+    if (marked_for_deletion || (is_new_profile && !can_create)) {
       if (!initialized_callback.is_null()) {
         std::move(initialized_callback).Run(nullptr);
       }
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm b/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm
index ff76333..b3a2523 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl_unittest.mm
@@ -309,6 +309,25 @@
   EXPECT_EQ(created_profile, loaded_profile);
 }
 
+// Tests that Profile marked for deletion does not create a new profile.
+TEST_F(ProfileManagerIOSImplTest, CreateProfile_MarkedForDeletion) {
+  // Create a few profiles synchronously.
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName1));
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName2));
+  // Check that the profiles are accessible.
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName2));
+
+  profile_manager().MarkProfileForDeletion(kProfileName2);
+  profile_manager().UnloadProfile(kProfileName2);
+
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_FALSE(profile_manager().GetProfileWithName(kProfileName2));
+
+  // Ensures that the profile cannot be created.
+  ASSERT_FALSE(profile_manager().CreateProfile(kProfileName2));
+}
+
 // Tests that calling CreateProfileAsync(...) a second time returns the Profile
 // that has already been laoded.
 TEST_F(ProfileManagerIOSImplTest, CreateProfileAsync_Reload) {
@@ -560,3 +579,75 @@
       attributes_storage().GetAttributesForProfileWithName(name);
   EXPECT_TRUE(attrs.IsNewProfile());
 }
+
+// Tests that marking profile for deletion invoke
+// OnProfileMarkedForPermanentDeletion(...) on the observers.
+TEST_F(ProfileManagerIOSImplTest, MarkProfileForDeletion) {
+  // Create a few profiles synchronously.
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName1));
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName2));
+
+  ScopedTestProfileManagerObserverIOS observer(profile_manager());
+  EXPECT_FALSE(observer.on_profile_marked_for_permanent_deletation_called());
+  EXPECT_FALSE(observer.on_profile_unloaded_called());
+
+  // Check that the profiles are accessible.
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName2));
+
+  // Mark profile for deletion, they should not longer be accessible and the
+  // observer must have been notified of that.
+  profile_manager().MarkProfileForDeletion(kProfileName2);
+
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName2));
+  EXPECT_TRUE(observer.on_profile_marked_for_permanent_deletation_called());
+}
+
+// Tests that marking unloaded profile for deletion does not invoke
+// OnProfileMarkedForPermanentDeletion(...) on the observers.
+TEST_F(ProfileManagerIOSImplTest,
+       MarkProfileForDeletion_UnloadedProfileShouldNotCallObserver) {
+  // Create a few profiles synchronously.
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName1));
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName2));
+
+  ScopedTestProfileManagerObserverIOS observer(profile_manager());
+  EXPECT_FALSE(observer.on_profile_marked_for_permanent_deletation_called());
+  EXPECT_FALSE(observer.on_profile_unloaded_called());
+
+  // Check that the profiles are accessible.
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName2));
+
+  // Unload a profile, it should not longer be accessible and the
+  // observer must have been notified of that.
+  profile_manager().UnloadProfile(kProfileName2);
+
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_FALSE(profile_manager().GetProfileWithName(kProfileName2));
+  EXPECT_TRUE(observer.on_profile_unloaded_called());
+
+  // Mark unloaded profile for deletion, ensure the observer is not called.
+  profile_manager().MarkProfileForDeletion(kProfileName2);
+
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_FALSE(profile_manager().GetProfileWithName(kProfileName2));
+  EXPECT_FALSE(observer.on_profile_marked_for_permanent_deletation_called());
+}
+
+// Tests that it is not possible to create a profile with a name marked for
+// deletion.
+TEST_F(ProfileManagerIOSImplTest,
+       MarkProfileForDeletion_CantCreateProfileWithProfileMarkedForDeletion) {
+  // Create a few profiles synchronously.
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName1));
+  ASSERT_TRUE(profile_manager().CreateProfile(kProfileName2));
+  // Check that the profiles are accessible.
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName1));
+  EXPECT_TRUE(profile_manager().GetProfileWithName(kProfileName2));
+
+  // Mark profile for deletion.
+  profile_manager().MarkProfileForDeletion(kProfileName2);
+  EXPECT_FALSE(profile_manager().CanCreateProfileWithName(kProfileName2));
+}
diff --git a/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view.mm b/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view.mm
index 1acddaa..9c7249b2 100644
--- a/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view.mm
+++ b/ios/chrome/browser/sad_tab/ui_bundled/sad_tab_view.mm
@@ -508,6 +508,7 @@
 
 #pragma mark - UITextViewDelegate
 
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
 - (BOOL)textView:(UITextView*)textView
     shouldInteractWithURL:(NSURL*)URL
                   inRange:(NSRange)characterRange
@@ -520,6 +521,20 @@
   // Returns NO as the app is handling the opening of the URL.
   return NO;
 }
+#endif
+
+- (UIAction*)textView:(UITextView*)textView
+    primaryActionForTextItem:(UITextItem*)textItem
+               defaultAction:(UIAction*)defaultAction API_AVAILABLE(ios(17.0)) {
+  DCHECK(self.footerLabel == textView);
+  DCHECK(textItem.link);
+
+  __weak __typeof(self) weakSelf = self;
+  return [UIAction actionWithHandler:^(UIAction* action) {
+    [weakSelf.delegate sadTabView:weakSelf
+        showSuggestionsPageWithURL:net::GURLWithNSURL(textItem.link)];
+  }];
+}
 
 @end
 
diff --git a/ios/chrome/browser/share_kit/model/share_kit_read_configuration.h b/ios/chrome/browser/share_kit/model/share_kit_read_configuration.h
index 0d5743f5..6d6d46c8 100644
--- a/ios/chrome/browser/share_kit/model/share_kit_read_configuration.h
+++ b/ios/chrome/browser/share_kit/model/share_kit_read_configuration.h
@@ -27,10 +27,6 @@
 // Configuration object for reading a shared group.
 @interface ShareKitReadConfiguration : NSObject
 
-// The list of collabs ID to be read.
-// TODO(crbug.com/383530755): Remove this.
-@property(nonatomic, copy) NSArray<NSString*>* collabIDs;
-
 // The parameters for the groups to be read.
 @property(nonatomic, copy)
     NSArray<ShareKitReadGroupParamConfiguration*>* groupsParam;
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
index 81abbb8..29a1689 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -502,6 +502,7 @@
   registry->RegisterStringPref(prefs::kLastUsedProfile, std::string());
   registry->RegisterBooleanPref(prefs::kLegacyProfileHidden, false);
   registry->RegisterDictionaryPref(prefs::kLegacyProfileMap);
+  registry->RegisterListPref(prefs::kProfilesToRemove);
 
   [MemoryDebuggerManager registerLocalState:registry];
   [IncognitoReauthSceneAgent registerLocalState:registry];
diff --git a/ios/chrome/browser/shared/model/prefs/pref_names.h b/ios/chrome/browser/shared/model/prefs/pref_names.h
index f7fbdb98..5b2682f 100644
--- a/ios/chrome/browser/shared/model/prefs/pref_names.h
+++ b/ios/chrome/browser/shared/model/prefs/pref_names.h
@@ -64,6 +64,10 @@
 // A map of a scene and a profile.
 inline constexpr char kProfileForScene[] = "ios.multiprofile.profile_for_scene";
 
+// List of profiles' name that has been marked for deletion.
+inline constexpr char kProfilesToRemove[] =
+    "ios.multiprofile.profiles_marked_for_deletion";
+
 // A string of NSUUID used to access the WebKit storage per Profile.
 inline constexpr char kBrowserStateStorageIdentifier[] = "profile.storage_id";
 
diff --git a/ios/chrome/browser/shared/model/profile/profile_attributes_ios.cc b/ios/chrome/browser/shared/model/profile/profile_attributes_ios.cc
index 88c9a29..c3c4941 100644
--- a/ios/chrome/browser/shared/model/profile/profile_attributes_ios.cc
+++ b/ios/chrome/browser/shared/model/profile/profile_attributes_ios.cc
@@ -19,6 +19,7 @@
 constexpr std::string_view kAttachedGaiaIdsKey = "attached_gaia_ids";
 constexpr std::string_view kUserNameKey = "user_name";
 constexpr std::string_view kNewProfile = "new_profile";
+constexpr std::string_view kIsFullyInitializedKey = "fully_initialized";
 constexpr std::string_view kDiscardedSessions = "discarded_sessions";
 
 // Retrieves a bool value from the dictionary.
@@ -125,6 +126,10 @@
   return GetBool(storage_, kNewProfile);
 }
 
+bool ProfileAttributesIOS::IsFullyInitialized() const {
+  return GetBool(storage_, kIsFullyInitializedKey);
+}
+
 const std::string& ProfileAttributesIOS::GetGaiaId() const {
   return GetString(storage_, kGaiaIdKey);
 }
@@ -153,13 +158,17 @@
   return !GetGaiaId().empty() || !GetUserName().empty();
 }
 
+ProfileAttributesIOS::SessionIds ProfileAttributesIOS::GetDiscardedSessions()
+    const {
+  return GetStringSet<SessionIds>(storage_, kDiscardedSessions);
+}
+
 void ProfileAttributesIOS::ClearIsNewProfile() {
   SetBool(storage_, kNewProfile, false);
 }
 
-ProfileAttributesIOS::SessionIds ProfileAttributesIOS::GetDiscardedSessions()
-    const {
-  return GetStringSet<SessionIds>(storage_, kDiscardedSessions);
+void ProfileAttributesIOS::SetFullyInitialized() {
+  SetBool(storage_, kIsFullyInitializedKey, true);
 }
 
 void ProfileAttributesIOS::SetAuthenticationInfo(std::string_view gaia_id,
diff --git a/ios/chrome/browser/shared/model/profile/profile_attributes_ios.h b/ios/chrome/browser/shared/model/profile/profile_attributes_ios.h
index ec4acc8..b8e00e8 100644
--- a/ios/chrome/browser/shared/model/profile/profile_attributes_ios.h
+++ b/ios/chrome/browser/shared/model/profile/profile_attributes_ios.h
@@ -45,7 +45,13 @@
   const std::string& GetProfileName() const;
 
   // Gets information related to the profile.
+  // IsNewProfile() is true if the profile has been registered with
+  // ProfileAttributesStorageIOS, but has never been loaded.
   bool IsNewProfile() const;
+  // IsFullyInitialized() is true if the profile has been loaded at least once,
+  // and all first-time setup steps have been completed (e.g. for work profiles,
+  // this includes signing in the corresponding managed account).
+  bool IsFullyInitialized() const;
   const std::string& GetGaiaId() const;
   const std::string& GetUserName() const;
   bool HasAuthenticationError() const;
@@ -56,6 +62,7 @@
 
   // Sets information related to the profile.
   void ClearIsNewProfile();
+  void SetFullyInitialized();
   void SetAuthenticationInfo(std::string_view gaia_id,
                              std::string_view user_name);
   void SetHasAuthenticationError(bool value);
diff --git a/ios/chrome/browser/shared/model/profile/profile_attributes_ios_unittest.cc b/ios/chrome/browser/shared/model/profile/profile_attributes_ios_unittest.cc
index 8ecf58f..e6ac043 100644
--- a/ios/chrome/browser/shared/model/profile/profile_attributes_ios_unittest.cc
+++ b/ios/chrome/browser/shared/model/profile/profile_attributes_ios_unittest.cc
@@ -30,6 +30,17 @@
   EXPECT_FALSE(attributes.IsNewProfile());
 }
 
+// Tests that IsFullyInitialized() starts out false and can be set to true.
+TEST_F(ProfileAttributesIOSTest, FullyInitialized) {
+  ProfileAttributesIOS attributes =
+      ProfileAttributesIOS::CreateNew(kProfileName);
+  EXPECT_EQ(attributes.GetProfileName(), kProfileName);
+  EXPECT_FALSE(attributes.IsFullyInitialized());
+
+  attributes.SetFullyInitialized();
+  EXPECT_TRUE(attributes.IsFullyInitialized());
+}
+
 // Tests the GetName() method of ProfileAttributesIOS.
 TEST_F(ProfileAttributesIOSTest, GetName) {
   ProfileAttributesIOS attributes =
diff --git a/ios/chrome/browser/shared/model/profile/profile_manager_ios.h b/ios/chrome/browser/shared/model/profile/profile_manager_ios.h
index d2b891c..3edfde7b 100644
--- a/ios/chrome/browser/shared/model/profile/profile_manager_ios.h
+++ b/ios/chrome/browser/shared/model/profile/profile_manager_ios.h
@@ -55,6 +55,9 @@
   // profile with LoadProfileAsync() or LoadProfile() will also fail.
   virtual std::string ReserveNewProfileName() = 0;
 
+  // Returns whether a profile with `name` can be deleted.
+  virtual bool CanDeleteProfileWithName(std::string_view name) const = 0;
+
   // Asynchronously loads a Profile known by `name` if it exists. The
   // `created_callback` will be called with the Profile when it has been created
   // (but not yet initialised) and `initialised_callback` will be called once
@@ -104,6 +107,11 @@
   // ProfileManagerIOS itself is destroyed.
   virtual void UnloadAllProfiles() = 0;
 
+  // Marks the given Profile for deletion. This must not be called if the
+  // profile can not be deleted (for example, personal profile cannot be
+  // deleted). Observers will be notified only if the profile is loaded.
+  virtual void MarkProfileForDeletion(std::string_view name) = 0;
+
   // Returns the ProfileAttributesStorageIOS associated with this manager.
   virtual ProfileAttributesStorageIOS* GetProfileAttributesStorage() = 0;
 
diff --git a/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.h b/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.h
index 51c1b1aa..7ffeb35 100644
--- a/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.h
+++ b/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.h
@@ -38,6 +38,7 @@
   bool HasProfileWithName(std::string_view name) const override;
   bool CanCreateProfileWithName(std::string_view name) const override;
   std::string ReserveNewProfileName() override;
+  bool CanDeleteProfileWithName(std::string_view name) const override;
   bool LoadProfileAsync(std::string_view name,
                         ProfileLoadedCallback initialized_callback,
                         ProfileLoadedCallback created_callback) override;
@@ -48,6 +49,7 @@
   ProfileIOS* CreateProfile(std::string_view name) override;
   void UnloadProfile(std::string_view name) override;
   void UnloadAllProfiles() override;
+  void MarkProfileForDeletion(std::string_view name) override;
   ProfileAttributesStorageIOS* GetProfileAttributesStorage() override;
 
   // Builds and adds a TestProfileIOS using `builder`. Asserts that no Profile
diff --git a/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.mm b/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.mm
index a67a2513..1994ec8 100644
--- a/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.mm
+++ b/ios/chrome/browser/shared/model/profile/test/test_profile_manager_ios.mm
@@ -88,6 +88,11 @@
   return name;
 }
 
+bool TestProfileManagerIOS::CanDeleteProfileWithName(
+    std::string_view name) const {
+  return false;
+}
+
 bool TestProfileManagerIOS::LoadProfileAsync(
     std::string_view name,
     ProfileLoadedCallback initialized_callback,
@@ -149,6 +154,10 @@
   }
 }
 
+void TestProfileManagerIOS::MarkProfileForDeletion(std::string_view name) {
+  // No-op
+}
+
 ProfileAttributesStorageIOS*
 TestProfileManagerIOS::GetProfileAttributesStorage() {
   return &profile_attributes_storage_;
diff --git a/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h b/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h
index a2aff74..efb2b29 100644
--- a/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h
+++ b/ios/chrome/browser/shared/public/commands/lens_overlay_commands.h
@@ -19,6 +19,11 @@
                  entrypoint:(LensOverlayEntrypoint)entrypoint
                  completion:(void (^)(BOOL))completion;
 
+/// Responds to a search image with Lens request by creating a new Lens UI with
+/// the given image.
+- (void)searchImageWithLens:(UIImage*)image
+                 entrypoint:(LensOverlayEntrypoint)entrypoint;
+
 /// Display the lens overlay, if it exists.
 - (void)showLensUI:(BOOL)animated;
 
diff --git a/ios/chrome/browser/shared/ui/symbols/symbol_configurations.h b/ios/chrome/browser/shared/ui/symbols/symbol_configurations.h
index a7229c7..fc27577 100644
--- a/ios/chrome/browser/shared/ui/symbols/symbol_configurations.h
+++ b/ios/chrome/browser/shared/ui/symbols/symbol_configurations.h
@@ -33,6 +33,12 @@
 // The size of the cloud slash icon.
 extern const CGFloat kCloudSlashSymbolPointSize;
 
+// Returns the background color for incognito symbol.
+UIColor* LargeIncognitoBackgroundColor();
+
+// Returns the foreground color for incognito symbol.
+UIColor* LargeIncognitoForegroundColor();
+
 // Returns the palette to be used on incognito symbol when it is small.
 NSArray<UIColor*>* SmallIncognitoPalette();
 
diff --git a/ios/chrome/browser/shared/ui/symbols/symbol_configurations.mm b/ios/chrome/browser/shared/ui/symbols/symbol_configurations.mm
index 6d09250..96318f3b 100644
--- a/ios/chrome/browser/shared/ui/symbols/symbol_configurations.mm
+++ b/ios/chrome/browser/shared/ui/symbols/symbol_configurations.mm
@@ -21,6 +21,14 @@
 
 const CGFloat kCloudSlashSymbolPointSize = 20;
 
+UIColor* LargeIncognitoBackgroundColor() {
+  return [UIColor colorNamed:kGrey700Color];
+}
+
+UIColor* LargeIncognitoForegroundColor() {
+  return [UIColor colorNamed:kGrey100Color];
+}
+
 NSArray<UIColor*>* SmallIncognitoPalette() {
   return @[
     [UIColor colorNamed:kGrey400Color], [UIColor colorNamed:kGrey100Color]
@@ -28,9 +36,7 @@
 }
 
 NSArray<UIColor*>* LargeIncognitoPalette() {
-  return @[
-    [UIColor colorNamed:kGrey100Color], [UIColor colorNamed:kGrey700Color]
-  ];
+  return @[ LargeIncognitoForegroundColor(), LargeIncognitoBackgroundColor() ];
 }
 
 UIColor* CloudSlashTintColor() {
diff --git a/ios/chrome/browser/shared/ui/table_view/table_view_illustrated_empty_view.mm b/ios/chrome/browser/shared/ui/table_view/table_view_illustrated_empty_view.mm
index 6e2a7328..af5f7bf 100644
--- a/ios/chrome/browser/shared/ui/table_view/table_view_illustrated_empty_view.mm
+++ b/ios/chrome/browser/shared/ui/table_view/table_view_illustrated_empty_view.mm
@@ -117,6 +117,7 @@
 
 #pragma mark - UITextViewDelegate
 
+#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
 - (BOOL)textView:(UITextView*)textView
     shouldInteractWithURL:(NSURL*)URL
                   inRange:(NSRange)characterRange
@@ -125,6 +126,18 @@
 
   return NO;
 }
+#endif
+
+- (UIAction*)textView:(UITextView*)textView
+    primaryActionForTextItem:(UITextItem*)textItem
+               defaultAction:(UIAction*)defaultAction API_AVAILABLE(ios(17.0)) {
+  __weak __typeof(self) weakSelf = self;
+  NSURL* URL = textItem.link;
+  return [UIAction actionWithHandler:^(UIAction* action) {
+    [weakSelf.delegate tableViewIllustratedEmptyView:weakSelf
+                                  didTapSubtitleLink:URL];
+  }];
+}
 
 - (void)textViewDidChangeSelection:(UITextView*)textView {
   // Make the textView not selectable while allowing interactions with links in
diff --git a/ios/chrome/browser/signin/model/account_profile_mapper.mm b/ios/chrome/browser/signin/model/account_profile_mapper.mm
index c60e25e..4058b394 100644
--- a/ios/chrome/browser/signin/model/account_profile_mapper.mm
+++ b/ios/chrome/browser/signin/model/account_profile_mapper.mm
@@ -279,18 +279,19 @@
   }
   // Delete the old managed profile (if it exists).
   if (abandoned_managed_profile_name) {
-    // The old managed profile should still be "new", i.e. not actually created
-    // on disk, so that no actual user data gets deleted here.
-    CHECK(storage
-              ->GetAttributesForProfileWithName(*abandoned_managed_profile_name)
-              .IsNewProfile());
+    // The old managed profile must not have been initialized, so that no actual
+    // user data gets deleted here.
+    CHECK(
+        !storage
+             ->GetAttributesForProfileWithName(*abandoned_managed_profile_name)
+             .IsFullyInitialized());
     // Also, the old managed profile mustn't be loaded, since it's going to be
     // deleted and there's no good way to unload it.
     CHECK(
         !profile_manager_->GetProfileWithName(*abandoned_managed_profile_name));
 
-    // Since the profile doesn't exist on disk, just removing the entry in the
-    // attributes storage is sufficient.
+    // TODO(crbug.com/331783685): Call the proper profile deletion API once it
+    // exists, see crbug.com/380903168.
     storage->RemoveProfile(*abandoned_managed_profile_name);
   }
 
diff --git a/ios/chrome/browser/signin/model/account_profile_mapper_unittest.mm b/ios/chrome/browser/signin/model/account_profile_mapper_unittest.mm
index 3eeea89c..bb23d3c 100644
--- a/ios/chrome/browser/signin/model/account_profile_mapper_unittest.mm
+++ b/ios/chrome/browser/signin/model/account_profile_mapper_unittest.mm
@@ -159,6 +159,10 @@
     return profile_name;
   }
 
+  bool CanDeleteProfileWithName(std::string_view name) const override {
+    return false;
+  }
+
   bool LoadProfileAsync(std::string_view name,
                         ProfileLoadedCallback initialized_callback,
                         ProfileLoadedCallback created_callback) override {
@@ -190,6 +194,8 @@
   void UnloadProfile(std::string_view name) override { NOTREACHED(); }
   void UnloadAllProfiles() override { NOTREACHED(); }
 
+  void MarkProfileForDeletion(std::string_view name) override { NOTREACHED(); }
+
   ProfileAttributesStorageIOS* GetProfileAttributesStorage() override {
     return &profile_attributes_storage_;
   }
diff --git a/ios/chrome/browser/signin/model/authentication_service.mm b/ios/chrome/browser/signin/model/authentication_service.mm
index 024660be..21e0b36f 100644
--- a/ios/chrome/browser/signin/model/authentication_service.mm
+++ b/ios/chrome/browser/signin/model/authentication_service.mm
@@ -32,6 +32,7 @@
 #import "ios/chrome/browser/policy/model/policy_util.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
+#import "ios/chrome/browser/shared/model/profile/profile_attributes_ios.h"
 #import "ios/chrome/browser/shared/model/profile/profile_attributes_storage_ios.h"
 #import "ios/chrome/browser/shared/model/profile/profile_manager_ios.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
@@ -193,10 +194,9 @@
     base::UmaHistogramEnumeration("Signin.IOSDeviceRestoreSignedInState",
                                   signed_in_state);
   }
-  // If opening the managed identity profile, the user needs to be signed in
-  // automatically.
-  // TODO(crbug.com/375605572): Need to create an onboarding screen for a
-  // new profile.
+
+  // If opening a managed profile, the user needs to be signed in automatically.
+  // TODO(crbug.com/375605572): Move the entire logic below into a continuation.
   if (!AreSeparateProfilesForManagedAccountsEnabled()) {
     // Skip if the feature is not enabled.
     return;
@@ -208,11 +208,31 @@
     CHECK_IS_TEST();
     return;
   }
-  std::string default_profile_name =
-      profile_manager->GetProfileAttributesStorage()->GetPersonalProfileName();
+  ProfileAttributesStorageIOS* attributes_storage =
+      profile_manager->GetProfileAttributesStorage();
+
   std::string profile_name = account_manager_service_->GetProfileName();
-  if (profile_name == default_profile_name) {
-    // Skip if the current profile is the default profile.
+
+  // If the profile was already initialized before, nothing to do here.
+  if (attributes_storage->GetAttributesForProfileWithName(profile_name)
+          .IsFullyInitialized()) {
+    return;
+  }
+
+  // Once this method returns, the profile is considered fully initialized.
+  base::ScopedClosureRunner mark_profile_initialized(base::BindOnce(
+      [](ProfileAttributesStorageIOS* attributes_storage,
+         std::string_view profile_name) {
+        attributes_storage->UpdateAttributesForProfileWithName(
+            profile_name, base::BindOnce([](ProfileAttributesIOS attrs) {
+              attrs.SetFullyInitialized();
+              return attrs;
+            }));
+      },
+      attributes_storage, profile_name));
+
+  if (profile_name == attributes_storage->GetPersonalProfileName()) {
+    // Nothing to do if the current profile is the default profile.
     return;
   }
   NSArray<id<SystemIdentity>>* identities_for_profile =
@@ -221,7 +241,7 @@
   // this CHECK.
   CHECK_EQ(identities_for_profile.count, 1ul);
   if (HasPrimaryIdentity(signin::ConsentLevel::kSignin)) {
-    // Skip if the profile is already signed in.
+    // Nothing to do if the profile is already signed in.
     return;
   }
   // TODO(crbug.com/375605572): Need to set the right access point.
diff --git a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
index 72a0bad..2d9ca3b 100644
--- a/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
+++ b/ios/chrome/browser/ui/authentication/authentication_flow_performer.mm
@@ -521,11 +521,18 @@
   _managedConfirmationAlertCoordinator = nil;
   [self managedConfirmationDidAccept:accepted
                              browser:browser
-            keepBrowsingDataSeparate:NO];
+            keepBrowsingDataSeparate:
+                AreSeparateProfilesForManagedAccountsEnabled()];
 }
 
 // Called when the user accepted to continue to sign-in with a managed account.
 // `accepted` is YES when the user confirmed or NO if the user canceled.
+// If `keepBrowsingDataSeparate` is `YES`, the managed account gets signed in to
+// a new empty work profile. This must only be specified if
+// AreSeparateProfilesForManagedAccountsEnabled() is true.
+// If `keepBrowsingDataSeparate` is `NO`, the account gets signed in to the
+// current profile. If AreSeparateProfilesForManagedAccountsEnabled() is true,
+// this involves converting the current profile into a work profile.
 - (void)managedConfirmationDidAccept:(BOOL)accepted
                              browser:(Browser*)browser
             keepBrowsingDataSeparate:(BOOL)keepBrowsingDataSeparate {
@@ -536,6 +543,8 @@
     [self.delegate didCancelManagedConfirmation];
     return;
   }
+  CHECK(AreSeparateProfilesForManagedAccountsEnabled() ||
+        !keepBrowsingDataSeparate);
   base::RecordAction(
       base::UserMetricsAction("Signin_AuthenticationFlowPerformer_"
                               "ManagedConfirmationDialog_Confirmed"));
diff --git a/ios/chrome/browser/ui/authentication/change_profile/BUILD.gn b/ios/chrome/browser/ui/authentication/change_profile/BUILD.gn
new file mode 100644
index 0000000..852fe77
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/change_profile/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/chrome_build.gni")
+
+source_set("change_profile") {
+  sources = [
+    "change_profile_continuation.h",
+    "change_profile_observer.h",
+    "change_profile_observer.mm",
+    "change_profile_signout_continuation.h",
+    "change_profile_signout_continuation.mm",
+  ]
+
+  deps = [
+    "//ios/chrome/app:change_profile_commands",
+    "//ios/chrome/app/application_delegate:app_state",
+    "//ios/chrome/app/profile",
+    "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
+    "//ios/chrome/browser/shared/model/application_context",
+    "//ios/chrome/browser/shared/ui/util:snackbar_util",
+    "//ios/chrome/browser/ui/authentication/signin:signin_headers",
+  ]
+}
diff --git a/ios/chrome/browser/ui/authentication/change_profile/OWNERS b/ios/chrome/browser/ui/authentication/change_profile/OWNERS
new file mode 100644
index 0000000..6107c81
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/change_profile/OWNERS
@@ -0,0 +1,4 @@
+fedegermi@google.com
+jood@google.com
+sdefresne@chromium.org
+treib@chromium.org
\ No newline at end of file
diff --git a/ios/chrome/browser/ui/authentication/change_profile/change_profile_continuation.h b/ios/chrome/browser/ui/authentication/change_profile/change_profile_continuation.h
new file mode 100644
index 0000000..2b04a01a
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/change_profile/change_profile_continuation.h
@@ -0,0 +1,21 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_CONTINUATION_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_CONTINUATION_H_
+
+#import <UIKit/UIKit.h>
+
+#import "base/ios/block_types.h"
+
+@class SceneState;
+
+@protocol ChangeProfileContinuation <NSObject>
+
+- (void)executeWithSceneState:(SceneState*)sceneState
+                   completion:(ProceduralBlock)completion;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_CONTINUATION_H_
diff --git a/ios/chrome/browser/ui/authentication/change_profile/change_profile_observer.h b/ios/chrome/browser/ui/authentication/change_profile/change_profile_observer.h
new file mode 100644
index 0000000..a83c5a9e
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/change_profile/change_profile_observer.h
@@ -0,0 +1,23 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_OBSERVER_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_OBSERVER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/app/change_profile_commands.h"
+
+@protocol ChangeProfileContinuation;
+
+// This object is retained until -[<ChangeProfileObserving> operationFailed:] or
+// -[<ChangeProfileObserving> operationDidComplete:withSceneState:] is called.
+@interface ChangeProfileObserver : NSObject <ChangeProfileObserving>
+
+- (instancetype)initWithContinuations:
+    (NSArray<id<ChangeProfileContinuation>>*)continuations;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_OBSERVER_H_
diff --git a/ios/chrome/browser/ui/authentication/change_profile/change_profile_observer.mm b/ios/chrome/browser/ui/authentication/change_profile/change_profile_observer.mm
new file mode 100644
index 0000000..628936c
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/change_profile/change_profile_observer.mm
@@ -0,0 +1,60 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/change_profile/change_profile_observer.h"
+
+#import "base/ios/block_types.h"
+#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
+#import "ios/chrome/browser/ui/authentication/change_profile/change_profile_continuation.h"
+
+@implementation ChangeProfileObserver {
+  NSArray<id<ChangeProfileContinuation>>* _continuations;
+}
+
+- (instancetype)initWithContinuations:
+    (NSArray<id<ChangeProfileContinuation>>*)continuations {
+  self = [super init];
+  if (self) {
+    _continuations = continuations;
+  }
+  return self;
+}
+
+#pragma mark - ChangeProfileObserving
+
+- (void)operationFailed:(ChangeProfileFailure)failure {
+  // TODO(crbug.com/375605174): Present a minimalistic dialog in this case.
+}
+
+- (void)willStartOperation:(UIViewController*)viewController {
+  // Nothing to do.
+}
+
+- (void)operationDidComplete:(UIViewController*)viewController
+              withSceneState:(SceneState*)sceneState {
+  DCHECK(sceneState);
+  [self executeContinuationWithIndex:0 sceneState:sceneState];
+}
+
+#pragma mark - Private
+
+- (void)executeContinuationWithIndex:(NSUInteger)index
+                          sceneState:(SceneState*)sceneState {
+  if (index >= _continuations.count) {
+    return;
+  }
+
+  __weak __typeof(self) weakSelf = self;
+  __weak SceneState* weakSceneState = sceneState;
+  [_continuations[index]
+      executeWithSceneState:sceneState
+                 completion:^{
+                   if (weakSceneState) {
+                     [weakSelf executeContinuationWithIndex:index + 1
+                                                 sceneState:weakSceneState];
+                   }
+                 }];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/authentication/change_profile/change_profile_signout_continuation.h b/ios/chrome/browser/ui/authentication/change_profile/change_profile_signout_continuation.h
new file mode 100644
index 0000000..e3f6969d
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/change_profile/change_profile_signout_continuation.h
@@ -0,0 +1,30 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_SIGNOUT_CONTINUATION_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_SIGNOUT_CONTINUATION_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/authentication/change_profile/change_profile_continuation.h"
+
+@class MDCSnackbarMessage;
+
+namespace signin_metrics {
+enum class ProfileSignout;
+}  // namespace signin_metrics
+
+@interface ChangeProfileSignoutContinuation
+    : NSObject <ChangeProfileContinuation>
+
+- (instancetype)initWithSignoutSourceMetric:
+                    (signin_metrics::ProfileSignout)signoutSourceMetric
+                             forceClearData:(BOOL)forceClearData
+                   forceSnackbarOverToolbar:(BOOL)forceSnackbarOverToolbar
+                            snackbarMessage:
+                                (MDCSnackbarMessage*)snackbarMessage;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_CHANGE_PROFILE_CHANGE_PROFILE_SIGNOUT_CONTINUATION_H_
diff --git a/ios/chrome/browser/ui/authentication/change_profile/change_profile_signout_continuation.mm b/ios/chrome/browser/ui/authentication/change_profile/change_profile_signout_continuation.mm
new file mode 100644
index 0000000..4ebf303a
--- /dev/null
+++ b/ios/chrome/browser/ui/authentication/change_profile/change_profile_signout_continuation.mm
@@ -0,0 +1,46 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/authentication/change_profile/change_profile_signout_continuation.h"
+
+#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
+#import "ios/chrome/browser/shared/model/browser/browser.h"
+#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
+#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_utils.h"
+
+@implementation ChangeProfileSignoutContinuation {
+  signin_metrics::ProfileSignout _signoutSourceMetric;
+  BOOL _forceClearData;
+  BOOL _forceSnackbarOverToolbar;
+  MDCSnackbarMessage* _snackbarMessage;
+}
+
+- (instancetype)initWithSignoutSourceMetric:
+                    (signin_metrics::ProfileSignout)signoutSourceMetric
+                             forceClearData:(BOOL)forceClearData
+                   forceSnackbarOverToolbar:(BOOL)forceSnackbarOverToolbar
+                            snackbarMessage:
+                                (MDCSnackbarMessage*)snackbarMessage {
+  self = [super init];
+  if (self) {
+    _signoutSourceMetric = signoutSourceMetric;
+    _forceClearData = forceClearData;
+    _forceSnackbarOverToolbar = forceSnackbarOverToolbar;
+    _snackbarMessage = snackbarMessage;
+  }
+  return self;
+}
+
+#pragma mark - ChangeProfileContinuation
+
+- (void)executeWithSceneState:(SceneState*)sceneState
+                   completion:(ProceduralBlock)completion {
+  Browser* browser =
+      sceneState.browserProviderInterface.mainBrowserProvider.browser;
+  CHECK(browser);
+  // TODO(crbug.com/375605174): Complete the sign-out action.
+}
+
+@end
diff --git a/ios/chrome/browser/ui/whats_new/whats_new_instructions_view_controller.mm b/ios/chrome/browser/ui/whats_new/whats_new_instructions_view_controller.mm
index b06c6dd..d2df7fd 100644
--- a/ios/chrome/browser/ui/whats_new/whats_new_instructions_view_controller.mm
+++ b/ios/chrome/browser/ui/whats_new/whats_new_instructions_view_controller.mm
@@ -7,6 +7,7 @@
 #import "base/values.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h"
 #import "ios/chrome/common/ui/instruction_view/instruction_view.h"
 #import "ios/chrome/grit/ios_branded_strings.h"
@@ -76,8 +77,11 @@
   _alertScreen.titleView = self.titleLabel;
   _alertScreen.actionHandler = self.actionHandler;
   _alertScreen.showDismissBarButton = YES;
-  _alertScreen.customDismissBarButtonImage =
-      DefaultSymbolWithPointSize(kXMarkCircleFillSymbol, kDismissSymbolSize);
+  UIImage* xmarkSymbol = SymbolWithPalette(
+      DefaultSymbolWithPointSize(kXMarkCircleFillSymbol, kDismissSymbolSize),
+      @[ [UIColor colorNamed:kGrey600Color] ]);
+  _alertScreen.customDismissBarButtonImage = xmarkSymbol;
+
   _alertScreen.topAlignedLayout = YES;
 
   [NSLayoutConstraint activateConstraints:@[
diff --git a/ios/chrome/browser/webui/ui_bundled/BUILD.gn b/ios/chrome/browser/webui/ui_bundled/BUILD.gn
index 6f719b3..4d30739 100644
--- a/ios/chrome/browser/webui/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/webui/ui_bundled/BUILD.gn
@@ -101,6 +101,7 @@
     "//ios/chrome/browser/web/model/java_script_console",
     "//ios/chrome/browser/webui/model",
     "//ios/chrome/common",
+    "//ios/chrome/common/app_group",
     "//ios/web/public",
     "//ios/web/public/js_messaging",
     "//ios/web/public/webui",
diff --git a/ios/chrome/browser/webui/ui_bundled/userdefaults_internals_ui.mm b/ios/chrome/browser/webui/ui_bundled/userdefaults_internals_ui.mm
index 1945754..245c354 100644
--- a/ios/chrome/browser/webui/ui_bundled/userdefaults_internals_ui.mm
+++ b/ios/chrome/browser/webui/ui_bundled/userdefaults_internals_ui.mm
@@ -11,6 +11,7 @@
 #import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
+#import "ios/chrome/common/app_group/app_group_constants.h"
 #import "ios/web/public/thread/web_thread.h"
 #import "ios/web/public/webui/url_data_source_ios.h"
 #import "ios/web/public/webui/web_ui_ios.h"
@@ -52,16 +53,34 @@
     response.append(
         "<style>table, th, td {border: 1px solid black; "
         "border-collapse: collapse;}th {text-align: left;}</style>");
-    response.append("<h1>List of user defaults:</h1>\n\n");
 
-    NSDictionary<NSString*, id>* defaultsDict =
-        [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
-    NSArray* sortedKeys = [[defaultsDict allKeys]
+    response.append("<h1>List of user defaults:</h1>\n\n");
+    response.append(TableWithDefaults([NSUserDefaults standardUserDefaults]));
+
+    response.append("<h1>List of application group user defaults:</h1>\n\n");
+    response.append(TableWithDefaults(app_group::GetGroupUserDefaults()));
+
+    FinishDataRequest(std::move(response), std::move(callback));
+  }
+
+  void FinishDataRequest(std::string html,
+                         web::URLDataSourceIOS::GotDataCallback callback) {
+    std::move(callback).Run(
+        base::MakeRefCounted<base::RefCountedString>(std::move(html)));
+  }
+
+  // Returns a table containing info about keys stored in `user_defaults`.
+  std::string TableWithDefaults(NSUserDefaults* user_defaults) {
+    std::string table;
+    NSDictionary<NSString*, id>* defaults_dict =
+        [user_defaults dictionaryRepresentation];
+
+    NSArray* sortedKeys = [[defaults_dict allKeys]
         sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
 
-    response.append("<table>\n");
+    table.append("<table>\n");
     for (NSString* key in sortedKeys) {
-      id value = defaultsDict[key];
+      id value = defaults_dict[key];
       NSString* valueString;
       if ([value isKindOfClass:[NSArray class]]) {
         valueString = [value description];
@@ -77,20 +96,13 @@
       } else if ([value isKindOfClass:[NSDate class]]) {
         valueString = [value description];
       }
-      response.append("<tr>\n");
-      response.append("<th>" + base::SysNSStringToUTF8(key) + "</th>\n");
-      response.append("<td>" + base::SysNSStringToUTF8(valueString) +
-                      "</td>\n");
-      response.append("</tr>\n");
+      table.append("<tr>\n");
+      table.append("<th>" + base::SysNSStringToUTF8(key) + "</th>\n");
+      table.append("<td>" + base::SysNSStringToUTF8(valueString) + "</td>\n");
+      table.append("</tr>\n");
     }
-    response.append("</table>\n<html>");
-    FinishDataRequest(std::move(response), std::move(callback));
-  }
-
-  void FinishDataRequest(std::string html,
-                         web::URLDataSourceIOS::GotDataCallback callback) {
-    std::move(callback).Run(
-        base::MakeRefCounted<base::RefCountedString>(std::move(html)));
+    table.append("</table>\n<html>");
+    return table;
   }
 
  private:
diff --git a/ios/chrome/browser/youtube_incognito/ui/BUILD.gn b/ios/chrome/browser/youtube_incognito/ui/BUILD.gn
index d18096f..9fc46b4b 100644
--- a/ios/chrome/browser/youtube_incognito/ui/BUILD.gn
+++ b/ios/chrome/browser/youtube_incognito/ui/BUILD.gn
@@ -7,5 +7,12 @@
     "youtube_incognito_sheet.h",
     "youtube_incognito_sheet.mm",
   ]
-  deps = [ "//ios/chrome/common/ui/confirmation_alert:confirmation_alert" ]
+  deps = [
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/ntp/ui_bundled/incognito",
+    "//ios/chrome/browser/shared/ui/symbols",
+    "//ios/chrome/common/ui/colors",
+    "//ios/chrome/common/ui/confirmation_alert:confirmation_alert",
+    "//ios/chrome/common/ui/util",
+  ]
 }
diff --git a/ios/chrome/browser/youtube_incognito/ui/DEPS b/ios/chrome/browser/youtube_incognito/ui/DEPS
new file mode 100644
index 0000000..9001004
--- /dev/null
+++ b/ios/chrome/browser/youtube_incognito/ui/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view.h",
+]
diff --git a/ios/chrome/browser/youtube_incognito/ui/youtube_incognito_sheet.mm b/ios/chrome/browser/youtube_incognito/ui/youtube_incognito_sheet.mm
index 9615a3f2..74fd907 100644
--- a/ios/chrome/browser/youtube_incognito/ui/youtube_incognito_sheet.mm
+++ b/ios/chrome/browser/youtube_incognito/ui/youtube_incognito_sheet.mm
@@ -4,7 +4,33 @@
 
 #import "ios/chrome/browser/youtube_incognito/ui/youtube_incognito_sheet.h"
 
-@implementation YoutubeIncognitoSheet
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view.h"
+#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
+#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/util/constraints_ui_util.h"
+#import "ios/chrome/grit/ios_branded_strings.h"
+
+namespace {
+
+CGFloat const kUnderTitleViewHeightPadding = 50;
+CGFloat const kVerticalSpacing = 20;
+CGFloat const kTitleContainerCornerRadius = 15;
+CGFloat const kTitleContainerTopPadding = 33;
+CGFloat const kTitleContainerViewSize = 64;
+CGFloat const kChromeLogoSize = 52;
+CGFloat const kIncognitoLogoSize = 34;
+CGFloat const kAnimationDuration = 0.3;
+CGFloat const kAnimationDelay = 1;
+CGFloat const kAnimationTranslationOffset = 5;
+CGFloat const kAnimationScalFactor = 0.5;
+
+}  // namespace
+
+@implementation YoutubeIncognitoSheet {
+  UIView* _icognitoIconView;
+}
 
 - (instancetype)init {
   self = [super init];
@@ -13,7 +39,129 @@
 
 - (void)viewDidLoad {
   self.showDismissBarButton = NO;
+  self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
+  self.aboveTitleView = [self animatedTitleView];
+
+  // TODO(crbug.com/374935670): Add a11y strings.
+  self.titleString = @"TODO You're In Chrome Incognito";
+  self.subtitleString =
+      @"TODO Chrome automatically opens Youtube incognito links in Incognito";
+  self.primaryActionString = @"TODO Got It";
+
+  self.titleTextStyle = UIFontTextStyleTitle3;
+  self.scrollEnabled = YES;
+  self.customSpacing = kVerticalSpacing;
+  [self displayGradientView:YES];
+
+  UIView* underTitleView = [[UIView alloc] init];
+  underTitleView.translatesAutoresizingMaskIntoConstraints = NO;
+
+  IncognitoView* incognitoView =
+      [[IncognitoView alloc] initWithFrame:CGRectZero
+             showTopIncognitoImageAndTitle:NO
+                 stackViewHorizontalMargin:0
+                         stackViewMaxWidth:CGFLOAT_MAX];
+  incognitoView.bounces = NO;
+  incognitoView.showsHorizontalScrollIndicator = NO;
+  incognitoView.translatesAutoresizingMaskIntoConstraints = NO;
+  [underTitleView addSubview:incognitoView];
+
+  self.underTitleView = underTitleView;
+  [NSLayoutConstraint activateConstraints:@[
+    [underTitleView.heightAnchor
+        constraintEqualToAnchor:incognitoView.contentLayoutGuide.heightAnchor
+                       constant:-kUnderTitleViewHeightPadding],
+    [incognitoView.heightAnchor
+        constraintEqualToAnchor:incognitoView.contentLayoutGuide.heightAnchor],
+    [incognitoView.centerYAnchor
+        constraintEqualToAnchor:underTitleView.centerYAnchor],
+    [incognitoView.centerXAnchor
+        constraintEqualToAnchor:underTitleView.centerXAnchor],
+    [incognitoView.widthAnchor
+        constraintEqualToAnchor:underTitleView.widthAnchor],
+  ]];
+
   [super viewDidLoad];
 }
 
+- (void)viewDidAppear:(BOOL)animated {
+  __weak __typeof(self) weakSelf = self;
+  [super viewDidAppear:animated];
+  [UIView animateWithDuration:kAnimationDuration
+                        delay:kAnimationDelay
+                      options:UIViewAnimationOptionCurveEaseOut
+                   animations:^{
+                     [weakSelf titleViewAnimation];
+                   }
+                   completion:nil];
+}
+
+- (void)customizeSubtitle:(UITextView*)subtitle {
+  subtitle.textAlignment = NSTextAlignmentNatural;
+  subtitle.font = [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline];
+}
+
+#pragma mark - Private
+
+- (UIView*)animatedTitleView {
+  UIView* chromeIconView = [[UIView alloc] init];
+  chromeIconView.translatesAutoresizingMaskIntoConstraints = NO;
+  chromeIconView.layer.cornerRadius = kTitleContainerCornerRadius;
+  chromeIconView.backgroundColor = [UIColor whiteColor];
+#if BUILDFLAG(IOS_USE_BRANDED_SYMBOLS)
+  UIImage* chromeLogo = MakeSymbolMulticolor(
+      CustomSymbolWithPointSize(kMulticolorChromeballSymbol, kChromeLogoSize));
+#else
+  UIImage* chromeLogo =
+      CustomSymbolWithPointSize(kChromeProductSymbol, kChromeLogoSize);
+#endif  // BUILDFLAG(IOS_USE_BRANDED_SYMBOLS)
+
+  UIImageView* chromeLogoView = [[UIImageView alloc] initWithImage:chromeLogo];
+  chromeLogoView.translatesAutoresizingMaskIntoConstraints = NO;
+  [chromeIconView addSubview:chromeLogoView];
+
+  _icognitoIconView = [[UIView alloc] init];
+  _icognitoIconView.translatesAutoresizingMaskIntoConstraints = NO;
+  _icognitoIconView.layer.cornerRadius = kTitleContainerCornerRadius;
+  _icognitoIconView.backgroundColor = LargeIncognitoBackgroundColor();
+  UIImage* incognitoLogo =
+      CustomSymbolWithPointSize(kIncognitoSymbol, kIncognitoLogoSize);
+  UIImageView* incognitoLogoView =
+      [[UIImageView alloc] initWithImage:incognitoLogo];
+  incognitoLogoView.tintColor = LargeIncognitoForegroundColor();
+  incognitoLogoView.translatesAutoresizingMaskIntoConstraints = NO;
+  [_icognitoIconView addSubview:incognitoLogoView];
+
+  [NSLayoutConstraint activateConstraints:@[
+    [chromeIconView.widthAnchor
+        constraintEqualToConstant:kTitleContainerViewSize],
+    [chromeIconView.heightAnchor
+        constraintEqualToConstant:kTitleContainerViewSize],
+  ]];
+  AddSameCenterConstraints(chromeIconView, chromeLogoView);
+
+  UIView* outerView = [[UIView alloc] init];
+  [outerView addSubview:chromeIconView];
+  [outerView addSubview:_icognitoIconView];
+
+  AddSameCenterXConstraint(outerView, chromeIconView);
+  AddSameConstraints(chromeIconView, _icognitoIconView);
+  AddSameCenterConstraints(_icognitoIconView, incognitoLogoView);
+  AddSameConstraintsToSidesWithInsets(
+      chromeIconView, outerView, LayoutSides::kTop | LayoutSides::kBottom,
+      NSDirectionalEdgeInsetsMake(kTitleContainerTopPadding, 0, 0, 0));
+  return outerView;
+}
+
+- (void)titleViewAnimation {
+  CGFloat translationX =
+      _icognitoIconView.frame.size.width / 2 - kAnimationTranslationOffset;
+  CGFloat translationY =
+      _icognitoIconView.frame.size.height / 2 - kAnimationTranslationOffset;
+  _icognitoIconView.transform = CGAffineTransformConcat(
+      CGAffineTransformScale(CGAffineTransformIdentity, kAnimationScalFactor,
+                             kAnimationScalFactor),
+      CGAffineTransformMakeTranslation(translationX, translationY));
+}
+
 @end
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
index adc78aed..5b8a3b5 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
@@ -11,7 +11,7 @@
 #import "base/test/ios/wait_util.h"
 #import "components/autofill/core/browser/autofill_test_utils.h"
 #import "components/autofill/core/browser/data_manager/test_personal_data_manager.h"
-#import "components/autofill/core/browser/mock_autocomplete_history_manager.h"
+#import "components/autofill/core/browser/single_field_fillers/autocomplete/mock_autocomplete_history_manager.h"
 #import "components/autofill/core/browser/strike_databases/payments/test_strike_database.h"
 #import "components/autofill/core/common/autofill_prefs.h"
 #import "components/autofill/core/common/form_data.h"
diff --git a/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm b/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm
index 807b428..5b7b642 100644
--- a/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm
+++ b/ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.mm
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/no_destructor.h"
-#include "components/autofill/core/browser/autocomplete_history_manager.h"
+#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/keyed_service/ios/browser_state_dependency_manager.h"
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
index 36532e1..a39cdc1 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
@@ -11,14 +11,14 @@
 
 #import "base/memory/raw_ptr.h"
 #import "base/memory/weak_ptr.h"
-#import "components/autofill/core/browser/autocomplete_history_manager.h"
 #import "components/autofill/core/browser/autofill_client.h"
 #import "components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_manager.h"
 #import "components/autofill/core/browser/crowdsourcing/votes_uploader.h"
 #import "components/autofill/core/browser/data_manager/personal_data_manager.h"
 #import "components/autofill/core/browser/logging/log_manager.h"
 #import "components/autofill/core/browser/metrics/form_interactions_ukm_logger.h"
-#import "components/autofill/core/browser/single_field_fill_router.h"
+#import "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
+#import "components/autofill/core/browser/single_field_fillers/single_field_fill_router.h"
 #import "components/autofill/core/browser/strike_databases/strike_database.h"
 #import "components/prefs/pref_service.h"
 #import "components/sync/service/sync_service.h"
diff --git a/ios_internal b/ios_internal
index 60074ea..b597a98 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 60074ea61a85f6bbe01d1acf803f57a1e29c9218
+Subproject commit b597a98ad10c2c70d645059bf0e56de675144966
diff --git a/media/audio/simple_sources.cc b/media/audio/simple_sources.cc
index 05745212..5b85783f 100644
--- a/media/audio/simple_sources.cc
+++ b/media/audio/simple_sources.cc
@@ -17,6 +17,7 @@
 #include <string_view>
 
 #include "base/containers/heap_array.h"
+#include "base/containers/span.h"
 #include "base/files/file.h"
 #include "base/logging.h"
 #include "base/thread_annotations.h"
@@ -189,7 +190,7 @@
 
   // Attempt to create a handler with this data. If the data is invalid, return.
   wav_audio_handler_ =
-      WavAudioHandler::Create(base::as_string_view(raw_wav_data_));
+      WavAudioHandler::Create(base::as_byte_span(raw_wav_data_));
   if (!wav_audio_handler_) {
     LOG(ERROR) << "WAV data could be read but is not valid";
     load_failed_ = true;
diff --git a/media/audio/wav_audio_handler.cc b/media/audio/wav_audio_handler.cc
index 7548599c..720f3d2d 100644
--- a/media/audio/wav_audio_handler.cc
+++ b/media/audio/wav_audio_handler.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "media/audio/wav_audio_handler.h"
 
 #include <algorithm>
@@ -93,9 +88,7 @@
 
 // Parse a "fmt " chunk from wav data into its parameters. The `data` is in
 // little endian encoding.
-bool ParseFmtChunk(base::span<const uint8_t> data, WavAudioParameters* params) {
-  DCHECK(params);
-
+bool ParseFmtChunk(base::span<const uint8_t> data, WavAudioParameters& params) {
   // If the chunk is too small, return false.
   if (data.size() < kFmtChunkMinimumSize) {
     LOG(ERROR) << "Data size " << data.size() << " is too short.";
@@ -103,46 +96,43 @@
   }
 
   // Read in serialized parameters.
-  params->audio_format =
+  params.audio_format =
       static_cast<WavAudioHandler::AudioFormat>(base::U16FromLittleEndian(
           data.subspan<kAudioFormatOffset,
                        sizeof(WavAudioHandler::AudioFormat)>()));
-  params->num_channels =
+  params.num_channels =
       base::U16FromLittleEndian(data.subspan<kChannelOffset, 2u>());
-  params->sample_rate =
+  params.sample_rate =
       base::U32FromLittleEndian(data.subspan<kSampleRateOffset, 4u>());
-  params->bits_per_sample =
+  params.bits_per_sample =
       base::U16FromLittleEndian(data.subspan<kBitsPerSampleOffset, 2u>());
 
-  if (params->audio_format ==
+  if (params.audio_format ==
       WavAudioHandler::AudioFormat::kAudioFormatExtensible) {
     if (data.size() < kFmtChunkExtensibleMinimumSize) {
       LOG(ERROR) << "Data size " << data.size() << " is too short.";
       return false;
     }
 
-    params->is_extensible = true;
-    params->audio_format =
+    params.is_extensible = true;
+    params.audio_format =
         static_cast<WavAudioHandler::AudioFormat>(base::U16FromLittleEndian(
             data.subspan<kSubFormatOffset,
                          sizeof(WavAudioHandler::AudioFormat)>()));
-    params->valid_bits_per_sample = base::U16FromLittleEndian(
+    params.valid_bits_per_sample = base::U16FromLittleEndian(
         data.subspan<kValidBitsPerSampleOffset, 2u>());
   } else {
-    params->is_extensible = false;
+    params.is_extensible = false;
   }
   return true;
 }
 
 // The `wav_data` is encoded in little endian, as will be `audio_data_out`.
-bool ParseWavData(std::string_view wav_data,
-                  std::string_view* audio_data_out,
-                  WavAudioParameters* params_out) {
-  DCHECK(audio_data_out);
-  DCHECK(params_out);
-
+bool ParseWavData(base::span<const uint8_t> wav_data,
+                  base::span<const uint8_t>& audio_data_out,
+                  WavAudioParameters& params_out) {
   // The header should look like: |R|I|F|F|1|2|3|4|W|A|V|E|
-  auto buf = base::SpanReader(base::as_byte_span(wav_data));
+  auto buf = base::SpanReader(wav_data);
 
   // Read the chunk ID and compare to "RIFF".
   std::optional<base::span<const uint8_t, 4u>> chunk_id = buf.Read<4u>();
@@ -183,7 +173,7 @@
       if (!ParseFmtChunk(chunk_payload, params_out))
         return false;
     } else if (chunk_fmt == kDataSubchunkId) {
-      *audio_data_out = base::as_string_view(chunk_payload);
+      audio_data_out = chunk_payload;
     } else {
       DVLOG(1) << "Skipping unknown data chunk: "
                << base::as_string_view(chunk_fmt) << ".";
@@ -194,14 +184,14 @@
   if (!got_format) {
     LOG(ERROR) << "Invalid: No \"" << kFmtSubchunkId << "\" header found!";
     return false;
-  } else if (!ParamsAreValid(*params_out)) {
+  } else if (!ParamsAreValid(params_out)) {
     LOG(ERROR) << "Format is invalid. "
-               << "num_channels: " << params_out->num_channels << " "
-               << "sample_rate: " << params_out->sample_rate << " "
-               << "bits_per_sample: " << params_out->bits_per_sample << " "
-               << "valid_bits_per_sample: " << params_out->valid_bits_per_sample
+               << "num_channels: " << params_out.num_channels << " "
+               << "sample_rate: " << params_out.sample_rate << " "
+               << "bits_per_sample: " << params_out.bits_per_sample << " "
+               << "valid_bits_per_sample: " << params_out.valid_bits_per_sample
                << " "
-               << "is_extensible: " << params_out->is_extensible;
+               << "is_extensible: " << params_out.is_extensible;
     return false;
   }
   return true;
@@ -209,7 +199,7 @@
 
 }  // namespace
 
-WavAudioHandler::WavAudioHandler(std::string_view audio_data,
+WavAudioHandler::WavAudioHandler(base::span<const uint8_t> audio_data,
                                  uint16_t num_channels,
                                  uint32_t sample_rate,
                                  uint16_t bits_per_sample,
@@ -228,13 +218,14 @@
 
 // static
 std::unique_ptr<WavAudioHandler> WavAudioHandler::Create(
-    std::string_view wav_data) {
+    base::span<const uint8_t> wav_data) {
   WavAudioParameters params;
-  std::string_view audio_data;
+  base::span<const uint8_t> audio_data;
 
   // Attempt to parse the WAV data.
-  if (!ParseWavData(wav_data, &audio_data, &params))
+  if (!ParseWavData(wav_data, audio_data, params)) {
     return nullptr;
+  }
 
   return base::WrapUnique(
       new WavAudioHandler(audio_data, params.num_channels, params.sample_rate,
@@ -268,7 +259,7 @@
   const int bytes_per_frame = num_channels_ * bits_per_sample_ / 8;
   const int remaining_frames = (audio_data_.size() - cursor_) / bytes_per_frame;
   const int frames = std::min(bus->frames(), remaining_frames);
-  const auto* source = audio_data_.data() + cursor_;
+  const auto* source = audio_data_.subspan(cursor_).data();
 
   switch (audio_format_) {
     case AudioFormat::kAudioFormatPCM:
diff --git a/media/audio/wav_audio_handler.h b/media/audio/wav_audio_handler.h
index 485ab92..1fac32c 100644
--- a/media/audio/wav_audio_handler.h
+++ b/media/audio/wav_audio_handler.h
@@ -9,8 +9,8 @@
 #include <stdint.h>
 
 #include <memory>
-#include <string_view>
 
+#include "base/memory/raw_span.h"
 #include "base/time/time.h"
 #include "media/audio/audio_handler.h"
 #include "media/base/media_export.h"
@@ -38,7 +38,8 @@
   // Create a WavAudioHandler using |wav_data|. If |wav_data| cannot be parsed
   // correctly, the returned scoped_ptr will be null. The underlying memory for
   // wav_data must survive for the lifetime of this class.
-  static std::unique_ptr<WavAudioHandler> Create(std::string_view wav_data);
+  static std::unique_ptr<WavAudioHandler> Create(
+      base::span<const uint8_t> wav_data);
 
   // AudioHandler:
   bool Initialize() override;
@@ -50,7 +51,7 @@
   void Reset() override;
 
   // Accessors.
-  std::string_view data() const { return audio_data_; }
+  base::span<const uint8_t> data() const { return audio_data_; }
   AudioFormat audio_format() const { return audio_format_; }
 
   int total_frames_for_testing() const {
@@ -62,14 +63,14 @@
 
  private:
   // Note: It is preferred to pass |audio_data| by value here.
-  WavAudioHandler(std::string_view audio_data,
+  WavAudioHandler(base::span<const uint8_t> audio_data,
                   uint16_t num_channels,
                   uint32_t sample_rate,
                   uint16_t bits_per_sample,
                   AudioFormat audio_format);
 
   // Data part of the |wav_data_|.
-  const std::string_view audio_data_;
+  const base::raw_span<const uint8_t> audio_data_;
   const uint16_t num_channels_;
   const uint32_t sample_rate_;
   const uint16_t bits_per_sample_;
diff --git a/media/audio/wav_audio_handler_fuzzer.cc b/media/audio/wav_audio_handler_fuzzer.cc
index f2c303ce..4ae7f805 100644
--- a/media/audio/wav_audio_handler_fuzzer.cc
+++ b/media/audio/wav_audio_handler_fuzzer.cc
@@ -2,14 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "media/audio/wav_audio_handler.h"
+
 #include <stddef.h>
 #include <stdint.h>
 
 #include <memory>
 #include <string_view>
 
+#include "base/containers/span.h"
 #include "base/logging.h"
-#include "media/audio/wav_audio_handler.h"
 #include "media/base/audio_bus.h"
 
 struct Environment {
@@ -20,7 +22,7 @@
   static Environment env;
   std::string_view wav_data(reinterpret_cast<const char*>(data), size);
   std::unique_ptr<media::WavAudioHandler> handler =
-      media::WavAudioHandler::Create(wav_data);
+      media::WavAudioHandler::Create(base::as_byte_span(wav_data));
 
   // Abort early to avoid crashing inside AudioBus's ValidateConfig() function.
   if (!handler || !handler->Initialize() ||
diff --git a/media/audio/wav_audio_handler_unittest.cc b/media/audio/wav_audio_handler_unittest.cc
index 6a3f784..40e8e3a 100644
--- a/media/audio/wav_audio_handler_unittest.cc
+++ b/media/audio/wav_audio_handler_unittest.cc
@@ -10,6 +10,7 @@
 #include <string>
 #include <string_view>
 
+#include "base/containers/span.h"
 #include "media/audio/test_data.h"
 #include "media/base/audio_bus.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -36,7 +37,7 @@
 
 TEST(WavAudioHandlerTest, SampleDataTest) {
   std::string data(kTestAudioData, kTestAudioDataSize);
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   ASSERT_TRUE(handler);
   ASSERT_EQ(2, handler->GetNumChannels());
   ASSERT_EQ(16, handler->bits_per_sample_for_testing());
@@ -46,7 +47,7 @@
 
   ASSERT_EQ(4U, handler->data().size());
   const char kData[] = "\x01\x00\x01\x00";
-  ASSERT_EQ(std::string_view(kData, std::size(kData) - 1), handler->data());
+  ASSERT_EQ(base::byte_span_from_cstring(kData), handler->data());
 
   std::unique_ptr<AudioBus> bus =
       AudioBus::Create(handler->GetNumChannels(),
@@ -62,7 +63,7 @@
 
 TEST(WavAudioHandlerTest, SampleFloatDataTest) {
   std::string data(kTestFloatAudioData, kTestFloatAudioDataSize);
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   ASSERT_TRUE(handler);
   ASSERT_EQ(2, handler->GetNumChannels());
   ASSERT_EQ(32, handler->bits_per_sample_for_testing());
@@ -72,7 +73,7 @@
 
   ASSERT_EQ(8U, handler->data().size());
   const char kData[] = "\x00\x01\x00\x00\x01\x00\x00\x00";
-  ASSERT_EQ(std::string_view(kData, std::size(kData) - 1), handler->data());
+  ASSERT_EQ(base::byte_span_from_cstring(kData), handler->data());
 
   std::unique_ptr<AudioBus> bus =
       AudioBus::Create(handler->GetNumChannels(),
@@ -88,7 +89,7 @@
 
 TEST(WavAudioHandlerTest, SampleExtensibleDataTest) {
   std::string data(kTestExtensibleAudioData, kTestExtensibleAudioDataSize);
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   ASSERT_TRUE(handler);
   ASSERT_EQ(2, handler->GetNumChannels());
   ASSERT_EQ(32, handler->bits_per_sample_for_testing());
@@ -98,7 +99,7 @@
 
   ASSERT_EQ(8U, handler->data().size());
   const char kData[] = "\x01\x00\x00\x00\x01\x00\x00\x00";
-  ASSERT_EQ(std::string_view(kData, std::size(kData) - 1), handler->data());
+  ASSERT_EQ(base::byte_span_from_cstring(kData), handler->data());
 
   std::unique_ptr<AudioBus> bus =
       AudioBus::Create(handler->GetNumChannels(),
@@ -117,7 +118,7 @@
   std::string data(kTestAudioData, kTestAudioDataSize);
   data[kChannelIndex] = '\x00';
   data[kChannelIndex + 1] = '\x00';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_FALSE(handler);
 }
 
@@ -127,7 +128,7 @@
   std::string data(kTestAudioData, kTestAudioDataSize);
   data[kBitsPerSampleIndex] = '\x00';
   data[kBitsPerSampleIndex + 1] = '\x00';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_FALSE(handler);
 }
 
@@ -139,7 +140,7 @@
   data[kSampleRateIndex + 1] = '\x00';
   data[kSampleRateIndex + 2] = '\x00';
   data[kSampleRateIndex + 3] = '\x00';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_FALSE(handler);
 }
 
@@ -150,7 +151,7 @@
   data[kWavDataSizeIndex + 1] = '\xFF';
   data[kWavDataSizeIndex + 2] = '\xFF';
   data[kWavDataSizeIndex + 3] = '\x00';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_TRUE(handler);
   EXPECT_TRUE(handler->Initialize());
   ASSERT_EQ(2, handler->GetNumChannels());
@@ -161,7 +162,7 @@
 
   ASSERT_EQ(4U, handler->data().size());
   const char kData[] = "\x01\x00\x01\x00";
-  ASSERT_EQ(std::string_view(kData, std::size(kData) - 1), handler->data());
+  ASSERT_EQ(base::byte_span_from_cstring(kData), handler->data());
 }
 
 TEST(WavAudioHandlerTest, TestTooBigDataChunkSizeIsOkay) {
@@ -173,7 +174,7 @@
   data[kDataHeaderIndex + 5] = '\xFF';
   data[kDataHeaderIndex + 6] = '\xFF';
   data[kDataHeaderIndex + 7] = '\x00';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_TRUE(handler);
   EXPECT_TRUE(handler->Initialize());
   ASSERT_EQ(2, handler->GetNumChannels());
@@ -184,7 +185,7 @@
 
   ASSERT_EQ(4U, handler->data().size());
   const char kData[] = "\x01\x00\x01\x00";
-  ASSERT_EQ(std::string_view(kData, std::size(kData) - 1), handler->data());
+  ASSERT_EQ(base::byte_span_from_cstring(kData), handler->data());
 }
 
 TEST(WavAudioHandlerTest, TestTooSmallFormatSizeIsNotValid) {
@@ -196,7 +197,7 @@
   data[kFormatHeaderIndex + 5] = '\x00';
   data[kFormatHeaderIndex + 6] = '\x00';
   data[kFormatHeaderIndex + 7] = '\x00';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_FALSE(handler);
 }
 
@@ -207,7 +208,7 @@
   data.append("abcd\x04\x00\x00\x00\x01\x02\x03\x04");
   data[kWavDataSizeIndex] += 12;  // This should not overflow.
 
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_TRUE(handler);
   EXPECT_TRUE(handler->Initialize());
   ASSERT_EQ(2, handler->GetNumChannels());
@@ -225,7 +226,7 @@
   data[kFormatHeaderIndex + 1] = 'b';
   data[kFormatHeaderIndex + 2] = 'c';
   data[kFormatHeaderIndex + 3] = 'd';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_FALSE(handler);
 }
 
@@ -238,7 +239,7 @@
   data[kDataHeaderIndex + 1] = 'b';
   data[kDataHeaderIndex + 2] = 'c';
   data[kDataHeaderIndex + 3] = 'd';
-  auto handler = WavAudioHandler::Create(data);
+  auto handler = WavAudioHandler::Create(base::as_byte_span(data));
   EXPECT_TRUE(handler);
   EXPECT_TRUE(handler->Initialize());
   ASSERT_EQ(2, handler->GetNumChannels());
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index caf94f4..a7268707 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -1034,11 +1034,6 @@
 #endif
 );
 
-// Enables low-delay video rendering in media pipeline on "live" stream.
-BASE_FEATURE(kLowDelayVideoRenderingOnLiveStream,
-             "low-delay-video-rendering-on-live-stream",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Allows the AutoPictureInPictureTabHelper to automatically enter
 // picture-in-picture for websites with video playback (instead of only websites
 // using camera or microphone).
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index e3f0e5e..91bf0e2 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -287,7 +287,6 @@
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kLiveCaptionUseWaitK);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kLiveCaptionWebAudio);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kLiveTranslate);
-MEDIA_EXPORT BASE_DECLARE_FEATURE(kLowDelayVideoRenderingOnLiveStream);
 #if BUILDFLAG(IS_MAC)
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kMacLoopbackAudioForScreenShare);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kUseSCContentSharingPicker);
diff --git a/media/formats/mp4/avc.cc b/media/formats/mp4/avc.cc
index 80f7b61d..0a70b90 100644
--- a/media/formats/mp4/avc.cc
+++ b/media/formats/mp4/avc.cc
@@ -25,7 +25,8 @@
 static constexpr uint8_t kAnnexBStartCode[] = {0, 0, 0, 1};
 static constexpr int kAnnexBStartCodeSize = 4;
 
-static bool ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector<uint8_t>* buf) {
+// static
+bool AVC::ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector<uint8_t>* buf) {
   const size_t kLengthSize = 4;
   size_t pos = 0;
   while (buf->size() > kLengthSize && buf->size() - kLengthSize > pos) {
diff --git a/media/formats/mp4/avc.h b/media/formats/mp4/avc.h
index 97a3502e..cd74740bf 100644
--- a/media/formats/mp4/avc.h
+++ b/media/formats/mp4/avc.h
@@ -28,9 +28,9 @@
                                    std::vector<uint8_t>* buffer,
                                    std::vector<SubsampleEntry>* subsamples);
 
-  // Inserts the SPS & PPS data from |avc_config| into |buffer|.
-  // |buffer| is expected to contain AnnexB conformant data.
-  // |subsamples| contains the SubsampleEntry info if |buffer| contains
+  // Inserts the SPS & PPS data from `avc_config` into `buffer`.
+  // `buffer` is expected to contain AnnexB conformant data.
+  // `subsamples` contains the SubsampleEntry info if `buffer` contains
   // encrypted data.
   // Returns true if the param sets were successfully inserted.
   static bool InsertParamSetsAnnexB(
@@ -42,23 +42,28 @@
       const AVCDecoderConfigurationRecord& avc_config,
       std::vector<uint8_t>* buffer);
 
-  // Analyzes the contents of |buffer| for conformance to Section 7.4.1.2.3 of
-  // ISO/IEC 14496-10. Also analyzes |buffer| and reports if it looks like a
+  // Analyzes the contents of `buffer` for conformance to Section 7.4.1.2.3 of
+  // ISO/IEC 14496-10. Also analyzes `buffer` and reports if it looks like a
   // keyframe, if such can be determined. Determination of keyframe-ness is done
-  // only if |buffer| is conformant or if lack of conformance is detected after
+  // only if `buffer` is conformant or if lack of conformance is detected after
   // detecting keyframe-ness.
-  // |subsamples| contains the information about what parts of the buffer are
+  // `subsamples` contains the information about what parts of the buffer are
   // encrypted and which parts are clear.
   static BitstreamConverter::AnalysisResult AnalyzeAnnexB(
       const uint8_t* buffer,
       size_t size,
       const std::vector<SubsampleEntry>& subsamples);
 
-  // Given a |buffer| and |subsamples| information and |pts| pointer into the
-  // |buffer| finds the index of the subsample |ptr| is pointing into.
+  // Given a `buffer` and `subsamples` information and `pts` pointer into the
+  // `buffer` finds the index of the subsample `ptr` is pointing into.
   static int FindSubsampleIndex(const std::vector<uint8_t>& buffer,
                                 const std::vector<SubsampleEntry>* subsamples,
                                 const uint8_t* ptr);
+
+  // Convert a `buffer` from AVC bitstream to Annex-B bitstream by replacing
+  // 4-byte NALU length to 4-byte NALU start code.
+  static bool ConvertAVCToAnnexBInPlaceForLengthSize4(
+      std::vector<uint8_t>* buffer);
 };
 
 // AVCBitstreamConverter converts AVC/H.264 bitstream from MP4 container format
diff --git a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.cc b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.cc
index f1caa4c..4526833 100644
--- a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.cc
+++ b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.cc
@@ -11,7 +11,9 @@
 
 namespace media {
 
-H264AnnexBToAvcBitstreamConverter::H264AnnexBToAvcBitstreamConverter() {
+H264AnnexBToAvcBitstreamConverter::H264AnnexBToAvcBitstreamConverter(
+    bool add_parameter_sets_in_bitstream)
+    : add_parameter_sets_in_bitstream_(add_parameter_sets_in_bitstream) {
   // These parts of configuration never change.
   config_.version = 1;
   config_.length_size = 4;
@@ -30,7 +32,7 @@
     base::span<uint8_t> output,
     bool* config_changed_out,
     size_t* size_out) {
-  std::vector<H264NALU> slice_units;
+  std::vector<base::span<const uint8_t>> slice_units;
   size_t data_size = 0;
   bool config_changed = false;
   H264NALU nalu;
@@ -46,8 +48,9 @@
   base::flat_set<int> pps_to_include;
 
   // Scan input buffer looking for two main types of NALUs
-  //  1. SPS and PPS. They'll be added to the AVC configuration |config_|
-  //     and will *not* be copied to |output|.
+  //  1. SPS and PPS. They'll be added to the AVC configuration `config_`
+  //     and maybe be copied to `output` based on
+  //     `add_parameter_sets_in_bitstream_`.
   //  2. Slices. They'll being copied into the output buffer, but also affect
   //     what configuration (profile and level) is active now.
   parser_.SetStream(input.data(), input.size());
@@ -146,12 +149,39 @@
       }
         [[fallthrough]];
       default:
-        slice_units.push_back(nalu);
+        // TODO(crbug.com/40284755): The `nalu.data` should hold a span instead
+        // of a pointer.
+        slice_units.emplace_back(nalu.data.get(),
+                                 base::checked_cast<size_t>(nalu.size));
         data_size += config_.length_size + nalu.size;
         break;
     }
   }
 
+  if (config_changed && add_parameter_sets_in_bitstream_) {
+    // Insert parameter sets, in the order of PPS, SPS Extension, SPS.
+    for (auto& id : pps_to_include) {
+      auto it = id2pps_.find(id);
+      if (it == id2pps_.end()) {
+        return MP4Status::Codes::kFailedToLookupPPS;
+      }
+      slice_units.insert(slice_units.begin(), it->second);
+      data_size += config_.length_size + it->second.size();
+    }
+    for (auto& id : sps_to_include) {
+      auto it = id2sps_.find(id);
+      if (it == id2sps_.end()) {
+        return MP4Status::Codes::kFailedToLookupSPS;
+      }
+      if (id2sps_ext_.contains(id)) {
+        slice_units.insert(slice_units.begin(), id2sps_ext_[id]);
+        data_size += config_.length_size + id2sps_ext_[id].size();
+      }
+      slice_units.insert(slice_units.begin(), it->second);
+      data_size += config_.length_size + it->second.size();
+    }
+  }
+
   if (size_out)
     *size_out = data_size;
   if (data_size > output.size()) {
@@ -163,15 +193,7 @@
   base::SpanWriter writer(output);
   for (auto& unit : slice_units) {
     bool written_ok =
-        writer.WriteU32BigEndian(unit.size) &&
-        writer.Write(
-            // SAFETY: `unit` is constructed with a size that is the number of
-            // elements at the data pointer.
-            //
-            // TODO(crbug.com/40284755): The `unit` should hold a span instead
-            // of a pointer.
-            UNSAFE_TODO(base::span(unit.data.get(),
-                                   base::checked_cast<size_t>(unit.size))));
+        writer.WriteU32BigEndian(unit.size()) && writer.Write(unit);
     if (!written_ok) {
       return MP4Status::Codes::kBufferTooSmall;
     }
@@ -180,7 +202,7 @@
   DCHECK_EQ(writer.num_written(), data_size);
 
   // Now when we are sure that everything is written and fits nicely,
-  // we can update parts of the |config_| that were changed by this data chunk.
+  // we can update parts of the `config_` that were changed by this data chunk.
   if (config_changed) {
     if (new_active_sps_id < 0)
       new_active_sps_id = active_sps_id_;
@@ -201,14 +223,23 @@
 
     // flat_set is iterated in key-order
     for (int id : sps_to_include) {
-      config_.sps_list.push_back(id2sps_[id]);
+      auto it = id2sps_.find(id);
+      if (it == id2sps_.end()) {
+        return MP4Status::Codes::kFailedToLookupSPS;
+      }
+      config_.sps_list.push_back(it->second);
       if (id2sps_ext_.contains(id)) {
         config_.sps_ext_list.push_back(id2sps_ext_[id]);
       }
     }
 
-    for (int id : pps_to_include)
-      config_.pps_list.push_back(id2pps_[id]);
+    for (int id : pps_to_include) {
+      auto it = id2pps_.find(id);
+      if (it == id2pps_.end()) {
+        return MP4Status::Codes::kFailedToLookupPPS;
+      }
+      config_.pps_list.push_back(it->second);
+    }
 
     config_.profile_indication = active_sps->profile_idc;
 
diff --git a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.h b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.h
index 19f3c5d..c85f8c5 100644
--- a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.h
+++ b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter.h
@@ -22,7 +22,22 @@
 // Annex B (ISO/IEC 14496-10) to AVC (as specified in ISO/IEC 14496-15).
 class MEDIA_EXPORT H264AnnexBToAvcBitstreamConverter {
  public:
-  H264AnnexBToAvcBitstreamConverter();
+  // Construct the bitstream converter.
+  //
+  // `add_parameter_sets_in_bitstream` - indicates whether the parameter sets
+  // can be copied to the output bitstream or not. When set to false, parameter
+  // sets are only stored in the `AVCDecoderConfigurationRecord`, which complies
+  // with the requirements of `avc1` as defined in ISO/IEC 14496-15:2019
+  // - 5.3.2. When set to true, parameter sets are stored both in the output
+  // bitstream and in the `AVCDecoderConfigurationRecord`, which complies with
+  // the requirements of `avc3` as defined in ISO/IEC 14496-15:2019 - 5.3.2.
+  //
+  // NOTE: for `avc3`, the spec doesn't require the muxer to insert parameter
+  // sets into the bitstream, and only states that it is optional, nevertheless,
+  // this converter always assumes that they need to have the parameter sets
+  // inserted.
+  explicit H264AnnexBToAvcBitstreamConverter(
+      bool add_parameter_sets_in_bitstream);
 
   H264AnnexBToAvcBitstreamConverter(const H264AnnexBToAvcBitstreamConverter&) =
       delete;
@@ -34,17 +49,17 @@
   // Converts a video chunk from a format with in-place decoder configuration
   // into a format where configuration needs to be sent separately.
   //
-  // |input| - where to read the data from
-  // |output| - where to put the converted video data
-  // If error kBufferTooSmall is returned, it means that |output| was not
-  // big enough to contain a converted video chunk. In this case |size_out|
+  // `input` - where to read the data from
+  // `output` - where to put the converted video data
+  // If error kBufferTooSmall is returned, it means that `output` was not
+  // big enough to contain a converted video chunk. In this case `size_out`
   // is populated.
-  // |config_changed_out| is set to True if the video chunk
+  // `config_changed_out` is set to True if the video chunk
   // processed by this call contained decoder configuration information.
   // In this case latest configuration information can be obtained
   // from GetCurrentConfig().
-  // |size_out| - number of bytes written to |output|, or desired size of
-  // |output| if it's too small.
+  // `size_out` - number of bytes written to `output`, or desired size of
+  // `output` if it's too small.
   MP4Status ConvertChunk(base::span<const uint8_t> input,
                          base::span<uint8_t> output,
                          bool* config_changed_out,
@@ -65,6 +80,8 @@
 
   int active_sps_id_ = -1;
   int active_pps_id_ = -1;
+
+  const bool add_parameter_sets_in_bitstream_ = false;
 };
 
 }  // namespace media
diff --git a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc
index 28daaab..903240c 100644
--- a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc
+++ b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_fuzztest.cc
@@ -18,19 +18,22 @@
   if (!size)
     return 0;
 
-  std::vector<uint8_t> output(size);
-  size_t size_out;
-  bool config_changed;
-  media::H264AnnexBToAvcBitstreamConverter converter;
-  base::span<const uint8_t> input(data, data + size);
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    std::vector<uint8_t> output(size);
+    size_t size_out;
+    bool config_changed;
+    media::H264AnnexBToAvcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
+    base::span<const uint8_t> input(data, data + size);
 
-  auto status =
-      converter.ConvertChunk(input, output, &config_changed, &size_out);
+    auto status =
+        converter.ConvertChunk(input, output, &config_changed, &size_out);
 
-  auto& config = converter.GetCurrentConfig();
+    auto& config = converter.GetCurrentConfig();
 
-  std::vector<uint8_t> avc_config(size);
-  config.Serialize(avc_config);
+    std::vector<uint8_t> avc_config(size);
+    config.Serialize(avc_config);
+  }
 
   return 0;
 }
diff --git a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc
index dc782e2..9aaa042 100644
--- a/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc
+++ b/media/formats/mp4/h264_annex_b_to_avc_bitstream_converter_unittest.cc
@@ -7,10 +7,13 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 
 #include "base/files/file.h"
 #include "base/path_service.h"
+#include "media/formats/mp4/avc.h"
 #include "media/formats/mp4/box_definitions.h"
+#include "media/parsers/h264_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -41,42 +44,87 @@
       "chunk3-non-idr.bin",    "chunk4-non-idr.bin",
       "chunk5-non-idr.bin",    "chunk6-config-idr.bin",
       "chunk7-non-idr.bin",    "pps_neq_sps_config_idr.bin"};
-  H264AnnexBToAvcBitstreamConverter converter;
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H264AnnexBToAvcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
 
-  for (std::string& name : chunks) {
-    SCOPED_TRACE(name);
-    auto input = ReadTestFile(name);
-    SCOPED_TRACE(input.size());
-    std::vector<uint8_t> output;
-    size_t desired_size = 0;
-    bool config_changed = false;
+    for (std::string& name : chunks) {
+      SCOPED_TRACE(name);
+      auto input = ReadTestFile(name);
+      SCOPED_TRACE(input.size());
+      std::vector<uint8_t> output;
+      size_t desired_size = 0;
+      bool config_changed = false;
 
-    auto status =
-        converter.ConvertChunk(input, output, &config_changed, &desired_size);
-    ASSERT_EQ(status.code(), MP4Status::Codes::kBufferTooSmall);
-    output.resize(desired_size);
+      auto status =
+          converter.ConvertChunk(input, output, &config_changed, &desired_size);
+      ASSERT_EQ(status.code(), MP4Status::Codes::kBufferTooSmall);
+      output.resize(desired_size);
 
-    status = converter.ConvertChunk(input, output, &config_changed, nullptr);
-    EXPECT_TRUE(status.is_ok()) << status.message();
+      status = converter.ConvertChunk(input, output, &config_changed, nullptr);
+      EXPECT_TRUE(status.is_ok()) << status.message();
 
-    auto& config = converter.GetCurrentConfig();
-    if (name.find("config") != std::string::npos) {
-      // Chunks with configuration
-      EXPECT_TRUE(config_changed);
+      // Convert AVC bitstream back to Annex-B bitstream, and validate if
+      // parameter set are write to the output or not.
+      EXPECT_TRUE(mp4::AVC::ConvertAVCToAnnexBInPlaceForLengthSize4(&output));
+      H264Parser parser;
+      parser.SetStream(output.data(), output.size());
+      std::vector<H264NALU> parameter_sets;
+      while (true) {
+        H264NALU nalu;
+        H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
+        if (res == H264Parser::kEOStream) {
+          break;
+        }
+        EXPECT_EQ(res, H264Parser::kOk);
+        switch (nalu.nal_unit_type) {
+          case H264NALU::kSPS:
+            int sps_id;
+            EXPECT_EQ(parser.ParseSPS(&sps_id), H264Parser::kOk);
+            EXPECT_TRUE(!!parser.GetSPS(sps_id));
+            parameter_sets.push_back(nalu);
+            break;
+          case H264NALU::kPPS:
+            int pps_id;
+            EXPECT_EQ(parser.ParsePPS(&pps_id), H264Parser::kOk);
+            EXPECT_TRUE(!!parser.GetPPS(pps_id));
+            parameter_sets.push_back(nalu);
+            break;
+          default:
+            break;
+        }
+      }
 
-      EXPECT_EQ(config.version, 1);
-      EXPECT_EQ(config.profile_indication, 66);
-      EXPECT_EQ(config.profile_compatibility, 0xC0);
-      EXPECT_EQ(config.avc_level, 30);
-      EXPECT_EQ(config.length_size, 4);
-      EXPECT_EQ(config.sps_list.size(), 1ul);
-      EXPECT_EQ(config.pps_list.size(), 1ul);
-    } else {
-      EXPECT_FALSE(config_changed);
+      if (add_parameter_sets_in_bitstream && config_changed) {
+        // Parameter sets should write to the output stream in the order of SPS,
+        // PPS.
+        EXPECT_EQ(parameter_sets.size(), 2u);
+        EXPECT_EQ(parameter_sets.at(0).nal_unit_type, H264NALU::kSPS);
+        EXPECT_EQ(parameter_sets.at(1).nal_unit_type, H264NALU::kPPS);
+      } else {
+        // Parameter sets should not write to the output stream.
+        EXPECT_EQ(parameter_sets.size(), 0u);
+      }
+
+      auto& config = converter.GetCurrentConfig();
+      if (name.find("config") != std::string::npos) {
+        // Chunks with configuration
+        EXPECT_TRUE(config_changed);
+
+        EXPECT_EQ(config.version, 1);
+        EXPECT_EQ(config.profile_indication, 66);
+        EXPECT_EQ(config.profile_compatibility, 0xC0);
+        EXPECT_EQ(config.avc_level, 30);
+        EXPECT_EQ(config.length_size, 4);
+        EXPECT_EQ(config.sps_list.size(), 1ul);
+        EXPECT_EQ(config.pps_list.size(), 1ul);
+      } else {
+        EXPECT_FALSE(config_changed);
+      }
+
+      std::vector<uint8_t> config_bin;
+      EXPECT_TRUE(config.Serialize(config_bin)) << " file: " << name;
     }
-
-    std::vector<uint8_t> config_bin;
-    EXPECT_TRUE(config.Serialize(config_bin)) << " file: " << name;
   }
 }
 
@@ -109,19 +157,66 @@
       // Encoded data omitted here, it's not important for NALU parsing
   };
 
-  H264AnnexBToAvcBitstreamConverter converter;
-  std::vector<uint8_t> output(10000);
-  bool config_changed = false;
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H264AnnexBToAvcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
+    std::vector<uint8_t> output(10000);
+    bool config_changed = false;
 
-  auto status =
-      converter.ConvertChunk(first_chunk, output, &config_changed, nullptr);
-  EXPECT_TRUE(status.is_ok()) << status.message();
-  EXPECT_TRUE(config_changed);
+    auto status =
+        converter.ConvertChunk(first_chunk, output, &config_changed, nullptr);
+    EXPECT_TRUE(status.is_ok()) << status.message();
+    EXPECT_TRUE(config_changed);
 
-  status = converter.ConvertChunk(second_non_idr_chunk, output, &config_changed,
-                                  nullptr);
-  EXPECT_TRUE(status.is_ok()) << status.message();
-  EXPECT_FALSE(config_changed);
+    // Convert AVC bitstream back to Annex-B bitstream, and validate if
+    // parameter set are write to the output or not.
+    EXPECT_FALSE(mp4::AVC::ConvertAVCToAnnexBInPlaceForLengthSize4(&output));
+    H264Parser parser;
+    parser.SetStream(output.data(), output.size());
+    std::vector<H264NALU> parameter_sets;
+    while (true) {
+      H264NALU nalu;
+      H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
+      if (res == H264Parser::kEOStream) {
+        break;
+      }
+      EXPECT_EQ(res, H264Parser::kOk);
+      switch (nalu.nal_unit_type) {
+        case H264NALU::kSPS:
+          int sps_id;
+          EXPECT_EQ(parser.ParseSPS(&sps_id), H264Parser::kOk);
+          EXPECT_TRUE(!!parser.GetSPS(sps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        case H264NALU::kPPS:
+          int pps_id;
+          EXPECT_EQ(parser.ParsePPS(&pps_id), H264Parser::kOk);
+          EXPECT_TRUE(!!parser.GetPPS(pps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        default:
+          break;
+      }
+    }
+
+    if (add_parameter_sets_in_bitstream) {
+      // Parameter sets should write to the output stream in the order of SPS,
+      // PPS1, PPS2, PPS3.
+      EXPECT_EQ(parameter_sets.size(), 4u);
+      EXPECT_EQ(parameter_sets.at(0).nal_unit_type, H264NALU::kSPS);
+      EXPECT_EQ(parameter_sets.at(1).nal_unit_type, H264NALU::kPPS);
+      EXPECT_EQ(parameter_sets.at(2).nal_unit_type, H264NALU::kPPS);
+      EXPECT_EQ(parameter_sets.at(3).nal_unit_type, H264NALU::kPPS);
+    } else {
+      // Parameter sets should not write to the output stream.
+      EXPECT_EQ(parameter_sets.size(), 0u);
+    }
+
+    status = converter.ConvertChunk(second_non_idr_chunk, output,
+                                    &config_changed, nullptr);
+    EXPECT_TRUE(status.is_ok()) << status.message();
+    EXPECT_FALSE(config_changed);
+  }
 }
 
 // Tests that REXT metadata is handled correctly.
@@ -141,38 +236,97 @@
   chunk.insert(chunk.end(), pps.begin(), pps.end());
   chunk.insert(chunk.end(), idr.begin(), idr.end());
 
-  H264AnnexBToAvcBitstreamConverter converter;
-  std::vector<uint8_t> output(10000);
-  bool config_changed = false;
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H264AnnexBToAvcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
+    std::vector<uint8_t> output(10000);
+    bool config_changed = false;
 
-  auto status = converter.ConvertChunk(chunk, output, &config_changed, nullptr);
-  EXPECT_TRUE(status.is_ok()) << status.message();
-  EXPECT_TRUE(config_changed);
+    auto status =
+        converter.ConvertChunk(chunk, output, &config_changed, nullptr);
+    EXPECT_TRUE(status.is_ok()) << status.message();
+    EXPECT_TRUE(config_changed);
 
-  auto& config = converter.GetCurrentConfig();
-  EXPECT_EQ(config.version, 1);
-  EXPECT_EQ(config.profile_indication, 100);
-  EXPECT_EQ(config.profile_compatibility, 0);
-  EXPECT_EQ(config.avc_level, 31);
-  EXPECT_EQ(config.length_size, 4);
-  EXPECT_EQ(config.sps_list.size(), 1ul);
-  EXPECT_EQ(config.pps_list.size(), 1ul);
-  EXPECT_EQ(config.chroma_format, 1);
-  EXPECT_EQ(config.bit_depth_luma_minus8, 0);
-  EXPECT_EQ(config.bit_depth_chroma_minus8, 0);
-  EXPECT_EQ(config.sps_ext_list.size(), 1ul);
+    // Convert AVC bitstream back to Annex-B bitstream, and validate if
+    // parameter set are write to the output or not.
+    EXPECT_FALSE(mp4::AVC::ConvertAVCToAnnexBInPlaceForLengthSize4(&output));
+    H264Parser parser;
+    parser.SetStream(output.data(), output.size());
+    std::vector<H264NALU> parameter_sets;
+    while (true) {
+      H264NALU nalu;
+      H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
+      if (res == H264Parser::kEOStream) {
+        break;
+      }
+      EXPECT_EQ(res, H264Parser::kOk);
+      switch (nalu.nal_unit_type) {
+        case H264NALU::kSPS: {
+          int sps_id;
+          EXPECT_EQ(parser.ParseSPS(&sps_id), H264Parser::kOk);
+          EXPECT_TRUE(!!parser.GetSPS(sps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        }
+        case H264NALU::kSPSExt: {
+          int sps_id;
+          EXPECT_EQ(parser.ParseSPSExt(&sps_id), H264Parser::kOk);
+          EXPECT_TRUE(!!parser.GetSPS(sps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        }
+        case H264NALU::kPPS: {
+          int pps_id;
+          EXPECT_EQ(parser.ParsePPS(&pps_id), H264Parser::kOk);
+          EXPECT_TRUE(!!parser.GetPPS(pps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        }
+        default:
+          break;
+      }
+    }
+
+    if (add_parameter_sets_in_bitstream) {
+      // Parameter sets should write to the output stream in the order of SPS,
+      // SPS Extension, PPS.
+      EXPECT_EQ(parameter_sets.size(), 3u);
+      EXPECT_EQ(parameter_sets.at(0).nal_unit_type, H264NALU::kSPS);
+      EXPECT_EQ(parameter_sets.at(1).nal_unit_type, H264NALU::kSPSExt);
+      EXPECT_EQ(parameter_sets.at(2).nal_unit_type, H264NALU::kPPS);
+    } else {
+      // Parameter sets should not write to the output stream.
+      EXPECT_EQ(parameter_sets.size(), 0u);
+    }
+
+    auto& config = converter.GetCurrentConfig();
+    EXPECT_EQ(config.version, 1);
+    EXPECT_EQ(config.profile_indication, 100);
+    EXPECT_EQ(config.profile_compatibility, 0);
+    EXPECT_EQ(config.avc_level, 31);
+    EXPECT_EQ(config.length_size, 4);
+    EXPECT_EQ(config.sps_list.size(), 1ul);
+    EXPECT_EQ(config.pps_list.size(), 1ul);
+    EXPECT_EQ(config.chroma_format, 1);
+    EXPECT_EQ(config.bit_depth_luma_minus8, 0);
+    EXPECT_EQ(config.bit_depth_chroma_minus8, 0);
+    EXPECT_EQ(config.sps_ext_list.size(), 1ul);
+  }
 }
 
 TEST(H264AnnexBToAvcBitstreamConverterTest, Failure) {
-  H264AnnexBToAvcBitstreamConverter converter;
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H264AnnexBToAvcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
 
-  std::vector<uint8_t> input{0x0, 0x0, 0x0, 0x1, 0x9,  0x10,
-                             0x0, 0x0, 0x0, 0x1, 0x67, 0x42};
-  std::vector<uint8_t> output(input.size());
+    std::vector<uint8_t> input{0x0, 0x0, 0x0, 0x1, 0x9,  0x10,
+                               0x0, 0x0, 0x0, 0x1, 0x67, 0x42};
+    std::vector<uint8_t> output(input.size());
 
-  auto status = converter.ConvertChunk(input, output, nullptr, nullptr);
+    auto status = converter.ConvertChunk(input, output, nullptr, nullptr);
 
-  ASSERT_EQ(status.code(), MP4Status::Codes::kInvalidSPS);
+    ASSERT_EQ(status.code(), MP4Status::Codes::kInvalidSPS);
+  }
 }
 
 }  // namespace media
diff --git a/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.cc b/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.cc
index 0044d67..af8ae8c 100644
--- a/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.cc
+++ b/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.cc
@@ -10,7 +10,9 @@
 
 namespace media {
 
-H265AnnexBToHevcBitstreamConverter::H265AnnexBToHevcBitstreamConverter() {
+H265AnnexBToHevcBitstreamConverter::H265AnnexBToHevcBitstreamConverter(
+    bool add_parameter_sets_in_bitstream)
+    : add_parameter_sets_in_bitstream_(add_parameter_sets_in_bitstream) {
   // These configuration items never change.
   config_.configurationVersion = 1;
   config_.lengthSizeMinusOne = 3;
@@ -29,7 +31,7 @@
     base::span<uint8_t> output,
     bool* config_changed_out,
     size_t* size_out) {
-  std::vector<H265NALU> slice_units;
+  std::vector<base::span<const uint8_t>> slice_units;
   size_t data_size = 0;
   bool config_changed = false;
   H265NALU nalu;
@@ -47,8 +49,9 @@
   base::flat_set<int> vps_to_include;
 
   // Scan input buffer looking for two main types of NALUs
-  //  1. VPS, SPS and PPS. They'll be added to the HEVC configuration |config_|
-  //     and will *not* be copied to |output|.
+  //  1. VPS, SPS and PPS. They'll be added to the HEVC configuration `config_`
+  //     and maybe be copied to `output` based on
+  //     `add_parameter_sets_in_bitstream_`.
   //  2. Slices. They'll being copied into the output buffer, but also affect
   //     what configuration (profile and level) is active now.
   // A configure change will only happen on IDR frame. It is expected the
@@ -183,12 +186,43 @@
       }
         [[fallthrough]];
       default:
-        slice_units.push_back(nalu);
+        // TODO(crbug.com/40284755): The `nalu.data` should hold a span instead
+        // of a pointer.
+        slice_units.emplace_back(nalu.data.get(),
+                                 base::checked_cast<size_t>(nalu.size));
         data_size += config_.lengthSizeMinusOne + 1 + nalu.size;
         break;
     }
   }
 
+  if (config_changed && add_parameter_sets_in_bitstream_) {
+    // Insert parameter sets, in the order of PPS, SPS and VPS.
+    for (auto& id : pps_to_include) {
+      auto it = id2pps_.find(id);
+      if (it == id2pps_.end()) {
+        return MP4Status::Codes::kFailedToLookupPPS;
+      }
+      slice_units.insert(slice_units.begin(), it->second);
+      data_size += config_.lengthSizeMinusOne + 1 + it->second.size();
+    }
+    for (auto& id : sps_to_include) {
+      auto it = id2sps_.find(id);
+      if (it == id2sps_.end()) {
+        return MP4Status::Codes::kFailedToLookupSPS;
+      }
+      slice_units.insert(slice_units.begin(), it->second);
+      data_size += config_.lengthSizeMinusOne + 1 + it->second.size();
+    }
+    for (auto& id : vps_to_include) {
+      auto it = id2vps_.find(id);
+      if (it == id2vps_.end()) {
+        return MP4Status::Codes::kFailedToLookupVPS;
+      }
+      slice_units.insert(slice_units.begin(), it->second);
+      data_size += config_.lengthSizeMinusOne + 1 + it->second.size();
+    }
+  }
+
   if (size_out)
     *size_out = data_size;
   if (data_size > output.size()) {
@@ -200,15 +234,7 @@
   base::SpanWriter writer(output);
   for (auto& unit : slice_units) {
     bool written_ok =
-        writer.WriteU32BigEndian(unit.size) &&
-        writer.Write(
-            // SAFETY: `unit` is constructed with a size that is the number of
-            // elements at the data pointer.
-            //
-            // TODO(crbug.com/40284755): The `unit` should hold a span instead
-            // of a pointer.
-            UNSAFE_TODO(base::span(unit.data.get(),
-                                   base::checked_cast<size_t>(unit.size))));
+        writer.WriteU32BigEndian(unit.size()) && writer.Write(unit);
     if (!written_ok) {
       return MP4Status::Codes::kBufferTooSmall;
     }
@@ -217,7 +243,7 @@
   DCHECK_EQ(writer.num_written(), data_size);
 
   // Now when we are sure that everything is written and fits nicely,
-  // we can update parts of the |config_| that were changed by this data chunk.
+  // we can update parts of the `config_` that were changed by this data chunk.
   if (config_changed) {
     if (new_active_sps_id < 0)
       new_active_sps_id = active_sps_id_;
@@ -301,41 +327,53 @@
 
     // We write 3 arrays, in the order of VPS array, SPS array and PPS array.
     auto hvcc_array_idx = 0;
-    mp4::HEVCDecoderConfigurationRecord::HVCCNALUnit nal_unit;
+
     mp4::HEVCDecoderConfigurationRecord::HVCCNALArray nalu_array;
     // bit 7: array_completeness. When set to 1, corresponding type of
     // NAL unit will be in the array only and none are in the stream; otherwise
     // they may additionally be in the stream.
-    uint8_t first_byte = (1 << 7) | (H265NALU::VPS_NUT & 0x3F);
-    if (id2vps_.size() > 0) {
+    uint8_t first_byte = ((add_parameter_sets_in_bitstream_ ? 0 : 1) << 7) |
+                         (H265NALU::VPS_NUT & 0x3F);
+    if (vps_to_include.size() > 0) {
       nalu_array.first_byte = first_byte;
-      for (auto& vps : id2vps_) {
-        nal_unit.assign(vps.second.begin(), vps.second.end());
-        nalu_array.units.push_back(nal_unit);
+      for (int id : vps_to_include) {
+        auto it = id2vps_.find(id);
+        if (it == id2vps_.end()) {
+          return MP4Status::Codes::kFailedToLookupVPS;
+        }
+        nalu_array.units.push_back(it->second);
       }
       config_.arrays.push_back(nalu_array);
       hvcc_array_idx++;
     }
 
-    first_byte = (1 << 7) | (H265NALU::SPS_NUT & 0x3F);
+    first_byte = ((add_parameter_sets_in_bitstream_ ? 0 : 1) << 7) |
+                 (H265NALU::SPS_NUT & 0x3F);
     nalu_array.units.clear();
-    if (id2sps_.size() > 0) {
+    if (sps_to_include.size() > 0) {
       nalu_array.first_byte = first_byte;
-      for (auto& sps : id2sps_) {
-        nal_unit.assign(sps.second.begin(), sps.second.end());
-        nalu_array.units.push_back(nal_unit);
+      for (int id : sps_to_include) {
+        auto it = id2sps_.find(id);
+        if (it == id2sps_.end()) {
+          return MP4Status::Codes::kFailedToLookupSPS;
+        }
+        nalu_array.units.push_back(it->second);
       }
       config_.arrays.push_back(nalu_array);
       hvcc_array_idx++;
     }
 
-    first_byte = (1 << 7) | (H265NALU::PPS_NUT & 0x3F);
+    first_byte = ((add_parameter_sets_in_bitstream_ ? 0 : 1) << 7) |
+                 (H265NALU::PPS_NUT & 0x3F);
     nalu_array.units.clear();
-    if (id2sps_.size() > 0) {
+    if (pps_to_include.size() > 0) {
       nalu_array.first_byte = first_byte;
-      for (auto& pps : id2pps_) {
-        nal_unit.assign(pps.second.begin(), pps.second.end());
-        nalu_array.units.push_back(nal_unit);
+      for (int id : pps_to_include) {
+        auto it = id2pps_.find(id);
+        if (it == id2pps_.end()) {
+          return MP4Status::Codes::kFailedToLookupPPS;
+        }
+        nalu_array.units.push_back(it->second);
       }
       config_.arrays.push_back(nalu_array);
       hvcc_array_idx++;
diff --git a/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.h b/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.h
index f8552407..0216724 100644
--- a/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.h
+++ b/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter.h
@@ -24,7 +24,23 @@
 // Annex B (ISO/IEC 14496-10) to HEVC (as specified in ISO/IEC 14496-15).
 class MEDIA_EXPORT H265AnnexBToHevcBitstreamConverter {
  public:
-  H265AnnexBToHevcBitstreamConverter();
+  // Construct the bitstream converter.
+  //
+  // `add_parameter_sets_in_bitstream` - indicates whether the parameter sets
+  // can be copied to the output bitstream or not. When set to false, parameter
+  // sets are only stored in the `HEVCDecoderConfigurationRecord`, which
+  // complies with the requirements of `hvc1` as defined in ISO/IEC
+  // 14496-15:2019 - 8.3.2. When set to true, parameter sets are stored both in
+  // the output bitstream and in the `HEVCDecoderConfigurationRecord`, which
+  // complies with the requirements of `hev1` as defined in ISO/IEC
+  // 14496-15:2019 - 8.3.2.
+  //
+  // NOTE: for `hev1`, the spec doesn't require the muxer to insert parameter
+  // sets into the bitstream, and only states that it is optional, nevertheless,
+  // this converter always assumes that they need to have the parameter sets
+  // inserted.
+  explicit H265AnnexBToHevcBitstreamConverter(
+      bool add_parameter_sets_in_bitstream);
 
   H265AnnexBToHevcBitstreamConverter(
       const H265AnnexBToHevcBitstreamConverter&) = delete;
@@ -36,17 +52,17 @@
   // Converts a video chunk from a format with in-place decoder configuration
   // into a format where configuration needs to be sent separately.
   //
-  // |input| - where to read the data from
-  // |output| - where to put the converted video data
-  // If error kBufferTooSmall is returned, it means that |output| was not
-  // big enough to contain a converted video chunk. In this case |size_out|
+  // `input` - where to read the data from
+  // `output` - where to put the converted video data
+  // If error kBufferTooSmall is returned, it means that `output` was not
+  // big enough to contain a converted video chunk. In this case `size_out`
   // is populated.
-  // |config_changed_out| is set to True if the video chunk
+  // `config_changed_out` is set to True if the video chunk
   // processed by this call contained decoder configuration information.
   // In this case latest configuration information can be obtained
   // from GetCurrentConfig().
-  // |size_out| - number of bytes written to |output|, or desired size of
-  // |output| if it's too small.
+  // `size_out` - number of bytes written to `output`, or desired size of
+  // `output` if it's too small.
   MP4Status ConvertChunk(base::span<const uint8_t> input,
                          base::span<uint8_t> output,
                          bool* config_changed_out,
@@ -68,6 +84,8 @@
   int active_sps_id_ = -1;
   int active_pps_id_ = -1;
   int active_vps_id_ = -1;
+
+  const bool add_parameter_sets_in_bitstream_ = false;
 };
 
 }  // namespace media
diff --git a/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter_unittest.cc b/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter_unittest.cc
index f5f673b8..61d0e94 100644
--- a/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter_unittest.cc
+++ b/media/formats/mp4/h265_annex_b_to_hevc_bitstream_converter_unittest.cc
@@ -7,10 +7,13 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 
 #include "base/files/file.h"
 #include "base/path_service.h"
+#include "media/formats/mp4/avc.h"
 #include "media/formats/mp4/box_definitions.h"
+#include "media/parsers/h265_parser.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
@@ -40,55 +43,109 @@
                           "chunk3-non-idr.bin",    "chunk4-non-idr.bin",
                           "chunk5-non-idr.bin",    "chunk6-config-idr.bin",
                           "chunk7-non-idr.bin"};
-  H265AnnexBToHevcBitstreamConverter converter;
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H265AnnexBToHevcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
+    for (std::string& name : chunks) {
+      SCOPED_TRACE(name);
+      auto input = ReadTestFile(name);
+      SCOPED_TRACE(input.size());
+      std::vector<uint8_t> output;
+      size_t desired_size = 0;
+      bool config_changed = false;
 
-  for (std::string& name : chunks) {
-    SCOPED_TRACE(name);
-    auto input = ReadTestFile(name);
-    SCOPED_TRACE(input.size());
-    std::vector<uint8_t> output;
-    size_t desired_size = 0;
-    bool config_changed = false;
+      auto status =
+          converter.ConvertChunk(input, output, &config_changed, &desired_size);
+      ASSERT_EQ(status.code(), MP4Status::Codes::kBufferTooSmall);
+      output.resize(desired_size);
 
-    auto status =
-        converter.ConvertChunk(input, output, &config_changed, &desired_size);
-    ASSERT_EQ(status.code(), MP4Status::Codes::kBufferTooSmall);
-    output.resize(desired_size);
+      status = converter.ConvertChunk(input, output, &config_changed, nullptr);
+      EXPECT_TRUE(status.is_ok()) << status.message();
 
-    status = converter.ConvertChunk(input, output, &config_changed, nullptr);
-    EXPECT_TRUE(status.is_ok()) << status.message();
+      // Convert HEVC bitstream back to Annex-B bitstream, and validate if
+      // parameter set are write to the output or not.
+      EXPECT_TRUE(mp4::AVC::ConvertAVCToAnnexBInPlaceForLengthSize4(&output));
+      H265Parser parser;
+      parser.SetStream(output.data(), output.size());
+      std::vector<H265NALU> parameter_sets;
+      while (true) {
+        H265NALU nalu;
+        H265Parser::Result res = parser.AdvanceToNextNALU(&nalu);
+        if (res == H265Parser::kEOStream) {
+          break;
+        }
+        EXPECT_EQ(res, H265Parser::kOk);
+        switch (nalu.nal_unit_type) {
+          case H265NALU::VPS_NUT:
+            int vps_id;
+            EXPECT_EQ(parser.ParseVPS(&vps_id), H265Parser::kOk);
+            EXPECT_TRUE(!!parser.GetVPS(vps_id));
+            parameter_sets.push_back(nalu);
+            break;
+          case H265NALU::SPS_NUT:
+            int sps_id;
+            EXPECT_EQ(parser.ParseSPS(&sps_id), H265Parser::kOk);
+            EXPECT_TRUE(!!parser.GetSPS(sps_id));
+            parameter_sets.push_back(nalu);
+            break;
+          case H265NALU::PPS_NUT:
+            int pps_id;
+            EXPECT_EQ(parser.ParsePPS(nalu, &pps_id), H265Parser::kOk);
+            EXPECT_TRUE(!!parser.GetPPS(pps_id));
+            parameter_sets.push_back(nalu);
+            break;
+          default:
+            break;
+        }
+      }
 
-    auto& config = converter.GetCurrentConfig();
-    if (name.find("config") != std::string::npos) {
-      // Chunks with configuration
-      EXPECT_TRUE(config_changed);
+      if (add_parameter_sets_in_bitstream && config_changed) {
+        // Parameter sets should write to the output stream in the order of VPS,
+        // SPS, and PPS.
+        EXPECT_EQ(parameter_sets.size(), 3u);
+        EXPECT_EQ(parameter_sets.at(0).nal_unit_type, H265NALU::VPS_NUT);
+        EXPECT_EQ(parameter_sets.at(1).nal_unit_type, H265NALU::SPS_NUT);
+        EXPECT_EQ(parameter_sets.at(2).nal_unit_type, H265NALU::PPS_NUT);
+      } else {
+        // Parameter sets should not write to the output stream.
+        EXPECT_EQ(parameter_sets.size(), 0u);
+      }
 
-      EXPECT_EQ(config.configurationVersion, 1);
-      EXPECT_EQ(config.general_profile_space, 0);
-      EXPECT_EQ(config.general_tier_flag, 0);
-      EXPECT_EQ(config.general_profile_idc, 1);
-      EXPECT_EQ(config.general_profile_compatibility_flags, 0x60000000ul);
-      EXPECT_EQ(config.general_constraint_indicator_flags, 0x800000000000ull);
-      EXPECT_EQ(config.general_level_idc, 60);
-      EXPECT_EQ(config.min_spatial_segmentation_idc, 0);
-      EXPECT_EQ(config.parallelismType, 0);
-      EXPECT_EQ(config.chromaFormat, 1);
-      EXPECT_EQ(config.bitDepthLumaMinus8, 0);
-      EXPECT_EQ(config.bitDepthChromaMinus8, 0);
-      EXPECT_EQ(config.avgFrameRate, 0);
-      EXPECT_EQ(config.numOfArrays, 3);
-      EXPECT_EQ((config.arrays[0].first_byte & 0x3f), 32);
-      EXPECT_EQ(config.arrays[0].units.size(), 1ul);
-      EXPECT_EQ((config.arrays[1].first_byte & 0x3f), 33);
-      EXPECT_EQ(config.arrays[1].units.size(), 1ul);
-      EXPECT_EQ((config.arrays[2].first_byte & 0x3f), 34);
-      EXPECT_EQ(config.arrays[2].units.size(), 1ul);
-    } else {
-      EXPECT_FALSE(config_changed);
+      auto& config = converter.GetCurrentConfig();
+      if (name.find("config") != std::string::npos) {
+        // Chunks with configuration
+        EXPECT_TRUE(config_changed);
+
+        EXPECT_EQ(config.configurationVersion, 1);
+        EXPECT_EQ(config.general_profile_space, 0);
+        EXPECT_EQ(config.general_tier_flag, 0);
+        EXPECT_EQ(config.general_profile_idc, 1);
+        EXPECT_EQ(config.general_profile_compatibility_flags, 0x60000000ul);
+        EXPECT_EQ(config.general_constraint_indicator_flags, 0x800000000000ull);
+        EXPECT_EQ(config.general_level_idc, 60);
+        EXPECT_EQ(config.min_spatial_segmentation_idc, 0);
+        EXPECT_EQ(config.parallelismType, 0);
+        EXPECT_EQ(config.chromaFormat, 1);
+        EXPECT_EQ(config.bitDepthLumaMinus8, 0);
+        EXPECT_EQ(config.bitDepthChromaMinus8, 0);
+        EXPECT_EQ(config.avgFrameRate, 0);
+        EXPECT_EQ(config.numOfArrays, 3);
+        EXPECT_EQ(config.arrays[0].first_byte,
+                  add_parameter_sets_in_bitstream ? 32 : 160);
+        EXPECT_EQ(config.arrays[0].units.size(), 1ul);
+        EXPECT_EQ(config.arrays[1].first_byte,
+                  add_parameter_sets_in_bitstream ? 33 : 161);
+        EXPECT_EQ(config.arrays[1].units.size(), 1ul);
+        EXPECT_EQ(config.arrays[2].first_byte,
+                  add_parameter_sets_in_bitstream ? 34 : 162);
+        EXPECT_EQ(config.arrays[2].units.size(), 1ul);
+      } else {
+        EXPECT_FALSE(config_changed);
+      }
+
+      std::vector<uint8_t> config_bin;
+      EXPECT_TRUE(config.Serialize(config_bin)) << " file: " << name;
     }
-
-    std::vector<uint8_t> config_bin;
-    EXPECT_TRUE(config.Serialize(config_bin)) << " file: " << name;
   }
 }
 
@@ -154,39 +211,98 @@
       // Encoded data omitted here, it's not important for NALU parsing
   };
 
-  H265AnnexBToHevcBitstreamConverter converter;
-  std::vector<uint8_t> output(10000);
-  bool config_changed = false;
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H265AnnexBToHevcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
+    std::vector<uint8_t> output(10000);
+    bool config_changed = false;
 
-  auto status =
-      converter.ConvertChunk(first_chunk, output, &config_changed, nullptr);
-  EXPECT_TRUE(status.is_ok()) << status.message();
-  EXPECT_TRUE(config_changed);
+    auto status =
+        converter.ConvertChunk(first_chunk, output, &config_changed, nullptr);
+    EXPECT_TRUE(status.is_ok()) << status.message();
+    EXPECT_TRUE(config_changed);
 
-  auto& config = converter.GetCurrentConfig();
-  EXPECT_EQ(config.numOfArrays, 3);
-  EXPECT_EQ((config.arrays[0].first_byte & 0x3f), 32);
-  EXPECT_EQ(config.arrays[0].units.size(), 1ul);
-  EXPECT_EQ((config.arrays[1].first_byte & 0x3f), 33);
-  EXPECT_EQ(config.arrays[1].units.size(), 1ul);
-  EXPECT_EQ((config.arrays[2].first_byte & 0x3f), 34);
-  EXPECT_EQ(config.arrays[2].units.size(), 2ul);
-  status = converter.ConvertChunk(second_non_idr_chunk, output, &config_changed,
-                                  nullptr);
-  EXPECT_TRUE(status.is_ok()) << status.message();
-  EXPECT_FALSE(config_changed);
+    // Convert HEVC bitstream back to Annex-B bitstream, and validate if
+    // parameter set are write to the output.
+    EXPECT_FALSE(mp4::AVC::ConvertAVCToAnnexBInPlaceForLengthSize4(&output));
+    H265Parser parser;
+    parser.SetStream(output.data(), output.size());
+    std::vector<H265NALU> parameter_sets;
+    while (true) {
+      H265NALU nalu;
+      H265Parser::Result res = parser.AdvanceToNextNALU(&nalu);
+      if (res == H265Parser::kEOStream) {
+        break;
+      }
+      EXPECT_EQ(res, H265Parser::kOk);
+      switch (nalu.nal_unit_type) {
+        case H265NALU::VPS_NUT:
+          int vps_id;
+          EXPECT_EQ(parser.ParseVPS(&vps_id), H265Parser::kOk);
+          EXPECT_TRUE(!!parser.GetVPS(vps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        case H265NALU::SPS_NUT:
+          int sps_id;
+          EXPECT_EQ(parser.ParseSPS(&sps_id), H265Parser::kOk);
+          EXPECT_TRUE(!!parser.GetSPS(sps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        case H265NALU::PPS_NUT:
+          int pps_id;
+          EXPECT_EQ(parser.ParsePPS(nalu, &pps_id), H265Parser::kOk);
+          EXPECT_TRUE(!!parser.GetPPS(pps_id));
+          parameter_sets.push_back(nalu);
+          break;
+        default:
+          break;
+      }
+    }
+
+    if (add_parameter_sets_in_bitstream) {
+      // Parameter sets should write to the output stream in the order of VPS,
+      // SPS, PPS1, and PPS2.
+      EXPECT_EQ(parameter_sets.size(), 4u);
+      EXPECT_EQ(parameter_sets.at(0).nal_unit_type, H265NALU::VPS_NUT);
+      EXPECT_EQ(parameter_sets.at(1).nal_unit_type, H265NALU::SPS_NUT);
+      EXPECT_EQ(parameter_sets.at(2).nal_unit_type, H265NALU::PPS_NUT);
+      EXPECT_EQ(parameter_sets.at(3).nal_unit_type, H265NALU::PPS_NUT);
+    } else {
+      // Parameter sets should not write to the output stream.
+      EXPECT_EQ(parameter_sets.size(), 0u);
+    }
+
+    auto& config = converter.GetCurrentConfig();
+    EXPECT_EQ(config.numOfArrays, 3);
+    EXPECT_EQ(config.arrays[0].first_byte,
+              add_parameter_sets_in_bitstream ? 32 : 160);
+    EXPECT_EQ(config.arrays[0].units.size(), 1ul);
+    EXPECT_EQ(config.arrays[1].first_byte,
+              add_parameter_sets_in_bitstream ? 33 : 161);
+    EXPECT_EQ(config.arrays[1].units.size(), 1ul);
+    EXPECT_EQ(config.arrays[2].first_byte,
+              add_parameter_sets_in_bitstream ? 34 : 162);
+    EXPECT_EQ(config.arrays[2].units.size(), 2ul);
+    status = converter.ConvertChunk(second_non_idr_chunk, output,
+                                    &config_changed, nullptr);
+    EXPECT_TRUE(status.is_ok()) << status.message();
+    EXPECT_FALSE(config_changed);
+  }
 }
 
 TEST(H265AnnexBToHevcBitstreamConverterTest, Failure) {
-  H265AnnexBToHevcBitstreamConverter converter;
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H265AnnexBToHevcBitstreamConverter converter(
+        add_parameter_sets_in_bitstream);
 
-  std::vector<uint8_t> input{0x0,  0x0, 0x0, 0x1, 0x40, 0x01,
-                             0x0c, 0x0, 0x0, 0x1, 0x67, 0x42};
-  std::vector<uint8_t> output(input.size());
+    std::vector<uint8_t> input{0x0,  0x0, 0x0, 0x1, 0x40, 0x01,
+                               0x0c, 0x0, 0x0, 0x1, 0x67, 0x42};
+    std::vector<uint8_t> output(input.size());
 
-  auto status = converter.ConvertChunk(input, output, nullptr, nullptr);
+    auto status = converter.ConvertChunk(input, output, nullptr, nullptr);
 
-  ASSERT_EQ(status.code(), MP4Status::Codes::kInvalidVPS);
+    ASSERT_EQ(status.code(), MP4Status::Codes::kInvalidVPS);
+  }
 }
 
 }  // namespace media
diff --git a/media/formats/mp4/h26x_annex_b_to_bitstream_converter.cc b/media/formats/mp4/h26x_annex_b_to_bitstream_converter.cc
index b6a4003..098e1f0 100644
--- a/media/formats/mp4/h26x_annex_b_to_bitstream_converter.cc
+++ b/media/formats/mp4/h26x_annex_b_to_bitstream_converter.cc
@@ -14,18 +14,21 @@
 namespace media {
 
 H26xAnnexBToBitstreamConverter::H26xAnnexBToBitstreamConverter(
-    VideoCodec video_codec) {
+    VideoCodec video_codec,
+    bool add_parameter_sets_in_bitstream) {
   CHECK(video_codec == VideoCodec::kH264
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
         || video_codec == VideoCodec::kHEVC
 #endif
   );
   if (video_codec == VideoCodec::kH264) {
-    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>();
+    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>(
+        add_parameter_sets_in_bitstream);
   }
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
   if (video_codec == VideoCodec::kHEVC) {
-    h265_converter_ = std::make_unique<H265AnnexBToHevcBitstreamConverter>();
+    h265_converter_ = std::make_unique<H265AnnexBToHevcBitstreamConverter>(
+        add_parameter_sets_in_bitstream);
   }
 #endif  // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
 }
diff --git a/media/formats/mp4/h26x_annex_b_to_bitstream_converter.h b/media/formats/mp4/h26x_annex_b_to_bitstream_converter.h
index 89f8ea0..984d2737 100644
--- a/media/formats/mp4/h26x_annex_b_to_bitstream_converter.h
+++ b/media/formats/mp4/h26x_annex_b_to_bitstream_converter.h
@@ -30,7 +30,24 @@
 // `H265AnnexBToHevcBitstreamConverter`.
 class MEDIA_EXPORT H26xAnnexBToBitstreamConverter {
  public:
-  explicit H26xAnnexBToBitstreamConverter(VideoCodec video_codec);
+  // Construct the bitstream converter.
+  //
+  // `video_codec` - codec of the video bitstream
+  // `add_parameter_sets_in_bitstream` - indicates whether the parameter sets
+  // can be copied to the output bitstream or not. When set to false, parameter
+  // sets are only stored in the `{AVC|HEVC}DecoderConfigurationRecord`, which
+  // complies with the requirements of `avc1` and `hvc1` as defined in ISO/IEC
+  // 14496-15:2019 - 5.3.2, 8.3.2. When set to true, parameter sets are stored
+  // both in the `{AVC|HEVC}DecoderConfigurationRecord` and in the output
+  // bitstream, which complies with the requirements of `avc3` and `hev1` as
+  // defined in ISO/IEC 14496-15:2019 - 5.3.2, 8.3.2.
+  //
+  // NOTE: for `avc3` and `hev1`, the spec doesn't require the muxer to insert
+  // parameter sets into the bitstream, and only states that it is optional,
+  // nevertheless, this converter always assumes that they need to have the
+  // parameter sets inserted.
+  explicit H26xAnnexBToBitstreamConverter(VideoCodec video_codec,
+                                          bool add_parameter_sets_in_bitstream);
   H26xAnnexBToBitstreamConverter(const H26xAnnexBToBitstreamConverter&) =
       delete;
   H26xAnnexBToBitstreamConverter& operator=(
@@ -41,7 +58,7 @@
   // Converts a video chunk from a format with in-place decoder configuration
   // into a format where configuration needs to be sent separately.
   //
-  // |input| - where to read the data from
+  // `input` - where to read the data from
   //
   // NOTE: Caller needs to be careful using this function, and make sure the
   // conversion won't fail, otherwise there will be a `CHECK` failure.
@@ -59,17 +76,17 @@
   // Converts a video chunk from a format with in-place decoder configuration
   // into a format where configuration needs to be sent separately.
   //
-  // |input| - where to read the data from
-  // |output| - where to put the converted video data
-  // If error kBufferTooSmall is returned, it means that |output| was not
-  // big enough to contain a converted video chunk. In this case |size_out|
+  // `input` - where to read the data from
+  // `output` - where to put the converted video data
+  // If error kBufferTooSmall is returned, it means that `output` was not
+  // big enough to contain a converted video chunk. In this case `size_out`
   // is populated.
-  // |config_changed_out| is set to True if the video chunk
+  // `config_changed_out` is set to True if the video chunk
   // processed by this call contained decoder configuration information.
-  // In this case latest codec description can be obtained
-  // from GetCodecDescription().
-  // |size_out| - number of bytes written to |output|, or desired size of
-  // |output| if it's too small.
+  // In this case latest configuration information can be obtained
+  // from GetCurrentConfig().
+  // `size_out` - number of bytes written to `output`, or desired size of
+  // `output` if it's too small.
   MP4Status ConvertChunk(base::span<const uint8_t> input,
                          base::span<uint8_t> output,
                          bool* config_changed_out,
diff --git a/media/formats/mp4/h26x_annex_b_to_bitstream_converter_unittest.cc b/media/formats/mp4/h26x_annex_b_to_bitstream_converter_unittest.cc
index bb32c68..d9fcfced 100644
--- a/media/formats/mp4/h26x_annex_b_to_bitstream_converter_unittest.cc
+++ b/media/formats/mp4/h26x_annex_b_to_bitstream_converter_unittest.cc
@@ -32,14 +32,17 @@
   first_chunk.insert(first_chunk.end(), first_frame_idr.begin(),
                      first_frame_idr.end());
 
-  H26xAnnexBToBitstreamConverter converter(VideoCodec::kH264);
-  scoped_refptr<DecoderBuffer> output = converter.Convert(first_chunk);
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H26xAnnexBToBitstreamConverter converter(VideoCodec::kH264,
+                                             add_parameter_sets_in_bitstream);
+    scoped_refptr<DecoderBuffer> output = converter.Convert(first_chunk);
 
-  auto codec_profile_level = converter.GetCodecProfileLevel();
-  EXPECT_EQ(codec_profile_level.codec, VideoCodec::kH264);
-  EXPECT_EQ(codec_profile_level.profile,
-            VideoCodecProfile::H264PROFILE_BASELINE);
-  EXPECT_EQ(codec_profile_level.level, 30u);
+    auto codec_profile_level = converter.GetCodecProfileLevel();
+    EXPECT_EQ(codec_profile_level.codec, VideoCodec::kH264);
+    EXPECT_EQ(codec_profile_level.profile,
+              VideoCodecProfile::H264PROFILE_BASELINE);
+    EXPECT_EQ(codec_profile_level.level, 30u);
+  }
 }
 
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
@@ -95,13 +98,17 @@
   first_chunk.insert(first_chunk.end(), first_frame_idr.begin(),
                      first_frame_idr.end());
 
-  H26xAnnexBToBitstreamConverter converter(VideoCodec::kHEVC);
-  scoped_refptr<DecoderBuffer> output = converter.Convert(first_chunk);
+  for (bool add_parameter_sets_in_bitstream : {false, true}) {
+    H26xAnnexBToBitstreamConverter converter(VideoCodec::kHEVC,
+                                             add_parameter_sets_in_bitstream);
+    scoped_refptr<DecoderBuffer> output = converter.Convert(first_chunk);
 
-  auto codec_profile_level = converter.GetCodecProfileLevel();
-  EXPECT_EQ(codec_profile_level.codec, VideoCodec::kHEVC);
-  EXPECT_EQ(codec_profile_level.profile, VideoCodecProfile::HEVCPROFILE_MAIN10);
-  EXPECT_EQ(codec_profile_level.level, 153u);
+    auto codec_profile_level = converter.GetCodecProfileLevel();
+    EXPECT_EQ(codec_profile_level.codec, VideoCodec::kHEVC);
+    EXPECT_EQ(codec_profile_level.profile,
+              VideoCodecProfile::HEVCPROFILE_MAIN10);
+    EXPECT_EQ(codec_profile_level.level, 153u);
+  }
 }
 #endif
 
diff --git a/media/formats/mp4/writable_box_definitions.h b/media/formats/mp4/writable_box_definitions.h
index f624533..b4c45fa3 100644
--- a/media/formats/mp4/writable_box_definitions.h
+++ b/media/formats/mp4/writable_box_definitions.h
@@ -103,6 +103,15 @@
   // because it provides Serialize method and the format
   // is hard to be correct.
   AVCDecoderConfigurationRecord avc_config_record;
+
+  // Indicates whether the parameter sets can be copied to the output bitstream
+  // or not. When set to false, parameter sets are only stored in the
+  // `AVCDecoderConfigurationRecord`, which complies with the requirements of
+  // `avc1` as defined in ISO/IEC 14496-15:2019 - 5.3.2. When set to true,
+  // parameter sets are stored both in the output bitstream and in the
+  // `AVCDecoderConfigurationRecord`, which complies with the requirements of
+  // `avc3` as defined in ISO/IEC 14496-15:2019 - 5.3.2.
+  bool add_parameter_sets_in_bitstream;
 };
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
@@ -113,6 +122,15 @@
   // because it provides Serialize method and the format
   // is hard to be correct.
   HEVCDecoderConfigurationRecord hevc_config_record;
+
+  // Indicates whether the parameter sets can be copied to the output bitstream
+  // or not. When set to false, parameter sets are only stored in the
+  // `HEVCDecoderConfigurationRecord`, which complies with the requirements of
+  // `hvc1` as defined in ISO/IEC 14496-15:2019 - 8.3.2. When set to true,
+  // parameter sets are stored both in the output bitstream and in the
+  // `HEVCDecoderConfigurationRecord`, which complies with the requirements of
+  // `hev1` as defined in ISO/IEC 14496-15:2019 - 8.3.2.
+  bool add_parameter_sets_in_bitstream;
 };
 #endif  // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
 
diff --git a/media/muxers/mp4_movie_box_writer.cc b/media/muxers/mp4_movie_box_writer.cc
index 196c393..ec8db2c6 100644
--- a/media/muxers/mp4_movie_box_writer.cc
+++ b/media/muxers/mp4_movie_box_writer.cc
@@ -75,16 +75,19 @@
   writer.WriteU32(box_->major_brand);    // normal rate.
   writer.WriteU32(box_->minor_version);  // normal rate.
 
-  // It should include at least one of `avc1`, `av01`, `vp09`, or `hvc1`.
+  // It should include at least one of `avc1`, `avc3`, `av01`, `vp09`, `hvc1`,
+  // or `hev1`.
   CHECK_GE(box_->compatible_brands.size(), 1u);
   CHECK(box_->compatible_brands.end() !=
         std::find_if(box_->compatible_brands.begin(),
                      box_->compatible_brands.end(), [](const auto type) {
                        return type == mp4::FOURCC_AVC1 ||
+                              type == mp4::FOURCC_AVC3 ||
                               type == mp4::FOURCC_AV01 ||
                               type == mp4::FOURCC_VP09
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-                              || type == mp4::FOURCC_HVC1
+                              || type == mp4::FOURCC_HVC1 ||
+                              type == mp4::FOURCC_HEV1
 #endif
                            ;
                      }));
@@ -702,7 +705,8 @@
   writer.EndBox();
 }
 
-// Mp4MovieVisualSampleEntryBoxWriter (`vp09`, `av01`, `avc1`, `hvc1`) class.
+// Mp4MovieVisualSampleEntryBoxWriter (`vp09`, `av01`, `avc1`, `avc3`, `hvc1`,
+// `hev1`) class.
 Mp4MovieVisualSampleEntryBoxWriter::Mp4MovieVisualSampleEntryBoxWriter(
     const Mp4MuxerContext& context,
     const mp4::writable_boxes::VisualSampleEntry& box)
@@ -763,12 +767,18 @@
       break;
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     case VideoCodec::kH264:
-      writer.StartBox(mp4::FOURCC_AVC1);
+      writer.StartBox(
+          box_->avc_decoder_configuration->add_parameter_sets_in_bitstream
+              ? mp4::FOURCC_AVC3
+              : mp4::FOURCC_AVC1);
       break;
 #endif
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
     case VideoCodec::kHEVC:
-      writer.StartBox(mp4::FOURCC_HVC1);
+      writer.StartBox(
+          box_->hevc_decoder_configuration->add_parameter_sets_in_bitstream
+              ? mp4::FOURCC_HEV1
+              : mp4::FOURCC_HVC1);
       break;
 #endif
     default:
diff --git a/media/muxers/mp4_muxer_delegate.cc b/media/muxers/mp4_muxer_delegate.cc
index bf8d5623..bf838562 100644
--- a/media/muxers/mp4_muxer_delegate.cc
+++ b/media/muxers/mp4_muxer_delegate.cc
@@ -122,6 +122,7 @@
     VideoCodec video_codec,
     std::optional<VideoCodecProfile> video_profile,
     std::optional<VideoCodecLevel> video_level,
+    bool add_parameter_sets_in_bitstream,
     Muxer::WriteDataCB write_callback,
     size_t audio_sample_count_per_fragment)
     : write_callback_(std::move(write_callback)),
@@ -129,6 +130,7 @@
       video_codec_(video_codec),
       video_profile_(std::move(video_profile)),
       video_level_(std::move(video_level)),
+      add_parameter_sets_in_bitstream_(add_parameter_sets_in_bitstream),
       audio_sample_count_per_fragment_(audio_sample_count_per_fragment) {}
 
 Mp4MuxerDelegate::~Mp4MuxerDelegate() = default;
@@ -183,7 +185,8 @@
 
   if (video_codec_ == VideoCodec::kH264) {
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-    visual_sample_entry.compressor_name = "AVC1 Coding";
+    visual_sample_entry.compressor_name =
+        add_parameter_sets_in_bitstream_ ? "AVC3 Coding" : "AVC1 Coding";
 
     mp4::writable_boxes::AVCDecoderConfiguration avc_config;
     mp4::AVCDecoderConfigurationRecord avc_config_record;
@@ -192,13 +195,16 @@
     DCHECK(result);
 
     avc_config.avc_config_record = std::move(avc_config_record);
+    avc_config.add_parameter_sets_in_bitstream =
+        add_parameter_sets_in_bitstream_;
     visual_sample_entry.avc_decoder_configuration = std::move(avc_config);
 #else
     NOTREACHED();
 #endif
   } else if (video_codec_ == VideoCodec::kHEVC) {
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-    visual_sample_entry.compressor_name = "HVC1 Coding";
+    visual_sample_entry.compressor_name =
+        add_parameter_sets_in_bitstream_ ? "HEV1 Coding" : "HVC1 Coding";
 
     mp4::writable_boxes::HEVCDecoderConfiguration hevc_config;
     mp4::HEVCDecoderConfigurationRecord hevc_config_record;
@@ -207,6 +213,8 @@
     DCHECK(result);
 
     hevc_config.hevc_config_record = std::move(hevc_config_record);
+    hevc_config.add_parameter_sets_in_bitstream =
+        add_parameter_sets_in_bitstream_;
     visual_sample_entry.hevc_decoder_configuration = std::move(hevc_config);
 #else
     NOTREACHED();
@@ -549,12 +557,16 @@
       break;
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
     case VideoCodec::kHEVC:
-      mp4_file_type_box.compatible_brands.emplace_back(mp4::FOURCC_HVC1);
+      mp4_file_type_box.compatible_brands.emplace_back(
+          add_parameter_sets_in_bitstream_ ? mp4::FOURCC_HEV1
+                                           : mp4::FOURCC_HVC1);
       break;
 #endif
     case VideoCodec::kH264:
     default:
-      mp4_file_type_box.compatible_brands.emplace_back(mp4::FOURCC_AVC1);
+      mp4_file_type_box.compatible_brands.emplace_back(
+          add_parameter_sets_in_bitstream_ ? mp4::FOURCC_AVC3
+                                           : mp4::FOURCC_AVC1);
       break;
   }
   mp4_file_type_box.compatible_brands.emplace_back(mp4::FOURCC_MP41);
@@ -694,8 +706,8 @@
     scoped_refptr<DecoderBuffer> encoded_data) {
   CHECK(video_codec_ == VideoCodec::kH264 || video_codec_ == VideoCodec::kHEVC);
   if (!h26x_converter_) {
-    h26x_converter_ =
-        std::make_unique<H26xAnnexBToBitstreamConverter>(video_codec_);
+    h26x_converter_ = std::make_unique<H26xAnnexBToBitstreamConverter>(
+        video_codec_, add_parameter_sets_in_bitstream_);
   }
 
   return h26x_converter_->Convert(encoded_data->AsSpan());
diff --git a/media/muxers/mp4_muxer_delegate.h b/media/muxers/mp4_muxer_delegate.h
index 51e725c..d901352 100644
--- a/media/muxers/mp4_muxer_delegate.h
+++ b/media/muxers/mp4_muxer_delegate.h
@@ -61,8 +61,9 @@
   Mp4MuxerDelegate(
       AudioCodec audio_codec,
       VideoCodec video_codec,
-      std::optional<VideoCodecProfile> profile,
-      std::optional<VideoCodecLevel> level,
+      std::optional<VideoCodecProfile> video_profile,
+      std::optional<VideoCodecLevel> video_level,
+      bool add_parameter_sets_in_bitstream,
       Muxer::WriteDataCB write_callback,
       size_t audio_sample_count_per_fragment = kAudioFragmentCount);
   ~Mp4MuxerDelegate() override;
@@ -167,6 +168,8 @@
   const std::optional<media::VideoCodecProfile> video_profile_;
   const std::optional<media::VideoCodecLevel> video_level_;
 
+  const bool add_parameter_sets_in_bitstream_ = false;
+
   // 1000 is a count that audio samples in the same fragment
   // when no video frame is added. In Windows, when video frames are present,
   // the audio counts per fragment is much less than it.
diff --git a/media/muxers/mp4_muxer_delegate_unittest.cc b/media/muxers/mp4_muxer_delegate_unittest.cc
index c17451c..9d86759 100644
--- a/media/muxers/mp4_muxer_delegate_unittest.cc
+++ b/media/muxers/mp4_muxer_delegate_unittest.cc
@@ -30,9 +30,14 @@
 #include "media/formats/mp4/es_descriptor.h"
 #include "media/formats/mp4/mp4_stream_parser.h"
 #include "media/muxers/mp4_type_conversion.h"
+#include "media/parsers/h264_parser.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(USE_PROPRIETARY_CODECS)
+#include "media/formats/mp4/h26x_annex_b_to_bitstream_converter.h"
+#endif
+
 namespace media {
 
 namespace {
@@ -47,7 +52,12 @@
 constexpr uint32_t kBoxHeaderSize = 8u;
 #endif
 }  // namespace
-class Mp4MuxerDelegateTest : public testing::Test {
+
+struct TestParam {
+  bool add_parameter_sets_in_bitstream;
+};
+
+class Mp4MuxerDelegateTest : public ::testing::TestWithParam<TestParam> {
  public:
   Mp4MuxerDelegateTest() = default;
 
@@ -131,7 +141,7 @@
 };
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-TEST_F(Mp4MuxerDelegateTest, AddVideoFrame) {
+TEST_P(Mp4MuxerDelegateTest, AddVideoFrame) {
   // Add video stream only.
   base::MemoryMappedFile mapped_file_1;
   LoadEncodedFile("bear-320x180-10bit-frame-0.h264", mapped_file_1);
@@ -151,6 +161,7 @@
   int callback_count = 0;
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kH264, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         base::ranges::copy(mp4_data, std::back_inserter(total_written_data));
 
@@ -444,7 +455,7 @@
   }
 }
 
-TEST_F(Mp4MuxerDelegateTest, AddAudioFrame) {
+TEST_P(Mp4MuxerDelegateTest, AddAudioFrame) {
   // Add audio stream only.
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                 media::ChannelLayoutConfig::Stereo(),
@@ -467,6 +478,7 @@
   // 5 seconds.
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kUnknown, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         base::ranges::copy(mp4_data, std::back_inserter(total_written_data));
 
@@ -671,7 +683,7 @@
   }
 }
 
-TEST_F(Mp4MuxerDelegateTest, AudioOnlyNewFragmentCreation) {
+TEST_P(Mp4MuxerDelegateTest, AudioOnlyNewFragmentCreation) {
   // Add audio stream with counts over new fragment threshold..
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                 media::ChannelLayoutConfig::Stereo(),
@@ -690,6 +702,7 @@
   int callback_count = 0;
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kUnknown, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         base::ranges::copy(mp4_data, std::back_inserter(total_written_data));
 
@@ -787,7 +800,7 @@
   }
 }
 
-TEST_F(Mp4MuxerDelegateTest, AudioAndVideoAddition) {
+TEST_P(Mp4MuxerDelegateTest, AudioAndVideoAddition) {
   // Add stream audio first and video.
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                 media::ChannelLayoutConfig::Stereo(),
@@ -812,6 +825,7 @@
   int callback_count = 0;
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kH264, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         base::ranges::copy(mp4_data, std::back_inserter(total_written_data));
 
@@ -989,7 +1003,7 @@
   }
 }
 
-TEST_F(Mp4MuxerDelegateTest, MfraBoxOnAudioAndVideoAddition) {
+TEST_P(Mp4MuxerDelegateTest, MfraBoxOnAudioAndVideoAddition) {
   // Add stream audio first and video.
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                 media::ChannelLayoutConfig::Stereo(),
@@ -1015,6 +1029,7 @@
   int callback_count = 0;
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kH264, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         base::ranges::copy(mp4_data, std::back_inserter(total_written_data));
 
@@ -1196,7 +1211,7 @@
             fourth_moof_written_data);
 }
 
-TEST_F(Mp4MuxerDelegateTest, VideoAndAudioAddition) {
+TEST_P(Mp4MuxerDelegateTest, VideoAndAudioAddition) {
   // Add stream with video first, and audio.
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                 media::ChannelLayoutConfig::Stereo(),
@@ -1220,6 +1235,7 @@
   int callback_count = 0;
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kH264, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         base::ranges::copy(mp4_data, std::back_inserter(total_written_data));
 
@@ -1328,8 +1344,10 @@
     // `trun` test of video.
     ASSERT_EQ(1u, traf_boxes[1].runs.size());
     EXPECT_EQ(24u, traf_boxes[1].runs[0].sample_count);
-    EXPECT_EQ(5859u, traf_boxes[1].runs[0].data_offset);
-
+    // When `add_parameter_sets_in_bitstream` is set to true, there are
+    // additionally 29-byte SPS and 10-byte PPS.
+    EXPECT_EQ(GetParam().add_parameter_sets_in_bitstream ? 5898u : 5859u,
+              traf_boxes[1].runs[0].data_offset);
     ASSERT_EQ(24u, traf_boxes[1].runs[0].sample_durations.size());
 
     // The first and last item.
@@ -1338,7 +1356,7 @@
   }
 }
 
-TEST_F(Mp4MuxerDelegateTest, AudioVideoAndAudioVideoFragment) {
+TEST_P(Mp4MuxerDelegateTest, AudioVideoAndAudioVideoFragment) {
   // Add audio and video the first fragment, but video and audio
   // on the second segment.
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -1364,6 +1382,7 @@
   int callback_count = 0;
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kH264, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         base::ranges::copy(mp4_data, std::back_inserter(total_written_data));
 
@@ -1467,7 +1486,7 @@
   }
 }
 
-TEST_F(Mp4MuxerDelegateTest, ConvertedEncodedDataOnAvc1) {
+TEST_P(Mp4MuxerDelegateTest, ConvertedEncodedDataOnAvc) {
   // Add audio and video the first fragment, but video and audio
   // on the second fragment.
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
@@ -1488,6 +1507,7 @@
   int moof_box_start_offset = 0;
   Mp4MuxerDelegate delegate(
       AudioCodec::kAAC, VideoCodec::kH264, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
       base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
         switch (++callback_count) {
           case 1:
@@ -1542,7 +1562,7 @@
       nullptr, &moof_reader);
   EXPECT_EQ(result, mp4::ParseResult::kOk);
 
-  // `moof`box read.
+  // `moof` box read.
   EXPECT_EQ(mp4::FOURCC_MOOF, moof_reader->type());
   EXPECT_TRUE(moof_reader->ScanChildren());
 
@@ -1564,9 +1584,168 @@
     nalsize = (nalsize << 8) | total_written_data[nal_size_offset++];
   }
 
-  EXPECT_EQ(nalsize, 578u);
+  // When the value of `add_parameter_sets_in_bitstream` equals to true, it
+  // should refer to the NALU size of SPS. When its value equals to false, it
+  // should refer to the size of IDR instead.
+  EXPECT_EQ(nalsize, GetParam().add_parameter_sets_in_bitstream ? 24u : 578u);
 }
 
+TEST_P(Mp4MuxerDelegateTest, VideoFrameResolutionChanged) {
+  // Load the first `240x240` frame.
+  base::MemoryMappedFile mapped_file_1;
+  LoadEncodedFile("blackwhite_yuv444p-frame.h264", mapped_file_1);
+  auto video_stream_1 = media::DecoderBuffer::CopyFrom(mapped_file_1.bytes());
+
+  // Load the second `320x192` frame.
+  base::MemoryMappedFile mapped_file_2;
+  LoadEncodedFile("bear-320x192-baseline-frame-0.h264", mapped_file_2);
+  auto video_stream_2 = media::DecoderBuffer::CopyFrom(mapped_file_2.bytes());
+
+  base::RunLoop run_loop;
+
+  std::vector<uint8_t> first_moof_and_mdat_written_data;
+  std::vector<uint8_t> second_moof_and_mdat_written_data;
+  int callback_count = 0;
+  Mp4MuxerDelegate delegate(
+      AudioCodec::kAAC, VideoCodec::kH264, std::nullopt, std::nullopt,
+      GetParam().add_parameter_sets_in_bitstream,
+      base::BindLambdaForTesting([&](base::span<const uint8_t> mp4_data) {
+        switch (++callback_count) {
+          case 3:
+            // First `moof` + `mdat`.
+            base::ranges::copy(
+                mp4_data, std::back_inserter(first_moof_and_mdat_written_data));
+            break;
+          case 4:
+            // Second `moof` + `mdat`.
+            base::ranges::copy(
+                mp4_data,
+                std::back_inserter(second_moof_and_mdat_written_data));
+            run_loop.Quit();
+            break;
+          default:
+            break;
+        }
+      }));
+
+  H26xAnnexBToBitstreamConverter converter(
+      VideoCodec::kH264, GetParam().add_parameter_sets_in_bitstream);
+
+  base::TimeTicks base_time_ticks = base::TimeTicks::Now();
+
+  // Add the first `240x240` frame.
+  auto stream_buffer_1 = converter.Convert(video_stream_1->AsSpan());
+  media::Muxer::VideoParameters params_1(gfx::Size(240, 240), 30,
+                                         VideoCodec::kH264, gfx::ColorSpace());
+  video_stream_1->set_is_key_frame(true);
+  delegate.AddVideoFrame(params_1, video_stream_1,
+                         converter.GetCodecDescription(), base_time_ticks);
+
+  // Add the second `320x192` frame.
+  auto stream_buffer_2 = converter.Convert(video_stream_2->AsSpan());
+  media::Muxer::VideoParameters params_2(gfx::Size(320, 192), 30,
+                                         VideoCodec::kH264, gfx::ColorSpace());
+  video_stream_2->set_is_key_frame(true);
+  delegate.AddVideoFrame(params_2, video_stream_2,
+                         converter.GetCodecDescription(),
+                         base_time_ticks + base::Milliseconds(30));
+
+  // Write box data to the callback.
+  delegate.Flush();
+
+  run_loop.Run();
+
+  std::size_t index = 0;
+  for (auto moof_and_mdat_written_data :
+       {first_moof_and_mdat_written_data, second_moof_and_mdat_written_data}) {
+    // Parse `moof` + `mdat`.
+    std::unique_ptr<mp4::BoxReader> moof_reader;
+    EXPECT_EQ(mp4::BoxReader::ReadTopLevelBox(moof_and_mdat_written_data.data(),
+                                              moof_and_mdat_written_data.size(),
+                                              nullptr, &moof_reader),
+              mp4::ParseResult::kOk);
+
+    // `moof` box read.
+    EXPECT_EQ(mp4::FOURCC_MOOF, moof_reader->type());
+    EXPECT_TRUE(moof_reader->ScanChildren());
+
+    // `traf` box read.
+    std::vector<mp4::TrackFragment> traf_boxes;
+    EXPECT_TRUE(moof_reader->ReadChildren(&traf_boxes));
+
+    uint32_t mdat_video_data_offset;
+    EXPECT_EQ(112u, traf_boxes[0].runs[0].data_offset);
+    mdat_video_data_offset = traf_boxes[0].runs[0].data_offset;
+
+    // Get `mdat` data and convert it to `Annex-B` format.
+    std::vector<uint8_t> mdat_written_data(
+        moof_and_mdat_written_data.begin() + mdat_video_data_offset,
+        moof_and_mdat_written_data.end());
+    EXPECT_TRUE(
+        mp4::AVC::ConvertAVCToAnnexBInPlaceForLengthSize4(&mdat_written_data));
+
+    H264Parser parser;
+    parser.SetStream(mdat_written_data.data(), mdat_written_data.size());
+    std::vector<H264NALU> nalus;
+    while (true) {
+      H264NALU nalu;
+      H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
+      if (res == H264Parser::kEOStream) {
+        break;
+      }
+      EXPECT_EQ(res, H264Parser::kOk);
+      switch (nalu.nal_unit_type) {
+        case H264NALU::kSPS: {
+          int sps_id;
+          EXPECT_EQ(parser.ParseSPS(&sps_id), H264Parser::kOk);
+          EXPECT_TRUE(!!parser.GetSPS(sps_id));
+          nalus.push_back(nalu);
+          const H264SPS* sps = parser.GetSPS(sps_id);
+          // Frame width & height should change.
+          EXPECT_EQ((sps->pic_width_in_mbs_minus1 + 1) * 16,
+                    index == 0 ? 240 : 320);
+          EXPECT_EQ((sps->pic_height_in_map_units_minus1 + 1) * 16,
+                    index == 0 ? 240 : 192);
+          break;
+        }
+        case H264NALU::kPPS: {
+          int pps_id;
+          EXPECT_EQ(parser.ParsePPS(&pps_id), H264Parser::kOk);
+          EXPECT_TRUE(!!parser.GetPPS(pps_id));
+          nalus.push_back(nalu);
+          break;
+        }
+        case H264NALU::kIDRSlice: {
+          nalus.push_back(nalu);
+          break;
+        }
+        default:
+          break;
+      }
+    }
+
+    if (GetParam().add_parameter_sets_in_bitstream) {
+      // Expect SPS, PPS, IDR.
+      EXPECT_EQ(nalus.size(), 3u);
+      EXPECT_EQ(nalus[0].nal_unit_type, H264NALU::kSPS);
+      EXPECT_EQ(nalus[1].nal_unit_type, H264NALU::kPPS);
+      EXPECT_EQ(nalus[2].nal_unit_type, H264NALU::kIDRSlice);
+    } else {
+      // Expect only one IDR.
+      EXPECT_EQ(nalus.size(), 1u);
+      EXPECT_EQ(nalus[0].nal_unit_type, H264NALU::kIDRSlice);
+    }
+
+    index++;
+  }
+}
+
+static const TestParam kTestCases[] = {
+    {/*add_parameter_sets_in_bitstream=*/false},
+    {/*add_parameter_sets_in_bitstream=*/true}};
+
+INSTANTIATE_TEST_SUITE_P(, Mp4MuxerDelegateTest, testing::ValuesIn(kTestCases));
+
 #endif
 
 }  // namespace media
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index 420c561..6d4911a 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -41,11 +41,6 @@
 // SetLatencyHint(), so we needed to peg this with a constant.
 constexpr int kAbsoluteMaxFrames = 24;
 
-bool ShouldUseLowDelayMode(DemuxerStream* stream) {
-  return base::FeatureList::IsEnabled(kLowDelayVideoRenderingOnLiveStream) &&
-         stream->liveness() == StreamLiveness::kLive;
-}
-
 }  // namespace
 
 VideoRendererImpl::VideoRendererImpl(
@@ -194,7 +189,7 @@
         base::Unretained(gpu_memory_buffer_pool_.get())));
   }
 
-  low_delay_ = ShouldUseLowDelayMode(demuxer_stream_);
+  low_delay_ = stream->liveness() == StreamLiveness::kLive;
   if (low_delay_) {
     MEDIA_LOG(DEBUG, media_log_) << "Video rendering in low delay mode.";
 
diff --git a/media/video/openh264_video_encoder.cc b/media/video/openh264_video_encoder.cc
index 56fbe8b..9acff559 100644
--- a/media/video/openh264_video_encoder.cc
+++ b/media/video/openh264_video_encoder.cc
@@ -255,7 +255,8 @@
   }
 
   if (!options.avc.produce_annexb)
-    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>();
+    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>(
+        /*add_parameter_sets_in_bitstream=*/false);
 
   options_ = options;
   output_cb_ = BindCallbackToCurrentLoopIfNeeded(std::move(output_cb));
@@ -510,7 +511,8 @@
   if (options.avc.produce_annexb) {
     h264_converter_.reset();
   } else if (!h264_converter_) {
-    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>();
+    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>(
+        /*add_parameter_sets_in_bitstream=*/false);
   }
 
   options_ = options;
diff --git a/media/video/video_encode_accelerator_adapter.cc b/media/video/video_encode_accelerator_adapter.cc
index 7a022809..0297b74 100644
--- a/media/video/video_encode_accelerator_adapter.cc
+++ b/media/video/video_encode_accelerator_adapter.cc
@@ -473,12 +473,14 @@
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
   if (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX &&
       !options_.avc.produce_annexb) {
-    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>();
+    h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>(
+        /*add_parameter_sets_in_bitstream=*/false);
   }
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC) && \
     BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
   if (profile_ == HEVCPROFILE_MAIN && !options_.hevc.produce_annexb) {
-    h265_converter_ = std::make_unique<H265AnnexBToHevcBitstreamConverter>();
+    h265_converter_ = std::make_unique<H265AnnexBToHevcBitstreamConverter>(
+        /*add_parameter_sets_in_bitstream=*/false);
   }
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC) &&
         // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
@@ -703,7 +705,8 @@
     if (options.avc.produce_annexb) {
       h264_converter_.reset();
     } else if (!h264_converter_) {
-      h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>();
+      h264_converter_ = std::make_unique<H264AnnexBToAvcBitstreamConverter>(
+          /*add_parameter_sets_in_bitstream=*/false);
     }
   }
 #if BUILDFLAG(ENABLE_PLATFORM_HEVC) && \
@@ -712,7 +715,8 @@
     if (options.hevc.produce_annexb) {
       h265_converter_.reset();
     } else if (!h265_converter_) {
-      h265_converter_ = std::make_unique<H265AnnexBToHevcBitstreamConverter>();
+      h265_converter_ = std::make_unique<H265AnnexBToHevcBitstreamConverter>(
+          /*add_parameter_sets_in_bitstream=*/false);
     }
   }
 #endif  // BUILDFLAG(ENABLE_PLATFORM_HEVC) &&
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java
index af600d2..4c3af0c2 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java
@@ -4,5 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** Associated interface is not supported yet. */
+@NullMarked
 public class AssociatedInterfaceNotSupported {}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java
index 35e7316..4c749f81 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java
@@ -4,5 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** Associated interface is not supported yet. */
+@NullMarked
 public class AssociatedInterfaceRequestNotSupported {}
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
index 9bdc0ec..cce4dcfc 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/AutoCloseableRouter.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MessagePipeHandle;
 
@@ -14,6 +16,7 @@
  * connection without a try-with-resources statement. If the callsite isn't using try-with-resources
  * mechanism, it needs to call close() explicitly.
  */
+@NullMarked
 class AutoCloseableRouter implements Router {
     /** The underlying router. */
     private final Router mRouter;
@@ -31,7 +34,7 @@
     private boolean mClosed;
 
     /** Constructor. */
-    public AutoCloseableRouter(Core core, Router router) {
+    public AutoCloseableRouter(@Nullable Core core, Router router) {
         mRouter = router;
         mExecutor = ExecutorFactory.getExecutorForCurrentThread(core);
         mAllocationException = new Exception("AutocloseableRouter allocated at:");
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
index 522334d..24304bb3 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/BindingsHelper.java
@@ -4,10 +4,13 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.Watcher;
 
 /** Helper functions. */
+@NullMarked
 public class BindingsHelper {
     /** Alignment in bytes for mojo serialization. */
     public static final int ALIGNMENT = 8;
@@ -160,11 +163,16 @@
     /**
      * Returns an {@link AsyncWaiter} to use with the given handle, or |null| if none if available.
      */
-    static Watcher getWatcherForHandle(Handle handle) {
+    static @Nullable Watcher getWatcherForHandle(Handle handle) {
         if (handle.getCore() != null) {
             return handle.getCore().getWatcher();
         } else {
             return null;
         }
     }
+
+    static Watcher getWatcherForHandleNonNull(Handle handle) {
+        assert handle.getCore() != null;
+        return handle.getCore().getWatcher();
+    }
 }
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
index f3780e6..3cf40930 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ConnectionErrorHandler.java
@@ -4,12 +4,14 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.MojoException;
 
 /**
  * A {@link ConnectionErrorHandler} is notified of an error happening while using the bindings over
  * message pipes.
  */
+@NullMarked
 public interface ConnectionErrorHandler {
     public void onConnectionError(MojoException e);
 }
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
index bf71130..7bf5325 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Connector.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
@@ -22,6 +24,7 @@
  * The method |start| must be called before the {@link Connector} will start listening to incoming
  * messages.
  */
+@NullMarked
 public class Connector implements MessageReceiver, HandleOwner<MessagePipeHandle> {
 
     /** The callback that is notified when the state of the owned handle changes. */
@@ -34,17 +37,17 @@
     private final Watcher mWatcher;
 
     /** The {@link MessageReceiver} to which received messages are sent. */
-    private MessageReceiver mIncomingMessageReceiver;
+    private @Nullable MessageReceiver mIncomingMessageReceiver;
 
     /** The error handler to notify of errors. */
-    private ConnectionErrorHandler mErrorHandler;
+    private @Nullable ConnectionErrorHandler mErrorHandler;
 
     /**
      * Create a new connector over a |messagePipeHandle|. The created connector will use the default
      * {@link AsyncWaiter} from the {@link Core} implementation of |messagePipeHandle|.
      */
     public Connector(MessagePipeHandle messagePipeHandle) {
-        this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle));
+        this(messagePipeHandle, BindingsHelper.getWatcherForHandleNonNull(messagePipeHandle));
     }
 
     /**
@@ -180,7 +183,7 @@
      *            be <code>null</code>, in which case the message is discarded.
      */
     static ResultAnd<Boolean> readAndDispatchMessage(
-            MessagePipeHandle handle, MessageReceiver receiver) {
+            MessagePipeHandle handle, @Nullable MessageReceiver receiver) {
         ResultAnd<ReadMessageResult> result = handle.readMessage(MessagePipeHandle.ReadFlags.NONE);
         if (result.getMojoResult() != MojoResult.OK) {
             return new ResultAnd<Boolean>(result.getMojoResult(), false);
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java
index f074922cd5..92cf465 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DataHeader.java
@@ -4,7 +4,10 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** The header for a mojo complex element. */
+@NullMarked
 public final class DataHeader {
     /** The size of a serialized header, in bytes. */
     public static final int HEADER_SIZE = 8;
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
index 90b1290..004e41f 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Decoder.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.bindings.Interface.Proxy;
 import org.chromium.mojo.system.DataPipe;
 import org.chromium.mojo.system.Handle;
@@ -20,6 +22,7 @@
  * A Decoder is a helper class for deserializing a mojo struct. It enables deserialization of basic
  * types from a {@link Message} object at a given offset into it's byte buffer.
  */
+@NullMarked
 public class Decoder {
 
     /** Helper class to validate the decoded message. */
@@ -266,7 +269,7 @@
      * Deserializes a pointer at the given offset. Returns a Decoder suitable to decode the content
      * of the pointer.
      */
-    public Decoder readPointer(int offset, boolean nullable) {
+    public @Nullable Decoder readPointer(int offset, boolean nullable) {
         int basePosition = mBaseOffset + offset;
         long pointerOffset = readLong(offset);
         if (pointerOffset == 0) {
@@ -282,7 +285,7 @@
     }
 
     /** Deserializes an array of boolean at the given offset. */
-    public boolean[] readBooleans(int offset, int arrayNullability, int expectedLength) {
+    public boolean @Nullable [] readBooleans(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -304,7 +307,8 @@
     }
 
     /** Deserializes an array of Booleans at the given offset. */
-    public Boolean[] readBooleanNullables(int offset, int arrayNullability, int expectedLength) {
+    public @Nullable Boolean @Nullable [] readBooleanNullables(
+            int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -315,7 +319,7 @@
         boolean[] hasValueBitfield = readBitfield(1, si.elementsOrVersion, d.mMessage.getData());
         boolean[] values = readBitfield(1, si.elementsOrVersion, d.mMessage.getData());
 
-        Boolean[] result = new Boolean[si.elementsOrVersion];
+        @Nullable Boolean[] result = new Boolean[si.elementsOrVersion];
         for (int i = 0; i < si.elementsOrVersion; ++i) {
             if (hasValueBitfield[i]) {
                 result[i] = values[i];
@@ -327,7 +331,7 @@
     }
 
     /** Deserializes an array of bytes at the given offset. */
-    public byte[] readBytes(int offset, int arrayNullability, int expectedLength) {
+    public byte @Nullable [] readBytes(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -340,7 +344,8 @@
     }
 
     /** Deserializes an array of Bytes at the given offset. */
-    public Byte[] readByteNullables(int offset, int arrayNullability, int expectedLength) {
+    public @Nullable Byte @Nullable [] readByteNullables(
+            int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -352,7 +357,7 @@
         byte[] values = new byte[si.elementsOrVersion];
         d.mMessage.getData().get(values);
 
-        Byte[] result = new Byte[si.elementsOrVersion];
+        @Nullable Byte[] result = new Byte[si.elementsOrVersion];
         for (int i = 0; i < si.elementsOrVersion; ++i) {
             if (hasValueBitfield[i]) {
                 result[i] = values[i];
@@ -364,7 +369,7 @@
     }
 
     /** Deserializes an array of shorts at the given offset. */
-    public short[] readShorts(int offset, int arrayNullability, int expectedLength) {
+    public short @Nullable [] readShorts(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -377,7 +382,8 @@
     }
 
     /** Deserializes an array of Shorts at the given offset. */
-    public Short[] readShortNullables(int offset, int arrayNullability, int expectedLength) {
+    public @Nullable Short @Nullable [] readShortNullables(
+            int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -389,7 +395,7 @@
         short[] values = new short[si.elementsOrVersion];
         d.mMessage.getData().asShortBuffer().get(values);
 
-        Short[] result = new Short[si.elementsOrVersion];
+        @Nullable Short[] result = new Short[si.elementsOrVersion];
         for (int i = 0; i < si.elementsOrVersion; ++i) {
             if (hasValueBitfield[i]) {
                 result[i] = values[i];
@@ -401,7 +407,7 @@
     }
 
     /** Deserializes an array of ints at the given offset. */
-    public int[] readInts(int offset, int arrayNullability, int expectedLength) {
+    public int @Nullable [] readInts(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -414,7 +420,8 @@
     }
 
     /** Deserializes an array of Integers at the given offset. */
-    public Integer[] readIntNullables(int offset, int arrayNullability, int expectedLength) {
+    public @Nullable Integer @Nullable [] readIntNullables(
+            int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -426,7 +433,7 @@
         int[] values = new int[si.elementsOrVersion];
         d.mMessage.getData().asIntBuffer().get(values);
 
-        Integer[] result = new Integer[si.elementsOrVersion];
+        @Nullable Integer[] result = new Integer[si.elementsOrVersion];
         for (int i = 0; i < si.elementsOrVersion; ++i) {
             if (hasValueBitfield[i]) {
                 result[i] = values[i];
@@ -438,7 +445,7 @@
     }
 
     /** Deserializes an array of floats at the given offset. */
-    public float[] readFloats(int offset, int arrayNullability, int expectedLength) {
+    public float @Nullable [] readFloats(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -451,7 +458,8 @@
     }
 
     /** Deserializes an array of Integers at the given offset. */
-    public Float[] readFloatNullables(int offset, int arrayNullability, int expectedLength) {
+    public @Nullable Float @Nullable [] readFloatNullables(
+            int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -463,7 +471,7 @@
         float[] values = new float[si.elementsOrVersion];
         d.mMessage.getData().asFloatBuffer().get(values);
 
-        Float[] result = new Float[si.elementsOrVersion];
+        @Nullable Float[] result = new Float[si.elementsOrVersion];
         for (int i = 0; i < si.elementsOrVersion; ++i) {
             if (hasValueBitfield[i]) {
                 result[i] = values[i];
@@ -475,7 +483,7 @@
     }
 
     /** Deserializes an array of longs at the given offset. */
-    public long[] readLongs(int offset, int arrayNullability, int expectedLength) {
+    public long @Nullable [] readLongs(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -488,7 +496,8 @@
     }
 
     /** Deserializes an array of Longs at the given offset. */
-    public Long[] readLongNullables(int offset, int arrayNullability, int expectedLength) {
+    public @Nullable Long @Nullable [] readLongNullables(
+            int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -500,7 +509,7 @@
         long[] values = new long[si.elementsOrVersion];
         d.mMessage.getData().asLongBuffer().get(values);
 
-        Long[] result = new Long[si.elementsOrVersion];
+        @Nullable Long[] result = new Long[si.elementsOrVersion];
         for (int i = 0; i < si.elementsOrVersion; ++i) {
             if (hasValueBitfield[i]) {
                 result[i] = values[i];
@@ -512,7 +521,7 @@
     }
 
     /** Deserializes an array of doubles at the given offset. */
-    public double[] readDoubles(int offset, int arrayNullability, int expectedLength) {
+    public double @Nullable [] readDoubles(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -525,7 +534,8 @@
     }
 
     /** Deserializes an array of Doubles at the given offset. */
-    public Double[] readDoubleNullables(int offset, int arrayNullability, int expectedLength) {
+    public @Nullable Double @Nullable [] readDoubleNullables(
+            int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -536,7 +546,7 @@
         boolean[] hasValueBitfield = readBitfield(8, si.elementsOrVersion, d.mMessage.getData());
         double[] values = new double[si.elementsOrVersion];
         d.mMessage.getData().asDoubleBuffer().get(values);
-        Double[] result = new Double[si.elementsOrVersion];
+        @Nullable Double[] result = new Double[si.elementsOrVersion];
         for (int i = 0; i < si.elementsOrVersion; ++i) {
             if (hasValueBitfield[i]) {
                 result[i] = values[i];
@@ -591,7 +601,7 @@
      *
      * @return a proxy to the service.
      */
-    public <P extends Proxy> P readServiceInterface(
+    public <P extends Proxy> @Nullable P readServiceInterface(
             int offset, boolean nullable, Interface.Manager<?, P> manager) {
         MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
         if (!handle.isValid()) {
@@ -602,7 +612,7 @@
     }
 
     /** Deserializes a |InterfaceRequest| at the given offset. */
-    public <I extends Interface> InterfaceRequest<I> readInterfaceRequest(
+    public <I extends Interface> @Nullable InterfaceRequest<I> readInterfaceRequest(
             int offset, boolean nullable) {
         MessagePipeHandle handle = readMessagePipeHandle(offset, nullable);
         if (handle == null) {
@@ -612,19 +622,19 @@
     }
 
     /** Deserializes an associated interface at the given offset. Not yet supported. */
-    public AssociatedInterfaceNotSupported readAssociatedServiceInterfaceNotSupported(
+    public @Nullable AssociatedInterfaceNotSupported readAssociatedServiceInterfaceNotSupported(
             int offset, boolean nullable) {
         return null;
     }
 
     /** Deserializes an associated interface request at the given offset. Not yet supported. */
-    public AssociatedInterfaceRequestNotSupported readAssociatedInterfaceRequestNotSupported(
-            int offset, boolean nullable) {
+    public @Nullable AssociatedInterfaceRequestNotSupported
+            readAssociatedInterfaceRequestNotSupported(int offset, boolean nullable) {
         return null;
     }
 
     /** Deserializes a string at the given offset. */
-    public String readString(int offset, boolean nullable) {
+    public @Nullable String readString(int offset, boolean nullable) {
         final int arrayNullability = nullable ? BindingsHelper.ARRAY_NULLABLE : 0;
         byte[] bytes = readBytes(offset, arrayNullability, BindingsHelper.UNSPECIFIED_ARRAY_LENGTH);
         if (bytes == null) {
@@ -634,7 +644,7 @@
     }
 
     /** Deserializes an array of |Handle| at the given offset. */
-    public Handle[] readHandles(int offset, int arrayNullability, int expectedLength) {
+    public Handle @Nullable [] readHandles(int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
             return null;
@@ -651,7 +661,7 @@
     }
 
     /** Deserializes an array of |UntypedHandle| at the given offset. */
-    public UntypedHandle[] readUntypedHandles(
+    public UntypedHandle @Nullable [] readUntypedHandles(
             int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
@@ -669,7 +679,7 @@
     }
 
     /** Deserializes an array of |ConsumerHandle| at the given offset. */
-    public DataPipe.ConsumerHandle[] readConsumerHandles(
+    public DataPipe.ConsumerHandle @Nullable [] readConsumerHandles(
             int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
@@ -687,7 +697,7 @@
     }
 
     /** Deserializes an array of |ProducerHandle| at the given offset. */
-    public DataPipe.ProducerHandle[] readProducerHandles(
+    public DataPipe.ProducerHandle @Nullable [] readProducerHandles(
             int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
@@ -705,7 +715,7 @@
     }
 
     /** Deserializes an array of |MessagePipeHandle| at the given offset. */
-    public MessagePipeHandle[] readMessagePipeHandles(
+    public MessagePipeHandle @Nullable [] readMessagePipeHandles(
             int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
@@ -723,7 +733,7 @@
     }
 
     /** Deserializes an array of |SharedBufferHandle| at the given offset. */
-    public SharedBufferHandle[] readSharedBufferHandles(
+    public SharedBufferHandle @Nullable [] readSharedBufferHandles(
             int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
@@ -741,7 +751,7 @@
     }
 
     /** Deserializes an array of |ServiceHandle| at the given offset. */
-    public <S extends Interface, P extends Proxy> S[] readServiceInterfaces(
+    public <S extends Interface, P extends Proxy> @Nullable S @Nullable [] readServiceInterfaces(
             int offset, int arrayNullability, int expectedLength, Interface.Manager<S, P> manager) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
@@ -750,7 +760,7 @@
         DataHeader si =
                 d.readDataHeaderForArray(
                         BindingsHelper.SERIALIZED_INTERFACE_SIZE, expectedLength, false);
-        S[] result = manager.buildArray(si.elementsOrVersion);
+        @Nullable S[] result = manager.buildArray(si.elementsOrVersion);
         for (int i = 0; i < result.length; ++i) {
             // This cast is necessary because java 6 doesn't handle wildcard correctly when using
             // Manager<S, ? extends S>
@@ -768,7 +778,7 @@
     }
 
     /** Deserializes an array of |InterfaceRequest| at the given offset. */
-    public <I extends Interface> InterfaceRequest<I>[] readInterfaceRequests(
+    public <I extends Interface> @Nullable InterfaceRequest<I> @Nullable [] readInterfaceRequests(
             int offset, int arrayNullability, int expectedLength) {
         Decoder d = readPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
         if (d == null) {
@@ -776,6 +786,7 @@
         }
         DataHeader si = d.readDataHeaderForArray(4, expectedLength, false);
         @SuppressWarnings("unchecked")
+        @Nullable
         InterfaceRequest<I>[] result = new InterfaceRequest[si.elementsOrVersion];
         for (int i = 0; i < result.length; ++i) {
             result[i] =
@@ -787,7 +798,7 @@
     }
 
     /** Deserializes an array of associated interfaces at the given offset. Not yet supported. */
-    public AssociatedInterfaceNotSupported[] readAssociatedServiceInterfaceNotSupporteds(
+    public AssociatedInterfaceNotSupported @Nullable [] readAssociatedServiceInterfaceNotSupporteds(
             int offset, int arrayNullability, int expectedLength) {
         return null;
     }
@@ -796,8 +807,9 @@
      * Deserializes an array of associated interface requests at the given offset. Not yet
      * supported.
      */
-    public AssociatedInterfaceRequestNotSupported[] readAssociatedInterfaceRequestNotSupporteds(
-            int offset, int arrayNullability, int expectedLength) {
+    public AssociatedInterfaceRequestNotSupported @Nullable []
+            readAssociatedInterfaceRequestNotSupporteds(
+                    int offset, int arrayNullability, int expectedLength) {
         return null;
     }
 
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
index 4fbbf71..5591534 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DelegatingConnectionErrorHandler.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.MojoException;
 
 import java.util.Collections;
@@ -14,6 +15,7 @@
  * A {@link ConnectionErrorHandler} that delegate the errors to a list of registered handlers. This
  * class will use weak pointers to prevent keeping references to any handlers it delegates to.
  */
+@NullMarked
 public class DelegatingConnectionErrorHandler implements ConnectionErrorHandler {
 
     /**
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
index 8a22be9..b687178 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/DeserializationException.java
@@ -4,7 +4,10 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** Error when deserializing a mojo message. */
+@NullMarked
 public class DeserializationException extends RuntimeException {
 
     /** Constructs a new deserialization exception with the specified detail message. */
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
index bdd85a2..d805565 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Encoder.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.bindings.Interface.Proxy.Handler;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.Handle;
@@ -20,6 +22,7 @@
  * Helper class to encode a mojo struct. It keeps track of the output buffer, resizing it as needed.
  * It also keeps track of the associated handles, and the offset of the current data section.
  */
+@NullMarked
 public class Encoder {
 
     /**
@@ -29,7 +32,7 @@
     private static class EncoderState {
 
         /** The core used to encode interfaces. */
-        public final Core core;
+        public final @Nullable Core core;
 
         /** The ByteBuffer to which the message will be encoded. */
         public ByteBuffer byteBuffer;
@@ -46,7 +49,7 @@
          * @param bufferSize A hint on the size of the message. Used to build the initial byte
          *            buffer.
          */
-        private EncoderState(Core core, int bufferSize) {
+        private EncoderState(@Nullable Core core, int bufferSize) {
             assert bufferSize % BindingsHelper.ALIGNMENT == 0;
             this.core = core;
             byteBuffer =
@@ -102,7 +105,7 @@
      *            structure being encoded contains interfaces, can be |null| otherwise.
      * @param sizeHint A hint on the size of the message. Used to build the initial byte buffer.
      */
-    public Encoder(Core core, int sizeHint) {
+    public Encoder(@Nullable Core core, int sizeHint) {
         this(new EncoderState(core, sizeHint));
     }
 
@@ -169,7 +172,7 @@
     }
 
     /** Encode a {@link Struct} at the given offset. */
-    public void encode(Struct v, int offset, boolean nullable) {
+    public void encode(@Nullable Struct v, int offset, boolean nullable) {
         if (v == null) {
             encodeNullPointer(offset, nullable);
             return;
@@ -179,7 +182,7 @@
     }
 
     /** Encode a {@link Union} at the given offset. */
-    public void encode(Union v, int offset, boolean nullable) {
+    public void encode(@Nullable Union v, int offset, boolean nullable) {
         if (v == null && !nullable) {
             throw new SerializationException(
                     "Trying to encode a null pointer for a non-nullable type.");
@@ -193,7 +196,7 @@
     }
 
     /** Encodes a String. */
-    public void encode(String v, int offset, boolean nullable) {
+    public void encode(@Nullable String v, int offset, boolean nullable) {
         if (v == null) {
             encodeNullPointer(offset, nullable);
             return;
@@ -219,7 +222,7 @@
 
     /** Encode an {@link Interface}. */
     public <T extends Interface> void encode(
-            T v, int offset, boolean nullable, Interface.Manager<T, ?> manager) {
+            @Nullable T v, int offset, boolean nullable, Interface.Manager<T, ?> manager) {
         if (v == null) {
             encodeInvalidHandle(offset, nullable);
             encode(0, offset + BindingsHelper.SERIALIZED_HANDLE_SIZE);
@@ -358,7 +361,7 @@
     }
 
     /** Encodes an array of bytes. */
-    public void encode(byte[] v, int offset, int arrayNullability, int expectedLength) {
+    public void encode(byte @Nullable [] v, int offset, int arrayNullability, int expectedLength) {
         if (v == null) {
             encodeNullPointer(offset, BindingsHelper.isArrayNullable(arrayNullability));
             return;
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
index c40b5cc..1c5213a 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExceptionHandler.java
@@ -4,10 +4,14 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
 /**
  * An {@link ExceptionHandler} is notified of any {@link RuntimeException} happening in the
  * bindings or any of the callbacks.
  */
+@NullMarked
 public interface ExceptionHandler {
     /**
      * Receives a notification that an unhandled {@link RuntimeException} has been thrown in an
@@ -23,7 +27,7 @@
      * also delegate the handling of the exceptions to another instance of ExceptionHandler.
      */
     public static class DefaultExceptionHandler implements ExceptionHandler {
-        private ExceptionHandler mDelegate;
+        private @Nullable ExceptionHandler mDelegate;
 
         @Override
         public boolean handleException(RuntimeException e) {
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
index c8b6e8f..693800bd 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ExecutorFactory.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.MessagePipeHandle.ReadMessageResult;
@@ -23,12 +25,13 @@
  * A factory which provides per-thread executors, which enable execution on the thread from which
  * they were obtained.
  */
+@NullMarked
 public class ExecutorFactory {
     /**
      * A null buffer which is used to send messages without any data on the PipedExecutor's
      * signaling handles.
      */
-    private static final ByteBuffer NOTIFY_BUFFER = null;
+    private static final @Nullable ByteBuffer NOTIFY_BUFFER = null;
 
     /**
      * Implementation of the executor which uses a pair of {@link MessagePipeHandle} for signaling.
@@ -143,9 +146,10 @@
     private static final ThreadLocal<Executor> EXECUTORS = new ThreadLocal<Executor>();
 
     /** Returns an {@link Executor} that will run all of its actions in the current thread. */
-    public static Executor getExecutorForCurrentThread(Core core) {
+    public static Executor getExecutorForCurrentThread(@Nullable Core core) {
         Executor executor = EXECUTORS.get();
         if (executor == null) {
+            assert core != null;
             executor = new PipedExecutor(core);
             EXECUTORS.set(executor);
         }
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
index bc0cc39..d0cf790 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/HandleOwner.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.Handle;
 
 import java.io.Closeable;
@@ -13,6 +14,7 @@
  *
  * @param <H> The type of the owned handle.
  */
+@NullMarked
 public interface HandleOwner<H extends Handle> extends Closeable {
 
     /** Pass the handle owned by this class. */
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
index 51ab799..797695dd 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Interface.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.bindings.Interface.AbstractProxy.HandlerImpl;
 import org.chromium.mojo.bindings.interfacecontrol.QueryVersion;
 import org.chromium.mojo.bindings.interfacecontrol.RequireVersion;
@@ -22,6 +24,7 @@
 import java.util.concurrent.Executor;
 
 /** Base class for mojo generated interfaces. */
+@NullMarked
 public interface Interface extends ConnectionErrorHandler, Closeable {
 
     /**
@@ -94,7 +97,7 @@
             private final MessageReceiverWithResponder mMessageReceiver;
 
             /** The {@link ConnectionErrorHandler} that will be notified of errors. */
-            private ConnectionErrorHandler mErrorHandler;
+            private @Nullable ConnectionErrorHandler mErrorHandler;
 
             /** The currently known version of the interface. */
             private int mVersion;
@@ -204,8 +207,9 @@
                 mVersion = version;
                 RunOrClosePipeMessageParams message = new RunOrClosePipeMessageParams();
                 message.input = new RunOrClosePipeInput();
-                message.input.setRequireVersion(new RequireVersion());
-                message.input.getRequireVersion().version = version;
+                RequireVersion requireVersion = new RequireVersion();
+                requireVersion.version = version;
+                message.input.setRequireVersion(requireVersion);
                 InterfaceControlMessagesHelper.sendRunOrClosePipeMessage(
                         getCore(), mMessageReceiver, message);
             }
@@ -445,13 +449,13 @@
         }
 
         /** Binds the implementation to the given |router|. */
-        final void bind(Core core, I impl, Router router) {
+        final void bind(@Nullable Core core, I impl, Router router) {
             router.setErrorHandler(impl);
             router.setIncomingMessageReceiver(buildStub(core, impl));
         }
 
         /** Returns a Proxy that will send messages to the given |router|. */
-        final P attachProxy(Core core, Router router) {
+        final P attachProxy(@Nullable Core core, Router router) {
             return buildProxy(core, new AutoCloseableRouter(core, router));
         }
 
@@ -459,9 +463,10 @@
         protected abstract I[] buildArray(int size);
 
         /** Constructs a Stub delegating to the given implementation. */
-        protected abstract Stub<I> buildStub(Core core, I impl);
+        protected abstract Stub<I> buildStub(@Nullable Core core, I impl);
 
         /** Constructs a Proxy forwarding the calls to the given message receiver. */
-        protected abstract P buildProxy(Core core, MessageReceiverWithResponder messageReceiver);
+        protected abstract P buildProxy(
+                @Nullable Core core, MessageReceiverWithResponder messageReceiver);
     }
 }
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
index 33882dd..6a2fb2f 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceControlMessagesHelper.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.bindings.Interface.Manager;
 import org.chromium.mojo.bindings.Interface.Proxy;
 import org.chromium.mojo.bindings.interfacecontrol.InterfaceControlMessagesConstants;
@@ -20,6 +21,7 @@
  * Helper class to handle interface control messages. See
  * mojo/public/interfaces/bindings/interface_control_messages.mojom.
  */
+@NullMarked
 public class InterfaceControlMessagesHelper {
     /**
      * Callback interface for the async response to {@link
@@ -81,6 +83,7 @@
     }
 
     /** Handles a received run message. */
+    @SuppressWarnings("NullAway") // Thinks response.output is @NonNull.
     public static <I extends Interface, P extends Proxy> boolean handleRun(
             Core core, Manager<I, P> manager, ServiceMessage message, MessageReceiver responder) {
         Message payload = message.getPayload();
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
index a1160eb2..44906ae 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/InterfaceRequest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.MessagePipeHandle;
 
 /**
@@ -15,6 +16,7 @@
  *
  * @param <P> the type of the remote interface proxy.
  */
+@NullMarked
 public class InterfaceRequest<P extends Interface> implements HandleOwner<MessagePipeHandle> {
 
     /** The handle which will be sent and will be connected to the implementation. */
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
index 3695366b..f064bce 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Message.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.MessagePipeHandle;
 
@@ -14,16 +16,17 @@
  * A raw message to be sent/received from a {@link MessagePipeHandle}. Note that this can contain
  * any data, not necessarily a Mojo message with a proper header. See also {@link ServiceMessage}.
  */
+@NullMarked
 public class Message {
 
     /** The data of the message. */
     private final ByteBuffer mBuffer;
 
     /** The handles of the message. */
-    private final List<? extends Handle> mHandles;
+    private final @Nullable List<? extends Handle> mHandles;
 
     /** This message interpreted as a message for a mojo service with an appropriate header. */
-    private ServiceMessage mWithHeader;
+    private @Nullable ServiceMessage mWithHeader;
 
     /**
      * Constructor.
@@ -31,7 +34,7 @@
      * @param buffer The buffer containing the bytes to send. This must be a direct buffer.
      * @param handles The list of handles to send.
      */
-    public Message(ByteBuffer buffer, List<? extends Handle> handles) {
+    public Message(ByteBuffer buffer, @Nullable List<? extends Handle> handles) {
         mBuffer = buffer;
         mHandles = handles;
     }
@@ -43,6 +46,7 @@
 
     /** The handles of the message. */
     public List<? extends Handle> getHandles() {
+        assert mHandles != null;
         return mHandles;
     }
 
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
index 654b32e7..d58135b 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageHeader.java
@@ -4,9 +4,12 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.nio.ByteBuffer;
 
 /** Header information for a message. */
+@NullMarked
 public class MessageHeader {
 
     private static final int SIMPLE_MESSAGE_SIZE = 24;
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
index 7a9c04d2..46044a0 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiver.java
@@ -4,9 +4,12 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.io.Closeable;
 
 /** A class which implements this interface can receive {@link Message} objects. */
+@NullMarked
 public interface MessageReceiver extends Closeable {
 
     /**
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
index 8272a97..4a98d710 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/MessageReceiverWithResponder.java
@@ -4,10 +4,13 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 /**
  * A {@link MessageReceiver} that can also handle the handle the response message generated from the
  * given message.
  */
+@NullMarked
 public interface MessageReceiverWithResponder extends MessageReceiver {
 
     /**
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
index d02ad39..cc2f0d5d 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Router.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.MessagePipeHandle;
 
 /**
@@ -11,6 +12,7 @@
  * parsing of headers and adding of request ids in order to be able to match a response to a
  * request.
  */
+@NullMarked
 public interface Router extends MessageReceiverWithResponder, HandleOwner<MessagePipeHandle> {
 
     /** Start listening for incoming messages. */
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
index 8eb7f88d..88161fae 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/RouterImpl.java
@@ -6,6 +6,8 @@
 
 import android.annotation.SuppressLint;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.Watcher;
@@ -15,6 +17,7 @@
 import java.util.concurrent.Executor;
 
 /** Implementation of {@link Router}. */
+@NullMarked
 @SuppressLint("UseSparseArrays") // https://crbug.com/600699
 public class RouterImpl implements Router {
 
@@ -83,7 +86,7 @@
      * The {@link MessageReceiverWithResponder} that will consume the messages received from the
      * pipe.
      */
-    private MessageReceiverWithResponder mIncomingMessageReceiver;
+    private @Nullable MessageReceiverWithResponder mIncomingMessageReceiver;
 
     /** The next id to use for a request id which needs a response. It is auto-incremented. */
     private long mNextRequestId = 1;
@@ -96,7 +99,7 @@
      * this Router is bound. This may be {@code Null} if the MessagePipeHandle passed
      * in to the constructor is not valid.
      */
-    private final Executor mExecutor;
+    private final @Nullable Executor mExecutor;
 
     /**
      * Constructor that will use the default {@link Watcher}.
@@ -104,7 +107,7 @@
      * @param messagePipeHandle The {@link MessagePipeHandle} to route message for.
      */
     public RouterImpl(MessagePipeHandle messagePipeHandle) {
-        this(messagePipeHandle, BindingsHelper.getWatcherForHandle(messagePipeHandle));
+        this(messagePipeHandle, BindingsHelper.getWatcherForHandleNonNull(messagePipeHandle));
     }
 
     /**
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
index e2508ffdf..adb36b9 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SerializationException.java
@@ -4,7 +4,10 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** Error that can be thrown when serializing a mojo message. */
+@NullMarked
 public class SerializationException extends RuntimeException {
 
     /** Constructs a new serialization exception with the specified detail message. */
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
index 6179e34b..44eb70c 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/ServiceMessage.java
@@ -4,6 +4,9 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -11,10 +14,11 @@
  * Represents a {@link Message} which contains a {@link MessageHeader}. Deals with parsing the
  * {@link MessageHeader} for a message.
  */
+@NullMarked
 public class ServiceMessage extends Message {
 
     private final MessageHeader mHeader;
-    private Message mPayload;
+    private @Nullable Message mPayload;
 
     /**
      * Reinterpret the given |message| as a message with the given |header|. The |message| must
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
index 83b08a06..531545a 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/SideEffectFreeCloseable.java
@@ -4,9 +4,12 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.io.Closeable;
 
 /** An implementation of closeable that doesn't do anything. */
+@NullMarked
 public class SideEffectFreeCloseable implements Closeable {
 
     /**
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
index e4ed95a..f6b0629 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Struct.java
@@ -4,11 +4,14 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core;
 
 import java.nio.ByteBuffer;
 
 /** Base class for all mojo structs. */
+@NullMarked
 public abstract class Struct {
     /** The base size of the encoded struct. */
     private final int mEncodedBaseSize;
@@ -36,7 +39,7 @@
      * @param core the |Core| implementation used to generate handles. Only used if the data
      *            structure being encoded contains interfaces, can be |null| otherwise.
      */
-    public Message serialize(Core core) {
+    public Message serialize(@Nullable Core core) {
         Encoder encoder = new Encoder(core, mEncodedBaseSize);
         encode(encoder);
         return encoder.getMessage();
diff --git a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java
index f01338f..a3caa77 100644
--- a/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java
+++ b/mojo/public/java/bindings/src/org/chromium/mojo/bindings/Union.java
@@ -4,9 +4,11 @@
 
 package org.chromium.mojo.bindings;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.Core;
 
 /** Base class for all mojo unions. */
+@NullMarked
 public abstract class Union {
     /** They type of object that has been set. */
     protected int mTag;
diff --git a/mojo/public/java/system/javatests/src/org/chromium/mojo/HandleMock.java b/mojo/public/java/system/javatests/src/org/chromium/mojo/HandleMock.java
index 5d173b1..28e46c4 100644
--- a/mojo/public/java/system/javatests/src/org/chromium/mojo/HandleMock.java
+++ b/mojo/public/java/system/javatests/src/org/chromium/mojo/HandleMock.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo;
 
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.Core.HandleSignalsState;
 import org.chromium.mojo.system.DataPipe;
@@ -149,7 +150,10 @@
      *      MessagePipeHandle.WriteFlags)
      */
     @Override
-    public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) {
+    public void writeMessage(
+            @Nullable ByteBuffer bytes,
+            @Nullable List<? extends Handle> handles,
+            WriteFlags flags) {
         // Do nothing.
     }
 
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
index a51298b..0f3e1b4 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Core.java
@@ -6,10 +6,14 @@
 
 import android.os.ParcelFileDescriptor;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
 /**
  * Core mojo interface giving access to the base operations. See |src/mojo/public/c/system/core.h|
  * for the underlying api.
  */
+@NullMarked
 public interface Core {
 
     /** Used to indicate an infinite deadline (timeout). */
@@ -117,7 +121,7 @@
      * @return the set of handles for the two endpoints (ports) of the message pipe.
      */
     public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe(
-            MessagePipeHandle.CreateOptions options);
+            MessagePipeHandle.@Nullable CreateOptions options);
 
     /**
      * Creates a data pipe, which is a unidirectional communication channel for unframed data, with
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
index 5dc391f..2230e24 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/DataPipe.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.nio.ByteBuffer;
 
 /**
@@ -11,6 +13,7 @@
  * data. Data is unframed, but must come as (multiples of) discrete elements, of the size given at
  * creation time.
  */
+@NullMarked
 public interface DataPipe {
 
     /** Flags for the data pipe creation operation. */
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
index e619c4732..b2f6f5e7 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Flags.java
@@ -4,11 +4,14 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 /**
  * Base class for bit field used as flags.
  *
  * @param <F> the type of the flags.
  */
+@NullMarked
 public abstract class Flags<F extends Flags<F>> {
     private int mFlags;
     private boolean mImmutable;
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
index 96577e3..13e294a 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Handle.java
@@ -4,11 +4,14 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core.HandleSignalsState;
 
 import java.io.Closeable;
 
 /** A generic mojo handle. */
+@NullMarked
 public interface Handle extends Closeable {
 
     /**
@@ -40,7 +43,7 @@
      * Returns the {@link Core} implementation for this handle. Can be null if this handle is
      * invalid.
      */
-    public Core getCore();
+    public @Nullable Core getCore();
 
     /**
      * Passes ownership of the handle from this handle to the newly created Handle object,
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
index 4d3e13b8..90be10b 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/InvalidHandle.java
@@ -4,6 +4,8 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core.HandleSignalsState;
 import org.chromium.mojo.system.DataPipe.ConsumerHandle;
 import org.chromium.mojo.system.DataPipe.ProducerHandle;
@@ -12,6 +14,7 @@
 import java.util.List;
 
 /** A handle that will always be invalid. */
+@NullMarked
 public class InvalidHandle
         implements UntypedHandle,
                 MessagePipeHandle,
@@ -53,7 +56,7 @@
      * @see Handle#getCore()
      */
     @Override
-    public Core getCore() {
+    public @Nullable Core getCore() {
         return null;
     }
 
@@ -198,7 +201,10 @@
      *      MessagePipeHandle.WriteFlags)
      */
     @Override
-    public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) {
+    public void writeMessage(
+            @Nullable ByteBuffer bytes,
+            @Nullable List<? extends Handle> handles,
+            WriteFlags flags) {
         throw new MojoException(MojoResult.INVALID_ARGUMENT);
     }
 
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
index e85552fa..5271408 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MessagePipeHandle.java
@@ -4,6 +4,9 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
 import java.nio.ByteBuffer;
 import java.util.List;
 
@@ -11,6 +14,7 @@
  * Message pipes are bidirectional communication channel for framed data (i.e., messages). Messages
  * can contain plain data and/or Mojo handles.
  */
+@NullMarked
 public interface MessagePipeHandle extends Handle {
 
     /** Flags for the message pipe creation operation. */
@@ -100,13 +104,13 @@
     /** Result of the |readMessage| method. */
     public static class ReadMessageResult {
         /** If a message was read, this contains the bytes of its data. */
-        public byte[] mData;
+        public byte @Nullable [] mData;
 
         /** If a message was read, this contains the raw handle values. */
-        public long[] mRawHandles;
+        public long @Nullable [] mRawHandles;
 
         /** If a message was read, the handles contained in the message, undefined otherwise. */
-        public List<UntypedHandle> mHandles;
+        public @Nullable List<UntypedHandle> mHandles;
     }
 
     /**
@@ -125,7 +129,8 @@
      * receive equivalent, but logically different, handles). Handles to be sent should not be in
      * simultaneous use (e.g., on another thread).
      */
-    void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags);
+    void writeMessage(
+            @Nullable ByteBuffer bytes, @Nullable List<? extends Handle> handles, WriteFlags flags);
 
     /**
      * Reads a message from the message pipe endpoint; also usable to query the size of the next
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
index 2dbfaf55db..642fc855 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoException.java
@@ -4,7 +4,10 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** Exception for the core mojo API. */
+@NullMarked
 public class MojoException extends RuntimeException {
 
     private final int mCode;
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
index 3298504d..4ab42b1 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/MojoResult.java
@@ -4,7 +4,10 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** The different mojo result codes. */
+@NullMarked
 public final class MojoResult {
     public static final int OK = 0;
     public static final int CANCELLED = 1;
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
index 625f8bf..3d329d4 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Pair.java
@@ -4,12 +4,15 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 /**
  * A pair of object.
  *
  * @param <F> Type of the first element.
  * @param <S> Type of the second element.
  */
+@NullMarked
 public class Pair<F, S> {
 
     public final F first;
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java
index c2f2fec..3fcae326 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/ResultAnd.java
@@ -4,11 +4,14 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 /**
  * Container that contains a mojo result and a value.
  *
  * @param <A> the type of the value.
  */
+@NullMarked
 public class ResultAnd<A> {
     private final int mMojoResult;
     private final A mValue;
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java
index fbe76c70..304d2e60 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/RunLoop.java
@@ -4,9 +4,12 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.io.Closeable;
 
 /** Definition of a run loop. */
+@NullMarked
 public interface RunLoop extends Closeable {
     /** Start the run loop. It will continue until quit() is called. */
     public void run();
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
index db397e2..a03848b 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/SharedBufferHandle.java
@@ -4,9 +4,12 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.nio.ByteBuffer;
 
 /** A buffer that can be shared between applications. */
+@NullMarked
 public interface SharedBufferHandle extends Handle {
 
     /** Flags for the shared buffer creation operation. */
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
index bab5931b..c002275 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/UntypedHandle.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.DataPipe.ConsumerHandle;
 import org.chromium.mojo.system.DataPipe.ProducerHandle;
 
@@ -12,6 +13,7 @@
  * return a handle of the requested type and invalidate this object. No validation is made when the
  * conversion operation is called.
  */
+@NullMarked
 public interface UntypedHandle extends Handle {
 
     /**
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java b/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
index 20f6269b..a43a871b 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/Watcher.java
@@ -4,9 +4,11 @@
 
 package org.chromium.mojo.system;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.mojo.system.Core.HandleSignals;
 
 /** Watches a handle for signals being satisfied. */
+@NullMarked
 public interface Watcher {
     /** Callback passed to {@link Watcher#start}. */
     public interface Callback {
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/impl/CoreImpl.java b/mojo/public/java/system/src/org/chromium/mojo/system/impl/CoreImpl.java
index efe3aed..1be1a3f 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/impl/CoreImpl.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/impl/CoreImpl.java
@@ -10,12 +10,14 @@
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Core;
 import org.chromium.mojo.system.DataPipe;
 import org.chromium.mojo.system.DataPipe.ConsumerHandle;
 import org.chromium.mojo.system.DataPipe.ProducerHandle;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.MessagePipeHandle;
+import org.chromium.mojo.system.MessagePipeHandle.CreateOptions;
 import org.chromium.mojo.system.MojoException;
 import org.chromium.mojo.system.MojoResult;
 import org.chromium.mojo.system.Pair;
@@ -83,7 +85,7 @@
      */
     @Override
     public Pair<MessagePipeHandle, MessagePipeHandle> createMessagePipe(
-            MessagePipeHandle.CreateOptions options) {
+            @Nullable CreateOptions options) {
         ByteBuffer optionsBuffer = null;
         if (options != null) {
             optionsBuffer = allocateDirectBuffer(8);
diff --git a/mojo/public/java/system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java b/mojo/public/java/system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java
index 1dc9d73..57a5dcf 100644
--- a/mojo/public/java/system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java
+++ b/mojo/public/java/system/src/org/chromium/mojo/system/impl/MessagePipeHandleImpl.java
@@ -4,6 +4,7 @@
 
 package org.chromium.mojo.system.impl;
 
+import org.chromium.build.annotations.Nullable;
 import org.chromium.mojo.system.Handle;
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.ResultAnd;
@@ -39,7 +40,10 @@
      * @see MessagePipeHandle#writeMessage(ByteBuffer, List, WriteFlags)
      */
     @Override
-    public void writeMessage(ByteBuffer bytes, List<? extends Handle> handles, WriteFlags flags) {
+    public void writeMessage(
+            @Nullable ByteBuffer bytes,
+            @Nullable List<? extends Handle> handles,
+            WriteFlags flags) {
         mCore.writeMessage(this, bytes, handles, flags);
     }
 
diff --git a/net/cookies/cookie_base.cc b/net/cookies/cookie_base.cc
index 7a2ab64..511eb87 100644
--- a/net/cookies/cookie_base.cc
+++ b/net/cookies/cookie_base.cc
@@ -575,6 +575,10 @@
   return std::make_tuple(partition_key_, name_, domain_, path_, source_scheme);
 }
 
+CookieBase::LegacyUniqueCookieKey CookieBase::LegacyUniqueKey() const {
+  return std::make_tuple(partition_key_, name_, domain_, path_);
+}
+
 void CookieBase::SetSourcePort(int port) {
   source_port_ = ValidateAndAdjustSourcePort(port);
 }
diff --git a/net/cookies/cookie_base.h b/net/cookies/cookie_base.h
index 1e5625e1..c803305f 100644
--- a/net/cookies/cookie_base.h
+++ b/net/cookies/cookie_base.h
@@ -51,6 +51,13 @@
                                            /*path=*/std::string,
                                            std::optional<CookieSourceScheme>>;
 
+  // Same as UniqueCookieKey but for use with Legacy Scoped cookies, which do
+  // not consider the source_port or source_scheme.
+  using LegacyUniqueCookieKey = std::tuple<std::optional<CookiePartitionKey>,
+                                           /*name=*/std::string,
+                                           /*domain=*/std::string,
+                                           /*path=*/std::string>;
+
   // Returns if the cookie should be included (and if not, why) for the given
   // request |url| using the CookieInclusionStatus enum. HTTP only cookies can
   // be filter by using appropriate cookie |options|.
@@ -170,6 +177,11 @@
   // with Domain cookies, which do not consider the source_port.
   UniqueDomainCookieKey UniqueDomainKey() const;
 
+  // Same as UniqueKey() except it does not contain a source_port or
+  // source_scheme field. For use for determining aliasing cookies, which do not
+  // consider the source_port or source_scheme.
+  LegacyUniqueCookieKey LegacyUniqueKey() const;
+
   void SetSourceScheme(CookieSourceScheme source_scheme) {
     source_scheme_ = source_scheme;
   }
diff --git a/net/cookies/cookie_inclusion_status.cc b/net/cookies/cookie_inclusion_status.cc
index 3018917..a6055c4 100644
--- a/net/cookies/cookie_inclusion_status.cc
+++ b/net/cookies/cookie_inclusion_status.cc
@@ -239,6 +239,7 @@
       {EXCLUDE_DISALLOWED_CHARACTER, "EXCLUDE_DISALLOWED_CHARACTER"},
       {EXCLUDE_THIRD_PARTY_PHASEOUT, "EXCLUDE_THIRD_PARTY_PHASEOUT"},
       {EXCLUDE_NO_COOKIE_CONTENT, "EXCLUDE_NO_COOKIE_CONTENT"},
+      {EXCLUDE_ALIASING, "EXCLUDE_ALIASING"},
   };
   static_assert(
       std::size(exclusion_reasons) == ExclusionReason::NUM_EXCLUSION_REASONS,
diff --git a/net/cookies/cookie_inclusion_status.h b/net/cookies/cookie_inclusion_status.h
index 4556b80..9309e8e 100644
--- a/net/cookies/cookie_inclusion_status.h
+++ b/net/cookies/cookie_inclusion_status.h
@@ -108,6 +108,10 @@
     EXCLUDE_THIRD_PARTY_PHASEOUT = 25,
     // Cookie contains no content or only whitespace.
     EXCLUDE_NO_COOKIE_CONTENT = 26,
+    // Cookie aliases that of another with a different source_port or
+    // source_scheme. I.e.: Two or more cookies share the same name but have
+    // different ports/schemes..
+    EXCLUDE_ALIASING = 27,
 
     // This should be kept last.
     NUM_EXCLUSION_REASONS
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 91e4285..2917262 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -50,6 +50,7 @@
 #include <optional>
 #include <set>
 #include <string_view>
+#include <tuple>
 #include <utility>
 
 #include "base/check_is_test.h"
@@ -1368,9 +1369,8 @@
   bool delegate_treats_url_as_trustworthy =
       cookie_access_delegate() &&
       cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(url);
-
-  std::vector<std::pair<CanonicalCookie*, CookieAccessResult>>
-      cookies_and_access_results;
+  using CookieAndAccessResult = std::pair<CanonicalCookie*, CookieAccessResult>;
+  std::vector<CookieAndAccessResult> cookies_and_access_results;
   cookies_and_access_results.reserve(cookie_ptrs->size());
   std::set<std::string> origin_cookie_names;
 
@@ -1405,6 +1405,10 @@
     }
   }
 
+  // Map to store the most recent aliasing cookie and it's CookieAccessResult
+  std::map<CanonicalCookie::LegacyUniqueCookieKey, CookieAndAccessResult*>
+      latest_aliasing_cookies;
+
   for (auto& cookie_result : cookies_and_access_results) {
     CanonicalCookie* cookie_ptr = cookie_result.first;
     CookieAccessResult& access_result = cookie_result.second;
@@ -1440,27 +1444,64 @@
       }
     }
 
-    // Filter out any domain `cookie_ptr` which are shadowing origin cookies.
-    // Don't apply domain shadowing exclusion/warning reason if `cookie_ptr` is
-    // already being excluded/warned for scheme matching reasons (Note, domain
-    // cookies match every port so they'll never get excluded/warned for port
-    // reasons).
-    bool scheme_mismatch =
-        access_result.status.HasExclusionReason(
-            CookieInclusionStatus::EXCLUDE_SCHEME_MISMATCH) ||
-        access_result.status.HasWarningReason(
-            CookieInclusionStatus::WARN_SCHEME_MISMATCH);
+    if (GetScopeSemanticsForCookie(*cookie_ptr) ==
+        CookieScopeSemantics::LEGACY) {
+      // For legacy scope semantics we want to exclude all but the most recent
+      // aliasing cookie(s).
+      auto existing_alias =
+          latest_aliasing_cookies.find(cookie_ptr->LegacyUniqueKey());
 
-    if (cookie_ptr->IsDomainCookie() && !scheme_mismatch &&
-        origin_cookie_names.count(cookie_ptr->Name())) {
-      if (cookie_util::IsSchemeBoundCookiesEnabled()) {
-        access_result.status.AddExclusionReason(
-            CookieInclusionStatus::EXCLUDE_SHADOWING_DOMAIN);
+      if (existing_alias == latest_aliasing_cookies.end()) {
+        // Cookie is new and not in map.
+        latest_aliasing_cookies[cookie_ptr->LegacyUniqueKey()] = &cookie_result;
       } else {
-        access_result.status.AddWarningReason(
-            CookieInclusionStatus::WARN_SHADOWING_DOMAIN);
+        CHECK(std::make_tuple(cookie_ptr->SourcePort(),
+                              cookie_ptr->SourceScheme()) !=
+              std::make_tuple(existing_alias->second->first->SourcePort(),
+                              existing_alias->second->first->SourceScheme()))
+            << "port: " << cookie_ptr->SourcePort()
+            << ", scheme: " << static_cast<size_t>(cookie_ptr->SourceScheme());
+        if (cookie_ptr->CreationDate() >
+            existing_alias->second->first->CreationDate()) {
+          existing_alias->second->second.status.AddExclusionReason(
+              CookieInclusionStatus::EXCLUDE_ALIASING);
+          latest_aliasing_cookies[cookie_ptr->LegacyUniqueKey()] =
+              &cookie_result;
+        } else {
+          access_result.status.AddExclusionReason(
+              CookieInclusionStatus::EXCLUDE_ALIASING);
+        }
+      }
+    } else {
+      // Filter out any domain `cookie_ptr` which are shadowing origin cookies.
+      // Don't apply domain shadowing exclusion/warning reason if `cookie_ptr`
+      // is already being excluded/warned for scheme matching reasons (Note,
+      // domain cookies match every port so they'll never get excluded/warned
+      // for port reasons).
+      bool scheme_mismatch =
+          access_result.status.HasExclusionReason(
+              CookieInclusionStatus::EXCLUDE_SCHEME_MISMATCH) ||
+          access_result.status.HasWarningReason(
+              CookieInclusionStatus::WARN_SCHEME_MISMATCH);
+
+      if (cookie_ptr->IsDomainCookie() && !scheme_mismatch &&
+          origin_cookie_names.count(cookie_ptr->Name())) {
+        if (cookie_util::IsSchemeBoundCookiesEnabled()) {
+          access_result.status.AddExclusionReason(
+              CookieInclusionStatus::EXCLUDE_SHADOWING_DOMAIN);
+        } else {
+          access_result.status.AddWarningReason(
+              CookieInclusionStatus::WARN_SHADOWING_DOMAIN);
+        }
       }
     }
+  }
+  // Loop through all cookies again and add to include and exclude list.
+  // TODO(crbug.com/40165805): Look for optimizations for excluding previous
+  // aliases.
+  for (auto& cookie_result : cookies_and_access_results) {
+    CanonicalCookie* cookie_ptr = cookie_result.first;
+    CookieAccessResult& access_result = cookie_result.second;
 
     if (!access_result.status.IsInclude()) {
       if (options.return_excluded_cookies()) {
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index 36b4e9b..5a81a20 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -273,6 +273,8 @@
                            FilterCookiesWithOptionsExcludeShadowingDomains);
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest,
                            FilterCookiesWithOptionsWarnShadowingDomains);
+  FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest,
+                           FilterCookiesWithOptionsExcludeAlising);
 
   // For StoreLoadedCookies behavior with origin-bound cookies.
   FRIEND_TEST_ALL_PREFIXES(CookieMonsterTest_StoreLoadedCookies,
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 7cc86085..f2a4ead 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -5838,6 +5838,115 @@
             GetCookiesWithOptions(cm_.get(), kHttpUrl, CookieOptions()));
 }
 
+// Test that Cookies are excluded for alisaing each other when ScopeSemantics
+// are LEGACY
+TEST_F(CookieMonsterTest, FilterCookiesWithOptionsExcludeAlising) {
+  std::unique_ptr<TestCookieAccessDelegate> access_delegate =
+      std::make_unique<TestCookieAccessDelegate>();
+  auto store = base::MakeRefCounted<MockPersistentCookieStore>();
+  auto cm = std::make_unique<CookieMonster>(store.get(), net::NetLog::Get());
+  base::Time creation_time = base::Time::Now();
+  base::Time three_days_earlier = base::Time::Now() - base::Days(3);
+  std::optional<base::Time> server_time;
+  CookieOptions options = CookieOptions::MakeAllInclusive();
+  options.set_return_excluded_cookies();
+
+  auto CookieListsMatch = [](const CookieAccessResultList& actual,
+                             const CookieList& expected) {
+    if (actual.size() != expected.size()) {
+      return false;
+    }
+
+    for (size_t i = 0; i < actual.size(); i++) {
+      if (!actual[i].cookie.IsEquivalent(expected[i])) {
+        return false;
+      }
+    }
+
+    return true;
+  };
+
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      {net::features::kEnableSchemeBoundCookies,
+       net::features::kEnablePortBoundCookies},
+      {});
+
+  std::vector<CanonicalCookie*> cookie_ptrs;
+  CookieAccessResultList included;
+  CookieAccessResultList excluded;
+
+  auto reset = [&cookie_ptrs, &included, &excluded]() {
+    cookie_ptrs.clear();
+    included.clear();
+    excluded.clear();
+  };
+
+  // Testing aliasing cookies
+  auto alias_cookie1 = CanonicalCookie::CreateForTesting(
+      GURL("https://www.example.com/withDomain"),
+      "W=D; Domain=example.com; Path=/withDomain", creation_time, server_time);
+
+  auto alias_cookie2 = CanonicalCookie::CreateForTesting(
+      GURL("http://www.example.com/withDomain"),
+      "W=D; Domain=example.com; Path=/withDomain", three_days_earlier,
+      server_time);
+
+  // Different name so should not alias above cookies
+  auto alias_cookie3 = CanonicalCookie::CreateForTesting(
+      GURL("https://www.example.com/withDomain"),
+      "A=B; Domain=example.com; Path=/withDomain", creation_time, server_time);
+
+  // Different port value so should be excluded
+  auto alias_cookie4 = CanonicalCookie::CreateForTesting(
+      GURL("https://www.example.com/withDomain"),
+      "W=D; Domain=example.com; Path=/withDomain", three_days_earlier,
+      server_time);
+  alias_cookie4->SetSourcePort(8000);
+
+  // Need to inject such a cookie under legacy semantics.
+  access_delegate->SetExpectationForCookieScope("example.com",
+                                                CookieScopeSemantics::LEGACY);
+
+  access_delegate->SetExpectationForCookieScope(
+      "test.com", CookieScopeSemantics::NONLEGACY);
+
+  cm->SetCookieAccessDelegate(std::move(access_delegate));
+
+  cookie_ptrs = {alias_cookie2.get(), alias_cookie3.get(), alias_cookie1.get(),
+                 alias_cookie4.get()};
+
+  cm->FilterCookiesWithOptions(GURL("https://www.example.com/withDomain"),
+                               options, &cookie_ptrs, &included, &excluded);
+
+  EXPECT_TRUE(CookieListsMatch(included, {*alias_cookie3, *alias_cookie1}));
+
+  EXPECT_TRUE(CookieListsMatch(excluded, {*alias_cookie2, *alias_cookie4}));
+
+  EXPECT_TRUE(excluded[0].access_result.status.HasExclusionReason(
+      CookieInclusionStatus::EXCLUDE_ALIASING));
+  reset();
+
+  // These cookies are to test when ScopeSemantics are NONLEGACY cookies should
+  // not be excluded for aliasing when this is the case
+  auto non_legacy_cookie1 = CanonicalCookie::CreateForTesting(
+      GURL("https://www.test.com/withDomain"),
+      "W=D; Domain=test.com; Path=/withDomain", three_days_earlier,
+      server_time);
+
+  auto non_legacy_cookie2 = CanonicalCookie::CreateForTesting(
+      GURL("https://www.test.com/withDomain"),
+      "W=D; Domain=test.com; Path=/withDomain", creation_time, server_time);
+
+  cookie_ptrs = {non_legacy_cookie1.get(), non_legacy_cookie2.get()};
+  cm->FilterCookiesWithOptions(GURL("https://www.test.com/withDomain"), options,
+                               &cookie_ptrs, &included, &excluded);
+  EXPECT_TRUE(
+      CookieListsMatch(included, {*non_legacy_cookie1, *non_legacy_cookie2}));
+  // Since aliasing is not active nothing should be excluded
+  EXPECT_EQ(excluded.size(), 0u);
+}
+
 TEST_F(CookieMonsterTest, IsCookieSentToSamePortThatSetIt) {
   // Note: `IsCookieSentToSamePortThatSetIt()` only uses the source_scheme if
   // the port is valid, specified, and doesn't match the url's port. So for test
diff --git a/net/cookies/test_cookie_access_delegate.cc b/net/cookies/test_cookie_access_delegate.cc
index 09787bb..c45f502f 100644
--- a/net/cookies/test_cookie_access_delegate.cc
+++ b/net/cookies/test_cookie_access_delegate.cc
@@ -39,8 +39,8 @@
 
 CookieScopeSemantics TestCookieAccessDelegate::GetScopeSemantics(
     const CanonicalCookie& cookie) const {
-  auto it = expectations_legacy_.find(GetKeyForDomainValue(cookie.Domain()));
-  if (it != expectations_legacy_.end()) {
+  auto it = expectations_scoped_.find(GetKeyForDomainValue(cookie.Domain()));
+  if (it != expectations_scoped_.end()) {
     return it->second;
   }
   return CookieScopeSemantics::UNKNOWN;
@@ -134,6 +134,12 @@
   expectations_[GetKeyForDomainValue(cookie_domain)] = access_semantics;
 }
 
+void TestCookieAccessDelegate::SetExpectationForCookieScope(
+    const std::string& cookie_domain,
+    CookieScopeSemantics scoped_semantics) {
+  expectations_scoped_[GetKeyForDomainValue(cookie_domain)] = scoped_semantics;
+}
+
 void TestCookieAccessDelegate::SetIgnoreSameSiteRestrictionsScheme(
     const std::string& site_for_cookies_scheme,
     bool require_secure_origin) {
diff --git a/net/cookies/test_cookie_access_delegate.h b/net/cookies/test_cookie_access_delegate.h
index bcbbca15..024f8c26 100644
--- a/net/cookies/test_cookie_access_delegate.h
+++ b/net/cookies/test_cookie_access_delegate.h
@@ -64,6 +64,12 @@
   void SetExpectationForCookieDomain(const std::string& cookie_domain,
                                      CookieAccessSemantics access_semantics);
 
+  // Sets the expected return value for Cookie Scoped Semantics for any cookie
+  // whose Domain matches `cookie_domain`. Pass the value of `cookie.Domain()`
+  // and any leading dot will be discarded.
+  void SetExpectationForCookieScope(const std::string& cookie_domain,
+                                    CookieScopeSemantics scoped_semantics);
+
   // Sets the expected return value for ShouldAlwaysAttachSameSiteCookies.
   // Can set schemes that always attach SameSite cookies, or schemes that always
   // attach SameSite cookies if the request URL is secure.
@@ -99,7 +105,7 @@
                                  base::OnceCallback<void(T)> callback) const;
 
   std::map<std::string, CookieAccessSemantics> expectations_;
-  std::map<std::string, CookieScopeSemantics> expectations_legacy_;
+  std::map<std::string, CookieScopeSemantics> expectations_scoped_;
   std::map<std::string, bool> ignore_samesite_restrictions_schemes_;
   base::flat_map<SchemefulSite, FirstPartySetEntry> first_party_sets_;
   FirstPartySetsCacheFilter first_party_sets_cache_filter_;
diff --git a/net/http/transport_security_state_static.pins b/net/http/transport_security_state_static.pins
index d95efca..ff0e92c 100644
--- a/net/http/transport_security_state_static.pins
+++ b/net/http/transport_security_state_static.pins
@@ -43,9 +43,9 @@
 #   hash function for preloaded entries again (we have already done so once).
 #
 
-# Last updated: 2024-12-15 12:56 UTC
+# Last updated: 2024-12-16 12:55 UTC
 PinsListTimestamp
-1734267373
+1734353742
 
 TestSPKI
 sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
diff --git a/net/http/transport_security_state_static_pins.json b/net/http/transport_security_state_static_pins.json
index 3454a4c98..6753003 100644
--- a/net/http/transport_security_state_static_pins.json
+++ b/net/http/transport_security_state_static_pins.json
@@ -31,7 +31,7 @@
 // the 'static_spki_hashes' and 'bad_static_spki_hashes' fields in 'pinsets'
 // refer to, and the timestamp at which the pins list was last updated.
 //
-// Last updated: 2024-12-15 12:56 UTC
+// Last updated: 2024-12-16 12:55 UTC
 //
 {
   "pinsets": [
diff --git a/sandbox/linux/bpf_dsl/cons.h b/sandbox/linux/bpf_dsl/cons.h
index 99e02c2..52c36e0 100644
--- a/sandbox/linux/bpf_dsl/cons.h
+++ b/sandbox/linux/bpf_dsl/cons.h
@@ -6,6 +6,7 @@
 #define SANDBOX_LINUX_BPF_DSL_CONS_H_
 
 #include <memory>
+#include <utility>
 
 #include "sandbox/sandbox_export.h"
 
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 3976bd34..d6595d3 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -884,9 +884,8 @@
   // Note: There are some ordering dependencies here. `SetRequestCredentials`
   // depends on `SetLoadFlags`; `CalculateStorageAccessStatus` depends on
   // `cookie_setting_overrides` and `SetRequestCredentials`.
-  // `SetFetchMetadataHeaders` will depend on
-  // `url_request_->storage_access_status()`, once https://crbug.com/366284840
-  // is fixed.
+  // `SetFetchMetadataHeaders` depends on
+  // `url_request_->storage_access_status()`.
   url_request_->cookie_setting_overrides() = cookie_setting_overrides;
   url_request_->SetLoadFlags(request_load_flags);
   SetRequestCredentials(url);
@@ -1675,9 +1674,8 @@
 
   // Note: There are some ordering dependencies here.
   // `CalculateStorageAccessStatus` depends on
-  // `url_request->cookie_setting_overrides()`.  `SetFetchMetadataHeaders` will
-  // depend on `url_request_->storage_access_status()`, once
-  // https://crbug.com/366284840 is fixed.
+  // `url_request->cookie_setting_overrides()`. `SetFetchMetadataHeaders`
+  // depends on `url_request_->storage_access_status()`.
   url_request_->set_storage_access_status(
       url_request_->CalculateStorageAccessStatus(redirect_info));
 
diff --git a/services/tracing/perfetto/consumer_host_unittest.cc b/services/tracing/perfetto/consumer_host_unittest.cc
index e4c4584..8bf3b1e 100644
--- a/services/tracing/perfetto/consumer_host_unittest.cc
+++ b/services/tracing/perfetto/consumer_host_unittest.cc
@@ -557,7 +557,7 @@
   no_more_data.Run();
 }
 
-#if BUILDFLAG(IS_APPLE)
+#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID)
 // TODO(crbug.com/383878432): Re-enable this test
 #define MAYBE_FlushProducers DISABLED_FlushProducers
 #else
diff --git a/testing/buildbot/buildbot_json_magic_substitutions.py b/testing/buildbot/buildbot_json_magic_substitutions.py
index d785a86c..0e70e4f 100644
--- a/testing/buildbot/buildbot_json_magic_substitutions.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions.py
@@ -334,6 +334,7 @@
 
   unrooted_devices = {
       'a13',
+      'a13ve',
       'a23',
       'dm1q',  # Samsung S23.
       'devonn',  # Motorola Moto G Power 5G.
diff --git a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
index e07e6d9..1d93c7c 100755
--- a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
@@ -401,7 +401,7 @@
     self.assertEqual(retval, [])
 
   def testUnrootedDevices(self):
-    devices = ('a13', 'a23', 'dm1q', 'devonn')
+    devices = ('a13', 'a13ve', 'a23', 'dm1q', 'devonn')
     for d in devices:
       test_config = CreateConfigWithDeviceType(d)
       retval = magic_substitutions.GPUTelemetryNoRootForUnrootedDevices(
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index 02b2245..76560427 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -271,8 +271,8 @@
     'swarming': {
       'dimensions': {
         'os': 'Android',
-        'device_type': 'a13',
-        'device_os': 'S',
+        'device_type': 'a13ve',
+        'device_os': 'TP1A.220624.014',
         'device_os_type': 'user',
         'pool': 'chromium.tests.gpu',
       },
diff --git a/testing/test.gni b/testing/test.gni
index f075364..08033f3 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -306,13 +306,22 @@
             "close_fd_mask",
             "fork",
           ]
-          fuzzer_args =
-              "-undefok=" + string_join(",", known_libfuzzer_args) + " "
+          fuzzer_args = [ "-undefok=" + string_join(",", known_libfuzzer_args) ]
         } else {
-          fuzzer_args = ""
+          fuzzer_args = []
         }
-        fuzzer_args += "--fuzz=$fuzztest_unit --corpus_database=\"\""
-        contents = [ "const char* kFuzzerArgs = \"${fuzzer_args}\"; const char* kFuzzerBinary = \"${_fuzzer_binary_name}\";" ]
+
+        fuzzer_args += [
+          "--fuzz=${fuzztest_unit}",
+          "--corpus_database=",
+        ]
+        fuzzer_args_joined = string_join(" ", fuzzer_args)
+
+        NEWLINE = "$0x0A"
+
+        contents =
+            "const char* kFuzzerArgs = \"${fuzzer_args_joined}\";" + NEWLINE +
+            "const char* kFuzzerBinary = \"${_fuzzer_binary_name}\";" + NEWLINE
       }
 
       _fuzzer_target_name = target_name
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1a5900f..c1b57759 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2142,29 +2142,6 @@
             ]
         }
     ],
-    "AutofillI18nAddressModelNewCountries": [
-        {
-            "platforms": [
-                "android",
-                "android_webview",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "AutofillUseAUAddressModel",
-                        "AutofillUseCAAddressModel",
-                        "AutofillUseDEAddressModel"
-                    ]
-                }
-            ]
-        }
-    ],
     "AutofillI18nFRAddressModel": [
         {
             "platforms": [
@@ -2186,6 +2163,27 @@
             ]
         }
     ],
+    "AutofillI18nNLAddressModel": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillUseNLAddressModel"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillImproveSubmissionDetectionV2": [
         {
             "platforms": [
@@ -6419,36 +6417,6 @@
             ]
         }
     ],
-    "CrOSBluetoothCoredump": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_Coredump",
-                    "enable_features": [
-                        "BluetoothCoredump"
-                    ]
-                }
-            ]
-        }
-    ],
-    "CrOSBluetoothFlossCoredump": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BluetoothFlossCoredump"
-                    ]
-                }
-            ]
-        }
-    ],
     "CrOSBluetoothFlossTelephony": [
         {
             "platforms": [
@@ -26002,7 +25970,7 @@
                 {
                     "name": "EnabledThrottling0ms",
                     "params": {
-                        "poll_interval_ms": "0"
+                        "poll_interval_ms": "1"
                     },
                     "enable_features": [
                         "YieldWithInputHint"
diff --git a/third_party/angle b/third_party/angle
index 9f6c278..d8efbad 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 9f6c27832becdd66ae3db6c0bf261b85e6a87c20
+Subproject commit d8efbad88cb36a1d0d1baa6317145e80c6b89bf7
diff --git a/third_party/beto-core/README.chromium b/third_party/beto-core/README.chromium
index 020ee59e..0ac3342 100644
--- a/third_party/beto-core/README.chromium
+++ b/third_party/beto-core/README.chromium
@@ -2,7 +2,7 @@
 Short Name: beto-core
 URL: https://beto-core.googlesource.com/beto-core
 Version: N/A
-Date: 04/10/2023
+Date: 2023-04-10
 Revision: 9bfc955731213b299f893dbf4ca86874b31e6b18
 License: Apache-2.0
 License File: LICENSE
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index bf1550f..79ff716 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -284,6 +284,7 @@
     "scheduler/web_scheduler_tracked_feature.cc",
     "scheme_registry.cc",
     "security/address_space_feature.cc",
+    "service_worker/extended_service_worker_status_code.cc",
     "service_worker/service_worker_loader_helpers.cc",
     "service_worker/service_worker_router_rule.cc",
     "service_worker/service_worker_router_rule_mojom_traits.cc",
diff --git a/third_party/blink/common/service_worker/extended_service_worker_status_code.cc b/third_party/blink/common/service_worker/extended_service_worker_status_code.cc
new file mode 100644
index 0000000..3fa4ba44
--- /dev/null
+++ b/third_party/blink/common/service_worker/extended_service_worker_status_code.cc
@@ -0,0 +1,20 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/common/service_worker/extended_service_worker_status_code.h"
+
+#include "base/notreached.h"
+
+namespace blink {
+
+const char* ExtendedServiceWorkerStatusToString(
+    ExtendedServiceWorkerStatusCode status) {
+  switch (status) {
+    case ExtendedServiceWorkerStatusCode::kUnknown:
+      return "Unknown status";
+  }
+  NOTREACHED();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 0ee0448..d206a6b24 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -288,6 +288,7 @@
     "security/protocol_handler_security_level.h",
     "security/security_style.h",
     "security_context/insecure_request_policy.h",
+    "service_worker/extended_service_worker_status_code.h",
     "service_worker/service_worker_loader_helpers.h",
     "service_worker/service_worker_router_rule.h",
     "service_worker/service_worker_scope_match.h",
diff --git a/third_party/blink/public/common/service_worker/extended_service_worker_status_code.h b/third_party/blink/public/common/service_worker/extended_service_worker_status_code.h
new file mode 100644
index 0000000..2c83c15
--- /dev/null
+++ b/third_party/blink/public/common/service_worker/extended_service_worker_status_code.h
@@ -0,0 +1,24 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_SERVICE_WORKER_EXTENDED_SERVICE_WORKER_STATUS_CODE_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_SERVICE_WORKER_EXTENDED_SERVICE_WORKER_STATUS_CODE_H_
+
+namespace blink {
+
+// Generic service worker operation statuses.
+// This enum is used in UMA histograms. Append-only.
+enum class ExtendedServiceWorkerStatusCode {
+
+  // A placeholder value for when an extended status code cannot be determined.
+  kUnknown = 0,
+
+  // TODO(crbug.com/346732739): Implement the remaining extended status codes.
+
+  kMaxValue = kUnknown,
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_SERVICE_WORKER_EXTENDED_SERVICE_WORKER_STATUS_CODE_H_
diff --git a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc
index f7cf3c46..87287c9 100644
--- a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc
+++ b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.cc
@@ -4,14 +4,11 @@
 
 #include "third_party/blink/renderer/core/layout/anchor_evaluator_impl.h"
 
-#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
 #include "third_party/blink/renderer/core/css/anchor_query.h"
 #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h"
 #include "third_party/blink/renderer/core/layout/anchor_query_map.h"
 #include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
-#include "third_party/blink/renderer/core/layout/inline/inline_cursor.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/logical_fragment_link.h"
 #include "third_party/blink/renderer/core/style/anchor_specifier_value.h"
 #include "third_party/blink/renderer/core/style/position_area.h"
 
diff --git a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h
index 732c4a85..a43b2c7f 100644
--- a/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h
+++ b/third_party/blink/renderer/core/layout/anchor_evaluator_impl.h
@@ -17,8 +17,6 @@
 #include "third_party/blink/renderer/core/style/scoped_css_name.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc
index 80d905f..830f544 100644
--- a/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/timing/text_paint_timing_detector.cc
@@ -135,18 +135,14 @@
     const LayoutBoxModelObject& aggregator,
     const gfx::Rect& aggregated_visual_rect,
     const PropertyTreeStateOrAlias& property_tree_state) {
-  if (RuntimeEnabledFeatures::
-          ExcludeTransparentTextsFromBeingLcpEligibleEnabled()) {
-    bool is_color_transparent =
-        aggregator.StyleRef()
-            .VisitedDependentColor(GetCSSPropertyColor())
-            .IsFullyTransparent();
-    bool has_shadow = !!aggregator.StyleRef().TextShadow();
-    bool has_text_stroke = aggregator.StyleRef().TextStrokeWidth();
+  bool is_color_transparent = aggregator.StyleRef()
+                                  .VisitedDependentColor(GetCSSPropertyColor())
+                                  .IsFullyTransparent();
+  bool has_shadow = !!aggregator.StyleRef().TextShadow();
+  bool has_text_stroke = aggregator.StyleRef().TextStrokeWidth();
 
-    if (is_color_transparent && !has_shadow && !has_text_stroke) {
-      return;
-    }
+  if (is_color_transparent && !has_shadow && !has_text_stroke) {
+    return;
   }
 
   DCHECK(ShouldWalkObject(aggregator));
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index 1637f45..75107774 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -355,10 +355,8 @@
     size_t row_bytes,
     int x,
     int y) {
-  if (!GetOrCreateCanvasResourceProvider())
-    return false;
+  DCHECK(IsCanvas2DBufferValid());
 
-  DCHECK(IsPaintable());
   Host()->FlushRecording(FlushReason::kWritePixels);
 
   // Short-circuit out if an error occurred while flushing the recording.
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
index d6b3200..5094f43d 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.cc
@@ -48,6 +48,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track_state.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/modules/mediarecorder/media_recorder.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
@@ -128,22 +129,25 @@
     BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
 std::optional<VideoTrackRecorder::CodecProfile> VideoStringTagToCodecProfile(
     const String& codecs,
-    const StringView& codecs_tag) {
+    const std::vector<StringView>& codecs_tags) {
   std::optional<VideoTrackRecorder::CodecProfile> codec_profile;
-  wtf_size_t codecs_start = codecs.Find(codecs_tag);
-  if (codecs_start != kNotFound) {
-    wtf_size_t codecs_end = codecs.Find(",");
-    auto codec_id =
-        codecs
-            .Substring(codecs_start,
-                       codecs_end == kNotFound ? UINT_MAX : codecs_end)
-            .StripWhiteSpace()
-            .Ascii();
-    // Do not use lowercase `codecId` here, as `codecId` is case sensitive when
-    // parsing.
-    if (auto result = media::ParseCodec(codec_id)) {
-      codec_profile = {CodecIdFromMediaVideoCodec(result->codec),
-                       result->profile, result->level};
+  for (auto& codecs_tag : codecs_tags) {
+    wtf_size_t codecs_start = codecs.Find(codecs_tag);
+    if (codecs_start != kNotFound) {
+      wtf_size_t codecs_end = codecs.Find(",");
+      auto codec_id =
+          codecs
+              .Substring(codecs_start,
+                         codecs_end == kNotFound ? UINT_MAX : codecs_end)
+              .StripWhiteSpace()
+              .Ascii();
+      // Do not use lowercase `codecId` here, as `codecId` is case sensitive
+      // when parsing.
+      if (auto result = media::ParseCodec(codec_id)) {
+        codec_profile = {CodecIdFromMediaVideoCodec(result->codec),
+                         result->profile, result->level};
+        break;
+      }
     }
   }
   return codec_profile;
@@ -206,6 +210,18 @@
   return IsAllowedMp4Type(type);
 }
 
+bool ShouldAddParameterSetsToBitstream(const String& codecs) {
+#if BUILDFLAG(USE_PROPRIETARY_CODECS) || \
+    BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
+  String codecs_str = codecs.LowerASCII();
+  return codecs_str.Find("hev1") != kNotFound ||
+         codecs_str.Find("avc3") != kNotFound;
+#else
+  return false;
+#endif  // BUILDFLAG(USE_PROPRIETARY_CODECS) ||
+        // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
+}
+
 }  // anonymous namespace
 
 media::VideoCodec MediaVideoCodecFromCodecId(VideoTrackRecorder::CodecId id) {
@@ -246,18 +262,22 @@
   }
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
   if (codecs_str.Find("h264") != kNotFound ||
-      codecs_str.Find("avc1") != kNotFound) {
+      codecs_str.Find("avc1") != kNotFound ||
+      codecs_str.Find("avc3") != kNotFound) {
     codec_id = VideoTrackRecorder::CodecId::kH264;
   }
-  if (auto codec_profile = VideoStringTagToCodecProfile(codecs, "avc1")) {
+  if (auto codec_profile =
+          VideoStringTagToCodecProfile(codecs, {"avc1", "avc3"})) {
     return *codec_profile;
   }
 #endif
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-  if (codecs_str.Find("hvc1") != kNotFound) {
+  if (codecs_str.Find("hvc1") != kNotFound ||
+      codecs_str.Find("hev1") != kNotFound) {
     codec_id = VideoTrackRecorder::CodecId::kHevc;
   }
-  if (auto codec_profile = VideoStringTagToCodecProfile(codecs, "hvc1")) {
+  if (auto codec_profile =
+          VideoStringTagToCodecProfile(codecs, {"hvc1", "hev1"})) {
     return *codec_profile;
   }
 #endif
@@ -289,20 +309,20 @@
     return false;
 
   // Both |video| and |audio| support empty |codecs|; |type| == "video" supports
-  // vp8, vp9, h264, avc1, av01, av1, hvc1, opus, or pcm; |type| = "audio",
-  // supports opus or pcm (little-endian 32-bit float).
+  // vp8, vp9, h264, avc1, avc3, av01, av1, hvc1, hev1, opus, or pcm; |type| =
+  // "audio", supports opus or pcm (little-endian 32-bit float).
   // http://www.webmproject.org/docs/container Sec:"HTML5 Video Type Parameters"
   static const char* const kVideoCodecs[] = {
       "vp8", "vp9",
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-      "h264", "avc1",
+      "h264", "avc1", "avc3",
 #endif
       "av01",
       // TODO(crbug.com/40923648): Remove the wrong AV1 codecs string, "av1",
       // once we confirm nobody uses this in product.
       "av1",
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-      "hvc1",
+      "hvc1", "hev1",
 #endif
       "opus", "pcm"};
   static const char* const kAudioCodecs[] = {"opus", "pcm"};
@@ -318,12 +338,12 @@
   if (mp4_mime_type) {
     static const char* const kVideoCodecsForMP4[] = {
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-        "avc1", "mp4a.40.2",
+        "avc1", "avc3", "mp4a.40.2",
 #endif
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-        "hvc1",
+        "hvc1", "hev1",
 #endif
-        "vp9",  "av01",      "opus",
+        "vp9",  "av01", "opus",
     };
     static const char* const kAudioCodecsForMp4[] = {
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
@@ -372,10 +392,12 @@
       std::string mime_type = EqualIgnoringASCIICase(type, "video/x-matroska")
                                   ? "video/mp4"
                                   : type.Ascii();
-      // It supports full qualified string for `avc1`, `hvc1`, and `av01`
-      // codecs, e.g.
+      // It supports full qualified string for `avc1`, `avc3`, `hvc1`, `hev1`,
+      // and `av01` codecs, e.g.
       //  `avc1.<profile>.<level>`,
+      //  `avc3.<profile>.<level>`,
       //  `hvc1.<profile>.<profile_compatibility>.<tier and level>.*`,
+      //  `hev1.<profile>.<profile_compatibility>.<tier and level>.*`,
       //  `av01.<profile>.<level>.<color depth>.*`.
       auto parsed_result =
           media::ParseVideoCodecString(mime_type, codec,
@@ -387,12 +409,14 @@
       }
 
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
-      // Only support HEVC main profile with `hvc1` tag instead of `hev1` tag
-      // for better compatibility given the fact that QuickTime and Safari only
-      // support playing `hvc1` tag mp4 videos, and Apple only recommend using
-      // `hvc1` for HLS.
+      // Support `hev1` tag as it allow parameter sets write into the bitstream,
+      // which is the only option if the MediaStream has dynamically changing
+      // resolution. Also support `hvc1` tag for better compatibility given the
+      // fact that QuickTime and Safari only support playing `hvc1` tag mp4
+      // videos, and Apple only recommend using `hvc1` for HLS.
       // https://developer.apple.com/documentation/http-live-streaming/hls-authoring-specification-for-apple-devices#2969487
-      if (codec_string.StartsWith("hvc1", kTextCaseASCIIInsensitive)) {
+      if (codec_string.StartsWith("hvc1", kTextCaseASCIIInsensitive) ||
+          codec_string.StartsWith("hev1", kTextCaseASCIIInsensitive)) {
         match =
             // If the profile can be parsed, ensure it must be HEVC main
             // profile.
@@ -475,6 +499,8 @@
              << static_cast<int>(video_codec_profile_.codec_id);
   }
 
+  add_parameter_sets_in_bitstream_ = ShouldAddParameterSetsToBitstream(codecs);
+
   // Do the same for the audio codec(s).
   const AudioTrackRecorder::CodecId audio_codec_id =
       AudioStringToCodecId(codecs);
@@ -581,7 +607,7 @@
             audio_codec,
             MediaVideoCodecFromCodecId(video_codec_profile_.codec_id),
             video_codec_profile_.profile, video_codec_profile_.level,
-            write_callback),
+            add_parameter_sets_in_bitstream_, write_callback),
         optional_timeslice);
 
 #if BUILDFLAG(IS_WIN)
@@ -816,7 +842,7 @@
         break;
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
       case VideoTrackRecorder::CodecId::kH264:
-        mime_type.Append("avc1");
+        mime_type.Append(add_parameter_sets_in_bitstream_ ? "avc3" : "avc1");
         if (video_codec_profile_.profile && video_codec_profile_.level) {
           mime_type.Append(
               media::BuildH264MimeSuffix(*video_codec_profile_.profile,
@@ -830,7 +856,7 @@
         break;
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
       case VideoTrackRecorder::CodecId::kHevc:
-        mime_type.Append("hvc1");
+        mime_type.Append(add_parameter_sets_in_bitstream_ ? "hev1" : "hvc1");
         break;
 #endif
       case VideoTrackRecorder::CodecId::kLast:
@@ -908,8 +934,8 @@
       encoded_data->is_key_frame() && !codec_description.has_value()) {
     bool first_key_frame = false;
     if (!h26x_converter_) {
-      h26x_converter_ =
-          std::make_unique<media::H26xAnnexBToBitstreamConverter>(video_codec);
+      h26x_converter_ = std::make_unique<media::H26xAnnexBToBitstreamConverter>(
+          video_codec, add_parameter_sets_in_bitstream_);
       first_key_frame = true;
     }
 
@@ -921,6 +947,33 @@
       video_codec_profile_.level =
           h26x_converter_->GetCodecProfileLevel().level;
     }
+
+    // For `avc1` or `hvc1` mp4 recording, since the codec description is only
+    // written to the sample entries, and not allowed to write those to the
+    // bitstream, we print a error message telling the user switch to `avc3` or
+    // `hev1` instead.
+    if (!add_parameter_sets_in_bitstream_ &&
+        !has_codec_description_changed_error_printed_ &&
+        EqualIgnoringASCIICase(type_, "video/mp4") &&
+        last_seen_codec_description_.size() &&
+        last_seen_codec_description_ != codec_description.value() &&
+        recorder_) {
+      const String& message = String::Format(
+          "When using \"%s\" for mp4 encoding, the codec description is not "
+          "supposed to change during the entire recording. Normally, a change "
+          "in the encoding resolution may lead to this situation. "
+          "Consider switching to \"%s\" instead to resolve this problem",
+          video_codec == media::VideoCodec::kH264 ? "avc1" : "hvc1",
+          video_codec == media::VideoCodec::kH264 ? "avc3" : "hev1");
+      auto* context = recorder_->GetExecutionContext();
+      if (context && !context->IsContextDestroyed()) {
+        context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+            mojom::blink::ConsoleMessageSource::kJavaScript,
+            mojom::blink::ConsoleMessageLevel::kError, message));
+      }
+      has_codec_description_changed_error_printed_ = true;
+    }
+    last_seen_codec_description_ = codec_description.value();
   }
 #endif
 
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
index 8a7d37f..eb8336ea 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler.h
@@ -181,6 +181,12 @@
   VideoTrackRecorder::CodecProfile video_codec_profile_{
       VideoTrackRecorder::CodecId::kLast};
 
+  // Indicate if the parameter sets are allowed to be inserted into the
+  // bitstream or must be "out of band" (can only be write to the
+  // `{AVC|HEVC}DecoderConfigurationRecord`). i.e. for `avc1` and `hvc1` this is
+  // false, and for `avc3` and `hev1` this is true.
+  bool add_parameter_sets_in_bitstream_ = false;
+
   // Audio Codec, OPUS is used by default.
   AudioTrackRecorder::CodecId audio_codec_id_{
       AudioTrackRecorder::CodecId::kLast};
@@ -219,6 +225,12 @@
     BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
   // Converter to get the codec description from Annex-B bitstream keyframes.
   std::unique_ptr<media::H26xAnnexBToBitstreamConverter> h26x_converter_;
+
+  // The last seen codec description of the last received encoded video frame.
+  media::VideoEncoder::CodecDescription last_seen_codec_description_;
+
+  // Indicate if the codec description changed message has been printed or not.
+  bool has_codec_description_changed_error_printed_ = false;
 #endif
 
   // For invalidation of in-flight callbacks back to ourselves. Need to track
diff --git a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
index 52b8f780..d1f29264f 100644
--- a/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
+++ b/third_party/blink/renderer/modules/mediarecorder/media_recorder_handler_unittest.cc
@@ -109,6 +109,7 @@
     {false, true, false, "video/webm", "av01", false},
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     {false, true, false, "video/x-matroska", "avc1", false},
+    {false, true, false, "video/x-matroska", "avc3", false},
 #endif
     {false, false, true, "audio/webm", "opus", true},
     {false, false, true, "audio/webm", "", true},  // Should default to opus.
@@ -120,10 +121,14 @@
     {true, true, false, "video/webm", "av01", false},
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
     {true, true, false, "video/x-matroska", "avc1", false},
+    {true, true, false, "video/x-matroska", "avc3", false},
     {true, true, false, "video/mp4", "avc1", false, true},
+    {true, true, false, "video/mp4", "avc3", false, true},
     {true, true, true, "video/mp4", "avc1,mp4a.40.2", false, true},
+    {true, true, true, "video/mp4", "avc3,mp4a.40.2", false, true},
     {true, false, true, "audio/mp4", "mp4a.40.2", false, true},
     {true, true, true, "video/mp4", "avc1,opus", false, true},
+    {true, true, true, "video/mp4", "avc3,opus", false, true},
     {true, true, true, "video/mp4", "vp9,mp4a.40.2", false, true},
 #endif
     {true, false, true, "audio/webm", "opus", true},
@@ -402,7 +407,8 @@
   bool IsCodecSupported() {
 #if !BUILDFLAG(ENABLE_OPENH264)
     // Test requires OpenH264 encoder. It can't use the VEA encoder.
-    if (String(GetParam().codecs).Find("avc1") != kNotFound) {
+    if (String(GetParam().codecs).Find("avc1") != kNotFound ||
+        String(GetParam().codecs).Find("avc3") != kNotFound) {
       return false;
     }
 #endif
@@ -426,8 +432,8 @@
     return true;
   }
 
-  bool IsAvc1CodecSupported(const String codecs) {
-    return codecs.Find("avc1") != kNotFound;
+  bool IsAvcCodecSupported(const String codecs) {
+    return codecs.Find("avc1") != kNotFound || codecs.Find("avc3") != kNotFound;
   }
 
  private:
@@ -484,19 +490,41 @@
 
   const String example_good_codecs_8("AV01,opus");
   EXPECT_TRUE(media_recorder_handler_->CanSupportMimeType(
-      mime_type_video, example_good_codecs_5));
+      mime_type_video, example_good_codecs_8));
+
+  const String example_good_codecs_9("avc1");
+  const String example_good_codecs_10("avc3");
+  const String example_good_codecs_11("avc1.42E01E");
+  const String example_good_codecs_12("avc3.42E01E");
+  EXPECT_EQ(media_recorder_handler_->CanSupportMimeType(mime_type_video,
+                                                        example_good_codecs_9),
+            BUILDFLAG(USE_PROPRIETARY_CODECS));
+  EXPECT_EQ(media_recorder_handler_->CanSupportMimeType(mime_type_video,
+                                                        example_good_codecs_10),
+            BUILDFLAG(USE_PROPRIETARY_CODECS));
+  EXPECT_EQ(media_recorder_handler_->CanSupportMimeType(mime_type_video,
+                                                        example_good_codecs_11),
+            BUILDFLAG(USE_PROPRIETARY_CODECS));
+  EXPECT_EQ(media_recorder_handler_->CanSupportMimeType(mime_type_video,
+                                                        example_good_codecs_12),
+            BUILDFLAG(USE_PROPRIETARY_CODECS));
 
   const String example_unsupported_codecs_2("vorbis");
   EXPECT_FALSE(media_recorder_handler_->CanSupportMimeType(
       mime_type_audio, example_unsupported_codecs_2));
 
-  const String example_good_codecs_with_unsupported_tag("HEV1");
-  EXPECT_FALSE(media_recorder_handler_->CanSupportMimeType(
-      mime_type_video, example_good_codecs_with_unsupported_tag));
-
-  const String example_good_codecs_with_supported_tag("hvc1");
+  // HEVC only supports hardware encoding, and whether hardware encoding is
+  // supported depends on the supported profiles retrieved by the
+  // `RenderMediaClient` from the GPU factory. As a result, the current test is
+  // unable to know if HEVC is supported or not, so it should always return
+  // false here.
+  const String example_good_codecs_with_supported_tag("hev1.1.6.L93.B0");
   EXPECT_FALSE(media_recorder_handler_->CanSupportMimeType(
       mime_type_video, example_good_codecs_with_supported_tag));
+
+  const String example_good_codecs_with_supported_tag_2("hvc1.1.6.L93.B0");
+  EXPECT_FALSE(media_recorder_handler_->CanSupportMimeType(
+      mime_type_video, example_good_codecs_with_supported_tag_2));
 }
 
 // Checks that it uses the specified bitrate mode.
@@ -910,7 +938,7 @@
     EXPECT_CALL(*recorder, WriteData).Times(AtLeast(1));
     media::Muxer::VideoParameters params(
         gfx::Size(), 1, media::VideoCodec::kVP9, gfx::ColorSpace());
-    if (IsAvc1CodecSupported(codecs)) {
+    if (IsAvcCodecSupported(codecs)) {
       OnEncodedH264VideoForTesting(base::TimeTicks::Now());
     } else {
       auto buffer =
@@ -961,7 +989,7 @@
   EXPECT_CALL(*recorder, WriteData).Times(AtLeast(1));
   media::Muxer::VideoParameters params(gfx::Size(), 1, media::VideoCodec::kVP9,
                                        gfx::ColorSpace());
-  if (IsAvc1CodecSupported(codecs)) {
+  if (IsAvcCodecSupported(codecs)) {
     OnEncodedH264VideoForTesting(base::TimeTicks::Now());
   } else {
     auto buffer =
@@ -1001,11 +1029,13 @@
 // Array of valid combinations of video/audio/codecs for mp4.
 static const MediaRecorderTestParams kMediaRecorderTestParamsForMp4[] = {
     {false, true, false, "video/mp4", "avc1", false},
-    {false, true, false, "video/mp4", "avc1", false},
+    {false, true, false, "video/mp4", "avc3", false},
     {false, false, true, "audio/mp4", "mp4a.40.2", false},
     {false, true, true, "video/mp4", "avc1,mp4a.40.2", false},
+    {false, true, true, "video/mp4", "avc3,mp4a.40.2", false},
     {false, true, true, "audio/mp4", "opus", false},
     {false, true, true, "video/mp4", "avc1,opus", false},
+    {false, true, true, "video/mp4", "avc3,opus", false},
 };
 
 TEST_P(MediaRecorderHandlerTestForMp4,
@@ -1054,15 +1084,22 @@
   const String good_mp4_video_mime_types[] = {"video/mp4"};
   const String bad_mp4_video_mime_types[] = {"video/MP4"};
 
-  const String good_mp4_video_codecs[] = {"avc1", "avc1.420034", "vp9", "av01",
+  const String good_mp4_video_codecs[] = {"avc1",
+                                          "avc3",
+                                          "avc1.420034",
+                                          "avc3.420034",
+                                          "vp9",
+                                          "av01",
                                           "av01.2.19H.08.0.000.09.16.09.1"};
-  const String bad_mp4_video_codecs[] = {"h264", "vp8",         "avc11",
-                                         "aVc1", "avc1.123456", "av1"};
+  const String bad_mp4_video_codecs[] = {"h264",        "vp8",         "avc11",
+                                         "avc31",       "aVc1",        "aVc3",
+                                         "avc1.123456", "avc3.123456", "av1"};
 
   const String good_mp4_video_codecs_non_proprietory[] = {
       "vp9", "av01", "av01.2.19H.08.0.000.09.16.09.1"};
   const String bad_mp4_video_codecs_non_proprietory[] = {
-      "avc1", "h264", "vp8", "avc11", "aVc1", "avc1.123456", "av1"};
+      "avc1", "avc3", "h264",        "vp8",         "avc11", "avc31",
+      "aVc1", "aVc3", "avc1.123456", "avc3.123456", "av1"};
 
   // audio types.
   const String good_mp4_audio_mime_types[] = {"audio/mp4"};
@@ -1471,16 +1508,28 @@
 
 static const H264ProfileTestParams kH264ProfileTestParams[] = {
     {false, "video/x-matroska", "avc1.42000c"},  // H264PROFILE_BASELINE
+    {false, "video/x-matroska", "avc3.42000c"},  // H264PROFILE_BASELINE
     {false, "video/x-matroska", "avc1.4d000c"},  // H264PROFILE_MAIN
+    {false, "video/x-matroska", "avc3.4d000c"},  // H264PROFILE_MAIN
     {false, "video/x-matroska", "avc1.64000c"},  // H264PROFILE_HIGH
+    {false, "video/x-matroska", "avc3.64000c"},  // H264PROFILE_HIGH
     {false, "video/x-matroska", "avc1.640029"},
+    {false, "video/x-matroska", "avc3.640029"},
     {false, "video/x-matroska", "avc1.640034"},
+    {false, "video/x-matroska", "avc3.640034"},
     {true, "video/x-matroska", "avc1.64000c,pcm"},
+    {true, "video/x-matroska", "avc3.64000c,pcm"},
     {false, "video/mp4", "avc1.42000c"},  // H264PROFILE_BASELINE
+    {false, "video/mp4", "avc3.42000c"},  // H264PROFILE_BASELINE
     {false, "video/mp4", "avc1.4d000c"},  // H264PROFILE_MAIN
+    {false, "video/mp4", "avc3.4d000c"},  // H264PROFILE_MAIN
     {false, "video/mp4", "avc1.64000c"},  // H264PROFILE_HIGH
+
+    {false, "video/mp4", "avc3.64000c"},  // H264PROFILE_HIGH
     {false, "video/mp4", "avc1.640029"},
+    {false, "video/mp4", "avc3.640029"},
     {false, "video/mp4", "avc1.640034"},
+    {false, "video/mp4", "avc3.640034"},
 };
 
 class MediaRecorderHandlerH264ProfileTest
@@ -1731,18 +1780,30 @@
          VideoTrackRecorder::CodecProfile(VideoTrackRecorder::CodecId::kH264)},
         {"avc1,opus",
          VideoTrackRecorder::CodecProfile(VideoTrackRecorder::CodecId::kH264)},
+        {"avc3,opus",
+         VideoTrackRecorder::CodecProfile(VideoTrackRecorder::CodecId::kH264)},
         {"avc1.42E01E,opus", VideoTrackRecorder::CodecProfile(
                                  VideoTrackRecorder::CodecId::kH264,
                                  media::VideoCodecProfile::H264PROFILE_BASELINE,
                                  30u)},
+        {"avc3.42E01E,opus", VideoTrackRecorder::CodecProfile(
+                                 VideoTrackRecorder::CodecId::kH264,
+                                 media::VideoCodecProfile::H264PROFILE_BASELINE,
+                                 30u)},
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 #if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
         {"hvc1,opus",
          VideoTrackRecorder::CodecProfile(VideoTrackRecorder::CodecId::kHevc)},
+        {"hev1,opus",
+         VideoTrackRecorder::CodecProfile(VideoTrackRecorder::CodecId::kHevc)},
         {"hvc1.1.6.L93.B0,opus", VideoTrackRecorder::CodecProfile(
                                      VideoTrackRecorder::CodecId::kHevc,
                                      media::VideoCodecProfile::HEVCPROFILE_MAIN,
                                      93u)},
+        {"hev1.1.6.L93.B0,opus", VideoTrackRecorder::CodecProfile(
+                                     VideoTrackRecorder::CodecId::kHevc,
+                                     media::VideoCodecProfile::HEVCPROFILE_MAIN,
+                                     93u)},
 #endif  // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.cc b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
index bb8d9dc..bc177a1 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.cc
@@ -855,12 +855,6 @@
 const gpu::SyncToken
 ExternalCanvasResource::GetSyncTokenWithOptionalVerification(
     bool needs_verified_token) {
-  GenOrFlushSyncToken();
-  return sync_token_;
-}
-
-void ExternalCanvasResource::GenOrFlushSyncToken() {
-  TRACE_EVENT0("blink", "ExternalCanvasResource::GenOrFlushSyncToken");
   // This method is expected to be used both in WebGL and WebGPU, that's why it
   // uses InterfaceBase.
   if (!sync_token_.HasData()) {
@@ -869,7 +863,8 @@
       interface->GenSyncTokenCHROMIUM(sync_token_.GetData());
   } else if (!sync_token_.verified_flush()) {
     // The offscreencanvas usage needs the sync_token to be verified in order to
-    // be able to use it by the compositor.
+    // be able to use it by the compositor. This is why this method produces a
+    // verified token even if `needs_verified_token` is false.
     int8_t* token_data = sync_token_.GetData();
     auto* interface = InterfaceBase();
     DCHECK(interface);
@@ -877,6 +872,8 @@
     interface->VerifySyncTokensCHROMIUM(&token_data, 1);
     sync_token_.SetVerifyFlush();
   }
+
+  return sync_token_;
 }
 
 base::WeakPtr<WebGraphicsContext3DProviderWrapper>
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource.h b/third_party/blink/renderer/platform/graphics/canvas_resource.h
index 49d0af5b..db91b353 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource.h
@@ -444,7 +444,6 @@
       bool needs_verified_token) override;
   base::WeakPtr<WebGraphicsContext3DProviderWrapper> ContextProviderWrapper()
       const override;
-  void GenOrFlushSyncToken();
 
   ExternalCanvasResource(
       scoped_refptr<gpu::ClientSharedImage> client_si,
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
index 9fba279b..156b8908 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -127,7 +127,7 @@
   }
 
   DCHECK_EQ(old_layer_bounds, cc_picture_layer_->bounds());
-  only_empty_invalidations_ = false;
+  has_empty_invalidations_ = false;
   raster_invalidator_->Generate(paint_chunks, layer_offset, layer_bounds,
                                 layer_state);
 
@@ -157,11 +157,13 @@
       cc_display_item_list_ && layer_bounds == old_layer_bounds &&
       cc_picture_layer_->draws_content() == pending_layer.DrawsContent() &&
       !raster_under_invalidation_params;
+  bool only_empty_invalidations =
+      has_empty_invalidations_ && cc_display_item_list_;
   if (may_be_unchanged) {
     DCHECK_EQ(cc_picture_layer_->bounds(), layer_bounds);
     if (!RuntimeEnabledFeatures::RasterInducingScrollEnabled() ||
         // See InvalidateRect().
-        !only_empty_invalidations_) {
+        !only_empty_invalidations) {
       return;
     }
   }
@@ -176,7 +178,7 @@
 
   if (RuntimeEnabledFeatures::RasterInducingScrollEnabled()) {
     if ((had_raster_inducing_scroll || HasRasterInducingScroll()) &&
-        only_empty_invalidations_) {
+        only_empty_invalidations) {
       // See InvalidateRect().
       cc_picture_layer_->SetForceUpdateRecordingSource();
     } else if (may_be_unchanged) {
@@ -231,11 +233,10 @@
     // Set a flag so that UpdateCcPictureLayer can force update of the layer to
     // ensure the recording source is up to date.
     if (RuntimeEnabledFeatures::RasterInducingScrollEnabled()) {
-      only_empty_invalidations_ = true;
+      has_empty_invalidations_ = true;
     }
     return;
   }
-  only_empty_invalidations_ = false;
   cc_display_item_list_ = nullptr;
   cc_picture_layer_->SetNeedsDisplayRect(rect);
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
index e389b8d..1fc81eb2 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
@@ -60,7 +60,7 @@
   scoped_refptr<cc::DisplayItemList> cc_display_item_list_;
   Member<RasterInvalidator> raster_invalidator_;
   // Used during UpdateCcPictureLayer().
-  bool only_empty_invalidations_ = false;
+  bool has_empty_invalidations_ = false;
 
   String debug_name_;
 #if EXPENSIVE_DCHECKS_ARE_ON()
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index b9b8bf0..b86460f 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1688,10 +1688,6 @@
       status: "experimental",
     },
     {
-      name: "ExcludeTransparentTextsFromBeingLcpEligible",
-      status: "stable",
-    },
-    {
       name: "ExperimentalContentSecurityPolicyFeatures",
       status: "experimental",
       base_feature: "none",
@@ -2036,6 +2032,12 @@
       status: "stable",
     },
     {
+      // Enables the private model training API in Protected Audience.
+      // https://github.com/WICG/turtledove/blob/main/PA_private_model_training.md
+      name: "FledgePrivateModelTraining",
+      status: "test",
+    },
+    {
       // Enables real time reporting API in Protected Audience.
       // https://github.com/WICG/turtledove/blob/main/PA_real_time_monitoring.md
       name: "FledgeRealTimeReporting",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 6ec853a8..3d6a73a 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2686,6 +2686,8 @@
 crbug.com/343720396 external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-unsafe-none.https.html?1-2 [ Crash Failure Timeout ]
 
 # ====== New tests from wpt-importer added here ======
+[ Mac13 ] external/wpt/webrtc-encoded-transform/tentative/RTCEncodedVideoFrame-metadata.https.html [ Timeout ]
+[ Mac13 ] external/wpt/webrtc-encoded-transform/tentative/RTCEncodedVideoFrame-serviceworker-failure.https.html [ Timeout ]
 crbug.com/383880387 [ Mac13 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html?7-8 [ Crash ]
 crbug.com/383880387 [ Win10.20h2 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-unsafe-none-to-unsafe-none.https.html?7-8 [ Crash ]
 crbug.com/383911311 [ Mac13 ] external/wpt/webrtc/RTCRtpParameters-encodings.html [ Timeout ]
@@ -2701,6 +2703,7 @@
 crbug.com/383825172 [ Mac12 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-font-inheriting.tentative.html [ Failure ]
 crbug.com/383825172 [ Mac12-arm64 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-font-inheriting.tentative.html [ Failure Pass ]
 crbug.com/383825172 [ Mac11 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-font-inheriting.tentative.html [ Failure ]
+crbug.com/383825172 [ Mac14-arm64 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-font-inheriting.tentative.html [ Failure ]
 crbug.com/383825172 [ Mac14 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-font-inheriting.tentative.html [ Failure ]
 crbug.com/383825172 [ Mac15-arm64 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-font-inheriting.tentative.html [ Failure ]
 crbug.com/383814062 [ Mac13 ] virtual/disable-raster-inducing-scroll/external/wpt/acid/acid2/reftest.html [ Failure ]
@@ -5756,12 +5759,6 @@
 crbug.com/1249176 [ Mac12-arm64 ] external/wpt/largest-contentful-paint/multiple-redirects-TAO.html [ Failure Pass ]
 crbug.com/1249176 [ Mac11-arm64 ] http/tests/devtools/elements/styles-2/nested-pseudo-elements.js [ Failure Pass ]
 
-# These tests should be removed once we enable the flag
-crbug.com/373263977 virtual/expose-lcp-render-time/external/wpt/largest-contentful-paint/non-tao-image-load-after-fcp.tentative.html [ Failure Pass ]
-crbug.com/373263977 virtual/expose-lcp-render-time/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-after.tentative.html [ Failure Pass ]
-crbug.com/373263977 virtual/expose-lcp-render-time/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-at-fcp.tentative.html [ Failure Pass ]
-crbug.com/373263977 virtual/expose-lcp-render-time/external/wpt/largest-contentful-paint/non-tao-image-subsequent-lcp-candidate.tentative.html [ Failure Pass ]
-
 # mac-arm CI 10-05-2021
 crbug.com/1249176 [ Mac11-arm64 ] virtual/gpu-rasterization/images/directly-composited-image-orientation.html [ Failure Pass ]
 crbug.com/1249176 [ Mac12-arm64 ] virtual/gpu-rasterization/images/directly-composited-image-orientation.html [ Failure Pass ]
@@ -8691,8 +8688,8 @@
 crbug.com/366261815 external/wpt/signed-exchange/subresource/sxg-subresource-header-integrity-mismatch.tentative.html [ Failure Pass Timeout ]
 
 # Gardener 2024-10-21
-crbug.com/374661016 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/print/frame_tentative.py [ Timeout Failure Pass ]
-crbug.com/374661016 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/capture_screenshot/frame_tentative.py [ Timeout Failure Pass ]
+crbug.com/374661016 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/print/frame_tentative.py [ Failure Pass Timeout ]
+crbug.com/374661016 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/capture_screenshot/frame_tentative.py [ Failure Pass Timeout ]
 
 # Fix importer 2024-10-22
 crbug.com/374978346 [ Linux ] virtual/document-isolation-policy/external/wpt/html/document-isolation-policy/credentialless-dedicated-worker.https.tentative.window.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 2d22cfe..378af7d 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1438,27 +1438,6 @@
     "expires": "Jun 1, 2025"
   },
   {
-    "owners": [
-      "haoliuk@chromium.org"
-    ],
-    "prefix": "expose-lcp-render-time",
-    "platforms": [
-      "Linux",
-      "Mac",
-      "Win"
-    ],
-    "bases": [
-      "external/wpt/largest-contentful-paint/non-tao-image-load-after-fcp.tentative.html",
-      "external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-after.tentative.html",
-      "external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-at-fcp.tentative.html",
-      "external/wpt/largest-contentful-paint/non-tao-image-subsequent-lcp-candidate.tentative.html"
-    ],
-    "exclusive_tests": "ALL",
-    "args": [
-      "--enable-blink-features=ExposeRenderTimeNonTaoDelayedImage"],
-    "expires": "Jul 1, 2024"
-  },
-  {
     "prefix": "parakeet",
     "owners": ["brandm@microsoft.com"],
     "platforms": ["Linux", "Mac", "Win"],
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 21802584..11b0fd4c 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -671091,6 +671091,13 @@
        {}
       ]
      ],
+     "siblings-with-anonymous-timelines.html": [
+      "cc392856d0fea9193f2b142a99714dbb657deb26",
+      [
+       null,
+       {}
+      ]
+     ],
      "timeline-offset-in-keyframe-change-timeline.tentative.html": [
       "5a70820b881edf687961606f79153dab96d6ac2d",
       [
@@ -802927,7 +802934,7 @@
          ]
         ],
         "pointer_mouse_drag.py": [
-         "7cd2e386c67ee435401ee672d1ce142510d0bb38",
+         "df4a98c2f8a95510b6164b4ba6cb1317a8e48552",
          [
           null,
           {
@@ -805355,7 +805362,7 @@
         ]
        ],
        "pointer_mouse.py": [
-        "a3ff8ae80a9845fc840313adc8be627f1fb6cfd3",
+        "a3dff9ec9c51f90c65f587f9ed3e0f325decd6a2",
         [
          null,
          {}
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-001-ref.html
similarity index 70%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-001-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-001-ref.html
index cdb66847..95d820a 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-001-ref.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-001-ref.html
@@ -1,6 +1,6 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>CSS Test Reference File: ::scroll-next-button and ::scroll-prev-button</title>
+<title>CSS Test Reference File: ::scroll-button()s box creation</title>
 <style>
   div {
     background: green;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-001.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-001.html
similarity index 74%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-001.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-001.html
index 22a01712..2c5e435 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-001.html
@@ -1,8 +1,8 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>CSS Test: ::scroll-next-button and ::scroll-prev-button box creation</title>
+<title>CSS Test: ::scroll-button()s box creation</title>
 <link rel="match" href="scroll-buttons-001-ref.html">
-<link rel="help" href="https://github.com/flackr/carousel/tree/main/scroll-button">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons">
 <style>
   div::scroll-button(block-end) {
     content: "";
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-002.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-002.html
similarity index 82%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-002.html
index ab4d66b..0ea9bb6a 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-002.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-002.html
@@ -1,8 +1,8 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>CSS Test: ::scroll-next-button and ::scroll-prev-button invalidation</title>
+<title>CSS Test: ::scroll-button()s style invalidation</title>
 <link rel="match" href="scroll-buttons-001-ref.html">
-<link rel="help" href="https://github.com/flackr/carousel/tree/main/scroll-marker">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons">
 <style>
   :root {
     --scroll-buttons-background: red;
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-activation-with-columns.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-activation-with-columns.html
similarity index 89%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-activation-with-columns.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-activation-with-columns.html
index 544f24d..b0b8574c 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-activation-with-columns.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-activation-with-columns.html
@@ -1,7 +1,7 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>CSS Overflow Test: ::scroll-next-button and ::scroll-prev-button activation with column scroll markers</title>
-<link rel="help" href="https://github.com/flackr/carousel/tree/main/scroll-button">
+<title>CSS Overflow Test: ::scroll-button() activation with column scroll markers</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-activation.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-activation.html
similarity index 91%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-activation.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-activation.html
index 153fe80..973197f 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-activation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-activation.html
@@ -1,7 +1,7 @@
 <!doctype html>
 <meta charset="utf-8">
-<title>CSS Test: ::scroll-button(inline-end) activation</title>
-<link rel="help" href="https://github.com/flackr/carousel/tree/main/scroll-button">
+<title>CSS Test: ::scroll-button() activation</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-container-query-crash.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-container-query-crash.html
similarity index 78%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-container-query-crash.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-container-query-crash.html
index dcc07d1..e548922 100644
--- a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-container-query-crash.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-container-query-crash.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<title>CSS Overflow Test: ::scroll-button depending on size container query crash</title>
+<title>CSS Overflow Test: ::scroll-button() depending on size container query crash</title>
 <link rel="help" href="https://crbug.com/375973472">
 <style>
   #scroller {
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-ref.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-ref.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-rtl-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-rtl-ref.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-rtl-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-rtl-ref.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-rtl.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-rtl.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-rtl.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-rtl.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-vertical-ltr-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-vertical-ltr-ref.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-vertical-ltr-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-vertical-ltr-ref.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-vertical-ltr.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-vertical-ltr.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled-vertical-ltr.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled-vertical-ltr.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-disabled.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-disabled.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-dynamic-create-remove.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-dynamic-create-remove.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-dynamic-create-remove.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-dynamic-create-remove.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-ref.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-ref.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-rtl-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-rtl-ref.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-rtl-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-rtl-ref.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-rtl.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-rtl.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-rtl.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-rtl.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-vertical-ltr-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-vertical-ltr-ref.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-vertical-ltr-ref.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-vertical-ltr-ref.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-vertical-ltr.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-vertical-ltr.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled-vertical-ltr.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled-vertical-ltr.html
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled.html
similarity index 100%
rename from third_party/blink/web_tests/wpt_internal/css/css-overflow/scroll-buttons-enabled.html
rename to third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-buttons-enabled.html
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-after-fcp.tentative.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-after-fcp.tentative.html
deleted file mode 100644
index 06b065b..0000000
--- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-after-fcp.tentative.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE HTML>
-<meta charset=utf-8>
-<title>Non-Tao Image Load and Render After FCP.
-</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/largest-contentful-paint-helpers.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-
-<body>
-  <script>
-    promise_test(async t => {
-      // Add text so that FCP is set and image rendering time would be larger than FCP.
-      add_text('Add to set FCP');
-
-      // wait for 1 animation frame so that FCP is set.
-      await raf();
-
-      const non_tao_image_url = get_host_info().OTHER_ORIGIN + '/images/blue.png';
-
-      await loadImage(non_tao_image_url);
-
-      lcp = await getLCPStartTime('blue.png');
-
-      fcp = getFCPStartTime();
-
-      checkLCPEntryForNonTaoImages({'lcp':lcp, 'fcp':fcp});
-    }, 'Non-Tao Image Load and Render After FCP.')
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-after.tentative.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-after.tentative.html
deleted file mode 100644
index 57f29c3..0000000
--- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-after.tentative.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE HTML>
-<meta charset=utf-8>
-<title>Non-Tao Image Load Before FCP and Render After FCP.
-</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/largest-contentful-paint-helpers.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-
-<body>
-  <script>
-    promise_test(async t => {
-      const non_tao_image_url = get_host_info().OTHER_ORIGIN + '/images/blue.png';
-
-      img = await loadImage(non_tao_image_url, true);
-
-      // Add text so that FCP is set and image rendering time would be larger than FCP.
-      add_text('Add to set FCP');
-
-      // wait for 1 animation frame so that FCP is set.
-      await raf();
-
-      // Pass empty string to select the LCP entry corresponding to the text as LCP.url is
-      // empty for text elements.
-      lcp = await getLCPStartTime('');
-
-      img.style.opacity = 1;
-
-      lcp = await getLCPStartTime('blue.png');
-
-      fcp = getFCPStartTime();
-
-      checkLCPEntryForNonTaoImages({ 'lcp': lcp, 'fcp': fcp });
-    }, 'Non-Tao Image Load Before FCP and Render After FCP.')
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-at-fcp.tentative.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-at-fcp.tentative.html
deleted file mode 100644
index b209b50c..0000000
--- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-at-fcp.tentative.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE HTML>
-<meta charset=utf-8>
-<title>Non-Tao Image Load Before LCP and Render at the Same Time of FCP.
-</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/largest-contentful-paint-helpers.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-
-<body>
-  <script>
-    promise_test(async t => {
-      const non_tao_image_url = get_host_info().OTHER_ORIGIN + '/images/blue.png';
-
-      await loadImage(non_tao_image_url);
-
-      lcp = await getLCPStartTime('blue.png');
-
-      fcp = getFCPStartTime();
-
-      checkLCPEntryForNonTaoImages({ 'lcp': lcp, 'fcp': fcp });
-    }, 'Non-Tao Image Load Before LCP and Render at the Same Time of FCP.')
-  </script>
-  <!-- <img src='{{location[scheme]}}://{{hosts[alt][www]}}:{{ports[http][0]}}/images/blue.png'> -->
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-subsequent-lcp-candidate.tentative.html b/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-subsequent-lcp-candidate.tentative.html
deleted file mode 100644
index 4d799c1..0000000
--- a/third_party/blink/web_tests/external/wpt/largest-contentful-paint/non-tao-image-subsequent-lcp-candidate.tentative.html
+++ /dev/null
@@ -1,49 +0,0 @@
-<!DOCTYPE HTML>
-<meta charset=utf-8>
-<title>Non-Tao Image Subsequent LCP candidates.
-</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="resources/largest-contentful-paint-helpers.js"></script>
-<script src="/common/get-host-info.sub.js"></script>
-
-<body>
-  <script>
-    promise_test(async t => {
-      let small_image_path = '/images/lcp-100x50.png';
-      let big_image_path = '/images/lcp-256x256.png';
-      let non_tao_scheme = get_host_info().OTHER_ORIGIN;
-
-      // Load non-tao image with 0 opacity so it won't trigger an LCP entry.
-      big_image = await loadImage(non_tao_scheme + big_image_path, true);
-
-      // Load a smaller non-tao image with 0 opacity.
-      small_image = await loadImage(non_tao_scheme + small_image_path, true);
-
-      // Add text so that FCP is set.
-      add_text('text');
-
-      // wait for 1 animation frame so that FCP is set.
-      await raf();
-
-      // The url of text LCP element is empty.
-      lcp = await getLCPStartTime('');
-
-      // Rendered loaded image after FCP.
-      small_image.style.opacity = 1;
-      lcp = await getLCPStartTime(small_image_path);
-
-      fcp = getFCPStartTime();
-
-      checkLCPEntryForNonTaoImages({ 'lcp': lcp, 'fcp': fcp });
-
-      // This is to verify subsequent LCP candidates also have the start time set correctly.
-      big_image.style.opacity = 1;
-
-      lcp = await getLCPStartTime(big_image_path);
-
-      checkLCPEntryForNonTaoImages({ 'lcp': lcp, 'fcp': fcp });
-
-    }, 'Non-Tao Image Subsequent LCP candidates.')
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-canvas-media-source.https.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-canvas-media-source.https.html
index 3ae2dd78..0c5f47f 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-canvas-media-source.https.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-canvas-media-source.https.html
@@ -8,13 +8,17 @@
   <meta name=variant content="?mimeType=video/webm;codecs=vp8,opus">
   <meta name=variant content="?mimeType=video/webm;codecs=vp9,opus">
   <meta name=variant content="?mimeType=video/webm;codecs=av1,opus">
-  <meta name=variant content="?mimeType=video/mp4;codecs=avc1,mp4a.40.2">
+  <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2">
+  <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2">
   <meta name=variant content="?mimeType=video/mp4;codecs=vp9,opus">
   <meta name=variant content="?mimeType=video/mp4;codecs=av01,opus">
   <meta name=variant content="?mimeType=video/mp4;codecs=av01,mp4a.40.2">
   <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus">
+  <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus">
   <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2">
+  <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2">
   <meta name=variant content="?mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus">
+  <meta name=variant content="?mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus">
   <meta name=variant content="?mimeType=video/mp4">
   <link rel="help"
         href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#dom-mediarecorder-mimeType">
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-events-and-exceptions.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-events-and-exceptions.html
index b74b496..b49f4c6 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-events-and-exceptions.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-events-and-exceptions.html
@@ -7,13 +7,17 @@
     <meta name=variant content="?mimeType=video/webm;codecs=vp8,opus">
     <meta name=variant content="?mimeType=video/webm;codecs=vp9,opus">
     <meta name=variant content="?mimeType=video/webm;codecs=av1,opus">
-    <meta name=variant content="?mimeType=video/mp4;codecs=avc1,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2">
     <meta name=variant content="?mimeType=video/mp4;codecs=vp9,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=av01,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=av01,mp4a.40.2">
     <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus">
+    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2">
     <meta name=variant content="?mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus">
+    <meta name=variant content="?mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus">
     <meta name=variant content="?mimeType=video/mp4">
     <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
     <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-mimetype.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-mimetype.html
index 17eec0f..24a9a1db 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-mimetype.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-mimetype.html
@@ -34,10 +34,13 @@
   'video/webm; codecs="vp8"',
   'video/webm; codecs="vp9"',
   'video/webm; codecs="av1"',
-  'video/mp4; codecs="avc1"',
+  'video/mp4; codecs="avc1.64003E"',
+  'video/mp4; codecs="avc3.64003E"',
   'video/mp4; codecs="vp9"',
   'video/mp4; codecs="hvc1.1.6.L186.B0"',
+  'video/mp4; codecs="hev1.1.6.L186.B0"',
   'video/x-matroska; codecs="hvc1.1.6.L186.B0"',
+  'video/x-matroska; codecs="hev1.1.6.L186.B0"',
 ];
 
 const AUDIO_VIDEO_MIME_TYPES = [
@@ -46,11 +49,15 @@
   'video/webm; codecs="vp9, vorbis"',
   'video/webm; codecs="vp9, opus"',
   'video/webm; codecs="av1, opus"',
-  'video/mp4; codecs="avc1, mp4a.40.2"',
+  'video/mp4; codecs="avc1.64003E, mp4a.40.2"',
+  'video/mp4; codecs="avc3.64003E, mp4a.40.2"',
   'video/mp4; codecs="vp9, opus"',
   'video/mp4; codecs="hvc1.1.6.L186.B0, mp4a.40.2"',
+  'video/mp4; codecs="hev1.1.6.L186.B0, mp4a.40.2"',
   'video/mp4; codecs="hvc1.1.6.L186.B0, opus"',
+  'video/mp4; codecs="hev1.1.6.L186.B0, opus"',
   'video/x-matroska; codecs="hvc1.1.6.L186.B0, opus"',
+  'video/x-matroska; codecs="hev1.1.6.L186.B0, opus"',
 ];
 
 const AUDIO_MIME_TYPES = [
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-pause-resume.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-pause-resume.html
index 1797953a..88e2e6af65 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-pause-resume.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-pause-resume.html
@@ -7,15 +7,20 @@
     <meta name=variant content="?mimeType=video/webm;codecs=vp8,opus">
     <meta name=variant content="?mimeType=video/webm;codecs=vp9,opus">
     <meta name=variant content="?mimeType=video/webm;codecs=av1,opus">
-    <meta name=variant content="?mimeType=video/mp4;codecs=avc1,mp4a.40.2">
-    <meta name=variant content="?mimeType=video/mp4;codecs=avc1,opus">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,opus">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=vp9,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=vp9,mp4a.40.2">
     <meta name=variant content="?mimeType=video/mp4;codecs=av01,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=av01,mp4a.40.2">
     <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus">
+    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2">
     <meta name=variant content="?mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus">
+    <meta name=variant content="?mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus">
     <meta name=variant content="?mimeType=video/mp4">
     <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
     <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-peerconnection.https.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-peerconnection.https.html
index f956a3d1..e101749d 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-peerconnection.https.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-peerconnection.https.html
@@ -12,15 +12,19 @@
   <meta name=variant content="?kinds=video,audio&mimeType=video/webm;codecs=vp8,opus">
   <meta name=variant content="?kinds=video&mimeType=video/webm;codecs=vp9">
   <meta name=variant content="?kinds=video,audio&mimeType=video/webm;codecs=vp9,opus">
-  <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=avc1,mp4a.40.2">
+  <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2">
+  <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2">
   <meta name=variant content="?kinds=video&mimeType=video/mp4;codecs=vp9">
   <meta name=variant content="?kinds=audio&mimeType=audio/mp4;codecs=opus">
   <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=vp9,opus">
   <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=av01,opus">
   <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=av01,mp4a.40.2">
   <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus">
+  <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus">
   <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2">
+  <meta name=variant content="?kinds=video,audio&mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2">
   <meta name=variant content="?kinds=video,audio&mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus">
+  <meta name=variant content="?kinds=video,audio&mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus">
   <meta name=variant content="?kinds=video,audio&mimeType=video/mp4">
 
   <link rel="help"
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html
index f1c16a7..b7b4522 100644
--- a/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html
+++ b/third_party/blink/web_tests/external/wpt/mediacapture-record/MediaRecorder-stop.html
@@ -6,13 +6,17 @@
     <meta name=variant content="?mimeType=video/webm;codecs=vp8,opus">
     <meta name=variant content="?mimeType=video/webm;codecs=vp9,opus">
     <meta name=variant content="?mimeType=video/webm;codecs=av1,opus">
-    <meta name=variant content="?mimeType=video/mp4;codecs=avc1,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc1.64003E,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=avc3.64003E,mp4a.40.2">
     <meta name=variant content="?mimeType=video/mp4;codecs=vp9,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=av01,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=av01,mp4a.40.2">
     <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,opus">
+    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,opus">
     <meta name=variant content="?mimeType=video/mp4;codecs=hvc1.1.6.L186.B0,mp4a.40.2">
+    <meta name=variant content="?mimeType=video/mp4;codecs=hev1.1.6.L186.B0,mp4a.40.2">
     <meta name=variant content="?mimeType=video/x-matroska;codecs=hvc1.1.6.L186.B0,opus">
+    <meta name=variant content="?mimeType=video/x-matroska;codecs=hev1.1.6.L186.B0,opus">
     <meta name=variant content="?mimeType=video/mp4">
     <link rel="help" href="https://w3c.github.io/mediacapture-record/MediaRecorder.html#mediarecorder">
     <script src="/resources/testharness.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/siblings-with-anonymous-timelines.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/siblings-with-anonymous-timelines.html
new file mode 100644
index 0000000..cc39285
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/siblings-with-anonymous-timelines.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines-anonymous">
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#view-timelines-anonymous">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/scroll-animations/scroll-timelines/testcommon.js"></script>
+<title>The "animation-timeline" CSS property yields a unique anonymous timeline for siblings matching the same CSS rules</title>
+<style>
+
+  @keyframes anim {
+    from { opacity: 0 }
+  }
+
+  .scroller {
+    overflow-y: scroll;
+    width: 100px;
+    height: 100px;
+  }
+
+  .scroller > div {
+    width: 100%;
+    height: 100%;
+
+    animation: anim auto linear;
+  }
+
+  .scroller.scroll > div {
+    animation-timeline: scroll();
+  }
+
+  .scroller.view > div {
+    animation-timeline: view();
+  }
+
+</style>
+</head>
+<body>
+
+  <div class="scroller scroll">
+    <div></div>
+    <div></div>
+    <div></div>
+    <div></div>
+    <div></div>
+  </div>
+
+  <div class="scroller view">
+    <div></div>
+    <div></div>
+    <div></div>
+    <div></div>
+    <div></div>
+  </div>
+
+  <script>
+
+const types = ["scroll", "view"];
+
+types.forEach(type => {
+  test(t => {
+    const scroller = document.querySelector(`.scroller.${type}`);
+
+    const animations = scroller.getAnimations({ subtree: true });
+    const numberOfChildren = scroller.childElementCount;
+    assert_equals(animations.length, numberOfChildren, "There are as many animations as there are children");
+
+    const timelines = new Set;
+    for (const animation of animations)
+      timelines.add(animation.timeline);
+    assert_equals(timelines.size, numberOfChildren, `There are as many ${type} timelines as there are children`);
+  }, `Setting "animation-timeline: ${type}()" yields a unique ${type} timeline for siblings matching the same CSS rules`);
+});
+
+  </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse_drag.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse_drag.py
index 7cd2e38..df4a98c 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse_drag.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse_drag.py
@@ -9,6 +9,7 @@
 
 pytestmark = pytest.mark.asyncio
 
+
 @pytest.mark.parametrize("drag_duration", [0, 300, 800])
 @pytest.mark.parametrize(
     "dx, dy", [(20, 0), (0, 15), (10, 15), (-20, 0), (10, -15), (-10, -15)]
@@ -54,12 +55,13 @@
     assert e["type"] == "mouseup"
     assert e["pageX"] == pytest.approx(initial_center["x"] + dx, abs=1.0)
     assert e["pageY"] == pytest.approx(initial_center["y"] + dy, abs=1.0)
+
     # check resulting location of the dragged element
     final_rect = await get_element_rect(
         bidi_session, context=top_context, element=drag_target
     )
-    assert initial_rect["x"] + dx == final_rect["x"]
-    assert initial_rect["y"] + dy == final_rect["y"]
+    assert final_rect["x"] == pytest.approx(initial_rect["x"] + dx, abs=1.0)
+    assert final_rect["y"] == pytest.approx(initial_rect["y"] + dy, abs=1.0)
 
 
 @pytest.mark.parametrize("drag_duration", [0, 300, 800])
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py
index a3ff8ae..a3dff9e 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py
@@ -293,6 +293,7 @@
     drag_target = session.find.css("#dragTarget", all=False)
     initial_rect = drag_target.rect
     initial_center = get_inview_center(initial_rect, get_viewport_rect(session))
+
     # Conclude chain with extra move to allow time for last queued
     # coordinate-update of drag_target and to test that drag_target is "dropped".
     mouse_chain \
@@ -302,15 +303,17 @@
         .pointer_up() \
         .pointer_move(80, 50, duration=100, origin="pointer") \
         .perform()
+
     # mouseup that ends the drag is at the expected destination
     e = get_events(session)[1]
     assert e["type"] == "mouseup"
     assert e["pageX"] == pytest.approx(initial_center["x"] + dx, abs=1.0)
     assert e["pageY"] == pytest.approx(initial_center["y"] + dy, abs=1.0)
+
     # check resulting location of the dragged element
     final_rect = drag_target.rect
-    assert initial_rect["x"] + dx == final_rect["x"]
-    assert initial_rect["y"] + dy == final_rect["y"]
+    assert final_rect["x"] == pytest.approx(initial_rect["x"] + dx, abs=1.0)
+    assert final_rect["y"] == pytest.approx(initial_rect["y"] + dy, abs=1.0)
 
 
 @pytest.mark.parametrize("drag_duration", [0, 300, 800])
diff --git a/third_party/blink/web_tests/virtual/expose-lcp-render-time/README.md b/third_party/blink/web_tests/virtual/expose-lcp-render-time/README.md
deleted file mode 100644
index 025e2e7..0000000
--- a/third_party/blink/web_tests/virtual/expose-lcp-render-time/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-This directory contains a test that is run with
---enable-blink-features=ExposeRenderTimeNonTaoDelayedImage command line flag.
-The tests are
-external/wpt/largest-contentful-paint/non-tao-image-load-after-fcp.tentative.html,
-external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-after.tentative.html,
-external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-at-fcp.tentative.html,
-external/wpt/largest-contentful-paint/non-tao-image-subsequent-lcp-candidate.tentative.html.
diff --git a/third_party/blink/web_tests/virtual/expose-lcp-render-time/external/wpt/performance-timeline/README.text b/third_party/blink/web_tests/virtual/expose-lcp-render-time/external/wpt/performance-timeline/README.text
deleted file mode 100644
index 025e2e7..0000000
--- a/third_party/blink/web_tests/virtual/expose-lcp-render-time/external/wpt/performance-timeline/README.text
+++ /dev/null
@@ -1,7 +0,0 @@
-This directory contains a test that is run with
---enable-blink-features=ExposeRenderTimeNonTaoDelayedImage command line flag.
-The tests are
-external/wpt/largest-contentful-paint/non-tao-image-load-after-fcp.tentative.html,
-external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-after.tentative.html,
-external/wpt/largest-contentful-paint/non-tao-image-load-before-fcp-render-at-fcp.tentative.html,
-external/wpt/largest-contentful-paint/non-tao-image-subsequent-lcp-candidate.tentative.html.
diff --git a/third_party/chromite b/third_party/chromite
index 1a227b9..0eb1f9f 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 1a227b9c2e36d43819765488191b04d8089b5709
+Subproject commit 0eb1f9f5bb6e004cd0593b65bf01a9022d5b69a4
diff --git a/third_party/crossbench b/third_party/crossbench
index d64333a..7ed7cb2 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit d64333a2353b602e2623119369c0b120c505bd22
+Subproject commit 7ed7cb238e3999bc37a0bfa6d33c0d17fcadf17f
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index cfa1e22c..6551e43 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit cfa1e22c8d59a3f2912aa488172f7185a6eecfad
+Subproject commit 6551e43966d7dd4d085ca6c0fc05a9ef2f3f4176
diff --git a/third_party/libc++/src b/third_party/libc++/src
index 5e0e903..d0ddad5 160000
--- a/third_party/libc++/src
+++ b/third_party/libc++/src
@@ -1 +1 @@
-Subproject commit 5e0e903f1345443d9e4c5946c9af1fa95a974063
+Subproject commit d0ddad5b79581e19d8e1aec627bb2ad86e1554cd
diff --git a/third_party/llvm-libc/src b/third_party/llvm-libc/src
index ac38246..2394cbb 160000
--- a/third_party/llvm-libc/src
+++ b/third_party/llvm-libc/src
@@ -1 +1 @@
-Subproject commit ac382467315c999745520e6d4de94a3e54a95789
+Subproject commit 2394cbb7cbc6683052e6ecb33633b293baf161eb
diff --git a/third_party/perfetto b/third_party/perfetto
index 57f2ef8..076d398 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 57f2ef884f18866748bf7a36526caab554111a03
+Subproject commit 076d3983da9bbbe312f1c8d5fb77867e9a41779d
diff --git a/third_party/skia b/third_party/skia
index 648d444..320586c 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 648d444741bd3ba3f105aafe4b86841c7f05be9b
+Subproject commit 320586ca7966c2e9fa77456de01f479d7f5a6da0
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6b62e4f..3b76cc29 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21735,6 +21735,8 @@
   <int value="546464390" label="LiveCaption:enabled"/>
   <int value="546520086" label="enable-data-reduction-proxy-savings-promo"/>
   <int value="546710806" label="disable-easy-signin"/>
+  <int value="547077683"
+      label="UseManagedPrintJobOptionsInPrintPreview:disabled"/>
   <int value="547170394"
       label="OmniboxAlternateMatchDescriptionSeparator:disabled"/>
   <int value="547654419" label="CrOSEnforceSystemAecAgc:enabled"/>
@@ -22942,6 +22944,8 @@
   <int value="1034347979"
       label="OmniboxFocusTriggersContextualWebZeroSuggest:disabled"/>
   <int value="1034991480" label="ExperimentalOmniboxLabs:disabled"/>
+  <int value="1035400639"
+      label="UseManagedPrintJobOptionsInPrintPreview:enabled"/>
   <int value="1035517284" label="DownloadsMigrateToJobsAPI:disabled"/>
   <int value="1036007454"
       label="CmdDecoderAlwaysGetSizeFromSourceTexture:enabled"/>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml
index 2d8aabf..2728d2d 100644
--- a/tools/metrics/histograms/metadata/autofill/enums.xml
+++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -737,6 +737,9 @@
              edited"/>
   <int value="10561"
       label="ALTERNATIVE_FAMILY_NAME: Autofilled value accepted"/>
+  <int value="10688"
+      label="NAME_LAST_PREFIX: Not autofilled or autofilled value edited"/>
+  <int value="10689" label="NAME_LAST_PREFIX: Autofilled value accepted"/>
 </enum>
 
 <enum name="AutofillDeveloperEngagement">
@@ -965,6 +968,8 @@
   <int value="2625" label="ALTERNATIVE_GIVEN_NAME: accepted"/>
   <int value="2640" label="ALTERNATIVE_FAMILY_NAME: edited"/>
   <int value="2641" label="ALTERNATIVE_FAMILY_NAME: accepted"/>
+  <int value="2672" label="NAME_LAST_PREFIX: edited"/>
+  <int value="2673" label="NAME_LAST_PREFIX: accepted"/>
 </enum>
 
 <enum name="AutofillErrorDialogType">
@@ -1776,6 +1781,7 @@
   <int value="163" label="ALTERNATIVE_FULL_NAME"/>
   <int value="164" label="ALTERNATIVE_GIVEN_NAME"/>
   <int value="165" label="ALTERNATIVE_FAMILY_NAME"/>
+  <int value="167" label="NAME_LAST_PREFIX"/>
 </enum>
 
 <enum name="AutofillFilledCardInformationBubbleFieldClicked">
@@ -3796,6 +3802,22 @@
   <int value="996"
       label="ALTERNATIVE_FAMILY_NAME - Predictions different - Value agrees
              with both predictions"/>
+  <int value="1003"
+      label="NAME_LAST_PREFIX - Predictions equal - Value agrees"/>
+  <int value="1004"
+      label="NAME_LAST_PREFIX - Predictions equal - Value disagrees"/>
+  <int value="1005"
+      label="NAME_LAST_PREFIX - Predictions different - Value agrees with old
+             prediction"/>
+  <int value="1006"
+      label="NAME_LAST_PREFIX - Predictions different - Value agrees with new
+             prediction"/>
+  <int value="1007"
+      label="NAME_LAST_PREFIX - Predictions different - Value agrees with
+             neither prediction"/>
+  <int value="1008"
+      label="NAME_LAST_PREFIX - Predictions different - Value agrees with
+             both predictions"/>
 </enum>
 
 <enum name="AutofillPreferenceSetter">
@@ -4078,6 +4100,8 @@
   <int value="2625" label="ALTERNATIVE_GIVEN_NAME: Empty on page load"/>
   <int value="2640" label="ALTERNATIVE_FAMILY_NAME: Pre-filled on page load"/>
   <int value="2641" label="ALTERNATIVE_FAMILY_NAME: Empty on page load"/>
+  <int value="2672" label="NAME_LAST_PREFIX: Pre-filled on page load"/>
+  <int value="2673" label="NAME_LAST_PREFIX: Empty on page load"/>
 </enum>
 
 <enum name="AutofillProfileImportDecision">
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 777b472..2e76a181 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -525,6 +525,7 @@
   <variant name="NAME_LAST"/>
   <variant name="NAME_LAST_CONJUNCTION"/>
   <variant name="NAME_LAST_FIRST"/>
+  <variant name="NAME_LAST_PREFIX"/>
   <variant name="NAME_LAST_SECOND"/>
   <variant name="NAME_MIDDLE"/>
   <variant name="NAME_SUFFIX"/>
diff --git a/tools/metrics/histograms/metadata/facilitated_payments/enums.xml b/tools/metrics/histograms/metadata/facilitated_payments/enums.xml
index d6ee64c..ebb022a22 100644
--- a/tools/metrics/histograms/metadata/facilitated_payments/enums.xml
+++ b/tools/metrics/histograms/metadata/facilitated_payments/enums.xml
@@ -39,6 +39,8 @@
   <int value="2" label="Abandoned"/>
 </enum>
 
+<!-- LINT.IfChange(FacilitatedPayments.PayFlowExitedReason) -->
+
 <enum name="FacilitatedPayments.PayFlowExitedReason">
   <int value="0" label="Code validator failed"/>
   <int value="1" label="Invalid code"/>
@@ -58,8 +60,11 @@
       label="The FOP selector either wasn't shown, or was dismissed not as a
              result of a user action"/>
   <int value="12" label="The FOP selector was closed by the user"/>
+  <int value="13" label="The purchase action could not be invoked"/>
 </enum>
 
+<!-- LINT.ThenChange(/components/facilitated_payments/core/metrics/facilitated_payments_metrics.h:PayflowExitedReason) -->
+
 <!-- LINT.IfChange(FacilitatedPayments.UiScreen) -->
 
 <enum name="FacilitatedPayments.UiScreen">
diff --git a/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml b/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml
index 5c28aa37..b8056c9 100644
--- a/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml
+++ b/tools/metrics/histograms/metadata/facilitated_payments/histograms.xml
@@ -59,6 +59,24 @@
   <token key="Scheme" variants="EwalletScheme"/>
 </histogram>
 
+<histogram
+    name="FacilitatedPayments.Ewallet.LoadRiskData.{Result}.Latency.{Scheme}"
+    units="ms" expires_after="2025-07-01">
+  <owner>junhuihe@google.com</owner>
+  <owner>qihuizhao@google.com</owner>
+  <owner>rouslan@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Latency for the call to fetch risk data which was a {Result}. The payment is
+    supported by {Scheme}. Logged then the eWallet payflow is triggered. The
+    risk data gives device's risk fingerprint which is used to decide whether or
+    not to process the transaction. [Frequency] Logged at most once per page
+    load.
+  </summary>
+  <token key="Result" variants="BooleanResult"/>
+  <token key="Scheme" variants="EwalletScheme"/>
+</histogram>
+
 <histogram name="FacilitatedPayments.Pix.FopSelector.UserAction"
     enum="FacilitatedPayments.FopSelectorAction" expires_after="2025-07-01">
   <owner>siashah@google.com</owner>
@@ -170,21 +188,6 @@
   </token>
 </histogram>
 
-<histogram name="FacilitatedPayments.Pix.LoadRiskData.{Result}.Latency"
-    units="ms" expires_after="2025-07-01">
-  <owner>siashah@google.com</owner>
-  <owner>vishwasuppoor@google.com</owner>
-  <owner>rouslan@google.com</owner>
-  <owner>payments-autofill-team@google.com</owner>
-  <summary>
-    Latency for the call to fetch risk data which was a {Result}. The risk data
-    gives device's risk fingerprint which is used to decide whether or not to
-    process the transaction. [Frequency] Logged at most once per page load.
-    [Trigger] The browser gets signal that a Pix code exists on the page.
-  </summary>
-  <token key="Result" variants="BooleanResult"/>
-</histogram>
-
 <histogram name="FacilitatedPayments.Pix.PayflowExitedReason"
     enum="FacilitatedPayments.PayFlowExitedReason" expires_after="2025-07-01">
   <owner>siashah@google.com</owner>
@@ -305,6 +308,24 @@
   <token key="Result" variants="BooleanResult"/>
 </histogram>
 
+<histogram
+    name="FacilitatedPayments.{FacilitatedPaymentsType}.LoadRiskData.{Result}.Latency"
+    units="ms" expires_after="2025-07-01">
+  <owner>siashah@google.com</owner>
+  <owner>vishwasuppoor@google.com</owner>
+  <owner>rouslan@google.com</owner>
+  <owner>payments-autofill-team@google.com</owner>
+  <summary>
+    Latency for the call to fetch risk data which was a {Result}. The risk data
+    gives device's risk fingerprint which is used to decide whether or not to
+    process the transaction. [Frequency] Logged at most once per page load.
+    [Trigger] The browser gets signal that a {FacilitatedPaymentsType} code
+    exists on the page.
+  </summary>
+  <token key="FacilitatedPaymentsType" variants="FacilitatedPaymentsTypes"/>
+  <token key="Result" variants="BooleanResult"/>
+</histogram>
+
 </histograms>
 
 </histogram-configuration>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index cf86726..58e7930 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -1267,6 +1267,38 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Ads.InterestGroup.Auction.SellerWorkletIsolateTotalHeapSizeKilobytes"
+    units="KB" expires_after="2025-04-06">
+  <owner>abigailkatcoff@google.com</owner>
+  <owner>privacy-sandbox-dev@chromium.org</owner>
+  <summary>
+    The total number of kilobytes v8 has allocated to the heap of the seller
+    worklet isolate. This number should be greater than or equal to its
+    corresponding SellerWorkletIsolateUsedHeapSizeKilobytes. Recorded when the
+    worklet is destroyed.
+
+    See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
+    version of the FLEDGE explainer.
+  </summary>
+</histogram>
+
+<histogram
+    name="Ads.InterestGroup.Auction.SellerWorkletIsolateUsedHeapSizeKilobytes"
+    units="KB" expires_after="2025-04-06">
+  <owner>abigailkatcoff@google.com</owner>
+  <owner>privacy-sandbox-dev@chromium.org</owner>
+  <summary>
+    The number of kilobytes used in the heap of the seller worklet isolate. This
+    number should be less than or equal to its corresponding
+    SellerWorkletIsolateTotalHeapSizeKilobytes. Recorded when the worklet is
+    destroyed.
+
+    See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
+    version of the FLEDGE explainer.
+  </summary>
+</histogram>
+
 <histogram name="Ads.InterestGroup.Auction.TimeSinceLastAuctionPerPage"
     units="ms" expires_after="2025-05-04">
   <owner>caraitto@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index 5c5063d9..62f31592 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -1410,30 +1410,6 @@
 </histogram>
 
 <histogram
-    name="PageLoad.Clients.GoogleSearch.PaintTiming.NavigationToLargestContentfulPaint{Granularity}{SafeSitesFilter}"
-    units="ms" expires_after="2025-06-08">
-  <owner>spelchat@chromium.org</owner>
-  <owner>chrome-brapp-loading@google.com</owner>
-  <summary>
-    Measures the time in milliseconds from navigation timing's navigation start
-    to the time when the page first paints the experimental largest content
-    (text or image) within viewport, for Google Search page loads,
-    {SafeSitesFilter}. See http://bit.ly/largest_contentful_paint_explainer for
-    more details. Differs from
-    SubFrame.PaintTiming.NavigationToLargestContentfulPaint in that removed
-    content is still considered a valid candidate. Only recorded for Search
-    navigations that happen entirely in the foreground. The metric is emitted
-    when the navigation is completed or the app is backgrounded on Android.
-    {Granularity}
-  </summary>
-  <token key="Granularity">
-    <variant name="" summary=""/>
-    <variant name=".FineGrained" summary="Metric is clipped to 10 seconds."/>
-  </token>
-  <token key="SafeSitesFilter" variants="SafeSitesFilterType"/>
-</histogram>
-
-<histogram
     name="PageLoad.Clients.GoogleSearch.PaintTiming.{MileStone}{SafeSitesFilter}"
     units="ms" expires_after="2025-08-01">
   <owner>sisidovski@chromium.org</owner>
@@ -1504,6 +1480,30 @@
   </summary>
 </histogram>
 
+<histogram
+    name="PageLoad.Clients.GoogleSearch{Granularity}.PaintTiming.NavigationToLargestContentfulPaint{SafeSitesFilter}"
+    units="ms" expires_after="2025-06-08">
+  <owner>spelchat@chromium.org</owner>
+  <owner>chrome-brapp-loading@google.com</owner>
+  <summary>
+    Measures the time in milliseconds from navigation timing's navigation start
+    to the time when the page first paints the experimental largest content
+    (text or image) within viewport, for Google Search page loads,
+    {SafeSitesFilter}. See http://bit.ly/largest_contentful_paint_explainer for
+    more details. Differs from
+    SubFrame.PaintTiming.NavigationToLargestContentfulPaint in that removed
+    content is still considered a valid candidate. Only recorded for Search
+    navigations that happen entirely in the foreground. The metric is emitted
+    when the navigation is completed or the app is backgrounded on Android.
+    {Granularity}
+  </summary>
+  <token key="Granularity">
+    <variant name="" summary=""/>
+    <variant name=".FineGrained" summary="Metric is clipped to 10 seconds."/>
+  </token>
+  <token key="SafeSitesFilter" variants="SafeSitesFilterType"/>
+</histogram>
+
 <histogram name="PageLoad.Clients.LCPP.PaintTiming.ActualLCPIndex"
     units="Index(1 origin)" expires_after="2025-04-13">
   <owner>yoichio@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 58bf26b..5b58aef2 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -538,7 +538,7 @@
 </histogram>
 
 <histogram
-    name="SafeBrowsing.ClientSafeBrowsingReport.NetworkResult.{IsAccessTokenIncluded}"
+    name="SafeBrowsing.ClientSafeBrowsingReport.NetworkResult{IsAccessTokenIncluded}"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-07-05">
   <owner>thefrog@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
@@ -547,8 +547,9 @@
     the request {IsAccessTokenIncluded}.
   </summary>
   <token key="IsAccessTokenIncluded">
-    <variant name="NoAccessToken" summary="has no access token"/>
-    <variant name="YesAccessToken" summary="has an access token"/>
+    <variant name="" summary="response is received"/>
+    <variant name=".NoAccessToken" summary="has no access token"/>
+    <variant name=".YesAccessToken" summary="has an access token"/>
   </token>
 </histogram>
 
@@ -1398,6 +1399,15 @@
   </summary>
 </histogram>
 
+<histogram name="SafeBrowsing.HitReport.NetworkResult"
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-07-05">
+  <owner>thefrog@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records the network result for a Safe Browsing hit report request.
+  </summary>
+</histogram>
+
 <histogram name="SafeBrowsing.HitReport.ThreatType" enum="SBThreatType"
     expires_after="2025-04-27">
   <owner>xinghuilu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index 08701f6..51f525f 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -225,6 +225,16 @@
   </summary>
 </histogram>
 
+<histogram name="SBClientDownload.DownloadRequestNetworkResult"
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-06-13">
+  <owner>chlily@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records the net error or HTTP response code after sending a
+    CheckClientDownloadRequest.
+  </summary>
+</histogram>
+
 <histogram name="SBClientDownload.DownloadRequestResponseCode"
     enum="HttpResponseCode" expires_after="2025-06-08">
   <owner>drubery@chromium.org</owner>
@@ -267,6 +277,16 @@
   </summary>
 </histogram>
 
+<histogram name="SBClientDownload.PPAPIDownloadRequest.NetworkResult"
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-06-13">
+  <owner>chlily@chromium.org</owner>
+  <owner>chrome-counter-abuse-alerts@google.com</owner>
+  <summary>
+    Records the net error or HTTP response code after sending PPAPI download
+    request.
+  </summary>
+</histogram>
+
 <histogram name="SBClientDownload.SafeDownloadOpenedLatency2.{ShowAction}"
     units="ms" expires_after="2025-02-10">
   <owner>xinghuilu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/signin/enums.xml b/tools/metrics/histograms/metadata/signin/enums.xml
index d6cbe84..cbeb7b63 100644
--- a/tools/metrics/histograms/metadata/signin/enums.xml
+++ b/tools/metrics/histograms/metadata/signin/enums.xml
@@ -368,6 +368,8 @@
 
 <!-- LINT.ThenChange(//components/signin/ios/browser/account_consistency_service.mm:GaiaPage) -->
 
+<!-- LINT.IfChange(GaiaRemoteConsentFlowResult) -->
+
 <enum name="GaiaRemoteConsentFlowResult">
   <int value="0" label="Success"/>
   <int value="1" label="Window was closed"/>
@@ -377,8 +379,13 @@
   <int value="5" label="Consent wasn't granted"/>
   <int value="6" label="User navigated away (deprecated 07/23)"/>
   <int value="7" label="Cannot create a window"/>
+  <int value="8" label="Failed to set resolution cookies"/>
 </enum>
 
+<!-- LINT.ThenChange(//chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h:GaiaRemoteConsentFlowResult) -->
+
+<!-- LINT.IfChange(GetAuthTokenResult) -->
+
 <enum name="GetAuthTokenResult">
   <int value="0" label="Success"/>
   <int value="1" label="kInvalidClientId"/>
@@ -411,8 +418,11 @@
   <int value="28" label="kInteractivityDenied"/>
   <int value="29" label="kCannotCreateWindow"/>
   <int value="30" label="kBrowserContextShutDown"/>
+  <int value="31" label="kSetRemoteConsentResolutionCookiesFailed"/>
 </enum>
 
+<!-- LINT.ThenChange(//chrome/browser/extensions/api/identity/identity_get_auth_token_error.h:GetAuthTokenResult) -->
+
 <enum name="IdentityConfirmationSnackbarDecision">
   <int value="0" label="Show"/>
   <int value="1" label="Don't show, no signed-in account"/>
diff --git a/tools/metrics/histograms/metadata/tab/enums.xml b/tools/metrics/histograms/metadata/tab/enums.xml
index 49f1081..0d358bf2 100644
--- a/tools/metrics/histograms/metadata/tab/enums.xml
+++ b/tools/metrics/histograms/metadata/tab/enums.xml
@@ -107,6 +107,7 @@
   <int value="8"
       label="Group closed when the last tab in the strip was closed"/>
   <int value="9" label="Converted to shared group from UI"/>
+  <int value="10" label="Converted to saved group from UI"/>
 </enum>
 
 <!-- LINT.ThenChange(//components/saved_tab_groups/public/types.h:ClosingSource) -->
@@ -125,6 +126,7 @@
   <int value="7"
       label="Invoked from session restore after autosave of a V1 group"/>
   <int value="8" label="Group shared from UI"/>
+  <int value="9" label="Group un-shared from UI"/>
 </enum>
 
 <!-- LINT.ThenChange(//components/saved_tab_groups/public/types.h:OpeningSource) -->
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 1fa36d2a..6eacad3 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/76778cdf92dd1795698ce9d4742484645e06074b/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "c631bf3821f9b4c95d7c2dc737219c970bdd76a1",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/aff4b361d523ed6f8549a3c5b0d99638ef286278/trace_processor_shell.exe"
+            "hash": "e9f966f0e511017b029496f0dcab1ceb2eea10fc",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/33841924bad65a13d90b83d924264f6bf7f6810d/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "296798e193cbca9d79cbe293f93ab8acf194bec1",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/76778cdf92dd1795698ce9d4742484645e06074b/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "46a63a1c714160804df7fd980237016874306af9",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/57f2ef884f18866748bf7a36526caab554111a03/trace_processor_shell"
+            "hash": "7e71ff430354dabc16692be552e513578b2c8623",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/33841924bad65a13d90b83d924264f6bf7f6810d/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts
index c7a48a7..fbe63f00 100644
--- a/tools/typescript/definitions/autofill_private.d.ts
+++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -89,6 +89,7 @@
         ADDRESS_HOME_HOUSE_NUMBER,
         ADDRESS_HOME_SUBPREMISE,
         ADDRESS_HOME_OTHER_SUBUNIT,
+        NAME_LAST_PREFIX,
         NAME_LAST_FIRST,
         NAME_LAST_CONJUNCTION,
         NAME_LAST_SECOND,
diff --git a/ui/base/accelerators/global_accelerator_listener/BUILD.gn b/ui/base/accelerators/global_accelerator_listener/BUILD.gn
new file mode 100644
index 0000000..cb1eb8f6
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/BUILD.gn
@@ -0,0 +1,48 @@
+# Copyright 2024 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//build/config/ozone.gni")
+import("//build/config/ui.gni")
+import("//extensions/buildflags/buildflags.gni")
+
+# TODO(crbug.com/378487333): Look into renaming from accelerator to hotkey.
+source_set("global_accelerator_listener") {
+  sources = [
+    "global_accelerator_listener.cc",
+    "global_accelerator_listener.h",
+  ]
+
+  deps = [
+    "//base",
+    "//content/public/browser",
+    "//ui/base",
+    "//ui/events:event_constants",
+    "//ui/events:events_base",
+    "//ui/gfx",
+  ]
+
+  if (is_mac) {
+    sources += [
+      "global_accelerator_listener_mac.h",
+      "global_accelerator_listener_mac.mm",
+    ]
+  }
+
+  if (is_win) {
+    sources += [
+      "global_accelerator_listener_win.cc",
+      "global_accelerator_listener_win.h",
+    ]
+  }
+
+  if (use_ozone) {
+    deps += [ "//ui/ozone" ]
+    sources += [
+      "global_accelerator_listener_ozone.cc",
+      "global_accelerator_listener_ozone.h",
+    ]
+    deps += [ "//build/config/linux/dbus:buildflags" ]
+  }
+}
diff --git a/ui/base/accelerators/global_accelerator_listener/DEPS b/ui/base/accelerators/global_accelerator_listener/DEPS
new file mode 100644
index 0000000..d44b907f
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+content/public",
+]
\ No newline at end of file
diff --git a/ui/base/accelerators/global_accelerator_listener/OWNERS b/ui/base/accelerators/global_accelerator_listener/OWNERS
new file mode 100644
index 0000000..9d301ca
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/OWNERS
@@ -0,0 +1 @@
+file://extensions/OWNERS
\ No newline at end of file
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc
new file mode 100644
index 0000000..ea58a9f
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
+
+#include <vector>
+
+#include "base/check.h"
+#include "base/not_fatal_until.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace ui {
+
+GlobalAcceleratorListener::GlobalAcceleratorListener() = default;
+
+GlobalAcceleratorListener::~GlobalAcceleratorListener() {
+  DCHECK(accelerator_map_.empty());  // Make sure we've cleaned up.
+}
+
+bool GlobalAcceleratorListener::RegisterAccelerator(
+    const ui::Accelerator& accelerator,
+    Observer* observer) {
+  AcceleratorMap::const_iterator it = accelerator_map_.find(accelerator);
+  if (it != accelerator_map_.end()) {
+    // The accelerator has been registered.
+    return false;
+  }
+
+  if (!StartListeningForAccelerator(accelerator)) {
+    // If the platform-specific registration fails, mostly likely the
+    // accelerator has been registered by other native applications.
+    return false;
+  }
+
+  if (accelerator_map_.empty()) {
+    StartListening();
+  }
+
+  accelerator_map_[accelerator] = observer;
+  return true;
+}
+
+void GlobalAcceleratorListener::UnregisterAccelerator(
+    const ui::Accelerator& accelerator,
+    Observer* observer) {
+  auto it = accelerator_map_.find(accelerator);
+  // We should never get asked to unregister something that we didn't register.
+  CHECK(it != accelerator_map_.end(), base::NotFatalUntil::M130);
+  // The caller should call this function with the right observer.
+  DCHECK(it->second == observer);
+
+  StopListeningForAccelerator(accelerator);
+  accelerator_map_.erase(it);
+  if (accelerator_map_.empty()) {
+    StopListening();
+  }
+}
+
+std::vector<ui::Accelerator> GlobalAcceleratorListener::UnregisterAccelerators(
+    Observer* observer) {
+  std::vector<ui::Accelerator> removed_accelerators;
+
+  auto it = accelerator_map_.begin();
+  while (it != accelerator_map_.end()) {
+    if (it->second == observer) {
+      auto to_remove = it++;
+      removed_accelerators.emplace_back(to_remove->first);
+      UnregisterAccelerator(to_remove->first, observer);
+    } else {
+      ++it;
+    }
+  }
+
+  return removed_accelerators;
+}
+
+void GlobalAcceleratorListener::NotifyKeyPressed(
+    const ui::Accelerator& accelerator) {
+  auto iter = accelerator_map_.find(accelerator);
+
+  // This should never occur, because if it does, we have failed to unregister
+  // or failed to clean up the map after unregistering the accelerator.
+  CHECK(iter != accelerator_map_.end());
+
+  iter->second->OnKeyPressed(accelerator);
+}
+
+}  // namespace ui
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h
new file mode 100644
index 0000000..21c5980
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h
@@ -0,0 +1,97 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_H_
+#define UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_H_
+
+#include <map>
+
+#include "base/memory/raw_ptr.h"
+
+namespace extensions {
+class GlobalShortcutListener;
+}
+
+namespace ui {
+
+class Accelerator;
+
+// Platform-neutral implementation of a class that keeps track of observers and
+// monitors keystrokes. It relays messages to the appropriate observer when a
+// global accelerator has been struck by the user.
+class GlobalAcceleratorListener {
+ public:
+  class Observer {
+   public:
+    // Called when your global accelerator is struck.
+    virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0;
+  };
+
+  GlobalAcceleratorListener(const GlobalAcceleratorListener&) = delete;
+  GlobalAcceleratorListener& operator=(const GlobalAcceleratorListener&) =
+      delete;
+
+  virtual ~GlobalAcceleratorListener();
+
+  // The instance may be nullptr.
+  static GlobalAcceleratorListener* GetInstance();
+
+  // Register an observer for when a certain `accelerator` is struck. Returns
+  // true if register successfully, or false if the specified `accelerator`
+  // has been registered by another caller or other native applications.
+  //
+  // Note that we do not support recognizing that an accelerator has been
+  // registered by another application on all platforms. This is a per-platform
+  // consideration.
+  bool RegisterAccelerator(const ui::Accelerator& accelerator,
+                           Observer* observer);
+
+  // Unregister and stop listening for the given `accelerator`.
+  void UnregisterAccelerator(const ui::Accelerator& accelerator,
+                             Observer* observer);
+
+  // Unregister and stop listening for all accelerators of the given `observer`.
+  // Returns a vector of the accelerators that were unregistered.
+  std::vector<ui::Accelerator> UnregisterAccelerators(Observer* observer);
+
+  // Begin listening to an accelerator that has already been registered by
+  // calling `RegisterAccelerator`.
+  virtual bool StartListeningForAccelerator(
+      const ui::Accelerator& accelerator) = 0;
+  // Stop listening to an accelerator that has already been registered by
+  // calling `RegisterAccelerator`.
+  virtual void StopListeningForAccelerator(
+      const ui::Accelerator& accelerator) = 0;
+
+ protected:
+  GlobalAcceleratorListener();
+
+  // Called by platform specific implementations of this class whenever a key
+  // is struck. Only called for keys that have an observer registered.
+  void NotifyKeyPressed(const ui::Accelerator& accelerator);
+
+ private:
+  // The following methods are implemented by platform-specific implementations
+  // of this class.
+  //
+  // Start/StopListening are called when transitioning between zero and nonzero
+  // registered accelerators. StartListening will be called after
+  // StartListeningForAccelerator and StopListening will be called after
+  // StopListeningForAccelerator.
+  //
+  // For StartListeningForAccelerator, implementations return false if
+  // registration did not complete successfully.
+  virtual void StartListening() = 0;
+  virtual void StopListening() = 0;
+
+  // The map of accelerators that have been successfully registered as global
+  // accelerators and their observer.
+  typedef std::map<ui::Accelerator, raw_ptr<Observer, CtnExperimental>>
+      AcceleratorMap;
+  AcceleratorMap accelerator_map_;
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_H_
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_mac.h b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_mac.h
new file mode 100644
index 0000000..64e7859
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_mac.h
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_MAC_H_
+#define UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_MAC_H_
+
+#include <Carbon/Carbon.h>
+
+#include <map>
+#include <memory>
+
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
+#include "ui/base/accelerators/media_keys_listener.h"
+
+namespace ui {
+
+// Mac-specific implementation of the GlobalAcceleratorListener class that
+// listens for global accelerators. Handles basic keyboard intercepting and
+// forwards its output to the base class for processing.
+//
+// This class does two things:
+// 1. Intercepts media keys. Uses an event tap for intercepting media keys
+// (PlayPause, NextTrack, PreviousTrack).
+// 2. Binds keyboard accelerators (hot keys). Carbon RegisterEventHotKey API for
+// binding to non-media key global hot keys (eg. Command-Shift-1).
+class GlobalAcceleratorListenerMac : public GlobalAcceleratorListener,
+                                     public ui::MediaKeysListener::Delegate {
+ public:
+  GlobalAcceleratorListenerMac();
+
+  GlobalAcceleratorListenerMac(const GlobalAcceleratorListenerMac&) = delete;
+  GlobalAcceleratorListenerMac& operator=(const GlobalAcceleratorListenerMac&) =
+      delete;
+
+  ~GlobalAcceleratorListenerMac() override;
+
+ private:
+  using KeyId = int;
+  using AcceleratorIdMap = std::map<ui::Accelerator, KeyId>;
+  using IdAcceleratorMap = std::map<KeyId, ui::Accelerator>;
+  using IdHotKeyRefMap = std::map<KeyId, EventHotKeyRef>;
+
+  // Keyboard event callbacks.
+  void OnHotKeyEvent(EventHotKeyID hot_key_id);
+
+  // GlobalAcceleratorListener implementation.
+  void StartListening() override;
+  void StopListening() override;
+  bool StartListeningForAccelerator(
+      const ui::Accelerator& accelerator) override;
+  void StopListeningForAccelerator(const ui::Accelerator& accelerator) override;
+
+  // ui::MediaKeysListener::Delegate:
+  void OnMediaKeysAccelerator(const ui::Accelerator& accelerator) override;
+
+  // Mac-specific functions for registering hot keys with modifiers.
+  bool RegisterHotKey(const ui::Accelerator& accelerator, KeyId hot_key_id);
+  void UnregisterHotKey(const ui::Accelerator& accelerator);
+
+  // Enable and disable the hot key event handler.
+  void StartWatchingHotKeys();
+  void StopWatchingHotKeys();
+
+  // Whether or not any hot keys are currently registered.
+  bool IsAnyHotKeyRegistered();
+
+  // The callback for when a hot key event happens.
+  static OSStatus HotKeyHandler(EventHandlerCallRef next_handler,
+                                EventRef event,
+                                void* user_data);
+
+  // Whether this object is listening for global accelerators.
+  bool is_listening_ = false;
+
+  // The hotkey identifier for the next global accelerator that is added.
+  KeyId hot_key_id_ = 0;
+
+  // A map of all hotkeys (media keys and accelerators) mapping to their
+  // corresponding hotkey IDs. For quickly finding if an accelerator is
+  // registered.
+  AcceleratorIdMap accelerator_ids_;
+
+  // The inverse map for quickly looking up accelerators by hotkey id.
+  IdAcceleratorMap id_accelerators_;
+
+  // Keyboard accelerator IDs to hotkeys map for unregistration.
+  IdHotKeyRefMap id_hot_key_refs_;
+
+  // Event handler for keyboard accelerator hot keys.
+  EventHandlerRef event_handler_ = nullptr;
+
+  // Media keys listener.
+  std::unique_ptr<ui::MediaKeysListener> media_keys_listener_;
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_MAC_H_
diff --git a/chrome/browser/extensions/global_shortcut_listener_mac.mm b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_mac.mm
similarity index 78%
rename from chrome/browser/extensions/global_shortcut_listener_mac.mm
rename to ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_mac.mm
index 83e68e0..229dbb14 100644
--- a/chrome/browser/extensions/global_shortcut_listener_mac.mm
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_mac.mm
@@ -2,12 +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/global_shortcut_listener_mac.h"
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_mac.h"
 
 #include <ApplicationServices/ApplicationServices.h>
 #import <Cocoa/Cocoa.h>
 #include <IOKit/hidsystem/ev_keymap.h>
 
+#include <memory>
+
 #import "base/apple/foundation_util.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/media_keys_listener_manager.h"
@@ -16,21 +18,20 @@
 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
 
 using content::BrowserThread;
-using extensions::GlobalShortcutListenerMac;
+using ui::GlobalAcceleratorListenerMac;
 
-namespace extensions {
+namespace ui {
 
 // static
-GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
+GlobalAcceleratorListener* GlobalAcceleratorListener::GetInstance() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  static GlobalShortcutListenerMac* instance =
-      new GlobalShortcutListenerMac();
+  static GlobalAcceleratorListenerMac* instance =
+      new GlobalAcceleratorListenerMac();
   return instance;
 }
 
-GlobalShortcutListenerMac::GlobalShortcutListenerMac() {
+GlobalAcceleratorListenerMac::GlobalAcceleratorListenerMac() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
   // If the MediaKeysListenerManager is not enabled, we need to create our own
   // global MediaKeysListener to receive media keys.
   if (!content::MediaKeysListenerManager::IsMediaKeysListenerManagerEnabled()) {
@@ -40,21 +41,21 @@
   }
 }
 
-GlobalShortcutListenerMac::~GlobalShortcutListenerMac() {
+GlobalAcceleratorListenerMac::~GlobalAcceleratorListenerMac() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
   // By this point, UnregisterAccelerator should have been called for all
-  // keyboard shortcuts. Still we should clean up.
-  if (is_listening_)
+  // keyboard accelerators. Still we should clean up.
+  if (is_listening_) {
     StopListening();
+  }
 
-  if (IsAnyHotKeyRegistered())
+  if (IsAnyHotKeyRegistered()) {
     StopWatchingHotKeys();
+  }
 }
 
-void GlobalShortcutListenerMac::StartListening() {
+void GlobalAcceleratorListenerMac::StartListening() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
   DCHECK(!accelerator_ids_.empty());
   DCHECK(!id_accelerators_.empty());
   DCHECK(!is_listening_);
@@ -62,9 +63,8 @@
   is_listening_ = true;
 }
 
-void GlobalShortcutListenerMac::StopListening() {
+void GlobalAcceleratorListenerMac::StopListening() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
   DCHECK(accelerator_ids_.empty());  // Make sure the set is clean.
   DCHECK(id_accelerators_.empty());
   DCHECK(is_listening_);
@@ -72,9 +72,8 @@
   is_listening_ = false;
 }
 
-void GlobalShortcutListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) {
+void GlobalAcceleratorListenerMac::OnHotKeyEvent(EventHotKeyID hot_key_id) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
   // This hot key should be registered.
   DCHECK(id_accelerators_.find(hot_key_id.id) != id_accelerators_.end());
   // Look up the accelerator based on this hot key ID.
@@ -82,7 +81,7 @@
   NotifyKeyPressed(accelerator);
 }
 
-bool GlobalShortcutListenerMac::RegisterAcceleratorImpl(
+bool GlobalAcceleratorListenerMac::StartListeningForAccelerator(
     const ui::Accelerator& accelerator) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(accelerator_ids_.find(accelerator) == accelerator_ids_.end());
@@ -106,9 +105,10 @@
       media_keys_listener_->StartWatchingMediaKey(accelerator.key_code());
     }
   } else {
-    // Register hot_key if they are non-media keyboard shortcuts.
-    if (!RegisterHotKey(accelerator, hot_key_id_))
+    // Register hot_key if they are non-media keyboard accelerators.
+    if (!RegisterHotKey(accelerator, hot_key_id_)) {
       return false;
+    }
 
     if (!IsAnyHotKeyRegistered()) {
       StartWatchingHotKeys();
@@ -122,12 +122,12 @@
   return true;
 }
 
-void GlobalShortcutListenerMac::UnregisterAcceleratorImpl(
+void GlobalAcceleratorListenerMac::StopListeningForAccelerator(
     const ui::Accelerator& accelerator) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
 
-  // Unregister the hot_key if it's a keyboard shortcut.
+  // Unregister the hot_key if it's a keyboard accelerator.
   if (!accelerator.IsMediaKey()) {
     UnregisterHotKey(accelerator);
   }
@@ -160,7 +160,7 @@
   }
 }
 
-void GlobalShortcutListenerMac::OnMediaKeysAccelerator(
+void GlobalAcceleratorListenerMac::OnMediaKeysAccelerator(
     const ui::Accelerator& accelerator) {
   if (accelerator_ids_.find(accelerator) != accelerator_ids_.end()) {
     // If matched, callback to the event handling system.
@@ -168,8 +168,9 @@
   }
 }
 
-bool GlobalShortcutListenerMac::RegisterHotKey(
-    const ui::Accelerator& accelerator, KeyId hot_key_id) {
+bool GlobalAcceleratorListenerMac::RegisterHotKey(
+    const ui::Accelerator& accelerator,
+    KeyId hot_key_id) {
   EventHotKeyID event_hot_key_id;
 
   // Signature uniquely identifies the application that owns this hot_key.
@@ -193,14 +194,15 @@
   OSStatus status = RegisterEventHotKey(key_code, modifiers, event_hot_key_id,
                                         GetApplicationEventTarget(),
                                         /*inOptions=*/0, &hot_key_ref);
-  if (status != noErr)
+  if (status != noErr) {
     return false;
+  }
 
   id_hot_key_refs_[hot_key_id] = hot_key_ref;
   return true;
 }
 
-void GlobalShortcutListenerMac::UnregisterHotKey(
+void GlobalAcceleratorListenerMac::UnregisterHotKey(
     const ui::Accelerator& accelerator) {
   // Ensure this accelerator is already registered.
   DCHECK(accelerator_ids_.find(accelerator) != accelerator_ids_.end());
@@ -214,23 +216,23 @@
   id_hot_key_refs_.erase(key_id);
 }
 
-void GlobalShortcutListenerMac::StartWatchingHotKeys() {
+void GlobalAcceleratorListenerMac::StartWatchingHotKeys() {
   DCHECK(!event_handler_);
   EventHandlerUPP hot_key_function = NewEventHandlerUPP(HotKeyHandler);
   EventTypeSpec event_type;
   event_type.eventClass = kEventClassKeyboard;
   event_type.eventKind = kEventHotKeyPressed;
-  InstallApplicationEventHandler(
-      hot_key_function, 1, &event_type, this, &event_handler_);
+  InstallApplicationEventHandler(hot_key_function, 1, &event_type, this,
+                                 &event_handler_);
 }
 
-void GlobalShortcutListenerMac::StopWatchingHotKeys() {
+void GlobalAcceleratorListenerMac::StopWatchingHotKeys() {
   DCHECK(event_handler_);
   RemoveEventHandler(event_handler_);
   event_handler_ = nullptr;
 }
 
-bool GlobalShortcutListenerMac::IsAnyHotKeyRegistered() {
+bool GlobalAcceleratorListenerMac::IsAnyHotKeyRegistered() {
   for (auto& accelerator_id : accelerator_ids_) {
     if (!accelerator_id.first.IsMediaKey()) {
       return true;
@@ -240,21 +242,24 @@
 }
 
 // static
-OSStatus GlobalShortcutListenerMac::HotKeyHandler(
-    EventHandlerCallRef next_handler, EventRef event, void* user_data) {
+OSStatus GlobalAcceleratorListenerMac::HotKeyHandler(
+    EventHandlerCallRef next_handler,
+    EventRef event,
+    void* user_data) {
   // Extract the hotkey from the event.
   EventHotKeyID hot_key_id;
   OSStatus result =
       GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID,
                         /*outActualType=*/nullptr, sizeof(hot_key_id),
                         /*outActualSize=*/nullptr, &hot_key_id);
-  if (result != noErr)
+  if (result != noErr) {
     return result;
+  }
 
-  GlobalShortcutListenerMac* shortcut_listener =
-      static_cast<GlobalShortcutListenerMac*>(user_data);
+  GlobalAcceleratorListenerMac* shortcut_listener =
+      static_cast<GlobalAcceleratorListenerMac*>(user_data);
   shortcut_listener->OnHotKeyEvent(hot_key_id);
   return noErr;
 }
 
-}  // namespace extensions
+}  // namespace ui
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_ozone.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_ozone.cc
new file mode 100644
index 0000000..44364991
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_ozone.cc
@@ -0,0 +1,130 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_ozone.h"
+
+#include "base/containers/contains.h"
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
+#include "build/config/linux/dbus/buildflags.h"
+#include "content/public/browser/browser_thread.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+using content::BrowserThread;
+
+namespace ui {
+
+// static
+GlobalAcceleratorListener* GlobalAcceleratorListener::GetInstance() {
+  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  static const base::NoDestructor<std::unique_ptr<GlobalAcceleratorListener>>
+      instance(GlobalAcceleratorListenerOzone::Create());
+  return instance->get();
+}
+
+// static
+std::unique_ptr<GlobalAcceleratorListener>
+GlobalAcceleratorListenerOzone::Create() {
+  auto listener = std::make_unique<GlobalAcceleratorListenerOzone>(
+      base::PassKey<GlobalAcceleratorListenerOzone>());
+  if (listener->platform_global_shortcut_listener_) {
+    return listener;
+  }
+  return nullptr;
+}
+
+GlobalAcceleratorListenerOzone::GlobalAcceleratorListenerOzone(
+    base::PassKey<GlobalAcceleratorListenerOzone>) {
+  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+  platform_global_shortcut_listener_ =
+      ui::OzonePlatform::GetInstance()->GetPlatformGlobalShortcutListener(this);
+}
+
+GlobalAcceleratorListenerOzone::~GlobalAcceleratorListenerOzone() {
+  if (is_listening_) {
+    StopListening();
+  }
+
+  if (platform_global_shortcut_listener_) {
+    platform_global_shortcut_listener_->ResetDelegate();
+  }
+}
+
+void GlobalAcceleratorListenerOzone::StartListening() {
+  DCHECK(!is_listening_);
+  DCHECK(!registered_hot_keys_.empty());
+
+  if (platform_global_shortcut_listener_) {
+    platform_global_shortcut_listener_->StartListening();
+  }
+
+  is_listening_ = true;
+}
+
+void GlobalAcceleratorListenerOzone::StopListening() {
+  DCHECK(is_listening_);
+  DCHECK(registered_hot_keys_.empty());
+
+  if (platform_global_shortcut_listener_) {
+    platform_global_shortcut_listener_->StopListening();
+  }
+
+  is_listening_ = false;
+}
+
+bool GlobalAcceleratorListenerOzone::StartListeningForAccelerator(
+    const ui::Accelerator& accelerator) {
+  DCHECK(!base::Contains(registered_hot_keys_, accelerator));
+
+  if (!platform_global_shortcut_listener_) {
+    return false;
+  }
+
+  const bool registered =
+      platform_global_shortcut_listener_->RegisterAccelerator(
+          accelerator.key_code(), accelerator.IsAltDown(),
+          accelerator.IsCtrlDown(), accelerator.IsShiftDown());
+  if (registered) {
+    registered_hot_keys_.insert(accelerator);
+  }
+  return registered;
+}
+
+void GlobalAcceleratorListenerOzone::StopListeningForAccelerator(
+    const ui::Accelerator& accelerator) {
+  DCHECK(base::Contains(registered_hot_keys_, accelerator));
+  // Otherwise how could the accelerator be registered?
+  DCHECK(platform_global_shortcut_listener_);
+
+  platform_global_shortcut_listener_->UnregisterAccelerator(
+      accelerator.key_code(), accelerator.IsAltDown(), accelerator.IsCtrlDown(),
+      accelerator.IsShiftDown());
+  registered_hot_keys_.erase(accelerator);
+}
+
+void GlobalAcceleratorListenerOzone::OnKeyPressed(ui::KeyboardCode key_code,
+                                                  bool is_alt_down,
+                                                  bool is_ctrl_down,
+                                                  bool is_shift_down) {
+  int modifiers = 0;
+  if (is_alt_down) {
+    modifiers |= ui::EF_ALT_DOWN;
+  }
+  if (is_ctrl_down) {
+    modifiers |= ui::EF_CONTROL_DOWN;
+  }
+  if (is_shift_down) {
+    modifiers |= ui::EF_SHIFT_DOWN;
+  }
+
+  NotifyKeyPressed(ui::Accelerator(key_code, modifiers));
+}
+
+void GlobalAcceleratorListenerOzone::OnPlatformListenerDestroyed() {
+  platform_global_shortcut_listener_ = nullptr;
+}
+
+}  // namespace ui
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_ozone.h b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_ozone.h
new file mode 100644
index 0000000..9e63dd9
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_ozone.h
@@ -0,0 +1,70 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_OZONE_H_
+#define UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_OZONE_H_
+
+#include <memory>
+#include <set>
+
+#include "base/memory/raw_ptr.h"
+#include "base/types/pass_key.h"
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/ozone/public/platform_global_shortcut_listener.h"
+
+namespace ui {
+class Accelerator;
+}  // namespace ui
+
+namespace ui {
+
+// Ozone-specific implementation of the GlobalAcceleratorListener interface.
+//
+// Connects Aura with the platform implementation, and manages data conversions
+// required on the way: Aura operates with ui::Accelerator while the platform is
+// only aware of the basic components such as the key code and modifiers.
+class GlobalAcceleratorListenerOzone
+    : public GlobalAcceleratorListener,
+      public ui::PlatformGlobalShortcutListenerDelegate {
+ public:
+  static std::unique_ptr<GlobalAcceleratorListener> Create();
+
+  // Clients should use Create() instead of using this constructor.
+  explicit GlobalAcceleratorListenerOzone(
+      base::PassKey<GlobalAcceleratorListenerOzone>);
+
+  GlobalAcceleratorListenerOzone(const GlobalAcceleratorListenerOzone&) =
+      delete;
+  GlobalAcceleratorListenerOzone& operator=(
+      const GlobalAcceleratorListenerOzone&) = delete;
+
+  ~GlobalAcceleratorListenerOzone() override;
+
+ private:
+  // GlobalAcceleratorListener:
+  void StartListening() override;
+  void StopListening() override;
+  bool StartListeningForAccelerator(
+      const ui::Accelerator& accelerator) override;
+  void StopListeningForAccelerator(const ui::Accelerator& accelerator) override;
+
+  // ui::PlatformGlobalShortcutListenerDelegate:
+  void OnKeyPressed(ui::KeyboardCode key_code,
+                    bool is_alt_down,
+                    bool is_ctrl_down,
+                    bool is_shift_down) override;
+  void OnPlatformListenerDestroyed() override;
+
+  bool is_listening_ = false;
+  std::set<ui::Accelerator> registered_hot_keys_;
+
+  // The platform implementation.
+  raw_ptr<ui::PlatformGlobalShortcutListener>
+      platform_global_shortcut_listener_ = nullptr;
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_OZONE_H_
diff --git a/chrome/browser/extensions/global_shortcut_listener_win.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_win.cc
similarity index 70%
rename from chrome/browser/extensions/global_shortcut_listener_win.cc
rename to ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_win.cc
index 687727e..034ccea0 100644
--- a/chrome/browser/extensions/global_shortcut_listener_win.cc
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_win.cc
@@ -2,7 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/extensions/global_shortcut_listener_win.h"
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_win.h"
+
+#include <memory>
 
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
@@ -18,43 +20,45 @@
 
 using content::BrowserThread;
 
-namespace extensions {
+namespace ui {
 
 // static
-GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
+GlobalAcceleratorListener* GlobalAcceleratorListener::GetInstance() {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  static GlobalShortcutListenerWin* instance =
-      new GlobalShortcutListenerWin();
+  static GlobalAcceleratorListenerWin* instance =
+      new GlobalAcceleratorListenerWin();
   return instance;
 }
 
-GlobalShortcutListenerWin::GlobalShortcutListenerWin()
+GlobalAcceleratorListenerWin::GlobalAcceleratorListenerWin()
     : is_listening_(false) {
   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 }
 
-GlobalShortcutListenerWin::~GlobalShortcutListenerWin() {
+GlobalAcceleratorListenerWin::~GlobalAcceleratorListenerWin() {
   if (is_listening_) {
     StopListening();
   }
 }
 
-void GlobalShortcutListenerWin::StartListening() {
-  DCHECK(!is_listening_);  // Don't start twice.
+void GlobalAcceleratorListenerWin::StartListening() {
+  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!is_listening_);     // Don't start twice.
   DCHECK(!hotkeys_.empty());  // Also don't start if no hotkey is registered.
   is_listening_ = true;
 }
 
-void GlobalShortcutListenerWin::StopListening() {
-  DCHECK(is_listening_);  // No point if we are not already listening.
+void GlobalAcceleratorListenerWin::StopListening() {
+  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(is_listening_);     // No point if we are not already listening.
   DCHECK(hotkeys_.empty());  // Make sure the map is clean before ending.
   is_listening_ = false;
 }
 
-void GlobalShortcutListenerWin::OnWndProc(HWND hwnd,
-                                          UINT message,
-                                          WPARAM wparam,
-                                          LPARAM lparam) {
+void GlobalAcceleratorListenerWin::OnWndProc(HWND hwnd,
+                                             UINT message,
+                                             WPARAM wparam,
+                                             LPARAM lparam) {
   // SingletonHwndHotKeyObservers should only send us hot key messages.
   DCHECK_EQ(WM_HOTKEY, static_cast<int>(message));
 
@@ -63,18 +67,18 @@
   modifiers |= (LOWORD(lparam) & MOD_SHIFT) ? ui::EF_SHIFT_DOWN : 0;
   modifiers |= (LOWORD(lparam) & MOD_ALT) ? ui::EF_ALT_DOWN : 0;
   modifiers |= (LOWORD(lparam) & MOD_CONTROL) ? ui::EF_CONTROL_DOWN : 0;
-  ui::Accelerator accelerator(
-      ui::KeyboardCodeForWindowsKeyCode(key_code), modifiers);
+  ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(key_code),
+                              modifiers);
 
   NotifyKeyPressed(accelerator);
 }
 
-bool GlobalShortcutListenerWin::RegisterAcceleratorImpl(
+bool GlobalAcceleratorListenerWin::StartListeningForAccelerator(
     const ui::Accelerator& accelerator) {
   DCHECK(hotkeys_.find(accelerator) == hotkeys_.end());
 
   // TODO(crbug.com/40622191): We should be using
-  // |media_keys_listener_manager->StartWatchingMediaKey(...)| here, but that
+  // `media_keys_listener_manager->StartWatchingMediaKey(...)` here, but that
   // currently breaks the GlobalCommandsApiTest.GlobalDuplicatedMediaKey test.
   // Instead, we'll just disable the MediaKeysListenerManager handling here, and
   // listen using the fallback RegisterHotKey method.
@@ -94,10 +98,10 @@
   modifiers |= accelerator.IsCtrlDown() ? MOD_CONTROL : 0;
   modifiers |= accelerator.IsAltDown() ? MOD_ALT : 0;
 
-  // Create an observer that registers a hot key for |accelerator|.
+  // Create an observer that registers a hot key for `accelerator`.
   std::unique_ptr<gfx::SingletonHwndHotKeyObserver> observer =
       gfx::SingletonHwndHotKeyObserver::Create(
-          base::BindRepeating(&GlobalShortcutListenerWin::OnWndProc,
+          base::BindRepeating(&GlobalAcceleratorListenerWin::OnWndProc,
                               base::Unretained(this)),
           accelerator.key_code(), modifiers);
 
@@ -110,13 +114,13 @@
   return true;
 }
 
-void GlobalShortcutListenerWin::UnregisterAcceleratorImpl(
+void GlobalAcceleratorListenerWin::StopListeningForAccelerator(
     const ui::Accelerator& accelerator) {
   HotKeyMap::iterator it = hotkeys_.find(accelerator);
   CHECK(it != hotkeys_.end(), base::NotFatalUntil::M130);
 
   // TODO(crbug.com/40622191): We should be using
-  // |media_keys_listener_manager->StopWatchingMediaKey(...)| here.
+  // `media_keys_listener_manager->StopWatchingMediaKey(...)` here.
   if (content::MediaKeysListenerManager::IsMediaKeysListenerManagerEnabled() &&
       accelerator.IsMediaKey()) {
     registered_media_keys_--;
@@ -133,11 +137,11 @@
   hotkeys_.erase(it);
 }
 
-void GlobalShortcutListenerWin::OnMediaKeysAccelerator(
+void GlobalAcceleratorListenerWin::OnMediaKeysAccelerator(
     const ui::Accelerator& accelerator) {
   // We should not receive media key events that we didn't register for.
   DCHECK(hotkeys_.find(accelerator) != hotkeys_.end());
   NotifyKeyPressed(accelerator);
 }
 
-}  // namespace extensions
+}  // namespace ui
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_win.h b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_win.h
new file mode 100644
index 0000000..f70d4b4d
--- /dev/null
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_win.h
@@ -0,0 +1,66 @@
+// Copyright 2013 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_WIN_H_
+#define UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_WIN_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
+#include "ui/base/accelerators/media_keys_listener.h"
+
+namespace gfx {
+
+class SingletonHwndHotKeyObserver;
+
+}  // namespace gfx
+
+namespace ui {
+
+// Windows-specific implementation of the GlobalAcceleratorListener class that
+// listens for global accelerators. Handles setting up a keyboard hook and
+// forwarding its output to the base class for processing.
+class GlobalAcceleratorListenerWin : public GlobalAcceleratorListener,
+                                     public ui::MediaKeysListener::Delegate {
+ public:
+  GlobalAcceleratorListenerWin();
+
+  GlobalAcceleratorListenerWin(const GlobalAcceleratorListenerWin&) = delete;
+  GlobalAcceleratorListenerWin& operator=(const GlobalAcceleratorListenerWin&) =
+      delete;
+
+  ~GlobalAcceleratorListenerWin() override;
+
+ private:
+  // The implementation of our Window Proc, called by SingletonHwndObserver.
+  void OnWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+  // GlobalAcceleratorListener implementation.
+  void StartListening() override;
+  void StopListening() override;
+  bool StartListeningForAccelerator(
+      const ui::Accelerator& accelerator) override;
+  void StopListeningForAccelerator(const ui::Accelerator& accelerator) override;
+
+  // ui::MediaKeysListener::Delegate implementation.
+  void OnMediaKeysAccelerator(const ui::Accelerator& accelerator) override;
+
+  // Whether this object is listening for global accelerators.
+  bool is_listening_;
+
+  // The number of media keys currently registered.
+  int registered_media_keys_ = 0;
+
+  // A map of registered accelerators and their registration ids. The value is
+  // null for media keys if kHardwareMediaKeyHandling is true.
+  using HotKeyMap = std::map<ui::Accelerator,
+                             std::unique_ptr<gfx::SingletonHwndHotKeyObserver>>;
+  HotKeyMap hotkeys_;
+};
+
+}  // namespace ui
+
+#endif  // UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_WIN_H_
diff --git a/ui/file_manager/integration_tests/remote_call.ts b/ui/file_manager/integration_tests/remote_call.ts
index c78cf04..616dc32 100644
--- a/ui/file_manager/integration_tests/remote_call.ts
+++ b/ui/file_manager/integration_tests/remote_call.ts
@@ -1436,13 +1436,14 @@
    *     microsoft_onedrive, google_drive.
    */
   async setLocalFilesMigrationDestination(provider: string) {
-    // Disable local storage - migration destination is ignored otherwise.
-    await sendTestMessage(
-        {name: 'skyvault:setLocalFilesEnabled', enabled: false});
-    // Set the destination.
+    // Set the destination first, otherwise migration can be complete
+    // immediately, if MyFiles are empty.
     await sendTestMessage({
       name: 'skyvault:setMigrationDestination',
       provider: provider,
     });
+    // Disable local storage - migration destination is ignored otherwise.
+    await sendTestMessage(
+        {name: 'skyvault:setLocalFilesEnabled', enabled: false});
   }
 }
diff --git a/url/android/java/src/org/chromium/url/GURL.java b/url/android/java/src/org/chromium/url/GURL.java
index c30dbcf2..f4df373 100644
--- a/url/android/java/src/org/chromium/url/GURL.java
+++ b/url/android/java/src/org/chromium/url/GURL.java
@@ -4,11 +4,11 @@
 
 package org.chromium.url;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.os.SystemClock;
 import android.text.TextUtils;
 
-import androidx.annotation.Nullable;
-
 import com.google.errorprone.annotations.DoNotMock;
 
 import org.jni_zero.CalledByNative;
@@ -23,6 +23,8 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
 import org.chromium.build.BuildConfig;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.url.mojom.Url;
 import org.chromium.url.mojom.UrlConstants;
 
@@ -31,16 +33,17 @@
 /**
  * An immutable Java wrapper for GURL, Chromium's URL parsing library.
  *
- * This class is safe to use during startup, but will block on the native library being sufficiently
- * loaded to use native GURL (and will not wait for content initialization). In practice it's very
- * unlikely that this will actually block startup unless used extremely early, in which case you
- * should probably seek an alternative solution to using GURL.
+ * <p>This class is safe to use during startup, but will block on the native library being
+ * sufficiently loaded to use native GURL (and will not wait for content initialization). In
+ * practice it's very unlikely that this will actually block startup unless used extremely early, in
+ * which case you should probably seek an alternative solution to using GURL.
  *
- * The design of this class avoids destruction/finalization by caching all values necessary to
+ * <p>The design of this class avoids destruction/finalization by caching all values necessary to
  * reconstruct a GURL in Java, allowing it to be much faster in the common case and easier to use.
  */
 @JNINamespace("url")
 @DoNotMock("Create a real instance instead.")
+@NullMarked
 public class GURL {
     private static final String TAG = "GURL";
     /* package */ static final int SERIALIZER_VERSION = 1;
@@ -59,13 +62,17 @@
 
     // Right now this is only collecting reports on Canary which has a relatively small population.
     private static final int DEBUG_REPORT_PERCENTAGE = 10;
-    private static ReportDebugThrowableCallback sReportCallback;
+    private static @Nullable ReportDebugThrowableCallback sReportCallback;
 
     // TODO(crbug.com/40113773): Right now we return a new String with each request for a
     //      GURL component other than the spec itself. Should we cache return Strings (as
     //      WeakReference?) so that callers can share String memory?
+    @SuppressWarnings("NullAway.Init")
     private String mSpec;
+
     private boolean mIsValid;
+
+    @SuppressWarnings("NullAway.Init")
     private Parsed mParsed;
 
     private static class Holder {
@@ -128,7 +135,7 @@
                 PostTask.postTask(
                         TaskTraits.BEST_EFFORT_MAY_BLOCK,
                         () -> {
-                            sReportCallback.run(throwable);
+                            assumeNonNull(sReportCallback).run(throwable);
                         });
             }
         }
@@ -264,7 +271,10 @@
      * @return Copy of the URL with replacements applied.
      */
     public GURL replaceComponents(
-            String username, boolean clearUsername, String password, boolean clearPassword) {
+            @Nullable String username,
+            boolean clearUsername,
+            @Nullable String password,
+            boolean clearPassword) {
         GURL result = new GURL();
         getNatives()
                 .replaceComponents(this, username, clearUsername, password, clearPassword, result);
@@ -330,6 +340,8 @@
         try {
             return deserializeLatestVersionOnly(gurl);
         } catch (BadSerializerVersionException be) {
+            // deserializeLatestVersionOnly() handles null.
+            gurl = assumeNonNull(gurl);
             // Just re-parse the GURL on version changes.
             String[] tokens = gurl.split(Character.toString(SERIALIZER_DELIMITER));
             return new GURL(getSpecFromTokens(gurl, tokens));
@@ -432,9 +444,9 @@
          */
         void replaceComponents(
                 @JniType("GURL") GURL self,
-                String username,
+                @Nullable String username,
                 boolean clearUsername,
-                String password,
+                @Nullable String password,
                 boolean clearPassword,
                 GURL result);
     }
diff --git a/url/android/java/src/org/chromium/url/IDNStringUtil.java b/url/android/java/src/org/chromium/url/IDNStringUtil.java
index 21d723a..6b3a3dc2 100644
--- a/url/android/java/src/org/chromium/url/IDNStringUtil.java
+++ b/url/android/java/src/org/chromium/url/IDNStringUtil.java
@@ -7,10 +7,14 @@
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
 import java.net.IDN;
 
 /** This class is used to convert unicode IDN domain names to ASCII, when not building with ICU. */
 @JNINamespace("url::android")
+@NullMarked
 public class IDNStringUtil {
     /**
      * Attempts to convert a Unicode string to an ASCII string using IDN rules. As of May 2014, the
@@ -20,7 +24,7 @@
      * @return String containing only ASCII characters on success, null on failure.
      */
     @CalledByNative
-    private static String idnToASCII(String src) {
+    private static @Nullable String idnToASCII(String src) {
         try {
             return IDN.toASCII(src, IDN.USE_STD3_ASCII_RULES);
         } catch (Exception e) {
diff --git a/url/android/java/src/org/chromium/url/Origin.java b/url/android/java/src/org/chromium/url/Origin.java
index a69ecb6d..c6f07ab 100644
--- a/url/android/java/src/org/chromium/url/Origin.java
+++ b/url/android/java/src/org/chromium/url/Origin.java
@@ -4,18 +4,19 @@
 
 package org.chromium.url;
 
-import androidx.annotation.NonNull;
-
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
 import org.jni_zero.JniType;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.util.Locale;
 import java.util.Objects;
 
 /** An origin is either a (scheme, host, port) tuple or is opaque. */
 @JNINamespace("url")
+@NullMarked
 public class Origin {
     private final String mScheme;
     private final String mHost;
@@ -125,7 +126,6 @@
      * "null" if it's opaque.
      */
     @Override
-    @NonNull
     public String toString() {
         return isOpaque()
                 ? "null"
diff --git a/url/android/java/src/org/chromium/url/Parsed.java b/url/android/java/src/org/chromium/url/Parsed.java
index 087f3f6..3fc10b9 100644
--- a/url/android/java/src/org/chromium/url/Parsed.java
+++ b/url/android/java/src/org/chromium/url/Parsed.java
@@ -4,12 +4,19 @@
 
 package org.chromium.url;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
 import org.jni_zero.NativeMethods;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.NullUnmarked;
+import org.chromium.build.annotations.Nullable;
+
 /** A java wrapper for Parsed, GURL's internal parsed URI representation. */
 @JNINamespace("url")
+@NullMarked
 /* package */ class Parsed {
     /* package */ final int mSchemeBegin;
     /* package */ final int mSchemeLength;
@@ -27,7 +34,7 @@
     /* package */ final int mQueryLength;
     /* package */ final int mRefBegin;
     /* package */ final int mRefLength;
-    private final Parsed mInnerUrl;
+    private final @Nullable Parsed mInnerUrl;
     private final boolean mPotentiallyDanglingMarkup;
 
     /* package */ static Parsed createEmpty() {
@@ -53,7 +60,7 @@
             int refBegin,
             int refLength,
             boolean potentiallyDanglingMarkup,
-            Parsed innerUrl) {
+            @Nullable Parsed innerUrl) {
         mSchemeBegin = schemeBegin;
         mSchemeLength = schemeLength;
         mUsernameBegin = usernameBegin;
@@ -74,7 +81,8 @@
         mInnerUrl = innerUrl;
     }
 
-    /* package */ void initNative(long nativePtr) {
+    /* package */ @NullUnmarked
+    void initNative(long nativePtr) {
         Parsed target = this;
         Parsed innerParsed = mInnerUrl;
         // Use a loop to avoid two copies of the long parameter list.
@@ -105,7 +113,7 @@
             if (isInner || innerParsed == null) {
                 break;
             }
-            target = mInnerUrl;
+            target = assumeNonNull(mInnerUrl);
         }
     }
 
diff --git a/url/android/java/src/org/chromium/url/URI.java b/url/android/java/src/org/chromium/url/URI.java
index ee149a1..0b878620 100644
--- a/url/android/java/src/org/chromium/url/URI.java
+++ b/url/android/java/src/org/chromium/url/URI.java
@@ -4,6 +4,8 @@
 
 package org.chromium.url;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.net.URISyntaxException;
 
 /**
@@ -12,6 +14,7 @@
  * @deprecated Please use GURL directly in new code.
  */
 @Deprecated
+@NullMarked
 public class URI extends GURL {
     /** Create a new GURL with a java.net.URI API shim. */
     public URI(String uri) throws URISyntaxException {
diff --git a/v8 b/v8
index 5cd1ea4..764291ff 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 5cd1ea449b74b8bc969825e24e9756e335d54e64
+Subproject commit 764291ffbf2b7850d3fc8e25f60ed17fa6044459