diff --git a/DEPS b/DEPS
index e01072d..01c2f84 100644
--- a/DEPS
+++ b/DEPS
@@ -302,7 +302,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Fuchsia sdk
   # and whatever else without interference from each other.
-  'fuchsia_version': 'version:8.20220607.2.1',
+  'fuchsia_version': 'version:8.20220607.3.1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling google-toolbox-for-mac
   # and whatever else without interference from each other.
@@ -354,7 +354,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': 'ed6446880ed002cc4d1c79b9b35ec4c9d72625ea',
+  'devtools_frontend_revision': '042fe07f91b82c1ef08d0d0dfaf3f563a6ba36bc',
   # 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.
@@ -390,7 +390,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '1e9ce77348af16938c7c04c2ffd027e64acdf286',
+  'dawn_revision': 'faa64a60ef10711532beb601d83fa3e56df25f89',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -756,7 +756,7 @@
   },
 
   'src/ios/third_party/edo/src': {
-      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + 'a99f9d76b0f014bb02fc0fdf4944cb4f6928f0fa',
+      'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git' + '@' + '4be4a94e0aa64b279a3eaae18414b2a7e7769be3',
       'condition': 'checkout_ios',
   },
 
@@ -920,7 +920,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '6e-zhUwS-KT1j_VrRmOMCbv07k-qvd16ONwqxtkR5mEC',
+          'version': 'j5iQQcfEJWgNhcd2zCVNcBHd4gFj3fzcuU6Q1EENWQ4C',
       },
     ],
     'condition': 'checkout_android',
@@ -1136,7 +1136,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6754c49e02b62d54b9b5d8b8b8a93e77bae935f8',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0a5bae7ebcceab7abdfa2d61b168c93d0fb01697',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1708,7 +1708,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'edd275f0ad33c708fba30643cf971adf0f6ba488',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '83ff95add93ca6cb262eb5f6f1ce4065858ae1dd',
+    Var('webrtc_git') + '/src.git' + '@' + '86c452ac5ae9cf84e92b8d28c35432b188728edf',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1781,7 +1781,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@585225b0f483a3352cb79978204d506d1ff2c913',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@7e09c774bdd1f66a6c8c38204f5efd70f1d2b115',
     'condition': 'checkout_src_internal',
   },
 
@@ -1833,7 +1833,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'bMlGFBW6vXhcbG22vzqhs12pjg7SUUfG8Dowh9Xd57oC',
+        'version': 'lMTa_xSEHLYjjYNBekNQJQ96OqEMEJIKyrka4gGxqFIC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/components/arc/ime/OWNERS b/ash/components/arc/ime/OWNERS
index 2c672f3..a7058d7 100644
--- a/ash/components/arc/ime/OWNERS
+++ b/ash/components/arc/ime/OWNERS
@@ -1 +1,2 @@
+hirokisato@chromium.org
 yhanada@chromium.org
diff --git a/ash/components/arc/mojom/intent_helper.mojom b/ash/components/arc/mojom/intent_helper.mojom
index 86de2a8d..7c1c0b5 100644
--- a/ash/components/arc/mojom/intent_helper.mojom
+++ b/ash/components/arc/mojom/intent_helper.mojom
@@ -167,8 +167,9 @@
   OSLANGUAGESLANGUAGES = 72,
   // 73-76 are removed intentionally. Do not reuse them.
   SMARTPRIVACY = 77,
+  PRIVACYHUB = 78,
 
-  // Next value to be used is 78.
+  // Next value to be used is 79.
 };
 
 // Describes an unique chrome app.
diff --git a/ash/components/cryptohome/userdataauth_util.cc b/ash/components/cryptohome/userdataauth_util.cc
index f3ec8d5..b5d9fea 100644
--- a/ash/components/cryptohome/userdataauth_util.cc
+++ b/ash/components/cryptohome/userdataauth_util.cc
@@ -85,6 +85,8 @@
 template COMPONENT_EXPORT(ASH_COMPONENTS_CRYPTOHOME) CryptohomeErrorCode
     ReplyToCryptohomeError(
         const absl::optional<PrepareVaultForMigrationReply>&);
+template COMPONENT_EXPORT(ASH_COMPONENTS_CRYPTOHOME) CryptohomeErrorCode
+    ReplyToCryptohomeError(const absl::optional<RemoveAuthFactorReply>&);
 
 std::vector<cryptohome::KeyDefinition> GetKeyDataReplyToKeyDefinitions(
     const absl::optional<GetKeyDataReply>& reply) {
diff --git a/ash/components/login/auth/auth_factor_editor.cc b/ash/components/login/auth/auth_factor_editor.cc
index 6ba69cc..775a268 100644
--- a/ash/components/login/auth/auth_factor_editor.cc
+++ b/ash/components/login/auth/auth_factor_editor.cc
@@ -10,6 +10,7 @@
 #include "ash/components/login/auth/cryptohome_key_constants.h"
 #include "ash/components/login/auth/cryptohome_parameter_utils.h"
 #include "ash/components/login/auth/user_context.h"
+#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "chromeos/dbus/userdataauth/userdataauth_client.h"
@@ -141,6 +142,65 @@
                               std::move(callback)));
 }
 
+void AuthFactorEditor::AddRecoveryFactor(std::unique_ptr<UserContext> context,
+                                         AuthOperationCallback callback) {
+  CHECK(features::IsCryptohomeRecoverySetupEnabled());
+  DCHECK(!context->GetAuthSessionId().empty());
+
+  // TODO(crbug.com/1310312): Check whether a recovery key already exists and
+  // return immediately.
+
+  LOGIN_LOG(EVENT) << "Adding recovery key";
+
+  user_data_auth::AddAuthFactorRequest req;
+  req.set_auth_session_id(context->GetAuthSessionId());
+
+  user_data_auth::AuthFactor* factor = req.mutable_auth_factor();
+  factor->set_type(user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY);
+  factor->set_label(kCryptohomeRecoveryKeyLabel);
+
+  // The cryptohome recovery meta data struct does not have members at the
+  // moment.
+  factor->mutable_cryptohome_recovery_metadata();
+
+  // TODO(crbug.com/1310312): We only need to set the mediator public key here;
+  // all other members are for other operations. The public key will likely be
+  // hardcoded, although perhaps configurable via a command line switch for
+  // testing.
+  user_data_auth::CryptohomeRecoveryAuthInput* input =
+      req.mutable_auth_input()->mutable_cryptohome_recovery_input();
+  input->set_mediator_pub_key("STUB MEDIATOR PUB KEY");
+
+  auto add_auth_factor_callback = base::BindOnce(
+      &AuthFactorEditor::OnRecoveryFactorAdded, weak_factory_.GetWeakPtr(),
+      std::move(context), std::move(callback));
+
+  UserDataAuthClient::Get()->AddAuthFactor(std::move(req),
+                                           std::move(add_auth_factor_callback));
+}
+
+void AuthFactorEditor::RemoveRecoveryFactor(
+    std::unique_ptr<UserContext> context,
+    AuthOperationCallback callback) {
+  CHECK(features::IsCryptohomeRecoverySetupEnabled());
+  DCHECK(!context->GetAuthSessionId().empty());
+
+  // TODO(crbug.com/1310312): Check whether a recovery key already exists and
+  // return immediately if there are no recovery keys.
+
+  LOGIN_LOG(EVENT) << "Removing recovery key";
+
+  user_data_auth::RemoveAuthFactorRequest req;
+  req.set_auth_session_id(context->GetAuthSessionId());
+  req.set_auth_factor_label(kCryptohomeRecoveryKeyLabel);
+
+  auto remove_auth_factor_callback = base::BindOnce(
+      &AuthFactorEditor::OnRecoveryFactorRemoved, weak_factory_.GetWeakPtr(),
+      std::move(context), std::move(callback));
+  UserDataAuthClient::Get()->RemoveAuthFactor(
+      req, std::move(remove_auth_factor_callback));
+}
+
 /// ---- private callbacks ----
 
 void AuthFactorEditor::OnAddCredentials(
@@ -175,4 +235,36 @@
   std::move(callback).Run(std::move(context), absl::nullopt);
 }
 
+void AuthFactorEditor::OnRecoveryFactorAdded(
+    std::unique_ptr<UserContext> context,
+    AuthOperationCallback callback,
+    absl::optional<::user_data_auth::AddAuthFactorReply> reply) {
+  auto error = user_data_auth::ReplyToCryptohomeError(reply);
+  if (error != user_data_auth::CRYPTOHOME_ERROR_NOT_SET) {
+    LOG(WARNING) << "AddAuthFactor for recovery failed with error " << error;
+    std::move(callback).Run(std::move(context), CryptohomeError{error});
+    return;
+  }
+
+  CHECK(reply.has_value());
+  LOGIN_LOG(EVENT) << "Successfully added recovery key";
+  std::move(callback).Run(std::move(context), absl::nullopt);
+}
+
+void AuthFactorEditor::OnRecoveryFactorRemoved(
+    std::unique_ptr<UserContext> context,
+    AuthOperationCallback callback,
+    absl::optional<::user_data_auth::RemoveAuthFactorReply> reply) {
+  auto error = user_data_auth::ReplyToCryptohomeError(reply);
+  if (error != user_data_auth::CRYPTOHOME_ERROR_NOT_SET) {
+    LOG(WARNING) << "RemoveAuthFactor for recovery failed with error " << error;
+    std::move(callback).Run(std::move(context), CryptohomeError{error});
+    return;
+  }
+
+  CHECK(reply.has_value());
+  LOGIN_LOG(EVENT) << "Successfully removed recovery key";
+  std::move(callback).Run(std::move(context), absl::nullopt);
+}
+
 }  // namespace ash
diff --git a/ash/components/login/auth/auth_factor_editor.h b/ash/components/login/auth/auth_factor_editor.h
index 378cf0e..273cad68 100644
--- a/ash/components/login/auth/auth_factor_editor.h
+++ b/ash/components/login/auth/auth_factor_editor.h
@@ -59,6 +59,17 @@
   void ReplaceContextKey(std::unique_ptr<UserContext> context,
                          AuthOperationCallback callback);
 
+  // Adds a recovery key for the user by `context`. No key is added if there is
+  // already a recovery key.
+  // Session must be authenticated.
+  void AddRecoveryFactor(std::unique_ptr<UserContext> context,
+                         AuthOperationCallback callback);
+
+  // Remove all recovery keys for the user by `context`.
+  // Session must be authenticated.
+  void RemoveRecoveryFactor(std::unique_ptr<UserContext> context,
+                            AuthOperationCallback callback);
+
  private:
   void HashContextKeyAndAdd(std::unique_ptr<UserContext> context,
                             AuthOperationCallback callback,
@@ -73,6 +84,16 @@
       AuthOperationCallback callback,
       absl::optional<user_data_auth::UpdateCredentialReply> reply);
 
+  void OnRecoveryFactorAdded(
+      std::unique_ptr<UserContext> context,
+      AuthOperationCallback callback,
+      absl::optional<user_data_auth::AddAuthFactorReply> reply);
+
+  void OnRecoveryFactorRemoved(
+      std::unique_ptr<UserContext> context,
+      AuthOperationCallback callback,
+      absl::optional<user_data_auth::RemoveAuthFactorReply> reply);
+
   base::WeakPtrFactory<AuthFactorEditor> weak_factory_{this};
 };
 
diff --git a/ash/components/login/auth/cryptohome_key_constants.cc b/ash/components/login/auth/cryptohome_key_constants.cc
index 4c08448c..7e04d8e0 100644
--- a/ash/components/login/auth/cryptohome_key_constants.cc
+++ b/ash/components/login/auth/cryptohome_key_constants.cc
@@ -25,4 +25,6 @@
 
 const char kCryptohomeWildcardLabel[] = "";
 
+const char kCryptohomeRecoveryKeyLabel[] = "recovery";
+
 }  // namespace ash
diff --git a/ash/components/login/auth/cryptohome_key_constants.h b/ash/components/login/auth/cryptohome_key_constants.h
index 50f9906..50fadd7 100644
--- a/ash/components/login/auth/cryptohome_key_constants.h
+++ b/ash/components/login/auth/cryptohome_key_constants.h
@@ -24,6 +24,9 @@
 COMPONENT_EXPORT(ASH_LOGIN_AUTH)
 extern const char kCryptohomeWildcardLabel[];
 
+COMPONENT_EXPORT(ASH_LOGIN_AUTH)
+extern const char kCryptohomeRecoveryKeyLabel[];
+
 }  // namespace ash
 
 // TODO(https://crbug.com/1164001): remove when the migration is finished.
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 7d793bb..008e44c 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -430,6 +430,12 @@
 const base::Feature kCryptohomeRecoveryFlow{"CryptohomeRecoveryFlow",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables the UI to enable or disable cryptohome recovery in the settings
+// page. Also guards the wiring of cryptohome recovery settings to the
+// cryptohome backend.
+const base::Feature kCryptohomeRecoverySetup{"CryptohomeRecoverySetup",
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kDemoModeSWA{"DemoModeSWA",
                                  base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -697,7 +703,7 @@
 // flag controls the second tier, whose support is more experimental.
 // https://crbug.com/1216245
 const base::Feature kFilesArchivemount2{"FilesArchivemount2",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable the simple archive extraction.
 // https://crbug.com/953256
@@ -1751,6 +1757,10 @@
   return base::FeatureList::IsEnabled(kCryptohomeRecoveryFlow);
 }
 
+bool IsCryptohomeRecoverySetupEnabled() {
+  return base::FeatureList::IsEnabled(kCryptohomeRecoverySetup);
+}
+
 bool IsDarkLightModeEnabled() {
   return base::FeatureList::IsEnabled(kNotificationsRefresh) ||
          chromeos::features::IsDarkLightModeEnabled();
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index ad2cbf5..d500c66a 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -174,6 +174,8 @@
 extern const base::Feature kCryptAuthV2Enrollment;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kCryptohomeRecoveryFlow;
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kCryptohomeRecoverySetup;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kDemoModeSWA;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kDeprecateAssistantStylusFeatures;
@@ -661,6 +663,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsConsumerAutoUpdateToggleAllowed();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDesksCloseAllEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCryptohomeRecoveryFlowEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCryptohomeRecoverySetupEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDarkLightModeEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeepLinkingEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeSWAEnabled();
diff --git a/ash/constants/ash_pref_names.cc b/ash/constants/ash_pref_names.cc
index dfd1226..960075c 100644
--- a/ash/constants/ash_pref_names.cc
+++ b/ash/constants/ash_pref_names.cc
@@ -715,6 +715,9 @@
 // Boolean pref to persist the expanded state of the system tray across reboots.
 const char kSystemTrayExpanded[] = "ash.system_tray.expanded";
 
+// A boolean pref indicating whether the camera is allowed to be used.
+const char kUserCameraAllowed[] = "ash.user.camera_allowed";
+
 // A boolean pref which determines whether tap-dragging is enabled.
 const char kTapDraggingEnabled[] = "settings.touchpad.enable_tap_dragging";
 
diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h
index 29c888c2..024838c 100644
--- a/ash/constants/ash_pref_names.h
+++ b/ash/constants/ash_pref_names.h
@@ -326,6 +326,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const char kSystemTrayExpanded[];
 
+COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kUserCameraAllowed[];
+
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kTapDraggingEnabled[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kTouchpadEnabled[];
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const char kTouchscreenEnabled[];
diff --git a/ash/login/login_screen_test_api.cc b/ash/login/login_screen_test_api.cc
index eec4b32..1d3ae9e 100644
--- a/ash/login/login_screen_test_api.cc
+++ b/ash/login/login_screen_test_api.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "ash/login/ui/arrow_button_view.h"
+#include "ash/login/ui/kiosk_app_default_message.h"
 #include "ash/login/ui/lock_contents_view.h"
 #include "ash/login/ui/lock_screen.h"
 #include "ash/login/ui/login_auth_user_view.h"
@@ -276,6 +277,15 @@
 }
 
 // static
+bool LoginScreenTestApi::IsKioskDefaultMessageShown() {
+  LockScreen::TestApi lock_screen_test(LockScreen::Get());
+  LockContentsView::TestApi test_api(lock_screen_test.contents_view());
+  return test_api.kiosk_default_message() &&
+         test_api.kiosk_default_message()->GetWidget() &&
+         test_api.kiosk_default_message()->GetWidget()->IsVisible();
+}
+
+// static
 bool LoginScreenTestApi::IsKioskInstructionBubbleShown() {
   LoginShelfView* view = GetLoginShelfView();
   return view->GetKioskInstructionBubbleForTesting() &&
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 7f28089..91bccac 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -63,6 +63,7 @@
 #include "chromeos/ui/vector_icons/vector_icons.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user_type.h"
+#include "lock_contents_view.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -610,6 +611,8 @@
   data_dispatcher_->AddObserver(this);
   Shell::Get()->system_tray_notifier()->AddSystemTrayObserver(this);
   keyboard::KeyboardUIController::Get()->AddObserver(this);
+  enterprise_domain_model_observation_.Observe(
+      Shell::Get()->system_tray_model()->enterprise_domain());
 
   // We reuse the focusable state on this view as a signal that focus should
   // switch to the system tray. LockContentsView should otherwise not be
@@ -848,24 +851,10 @@
   Shell::Get()->login_screen_controller()->ShowParentAccessButton(false);
 }
 
-void LockContentsView::SetKioskAppsButtonPresence(
-    bool is_kiosk_apps_button_present) {
-  if (!kiosk_license_mode_)
-    return;
+void LockContentsView::SetHasKioskApp(bool has_kiosk_apps) {
+  has_kiosk_apps_ = has_kiosk_apps;
 
-  // check if the kiosk app button is visible
-  if (is_kiosk_apps_button_present) {
-    if (kiosk_default_message_)
-      kiosk_default_message_->GetWidget()->Hide();
-  } else {
-    if (!kiosk_default_message_) {
-      // KioskAppDefaultMessage is owned by itself and would be destroyed when
-      // its widget got destroyed, which happened when the widget's window got
-      // destroyed.
-      kiosk_default_message_ = new KioskAppDefaultMessage();
-    }
-    kiosk_default_message_->GetWidget()->Show();
-  }
+  UpdateKioskDefaultMessageVisibility();
 }
 
 void LockContentsView::Layout() {
@@ -1569,6 +1558,23 @@
     big_user->auth_user()->password_view()->Reset();
 }
 
+void LockContentsView::OnDeviceEnterpriseInfoChanged() {
+  // If feature is enabled, update the boolean kiosk_license_mode_. Otherwise,
+  // it's false by default.
+  if (!features::IsKioskEnrollmentInOobeEnabled())
+    return;
+
+  kiosk_license_mode_ =
+      Shell::Get()
+          ->system_tray_model()
+          ->enterprise_domain()
+          ->management_device_mode() == ManagementDeviceMode::kKioskSku;
+
+  UpdateKioskDefaultMessageVisibility();
+}
+
+void LockContentsView::OnEnterpriseAccountDomainChanged() {}
+
 void LockContentsView::ShowAuthErrorMessageForDebug(int unlock_attempt) {
   unlock_attempt_ = unlock_attempt;
   ShowAuthErrorMessage();
@@ -2596,9 +2602,30 @@
       /*prefilled_account=*/EmptyAccountId());
 }
 
+void LockContentsView::UpdateKioskDefaultMessageVisibility() {
+  if (!kiosk_license_mode_)
+    return;
+
+  if (!kiosk_default_message_) {
+    // KioskAppDefaultMessage is owned by itself and would be destroyed when
+    // its widget got destroyed, which happened when the widget's window got
+    // destroyed.
+    kiosk_default_message_ = new KioskAppDefaultMessage();
+  }
+  if (has_kiosk_apps_)
+    kiosk_default_message_->GetWidget()->Hide();
+  else
+    kiosk_default_message_->GetWidget()->Show();
+}
+
 void LockContentsView::SetKioskLicenseModeForTesting(
     bool is_kiosk_license_mode) {
   kiosk_license_mode_ = is_kiosk_license_mode;
+
+  // Normally when management device mode is updated, via
+  // OnDeviceEnterpriseInfoChanged, it updates the visibility of Kiosk default
+  // meesage too.
+  UpdateKioskDefaultMessageVisibility();
 }
 
 BEGIN_METADATA(LockContentsView, NonAccessibleView)
diff --git a/ash/login/ui/lock_contents_view.h b/ash/login/ui/lock_contents_view.h
index 2ae93208..98ec6c2 100644
--- a/ash/login/ui/lock_contents_view.h
+++ b/ash/login/ui/lock_contents_view.h
@@ -24,6 +24,8 @@
 #include "ash/public/cpp/login_types.h"
 #include "ash/public/cpp/smartlock_state.h"
 #include "ash/public/cpp/system_tray_observer.h"
+#include "ash/system/enterprise/enterprise_domain_observer.h"
+#include "ash/system/model/enterprise_domain_model.h"
 #include "base/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -71,7 +73,8 @@
       public SystemTrayObserver,
       public display::DisplayObserver,
       public KeyboardControllerObserver,
-      public chromeos::PowerManagerClient::Observer {
+      public chromeos::PowerManagerClient::Observer,
+      public EnterpriseDomainObserver {
  public:
   METADATA_HEADER(LockContentsView);
   class AuthErrorBubble;
@@ -154,7 +157,7 @@
   void ShowAdbEnabled();
   void ToggleSystemInfo();
   void ShowParentAccessDialog();
-  void SetKioskAppsButtonPresence(bool is_kiosk_apps_button_present);
+  void SetHasKioskApp(bool has_kiosk_apps);
 
   // views::View:
   void Layout() override;
@@ -239,6 +242,10 @@
   // chromeos::PowerManagerClient::Observer:
   void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
 
+  // ash::EnterpriseDomainObserver
+  void OnDeviceEnterpriseInfoChanged() override;
+  void OnEnterpriseAccountDomainChanged() override;
+
   void ShowAuthErrorMessageForDebug(int unlock_attempt);
 
   // Called for debugging to make |user| managed and display an icon along with
@@ -464,6 +471,10 @@
   // Shows GAIA sign-in page.
   void OnBackToSigninButtonTapped();
 
+  // Update visibility of Kiosk default message. Called only if
+  // kiosk_license_mode_ is true.
+  void UpdateKioskDefaultMessageVisibility();
+
   const LockScreen::ScreenType screen_type_;
 
   std::vector<UserState> users_;
@@ -504,6 +515,9 @@
 
   display::ScopedDisplayObserver display_observer_{this};
 
+  base::ScopedObservation<EnterpriseDomainModel, EnterpriseDomainObserver>
+      enterprise_domain_model_observation_{this};
+
   // All error bubbles and the tooltip view are child views of LockContentsView,
   // and will be torn down when LockContentsView is torn down.
   // Bubble for displaying authentication error.
@@ -542,9 +556,10 @@
   // screen note state.
   bool disable_lock_screen_note_ = false;
 
-  // TODO(1307303): Change it to the real function that checks if device is with
-  // Kiosk License
+  // Whether the device is enrolled with Kiosk SKU.
   bool kiosk_license_mode_ = false;
+  // Whether any kiosk app is added.
+  bool has_kiosk_apps_ = false;
 
   // Whether the system information should be displayed or not be displayed
   // forcedly according to policy settings.
diff --git a/ash/login/ui/lock_contents_view_unittest.cc b/ash/login/ui/lock_contents_view_unittest.cc
index 369674bf..bc3d133 100644
--- a/ash/login/ui/lock_contents_view_unittest.cc
+++ b/ash/login/ui/lock_contents_view_unittest.cc
@@ -3286,7 +3286,8 @@
   NotifySessionStateChanged(session_manager::SessionState::LOGIN_PRIMARY);
   SetNumberOfKioskApps(1);
 
-  EXPECT_FALSE(test_api.kiosk_default_message());
+  EXPECT_TRUE(test_api.kiosk_default_message());
+  EXPECT_FALSE(test_api.kiosk_default_message()->GetWidget()->IsVisible());
 }
 
 // Checks default message hidden if device is not with kiosk license and has
diff --git a/ash/login/ui/lock_screen.cc b/ash/login/ui/lock_screen.cc
index edeedc9..9096305 100644
--- a/ash/login/ui/lock_screen.cc
+++ b/ash/login/ui/lock_screen.cc
@@ -165,8 +165,8 @@
   contents_view_->ShowParentAccessDialog();
 }
 
-void LockScreen::SetKioskAppsButtonPresence(bool is_kiosk_apps_button_present) {
-  contents_view_->SetKioskAppsButtonPresence(is_kiosk_apps_button_present);
+void LockScreen::SetHasKioskApp(bool has_kiosk_apps) {
+  contents_view_->SetHasKioskApp(has_kiosk_apps);
 }
 
 void LockScreen::OnLockScreenNoteStateChanged(mojom::TrayActionState state) {
diff --git a/ash/login/ui/lock_screen.h b/ash/login/ui/lock_screen.h
index e229028..2ac0e15 100644
--- a/ash/login/ui/lock_screen.h
+++ b/ash/login/ui/lock_screen.h
@@ -72,7 +72,7 @@
   void FocusNextUser();
   void FocusPreviousUser();
   void ShowParentAccessDialog();
-  void SetKioskAppsButtonPresence(bool has_kiosk_apps);
+  void SetHasKioskApp(bool has_kiosk_apps);
 
   // TrayActionObserver:
   void OnLockScreenNoteStateChanged(mojom::TrayActionState state) override;
diff --git a/ash/media/media_controller_impl.cc b/ash/media/media_controller_impl.cc
index f77760d..ae92d7c 100644
--- a/ash/media/media_controller_impl.cc
+++ b/ash/media/media_controller_impl.cc
@@ -13,6 +13,7 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
+#include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "media/base/media_switches.h"
@@ -49,6 +50,10 @@
 // static
 void MediaControllerImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
   registry->RegisterBooleanPref(prefs::kLockScreenMediaControlsEnabled, true);
+  registry->RegisterBooleanPref(
+      prefs::kUserCameraAllowed,
+      /*default_value=*/true,
+      user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
 }
 
 bool MediaControllerImpl::AreLockScreenMediaKeysEnabled() const {
diff --git a/ash/public/cpp/login_screen_test_api.h b/ash/public/cpp/login_screen_test_api.h
index dd67910..ba0b2f73 100644
--- a/ash/public/cpp/login_screen_test_api.h
+++ b/ash/public/cpp/login_screen_test_api.h
@@ -43,6 +43,7 @@
   static bool IsWarningBubbleShown();
   static bool IsUserAddingScreenIndicatorShown();
   static bool IsSystemInfoShown();
+  static bool IsKioskDefaultMessageShown();
   static bool IsKioskInstructionBubbleShown();
   static bool IsPasswordFieldShown(const AccountId& account_id);
   static bool IsDisplayPasswordButtonShown(const AccountId& account_id);
diff --git a/ash/shelf/login_shelf_view.cc b/ash/shelf/login_shelf_view.cc
index 8c1b5ea..108d3e3 100644
--- a/ash/shelf/login_shelf_view.cc
+++ b/ash/shelf/login_shelf_view.cc
@@ -750,10 +750,7 @@
   kiosk_apps_button_->SetApps(kiosk_apps);
   UpdateUi();
   if (LockScreen::HasInstance()) {
-    // Consider Kiosk apps button to be present when there are Kiosk apps installed.
-    // TODO(b/234765162): rename the method as the naming is confusing.
-    LockScreen::Get()->SetKioskAppsButtonPresence(
-        kiosk_apps_button_->HasApps());
+    LockScreen::Get()->SetHasKioskApp(kiosk_apps_button_->HasApps());
   }
 }
 
diff --git a/ash/webui/camera_app_ui/resources/css/main.css b/ash/webui/camera_app_ui/resources/css/main.css
index c5e2090..c382c39 100644
--- a/ash/webui/camera_app_ui/resources/css/main.css
+++ b/ash/webui/camera_app_ui/resources/css/main.css
@@ -1365,7 +1365,7 @@
   border: none;
 }
 
-#spinner {
+.spinner {
   background-image: url(/images/spinner.svg);
   height: 32px;
   visibility: hidden;
@@ -1373,15 +1373,15 @@
   z-index: 1;
 }
 
+body:not(.mode-switching):not(.streaming).view-expert-settings .spinner {
+  visibility: visible;
+}
+
 #view-splash {
   background: no-repeat center url(/images/camera_mode_photo.svg), var(--grey-900);
   z-index: 2;
 }
 
-body:not(.mode-switching):not(.streaming) #spinner {
-  visibility: visible;
-}
-
 .hidden {
   display: none;
 }
diff --git a/ash/webui/camera_app_ui/resources/js/views/crop_document.ts b/ash/webui/camera_app_ui/resources/js/views/crop_document.ts
index 4cc0e7e..1577391 100644
--- a/ash/webui/camera_app_ui/resources/js/views/crop_document.ts
+++ b/ash/webui/camera_app_ui/resources/js/views/crop_document.ts
@@ -336,6 +336,7 @@
         e.preventDefault();
       }
     });
+    this.image.hidden = false;
   }
 
   /**
diff --git a/ash/webui/camera_app_ui/resources/views/main.html b/ash/webui/camera_app_ui/resources/views/main.html
index cb1dbbf..ce418d4 100644
--- a/ash/webui/camera_app_ui/resources/views/main.html
+++ b/ash/webui/camera_app_ui/resources/views/main.html
@@ -22,6 +22,8 @@
     <script type="module" src="/js/init.js"></script>
   </head>
   <body class="sound mirror mic view-splash">
+    <div id="spoken_msg" class="centered-overlay" tabindex="-1"
+         aria-live="polite"></div>
     <div id="view-camera">
       <div id="preview-box">
         <div id="preview-viewport">
@@ -253,20 +255,20 @@
     </div>
     <div id="view-review" class="review-views">
       <div class="review-frame">
-        <img class="review-image">
-        <video class="review-video" controls autoplay></video>
+        <img class="review-image" hidden>
+        <video class="review-video" hidden controls autoplay></video>
       </div>
     </div>
     <div id="view-crop-document" class="review-views">
       <div id="document-corner-move-desc" i18n-text="document_corner_move_desc">
       </div>
       <div class="review-frame">
-        <div class="review-image">
+        <div class="review-image" hidden>
           <svg class="crop-area-container">
             <polygon class="crop-area"></polygon>
           </svg>
         </div>
-        <video class="review-video"></video>
+        <video class="review-video" hidden></video>
       </div>
       <div class="review-crop-rotation-button-group button-group">
         <button class="icon-button dark inkdrop"
@@ -481,7 +483,7 @@
           <span i18n-text="expert_enable_expert_mode"></span>
         </label>
       </div>
-      <div id="spinner" class="centered-overlay"></div>
+      <div class="spinner centered-overlay"></div>
     </div>
     <div id="view-flash">
       <div id="processing-indicator" class="centered-overlay">
@@ -506,8 +508,6 @@
     </div>
     <div id="toast" class="centered-overlay" tabindex="-1"
          aria-live="polite"></div>
-    <div id="spoken_msg" class="centered-overlay" tabindex="-1"
-         aria-live="polite"></div>
     <div id="tooltip" aria-hidden="true"></div>
     <audio id="sound-tick-final" src="/sounds/tick_final.ogg"></audio>
     <audio id="sound-tick-inc" src="/sounds/tick_inc.ogg"></audio>
diff --git a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
index 40c887d1..cba6698 100644
--- a/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
+++ b/ash/webui/os_feedback_ui/mojom/os_feedback_ui.mojom
@@ -19,6 +19,8 @@
 
 module ash.os_feedback_ui.mojom;
 
+import "mojo/public/mojom/base/big_buffer.mojom";
+import "mojo/public/mojom/base/safe_base_name.mojom";
 import "mojo/public/mojom/base/string16.mojom";
 import "url/mojom/url.mojom";
 
@@ -87,6 +89,15 @@
   kDelayed
 };
 
+// Used to hold the content and name of the attached file chosen by users.
+// Both fields are required.
+struct AttachedFile {
+  // The content of a file.
+  mojo_base.mojom.BigBuffer file_data;
+  // The name of the file.
+  mojo_base.mojom.SafeBaseName file_name;
+};
+
 // Used to hold all data and flags for the feedback report to be sent.
 struct Report {
   // The context info when the feedback tool is opened. Some fields may have
@@ -95,6 +106,8 @@
   FeedbackContext feedback_context;
   // The feedback text describing the user issue.
   mojo_base.mojom.String16 description;
+  // The optional attached file info.
+  AttachedFile? attached_file;
   // Whether or not to send system logs and metrics/histograms with this report.
   bool include_system_logs_and_histograms;
   // Whether or not to include the screenshot with this report. The screenshot
diff --git a/ash/webui/os_feedback_ui/os_feedback_untrusted_ui.cc b/ash/webui/os_feedback_ui/os_feedback_untrusted_ui.cc
index b0a9581..a43c157 100644
--- a/ash/webui/os_feedback_ui/os_feedback_untrusted_ui.cc
+++ b/ash/webui/os_feedback_ui/os_feedback_untrusted_ui.cc
@@ -61,6 +61,12 @@
   untrusted_source->AddResourcePath("feedback_types.js",
                                     IDR_ASH_OS_FEEDBACK_FEEDBACK_TYPES_JS);
   untrusted_source->AddResourcePath(
+      "file_path.mojom-lite.js",
+      IDR_ASH_OS_FEEDBACK_MOJO_PUBLIC_MOJOM_BASE_FILE_PATH_MOJOM_LITE_JS);
+  untrusted_source->AddResourcePath(
+      "safe_base_name.mojom-lite.js",
+      IDR_ASH_OS_FEEDBACK_MOJO_PUBLIC_MOJOM_BASE_SAFE_BASE_NAME_MOJOM_LITE_JS);
+  untrusted_source->AddResourcePath(
       "help_resources_icons.js", IDR_ASH_OS_FEEDBACK_HELP_RESOURCES_ICONS_JS);
   untrusted_source->AddResourcePath(
       "mojom/os_feedback_ui.mojom-lite.js",
diff --git a/ash/webui/os_feedback_ui/resources/BUILD.gn b/ash/webui/os_feedback_ui/resources/BUILD.gn
index a6ac9685..ecd0b8a3 100644
--- a/ash/webui/os_feedback_ui/resources/BUILD.gn
+++ b/ash/webui/os_feedback_ui/resources/BUILD.gn
@@ -14,6 +14,7 @@
 preprocessed_dir = "preprocessed"
 preprocessed_gen_manifest = "preprocessed_gen_manifest.json"
 preprocessed_mojo_manifest = "preprocessed_mojo_manifest.json"
+preprocess_external_mojo_manifest = "preprocess_external_mojo_manifest.json"
 
 polymer_element_files = [
   "confirmation_page.js",
@@ -26,6 +27,17 @@
   "share_data_page.js",
 ]
 
+preprocess_if_expr("preprocess_external_mojo") {
+  deps = [ "//mojo/public/mojom/base:base_js__generator" ]
+  in_folder = "$root_gen_dir"
+  out_folder = "$target_gen_dir/$preprocessed_dir"
+  out_manifest = "$target_gen_dir/$preprocess_external_mojo_manifest"
+  in_files = [
+    "mojo/public/mojom/base/file_path.mojom-lite.js",
+    "mojo/public/mojom/base/safe_base_name.mojom-lite.js",
+  ]
+}
+
 generate_grd("build_grd") {
   input_files = [
     "app_icon_48.png",
@@ -40,12 +52,18 @@
   ]
   input_files_base_dir = rebase_path(".", "//")
   deps = [
+    ":preprocess_external_mojo",
     ":preprocess_generated",
     ":preprocess_mojo",
   ]
   manifest_files = [
     "$target_gen_dir/$preprocessed_gen_manifest",
     "$target_gen_dir/$preprocessed_mojo_manifest",
+    "$target_gen_dir/$preprocess_external_mojo_manifest",
+  ]
+  resource_path_rewrites = [
+    "mojo/public/mojom/base/file_path.mojom-lite.js|file_path.mojom-lite.js",
+    "mojo/public/mojom/base/safe_base_name.mojom-lite.js|safe_base_name.mojom-lite.js",
   ]
   grd_prefix = "ash_os_feedback"
   out_grd = "$target_gen_dir/${grd_prefix}_resources.grd"
@@ -119,6 +137,7 @@
 
 js_library("file_attachment") {
   deps = [
+    ":feedback_types",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
 }
diff --git a/ash/webui/os_feedback_ui/resources/feedback_types.js b/ash/webui/os_feedback_ui/resources/feedback_types.js
index a7da5c2..8d21564 100644
--- a/ash/webui/os_feedback_ui/resources/feedback_types.js
+++ b/ash/webui/os_feedback_ui/resources/feedback_types.js
@@ -11,6 +11,8 @@
 import '//resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
 import '//resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import '//resources/mojo/url/mojom/url.mojom-lite.js';
+import './file_path.mojom-lite.js';
+import './safe_base_name.mojom-lite.js';
 import './mojom/os_feedback_ui.mojom-lite.js';
 
 /**
@@ -83,6 +85,12 @@
 export const SendReportStatus = ash.osFeedbackUi.mojom.SendReportStatus;
 
 /**
+ * Type alias for AttachedFile.
+ * @typedef {ash.osFeedbackUi.mojom.AttachedFile}
+ */
+export const AttachedFile = ash.osFeedbackUi.mojom.AttachedFile;
+
+/**
  * Type alias for Report.
  * @typedef {ash.osFeedbackUi.mojom.Report}
  */
diff --git a/ash/webui/os_feedback_ui/resources/file_attachment.js b/ash/webui/os_feedback_ui/resources/file_attachment.js
index 6033b7f..081c4e4 100644
--- a/ash/webui/os_feedback_ui/resources/file_attachment.js
+++ b/ash/webui/os_feedback_ui/resources/file_attachment.js
@@ -6,8 +6,11 @@
 import './os_feedback_shared_css.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 
+import {stringToMojoString16} from 'chrome://resources/ash/common/mojo_utils.js';
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {AttachedFile} from './feedback_types.js';
+
 /**
  * @fileoverview
  * 'file-attachment' allows users to select a file as an attachment to the
@@ -26,14 +29,7 @@
     return {
       hasSelectedAFile_: {
         type: Boolean,
-        computed: 'computeHasSelectedAFile_(selectedFile)',
-      },
-
-      selectedFile: {
-        type: File,
-        notify: true,
-        readOnly: true,
-        reflectToAttribute: true,
+        computed: 'computeHasSelectedAFile_(selectedFile_)',
       },
     };
   }
@@ -44,8 +40,9 @@
     /**
      * The file selected if any to be attached to the report.
      * @type {?File}
+     * @private
      */
-    this.selectedFile = null;
+    this.selectedFile_ = null;
 
     /**
      * True when there is a file selected.
@@ -59,7 +56,7 @@
    * @private
    */
   computeHasSelectedAFile_() {
-    return !!this.selectedFile;
+    return !!this.selectedFile_;
   }
 
   /**
@@ -72,6 +69,34 @@
   }
 
   /**
+   * Gather the file name and data chosen.
+   * @return {!Promise<?AttachedFile>}
+   */
+  async getAttachedFile() {
+    if (!this.getElement_('#selectFileCheckbox').checked) {
+      return null;
+    }
+    if (!this.selectedFile_) {
+      return null;
+    }
+
+    const fileDataBuffer = await this.selectedFile_.arrayBuffer();
+    const fileDataView = new Uint8Array(fileDataBuffer);
+    // fileData is of type BigBuffer which can take byte array format or
+    // shared memory form. For now, byte array is being used for its simplicity.
+    // For better performance, we may switch to shared memory.
+    const fileData = {bytes: Array.from(fileDataView)};
+
+    /** @type {!AttachedFile} */
+    const attachedFile = {
+      fileName: {path: {path: this.selectedFile_.name}},
+      fileData: fileData
+    };
+
+    return attachedFile;
+  }
+
+  /**
    * @param {!Event} e
    * @protected
    */
@@ -89,18 +114,26 @@
     // The feedback app takes maximum one attachment. And the file dialog is set
     // to accept one file only.
     if (fileInput.files.length > 0) {
-      const selectedFileName = this.getElement_('#selectedFileName');
-      selectedFileName.textContent = fileInput.files[0].name;
-      this.getElement_('#selectFileCheckbox').checked = true;
-      this.selectedFile = fileInput.files[0];
+      this.handleSelectedFileHelper_(fileInput.files[0]);
     }
   }
 
   /**
    * @param {!File} file
+   * @private
+   */
+  handleSelectedFileHelper_(file) {
+    // TODO(http://b/233398494): Handle it when file size exceeds the limit.
+    this.selectedFile_ = file;
+    this.getElement_('#selectedFileName').textContent = file.name;
+    this.getElement_('#selectFileCheckbox').checked = true;
+  }
+
+  /**
+   * @param {!File} file
    */
   setSelectedFileForTesting(file) {
-    this.selectedFile = file;
+    this.handleSelectedFileHelper_(file);
   }
 }
 
diff --git a/ash/webui/os_feedback_ui/resources/share_data_page.js b/ash/webui/os_feedback_ui/resources/share_data_page.js
index 2b869e7..50d32b9a 100644
--- a/ash/webui/os_feedback_ui/resources/share_data_page.js
+++ b/ash/webui/os_feedback_ui/resources/share_data_page.js
@@ -9,7 +9,7 @@
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {FeedbackFlowState} from './feedback_flow.js';
-import {FeedbackContext, Report} from './feedback_types.js';
+import {AttachedFile, FeedbackContext, Report} from './feedback_types.js';
 
 /**
  * @fileoverview
@@ -85,14 +85,13 @@
 
     e.stopPropagation();
 
-    this.dispatchEvent(new CustomEvent('continue-click', {
-      composed: true,
-      bubbles: true,
-      detail: {
-        currentState: FeedbackFlowState.SHARE_DATA,
-        report: this.createReport_()
-      }
-    }));
+    this.createReport_().then(report => {
+      this.dispatchEvent(new CustomEvent('continue-click', {
+        composed: true,
+        bubbles: true,
+        detail: {currentState: FeedbackFlowState.SHARE_DATA, report: report}
+      }));
+    });
   }
 
   /**
@@ -105,21 +104,22 @@
   }
 
   /**
-   * @return {!{
-   *  feedbackContext: FeedbackContext,
-   *  includeSystemLogsAndHistograms: boolean,
-   *  includeScreenshot: boolean,
-   * }}
+   * @return {!Promise<!Report>}
    * @private
    */
-  createReport_() {
-    const report = {
+  async createReport_() {
+    /* @type {!Report} */
+    const report = /** @type {!Report} */ ({
       feedbackContext: {},
+      description: null,
       includeSystemLogsAndHistograms:
           this.getElement_('#sysInfoCheckbox').checked,
       includeScreenshot: this.getElement_('#screenshotCheckbox').checked &&
           !!this.getElement_('#screenshotImage').src
-    };
+    });
+
+    report.attachedFile =
+        await this.getElement_('file-attachment').getAttachedFile();
 
     const email = this.getElement_('#userEmailDropDown').value;
     if (email) {
@@ -131,6 +131,7 @@
         url: this.getElement_('#pageUrlText').value
       };
     }
+
     return report;
   }
 
diff --git a/base/trace_event/trace_event_unittest.cc b/base/trace_event/trace_event_unittest.cc
index f5a0574..9f8851c 100644
--- a/base/trace_event/trace_event_unittest.cc
+++ b/base/trace_event/trace_event_unittest.cc
@@ -1404,13 +1404,22 @@
     // Test that static TRACE_STR_COPY NULL string arguments are not copied.
     const char* str1 = nullptr;
     const char* str2 = nullptr;
+#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
+    // DynamicString{nullptr} is not supported in perfetto for debug-args-value.
+    // After reverting the definition of `TRACE_STR_COPY` to nop, this
+    // difference in perfetto vs non-perfetto won't be required here.
+    [[maybe_unused]] TraceEventHandle handle2 =
+        trace_event_internal::AddTraceEvent(
+            TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2",
+            trace_event_internal::kGlobalScope, trace_event_internal::kNoId, 0,
+            trace_event_internal::kNoId, "arg1", str1, "arg2", str2);
+#else
     [[maybe_unused]] TraceEventHandle handle2 =
         trace_event_internal::AddTraceEvent(
             TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2",
             trace_event_internal::kGlobalScope, trace_event_internal::kNoId, 0,
             trace_event_internal::kNoId, "arg1", TRACE_STR_COPY(str1), "arg2",
             TRACE_STR_COPY(str2));
-#if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
     EXPECT_GT(tracer->GetStatus().event_count, 1u);
     const TraceEvent* event1 = tracer->GetEventByHandle(handle1);
     const TraceEvent* event2 = tracer->GetEventByHandle(handle2);
@@ -1420,7 +1429,7 @@
     EXPECT_STREQ("name2", event2->name());
     EXPECT_TRUE(event1->parameter_copy_storage().empty());
     EXPECT_TRUE(event2->parameter_copy_storage().empty());
-#endif  // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
+#endif  // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
     EndTraceAndFlush();
   }
 }
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 4cc92a8..739e28d4 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-8.20220607.3.1
+8.20220608.1.1
diff --git a/chrome/OWNERS b/chrome/OWNERS
index 714eda6..943b826 100644
--- a/chrome/OWNERS
+++ b/chrome/OWNERS
@@ -4,6 +4,7 @@
 set noparent
 
 # Reviewers (in CET):
+alexilin@chromium.org
 blundell@chromium.org
 droger@chromium.org
 treib@chromium.org
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/autofill/OWNERS
index 50a21bb..187488e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/OWNERS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/OWNERS
@@ -1 +1,2 @@
 file://components/autofill/OWNERS
+file://components/autofill/android/OWNERS
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
index b4a829c0..21308ec 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunSignInProcessor.java
@@ -49,10 +49,6 @@
      * @param activity The context for the FRE parameters processor.
      */
     public static void start(final Activity activity) {
-        SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
-                Profile.getLastUsedRegularProfile());
-        signinManager.onFirstRunCheckDone();
-
         // Skip signin if the first run flow is not complete. Examples of cases where the user
         // would not have gone through the FRE:
         // - FRE is disabled, or
@@ -73,6 +69,8 @@
             setFirstRunFlowSignInComplete(true);
         }
 
+        SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
+                Profile.getLastUsedRegularProfile());
         if (!FirstRunUtils.canAllowSync() || !signinManager.isSyncOptInAllowed()
                 || TextUtils.isEmpty(accountName)) {
             setFirstRunFlowSignInComplete(true);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SyncConsentFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SyncConsentFirstRunFragment.java
index 2e4063e..2b087fe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SyncConsentFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/SyncConsentFirstRunFragment.java
@@ -12,10 +12,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.ntp.cards.SignInPromo;
-import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.signin.services.FREMobileIdentityConsistencyFieldTrial;
-import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
-import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.ui.signin.SyncConsentFragmentBase;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.AccountUtils;
@@ -73,12 +70,6 @@
         }
 
         if (ChromeFeatureList.isEnabled(ChromeFeatureList.ENABLE_SYNC_IMMEDIATELY_IN_FRE)) {
-            // Mark First Run check as done before processing the sign-in.
-            // TODO(https://crbug.com/1316369): Remove onFirstRunCheckDone altogether.
-            SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
-                    Profile.getLastUsedRegularProfile());
-            signinManager.onFirstRunCheckDone();
-
             // Enable sync now. Leave the account pref empty in FirstRunSignInProcessor, so start()
             // doesn't try to do it a second time. Only set the advanced setup pref later in
             // closeAndMaybeOpenSyncSettings(), because settings shouldn't open if
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java
index fe23793..afeeace 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninManagerImpl.java
@@ -81,14 +81,6 @@
     private boolean mSigninAllowedByPolicy;
 
     /**
-     * Tracks whether the First Run check has been completed.
-     *
-     * A new sign-in can not be started while this is pending, to prevent the
-     * pending check from eventually starting a 2nd sign-in.
-     */
-    private boolean mFirstRunCheckIsPending = true;
-
-    /**
      * Will be set during the sign in process, and nulled out when there is not a pending sign in.
      * Needs to be null checked after ever async entry point because it can be nulled out at any
      * time by system accounts changing.
@@ -170,25 +162,11 @@
     }
 
     /**
-     * Notifies the SigninManager that the First Run check has completed.
-     *
-     * The user will be allowed to sign-in once this is signaled.
-     */
-    @Override
-    public void onFirstRunCheckDone() {
-        mFirstRunCheckIsPending = false;
-
-        if (isSyncOptInAllowed()) {
-            notifySignInAllowedChanged();
-        }
-    }
-
-    /**
      * Returns true if sign in can be started now.
      */
     @Override
     public boolean isSigninAllowed() {
-        return !mFirstRunCheckIsPending && mSignInState == null && mSigninAllowedByPolicy
+        return mSignInState == null && mSigninAllowedByPolicy
                 && mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN) == null
                 && isSigninSupported();
     }
@@ -198,7 +176,7 @@
      */
     @Override
     public boolean isSyncOptInAllowed() {
-        return !mFirstRunCheckIsPending && mSignInState == null && mSigninAllowedByPolicy
+        return mSignInState == null && mSigninAllowedByPolicy
                 && mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SYNC) == null
                 && isSigninSupported();
     }
@@ -293,11 +271,10 @@
     private void signinInternal(SignInState signInState) {
         assert isSyncOptInAllowed()
             : String.format("Sign-in isn't allowed!\n"
-                            + "  mFirstRunCheckIsPending: %s\n"
                             + "  mSignInState: %s\n"
                             + "  mSigninAllowedByPolicy: %s\n"
                             + "  Primary sync account: %s",
-                    mFirstRunCheckIsPending, mSignInState, mSigninAllowedByPolicy,
+                    mSignInState, mSigninAllowedByPolicy,
                     mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SYNC));
         assert signInState != null : "SigninState shouldn't be null!";
         assert signInState.mCoreAccountInfo == null : "mCoreAccountInfo shouldn't be set!";
@@ -356,14 +333,7 @@
             SigninPreferencesManager.getInstance().setLegacySyncAccountEmail(
                     mSignInState.mCoreAccountInfo.getEmail());
 
-            boolean atLeastOneDataTypeSynced = !SyncService.get().getChosenDataTypes().isEmpty();
-            if (atLeastOneDataTypeSynced) {
-                // Turn on sync only when user has at least one data type to sync, this is
-                // consistent with {@link ManageSyncSettings#updataSyncStateFromSelectedModelTypes},
-                // in which we turn off sync we stop sync service when the user toggles off all the
-                // sync types.
-                SyncService.get().setSyncRequested(true);
-            }
+            SyncService.get().setSyncRequested(true);
 
             RecordUserAction.record("Signin_Signin_Succeed");
             RecordHistogram.recordEnumeratedHistogram("Signin.SigninCompletedAccessPoint",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index 402498b1..f4e96e1b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -222,19 +222,6 @@
             }
 
             findPreference(PREF_ADVANCED_CATEGORY).setVisible(true);
-
-            /**
-             * Prior to the launch of MOBILE_IDENTITY_CONSISTENCY, sync request was done through a
-             * toggle that has now been removed. Currently sync is requested if the user checks
-             * any data type to sync. If no data type is checked then sync is not requested.
-             *
-             * This code is should be kept in place until M104 so that the users that had toggled
-             * sync request off prior to MOBILE_IDENTITY_CONSISTENCY get a chance to migrate to the
-             * new flow.
-             */
-            if (!SyncService.get().isSyncRequested()) {
-                SyncService.get().setChosenDataTypes(false, new HashSet<>());
-            }
         }
 
         mGoogleActivityControls = findPreference(PREF_GOOGLE_ACTIVITY_CONTROLS);
@@ -395,25 +382,13 @@
      * and {@link PersonalDataManager}.
      */
     private void updateSyncStateFromSelectedModelTypes() {
-        Set<Integer> selectedModelTypes = getSelectedModelTypes();
-        mSyncService.setChosenDataTypes(mSyncEverything.isChecked(), selectedModelTypes);
+        mSyncService.setChosenDataTypes(mSyncEverything.isChecked(), getSelectedModelTypes());
         // Note: mSyncPaymentsIntegration should be checked if mSyncEverything is checked, but if
         // mSyncEverything was just enabled, then that state may not have propagated to
         // mSyncPaymentsIntegration yet. See crbug.com/972863.
         PersonalDataManager.setPaymentsIntegrationEnabled(mSyncEverything.isChecked()
                 || (mSyncPaymentsIntegration.isChecked() && mSyncAutofill.isChecked()));
 
-        // For child profiles sync should always be on.
-        if (!Profile.getLastUsedRegularProfile().isChild()) {
-            boolean atLeastOneDataTypeEnabled =
-                    mSyncEverything.isChecked() || selectedModelTypes.size() > 0;
-            if (mSyncService.isSyncRequested() && !atLeastOneDataTypeEnabled) {
-                mSyncService.setSyncRequested(false);
-            } else if (!mSyncService.isSyncRequested() && atLeastOneDataTypeEnabled) {
-                mSyncService.setSyncRequested(true);
-            }
-        }
-
         // Some calls to setChosenDataTypes don't trigger syncStateChanged, so schedule update here.
         PostTask.postTask(UiThreadTaskTraits.DEFAULT, this::updateSyncPreferences);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
index cfa51bbc..53841af3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/WarmupManagerTest.java
@@ -29,8 +29,8 @@
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.profiles.OTRProfileID;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
+import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
 import org.chromium.content_public.browser.GlobalRenderFrameHostId;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
@@ -69,7 +69,7 @@
     }
 
     @Rule
-    public final ChromeBrowserTestRule mChromeBrowserTestRule = new ChromeBrowserTestRule();
+    public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
     private WarmupManager mWarmupManager;
     private Context mContext;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java
index 18162f1..1b3acf63 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchCriticalTest.java
@@ -58,7 +58,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     // Previously disabled:  https://crbug.com/1058297
     public void testResolveCausesOneLowPriorityRequest(@EnabledFeature int enabledFeature)
             throws Exception {
@@ -90,7 +90,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testPrefetchFailoverRequestMadeAfterOpen(@EnabledFeature int enabledFeature)
             throws Exception {
         mFakeServer.reset();
@@ -121,7 +121,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     @DisabledTest(message = "https://crbug.com/1140413")
     public void testLivePrefetchFailoverRequestMadeAfterOpen(@EnabledFeature int enabledFeature)
             throws Exception {
@@ -159,7 +159,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     // Previously flaky and disabled 4/2021.  https://crbug.com/1192285
     public void testResolveDisablePreload(@EnabledFeature int enabledFeature) throws Exception {
         simulateSlowResolveSearch("intelligence");
@@ -180,7 +180,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     // Previously disabled: crbug.com/765403
     public void testSearchTermResolutionError(@EnabledFeature int enabledFeature) throws Exception {
         simulateSlowResolveSearch("states");
@@ -202,7 +202,7 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testResolveContentVisibility(@EnabledFeature int enabledFeature) throws Exception {
         // Simulate a resolving search and make sure Content is not visible.
         simulateResolveSearch();
@@ -225,7 +225,7 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     // Previously disabled: http://crbug.com/1296677
     public void testNonResolveContentVisibility(@EnabledFeature int enabledFeature)
             throws Exception {
@@ -251,7 +251,7 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     // Previously flaky. See https://crbug.com/1032955
     @DisabledTest(message = "https://crbug.com/1291558")
     public void testResolveMultipleSwipeOnlyLoadsContentOnce(@EnabledFeature int enabledFeature)
@@ -291,7 +291,7 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     // Previously flaky https://crbug.com/1032760 on sdk<P.
     @DisabledTest(message = "https://crbug.com/1291558")
     public void testNonResolveMultipleSwipeOnlyLoadsContentOnce(@EnabledFeature int enabledFeature)
@@ -332,7 +332,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testChainedSearchCreatesNewContent(@EnabledFeature int enabledFeature)
             throws Exception {
         // This test depends on preloading the content - which is loaded and not made visible.
@@ -377,7 +377,7 @@
     @SmallTest
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testChainedSearchLoadsCorrectSearchTerm(@EnabledFeature int enabledFeature)
             throws Exception {
         // Simulate a resolving search and make sure Content is not visible.
@@ -464,7 +464,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testTapCloseRemovedFromHistory(@EnabledFeature int enabledFeature)
             throws Exception {
         // Simulate a resolving search and make sure a URL was loaded.
@@ -487,7 +487,7 @@
     @Feature({"ContextualSearch"})
     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
     //  @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.O, message = "crbug.com/1184410")
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testTapExpandNotRemovedFromHistory(@EnabledFeature int enabledFeature)
             throws Exception {
         // Simulate a resolving search and make sure a URL was loaded.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSystemTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSystemTest.java
index b9d38a9..53ad5c2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSystemTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchSystemTest.java
@@ -235,7 +235,7 @@
      * Tests that Contextual Search is fully disabled when offline.
      */
     @Test
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     @FlakyTest(message = "Disabled in 2017.  https://crbug.com/761946")
     // @SmallTest
     // @Feature({"ContextualSearch"})
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java
index d8a5330..53b3580 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchTriggerTest.java
@@ -111,7 +111,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testNonResolveTrigger(@EnabledFeature int enabledFeature) throws Exception {
         if (isConfigurationForResolvingGesturesOnly()) return;
         triggerNonResolve("states");
@@ -256,7 +256,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     @DisableIf.Build(sdk_is_greater_than = Build.VERSION_CODES.O, message = "crbug.com/1071080")
     public void testLongPressGestureFollowedByScrollMaintainsSelection(
             @EnabledFeature int enabledFeature) throws Exception {
@@ -350,7 +350,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testExpandBeforeSearchTermResolution(@EnabledFeature int enabledFeature)
             throws Exception {
         simulateSlowResolveSearch("states");
@@ -407,7 +407,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     // Previously flaky and disabled 4/2021.  https://crbug.com/1180304
     public void testSelectionExpansionOnSearchTermResolution(@EnabledFeature int enabledFeature)
             throws Exception {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUnbatchedTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUnbatchedTest.java
index e9763e44..5787983f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUnbatchedTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchUnbatchedTest.java
@@ -192,7 +192,7 @@
     @Test
     @SmallTest
     @Feature({"ContextualSearch"})
-    @ParameterAnnotations.UseMethodParameter(ContextualSearchManagerTest.FeatureParamProvider.class)
+    @ParameterAnnotations.UseMethodParameter(FeatureParamProvider.class)
     public void testTapWithLanguage(@EnabledFeature int enabledFeature) throws Exception {
         // Resolving a German word should trigger translation.
         mFakeServer.setExpectations("german",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java
index 81a3c38..c0acdfd2 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/dialogs/DownloadDialogIncognitoTest.java
@@ -13,10 +13,15 @@
 
 import static org.mockito.Mockito.verify;
 
+import static org.chromium.base.test.util.CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL;
+import static org.chromium.base.test.util.CriteriaHelper.DEFAULT_POLLING_INTERVAL;
+
 import android.content.Context;
 
+import androidx.test.espresso.NoMatchingViewException;
 import androidx.test.espresso.action.ViewActions;
-import androidx.test.filters.MediumTest;
+import androidx.test.espresso.matcher.ViewMatchers.Visibility;
+import androidx.test.filters.LargeTest;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -26,7 +31,8 @@
 
 import org.chromium.base.Callback;
 import org.chromium.base.test.util.CommandLineFlags;
-import org.chromium.base.test.util.DisabledTest;
+import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.CriteriaNotSatisfiedException;
 import org.chromium.chrome.browser.download.DuplicateDownloadDialog;
 import org.chromium.chrome.browser.download.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
@@ -36,7 +42,6 @@
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.components.browser_ui.modaldialog.AppModalPresenter;
-import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 
@@ -62,7 +67,6 @@
 
     @Before
     public void setUpTest() throws Exception {
-        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
         mActivityTestRule.startMainActivityOnBlankPage();
 
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -76,14 +80,14 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testDuplicateDownloadForIncognitoMode() throws Exception {
         // Showing a duplicate download dialog with an Incognito profile.
         OTRProfileID primaryProfileID = OTRProfileID.getPrimaryOTRProfileID();
         showDuplicateDialog(primaryProfileID);
 
         // Verify the Incognito warning message is shown.
-        onView(withId(R.id.message_paragraph_2)).check(matches(withEffectiveVisibility(VISIBLE)));
+        waitForWarningVisibilityToBe(VISIBLE);
 
         // Dismiss the dialog and verify the callback is called with false.
         onView(withId(R.id.negative_button)).perform(ViewActions.click());
@@ -91,27 +95,25 @@
     }
 
     @Test
-    @MediumTest
-    @DisabledTest(message = "https://crbug.com/1317700")
+    @LargeTest
     public void testDuplicateDownloadForRegularProfile() throws Exception {
         // Showing a duplicate download dialog with a regular profile.
         OTRProfileID regularProfileID = null;
         showDuplicateDialog(regularProfileID);
 
         // Verify the Incognito warning message is NOT shown.
-        onView(withId(R.id.message_paragraph_2)).check(matches(withEffectiveVisibility(GONE)));
+        waitForWarningVisibilityToBe(GONE);
     }
 
     @Test
-    @MediumTest
-    @DisabledTest(message = "https://crbug.com/1317700")
+    @LargeTest
     public void testDuplicateDownloadForIncognitoCCT() throws Exception {
         // Showing a duplicate download dialog with a non-primary off-the-record profile.
         OTRProfileID nonPrimaryOTRId = OTRProfileID.createUnique("CCT:Incognito");
         showDuplicateDialog(nonPrimaryOTRId);
 
         // Verify the Incognito warning message is shown.
-        onView(withId(R.id.message_paragraph_2)).check(matches(withEffectiveVisibility(VISIBLE)));
+        waitForWarningVisibilityToBe(VISIBLE);
 
         // Accept the dialog and verify the callback is called with true.
         onView(withId(R.id.positive_button)).perform(ViewActions.click());
@@ -119,13 +121,13 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testDangerousDownloadForOffTheRecordProfile() throws Exception {
         // Showing a dangerous download dialog with an off-the-record profile.
         showDangerousDialog(/*isOffTheRecord=*/true);
 
         // Verify the Incognito warning message is shown.
-        onView(withId(R.id.message_paragraph_2)).check(matches(withEffectiveVisibility(VISIBLE)));
+        waitForWarningVisibilityToBe(VISIBLE);
 
         // Accept the dialog and verify the callback is called with true.
         onView(withId(R.id.positive_button)).perform(ViewActions.click());
@@ -133,13 +135,13 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testDangerousDownloadForRegularProfile() throws Exception {
         // Showing a dangerous download dialog with a regular profile.
         showDangerousDialog(/*isOffTheRecord=*/false);
 
         // Verify the Incognito warning message is NOT shown.
-        onView(withId(R.id.message_paragraph_2)).check(matches(withEffectiveVisibility(GONE)));
+        waitForWarningVisibilityToBe(GONE);
 
         // Dismiss the dialog and verify the callback is called with false.
         onView(withId(R.id.negative_button)).perform(ViewActions.click());
@@ -147,13 +149,13 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testMixedContentDownloadForOffTheRecordProfile() throws Exception {
         // Showing a mixed content download dialog with an off-the-record profile.
         showMixedContentDialog(/*isOffTheRecord=*/true);
 
         // Verify the Incognito warning message is shown.
-        onView(withId(R.id.message_paragraph_2)).check(matches(withEffectiveVisibility(VISIBLE)));
+        waitForWarningVisibilityToBe(VISIBLE);
 
         // Accept the dialog and verify the callback is called with true.
         onView(withId(R.id.positive_button)).perform(ViewActions.click());
@@ -161,13 +163,13 @@
     }
 
     @Test
-    @MediumTest
+    @LargeTest
     public void testMixedContentDownloadDownloadForRegularProfile() throws Exception {
         // Showing a mixed content download dialog with a regular profile.
         showMixedContentDialog(/*isOffTheRecord=*/false);
 
         // Verify the Incognito warning message is NOT shown.
-        onView(withId(R.id.message_paragraph_2)).check(matches(withEffectiveVisibility(GONE)));
+        waitForWarningVisibilityToBe(GONE);
 
         // Dismiss the dialog and verify the callback is called with false.
         onView(withId(R.id.negative_button)).perform(ViewActions.click());
@@ -197,4 +199,17 @@
                     TOTAL_BYTES, isOffTheRecord, mResultCallback);
         });
     }
+
+    private void waitForWarningVisibilityToBe(Visibility visibility) {
+        CriteriaHelper.pollInstrumentationThread(() -> {
+            try {
+                onView(withId(R.id.message_paragraph_2))
+                        .check(matches(withEffectiveVisibility(visibility)));
+            } catch (NoMatchingViewException | AssertionError e) {
+                throw new CriteriaNotSatisfiedException(
+                        "Timeout while waiting for warning to have visibility: "
+                        + (visibility == VISIBLE ? "VISIBLE" : "GONE"));
+            }
+        }, DEFAULT_MAX_TIME_TO_POLL * 10, DEFAULT_POLLING_INTERVAL);
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
index f0a8157..20340c2b 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
@@ -162,7 +162,11 @@
     @Test
     @SmallTest
     @Feature({"Sync"})
-    public void testUnsettingAllDataTypesStopsSync() {
+    public void testUnsettingAllDataTypesDoesNotStopSync() {
+        // See crbug.com/1291946: The original MICE implementation stopped sync
+        // (by setting SyncRequested to false) when the user disabled all data
+        // types, for migration / backwards compatibility reasons. As of M104,
+        // that's no longer the case.
         mSyncTestRule.setUpAccountAndEnableSyncForTesting();
 
         ManageSyncSettings fragment = startManageSyncPreferences();
@@ -172,37 +176,8 @@
         for (CheckBoxPreference dataType : getDataTypes(fragment).values()) {
             mSyncTestRule.togglePreference(dataType);
         }
-        // All data types have been unchecked. Sync should stop.
-        Assert.assertFalse(SyncTestUtil.isSyncRequested());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testSettingAnyDataTypeStartsSync() {
-        mSyncTestRule.setUpAccountAndEnableSyncForTesting();
-        mSyncTestRule.setChosenDataTypes(false, new HashSet<>());
-        mSyncTestRule.stopSync();
-        ManageSyncSettings fragment = startManageSyncPreferences();
-
-        CheckBoxPreference syncAutofill =
-                (CheckBoxPreference) fragment.findPreference(ManageSyncSettings.PREF_SYNC_AUTOFILL);
-        mSyncTestRule.togglePreference(syncAutofill);
-        // Sync should start after any data type is checked.
-        Assert.assertTrue(SyncTestUtil.isSyncRequested());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Sync"})
-    public void testTogglingSyncEverythingStartsSync() {
-        mSyncTestRule.setUpAccountAndEnableSyncForTesting();
-        mSyncTestRule.setChosenDataTypes(false, new HashSet<>());
-        mSyncTestRule.stopSync();
-        ManageSyncSettings fragment = startManageSyncPreferences();
-
-        mSyncTestRule.togglePreference(getSyncEverything(fragment));
-        // Sync should start after setting sync everything toggle.
+        // All data types have been unchecked, but Sync itself should still be
+        // enabled.
         Assert.assertTrue(SyncTestUtil.isSyncRequested());
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdaterTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdaterTest.java
index 9a25635..9ea3b8d2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdaterTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/permissiondelegation/NotificationPermissionUpdaterTest.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.browserservices.permissiondelegation;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -35,6 +34,7 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.embedder_support.util.Origin;
 
@@ -58,7 +58,8 @@
     private NotificationPermissionUpdater mNotificationPermissionUpdater;
     private ShadowPackageManager mShadowPackageManager;
 
-    private boolean mNotificationsEnabled;
+    @ContentSettingValues
+    private int mNotificationPermission;
 
     @Before
     public void setUp() {
@@ -84,22 +85,22 @@
     @Feature("TrustedWebActivities")
     public void disablesNotifications_whenClientNotificationsAreDisabled() {
         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
-        setNotificationsEnabledForClient(false);
+        setNotificationPermission(ContentSettingValues.BLOCK);
 
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
 
-        verifyPermissionUpdated(false);
+        verifyPermissionUpdated(ContentSettingValues.BLOCK);
     }
 
     @Test
     @Feature("TrustedWebActivities")
     public void enablesNotifications_whenClientNotificationsAreEnabled() {
         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
-        setNotificationsEnabledForClient(true);
+        setNotificationPermission(ContentSettingValues.ALLOW);
 
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
 
-        verifyPermissionUpdated(true);
+        verifyPermissionUpdated(ContentSettingValues.ALLOW);
     }
 
     @Test
@@ -107,34 +108,34 @@
     public void updatesPermission_onSubsequentCalls() {
 
         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
-        setNotificationsEnabledForClient(true);
+        setNotificationPermission(ContentSettingValues.ALLOW);
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
-        verifyPermissionUpdated(true);
+        verifyPermissionUpdated(ContentSettingValues.ALLOW);
 
-        setNotificationsEnabledForClient(false);
+        setNotificationPermission(ContentSettingValues.BLOCK);
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
-        verifyPermissionUpdated(false);
+        verifyPermissionUpdated(ContentSettingValues.BLOCK);
     }
 
     @Test
     @Feature("TrustedWebActivities")
     public void updatesPermission_onNewClient() {
         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
-        setNotificationsEnabledForClient(true);
+        setNotificationPermission(ContentSettingValues.ALLOW);
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
-        verifyPermissionUpdated(true);
+        verifyPermissionUpdated(ContentSettingValues.ALLOW);
 
         installTrustedWebActivityService(ORIGIN, OTHER_PACKAGE_NAME);
-        setNotificationsEnabledForClient(false);
+        setNotificationPermission(ContentSettingValues.BLOCK);
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, OTHER_PACKAGE_NAME);
-        verifyPermissionUpdated(OTHER_PACKAGE_NAME, false);
+        verifyPermissionUpdated(OTHER_PACKAGE_NAME, ContentSettingValues.BLOCK);
     }
 
     @Test
     @Feature("TrustedWebActivities")
     public void unregisters_onClientUninstall() {
         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
-        setNotificationsEnabledForClient(true);
+        setNotificationPermission(ContentSettingValues.ALLOW);
 
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
 
@@ -149,18 +150,18 @@
     public void doesntUnregister_whenOtherClientsRemain() {
 
         installTrustedWebActivityService(ORIGIN, PACKAGE_NAME);
-        setNotificationsEnabledForClient(true);
+        setNotificationPermission(ContentSettingValues.ALLOW);
 
         mNotificationPermissionUpdater.onOriginVerified(ORIGIN, PACKAGE_NAME);
-        verifyPermissionUpdated(true);
+        verifyPermissionUpdated(ContentSettingValues.ALLOW);
 
         // Since we haven't called uninstallTrustedWebActivityService, the Updater sees that
         // notifications can still be handled by other apps. We don't unregister, but we do update
         // to the permission to that of the other app.
-        setNotificationsEnabledForClient(false);
+        setNotificationPermission(ContentSettingValues.BLOCK);
         mNotificationPermissionUpdater.onClientAppUninstalled(ORIGIN);
         verifyPermissionNotUnregistered();
-        verifyPermissionUpdated(false);
+        verifyPermissionUpdated(ContentSettingValues.BLOCK);
 
         uninstallTrustedWebActivityService(ORIGIN);
         mNotificationPermissionUpdater.onClientAppUninstalled(ORIGIN);
@@ -182,41 +183,38 @@
     @SuppressWarnings("unchecked")
     private void installTrustedWebActivityService(Origin origin, String packageName) {
         doAnswer(invocation -> {
-            TrustedWebActivityClient.PermissionCheckCallback callback =
-                    invocation.getArgument(1);
-            callback.onPermissionCheck(
-                    new ComponentName(packageName, "FakeClass"),
-                    mNotificationsEnabled);
+            TrustedWebActivityClient.PermissionCallback callback = invocation.getArgument(1);
+            callback.onPermission(
+                    new ComponentName(packageName, "FakeClass"), mNotificationPermission);
             return true;
-        }).when(mTrustedWebActivityClient).checkNotificationPermission(eq(origin), any());
+        }).when(mTrustedWebActivityClient).checkNotificationPermissionSetting(eq(origin), any());
     }
 
-    private void setNotificationsEnabledForClient(boolean enabled) {
-        mNotificationsEnabled = enabled;
+    private void setNotificationPermission(@ContentSettingValues int permission) {
+        mNotificationPermission = permission;
     }
 
     private void uninstallTrustedWebActivityService(Origin origin) {
         doAnswer(invocation -> {
-            TrustedWebActivityClient.PermissionCheckCallback callback =
-                    invocation.getArgument(1);
+            TrustedWebActivityClient.PermissionCallback callback = invocation.getArgument(1);
             callback.onNoTwaFound();
             return true;
-        }).when(mTrustedWebActivityClient).checkNotificationPermission(eq(origin), any());
+        }).when(mTrustedWebActivityClient).checkNotificationPermissionSetting(eq(origin), any());
     }
 
     private void verifyPermissionNotUpdated() {
         verify(mPermissionManager, never())
-                .updatePermission(any(), anyString(), anyInt(), anyBoolean());
+                .updatePermission(any(), anyString(), anyInt(), anyInt());
     }
 
-    private void verifyPermissionUpdated(boolean enabled) {
-        verifyPermissionUpdated(PACKAGE_NAME, enabled);
+    private void verifyPermissionUpdated(@ContentSettingValues int permission) {
+        verifyPermissionUpdated(PACKAGE_NAME, permission);
     }
 
-    private void verifyPermissionUpdated(String packageName, boolean enabled) {
+    private void verifyPermissionUpdated(String packageName, @ContentSettingValues int permission) {
         verify(mPermissionManager)
                 .updatePermission(eq(ORIGIN), eq(packageName),
-                        eq(ContentSettingsType.NOTIFICATIONS), eq(enabled));
+                        eq(ContentSettingsType.NOTIFICATIONS), eq(permission));
     }
 
     private void verifyPermissionUnregistered() {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java
index b3c443b..aab2b311 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerImplTest.java
@@ -130,12 +130,7 @@
         when(mIdentityMutator.setPrimaryAccount(any(), anyInt())).thenReturn(true);
         when(mSyncService.getChosenDataTypes()).thenReturn(Set.of(ModelType.BOOKMARKS));
 
-        // Until the first run check is done, we do not allow sign in.
-        assertFalse(mSigninManager.isSigninAllowed());
-        assertFalse(mSigninManager.isSyncOptInAllowed());
-
-        // First run check is complete and there is no signed in account.  Sign in is allowed.
-        mSigninManager.onFirstRunCheckDone();
+        // There is no signed in account.  Sign in is allowed.
         assertTrue(mSigninManager.isSigninAllowed());
         assertTrue(mSigninManager.isSyncOptInAllowed());
 
@@ -169,10 +164,6 @@
     public void signinNoTurnSyncOn() {
         when(mIdentityMutator.setPrimaryAccount(any(), anyInt())).thenReturn(true);
 
-        assertFalse(mSigninManager.isSigninAllowed());
-        assertFalse(mSigninManager.isSyncOptInAllowed());
-
-        mSigninManager.onFirstRunCheckDone();
         assertTrue(mSigninManager.isSigninAllowed());
         assertTrue(mSigninManager.isSyncOptInAllowed());
 
@@ -494,8 +485,6 @@
                 .when(mIdentityMutator)
                 .setPrimaryAccount(ACCOUNT_INFO.getId(), ConsentLevel.SYNC);
 
-        mSigninManager.onFirstRunCheckDone(); // Allow sign-in.
-
         mSigninManager.signinAndEnableSync(SigninAccessPoint.UNKNOWN,
                 AccountUtils.createAccountFromName(ACCOUNT_INFO.getEmail()), null);
 
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index e05328a..b563963 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -4244,6 +4244,12 @@
   <message name="IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_NOTIFICATIONS" desc="Text for the name of the toggle in the smart privacy section requesting notification hiding when a snooper is detected.">
     Hide notification content when someone else is detected
   </message>
+  <message name="IDS_OS_SETTINGS_PRIVACY_HUB_TITLE" desc="Text on the privacy page that opens up the privacy hub section.">
+    Privacy Hub
+  </message>
+  <message name="IDS_OS_SETTINGS_CAMERA_TOGGLE_TITLE" desc="The title of the toggle to enable/disable camera from the privacy hub.">
+    Camera
+  </message>
   <message name="IDS_OS_SETTINGS_HW_DATA_USAGE_TOGGLE_TITLE" desc="The label of the checkbox to enable/disable device hardware data collection and usage in ChromeOS Flex.">
     <ph name="DEVICE_OS">$1<ex>ChromeOS Flex</ex></ph> hardware support and stability
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_CAMERA_TOGGLE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_CAMERA_TOGGLE_TITLE.png.sha1
new file mode 100644
index 0000000..1a7fc544
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_CAMERA_TOGGLE_TITLE.png.sha1
@@ -0,0 +1 @@
+a640f3a499345c537d49f76e864ca9d61ebd3c5b
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_TITLE.png.sha1
new file mode 100644
index 0000000..48edd7a
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_PRIVACY_HUB_TITLE.png.sha1
@@ -0,0 +1 @@
+1a002f728dbf685ef6d4fdc3406354c1e9aa0a2a
\ No newline at end of file
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 9c978575..b87e04f7 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2815,6 +2815,11 @@
   <message name="IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT" desc="Label for the toggle that allows the user to automatically delete their cookies and site data when they close all browser windows.">
     Clear cookies and site data when you close all windows
   </message>
+  <if expr="chromeos_lacros">
+    <message name="IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT_DESC" desc="Secondary text for the toggle that allows the user to automatically delete their cookies and site data when they close all browser windows. This sublabel clarifies that when the toggle is enabled the user won't be signed out of Chrome each time they close all Chrome windows.">
+      Signs you out of most sites. You won't be signed out of your Google Account.
+    </message>
+  </if>
   <message name="IDS_SETTINGS_COOKIES_ALL_SITES_LINK" desc="Label for the link row which takes the user to the page listing all sites with storage and permissions">
     See all site data and permissions
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT_DESC.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT_DESC.png.sha1
new file mode 100644
index 0000000..b5106fec
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT_DESC.png.sha1
@@ -0,0 +1 @@
+14d766028b9f4509cb83ea8dc050c0afff0f0189
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9808cfc1..eacc45d7 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4637,6 +4637,8 @@
       "apps/app_service/metrics/app_platform_metrics_utils.h",
       "apps/app_service/metrics/browser_to_tab_list.cc",
       "apps/app_service/metrics/browser_to_tab_list.h",
+      "apps/app_service/metrics/website_metrics.cc",
+      "apps/app_service/metrics/website_metrics.h",
       "apps/app_service/publishers/arc_apps.cc",
       "apps/app_service/publishers/arc_apps.h",
       "apps/app_service/publishers/arc_apps_factory.cc",
diff --git a/chrome/browser/apps/app_discovery_service/game_fetcher.cc b/chrome/browser/apps/app_discovery_service/game_fetcher.cc
index df5075d..a6c2f8c 100644
--- a/chrome/browser/apps/app_discovery_service/game_fetcher.cc
+++ b/chrome/browser/apps/app_discovery_service/game_fetcher.cc
@@ -212,8 +212,8 @@
   // `test_country_` overrides the current locale if it is present.
   int current_country_id =
       test_country_.has_value()
-          ? country_codes::GetCurrentCountryID()
-          : country_codes::CountryStringToCountryID(test_country_.value());
+          ? country_codes::CountryStringToCountryID(test_country_.value())
+          : country_codes::GetCurrentCountryID();
 
   if (current_country_id == -1) {
     // Try using the timezone to get the country as a fallback.
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
index 2d0d26e..dcefdfac 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
@@ -63,6 +63,7 @@
       profile_, app_registry_cache, instance_registry);
   app_platform_input_metrics_ = std::make_unique<apps::AppPlatformInputMetrics>(
       profile_, instance_registry);
+  website_metrics_ = std::make_unique<apps::WebsiteMetrics>();
 
   day_id_ = profile_->GetPrefs()->GetInteger(kAppPlatformMetricsDayId);
   CheckForNewDay();
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h
index d2318a28..a8131064c 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.h
@@ -10,6 +10,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h"
 #include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
+#include "chrome/browser/apps/app_service/metrics/website_metrics.h"
 #include "chrome/browser/profiles/profile.h"
 
 class PrefRegistrySimple;
@@ -73,6 +74,7 @@
 
   std::unique_ptr<apps::AppPlatformMetrics> app_platform_app_metrics_;
   std::unique_ptr<apps::AppPlatformInputMetrics> app_platform_input_metrics_;
+  std::unique_ptr<apps::WebsiteMetrics> website_metrics_;
 };
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/website_metrics.cc b/chrome/browser/apps/app_service/metrics/website_metrics.cc
new file mode 100644
index 0000000..f1eb23f
--- /dev/null
+++ b/chrome/browser/apps/app_service/metrics/website_metrics.cc
@@ -0,0 +1,111 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/apps/app_service/metrics/website_metrics.h"
+
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "ui/aura/window.h"
+
+namespace {
+
+wm::ActivationClient* GetActivationClientWithTabStripModel(
+    TabStripModel* tab_strip_model) {
+  for (auto* browser : *BrowserList::GetInstance()) {
+    if (browser->tab_strip_model() == tab_strip_model) {
+      BrowserWindow* browser_window = browser->window();
+      DCHECK(browser_window);
+      aura::Window* window = browser_window->GetNativeWindow();
+      DCHECK(window);
+      auto* root_window = window->GetRootWindow();
+      DCHECK(root_window);
+      return wm::GetActivationClient(root_window);
+    }
+  }
+  return nullptr;
+}
+
+}  // namespace
+
+namespace apps {
+
+WebsiteMetrics::WebsiteMetrics() : browser_tab_strip_tracker_(this, nullptr) {
+  BrowserList::GetInstance()->AddObserver(this);
+  browser_tab_strip_tracker_.Init();
+}
+
+WebsiteMetrics::~WebsiteMetrics() {
+  BrowserList::RemoveObserver(this);
+}
+
+void WebsiteMetrics::OnBrowserAdded(Browser* browser) {
+  // TODO(crbug.com/1334173): Save the browser window.
+}
+
+void WebsiteMetrics::OnBrowserRemoved(Browser* browser) {
+  // TODO(crbug.com/1334173): Remove the browser window.
+}
+
+void WebsiteMetrics::OnTabStripModelChanged(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange& change,
+    const TabStripSelectionChange& selection) {
+  switch (change.type()) {
+    case TabStripModelChange::kInserted:
+      OnTabStripModelChangeInsert(tab_strip_model, *change.GetInsert(),
+                                  selection);
+      break;
+    case TabStripModelChange::kRemoved:
+      OnTabStripModelChangeRemove(tab_strip_model, *change.GetRemove(),
+                                  selection);
+      break;
+    case TabStripModelChange::kReplaced:
+    case TabStripModelChange::kMoved:
+    case TabStripModelChange::kSelectionOnly:
+      break;
+  }
+}
+
+void WebsiteMetrics::OnWindowActivated(ActivationReason reason,
+                                       aura::Window* gained_active,
+                                       aura::Window* lost_active) {
+  // TODO(crbug.com/1334173): Calculate the usage time for the activated tab
+  // url.
+}
+
+void WebsiteMetrics::OnTabStripModelChangeInsert(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange::Insert& insert,
+    const TabStripSelectionChange& selection) {
+  if (insert.contents.size() == 0) {
+    return;
+  }
+  // First tab attached.
+  if (tab_strip_model->count() == static_cast<int>(insert.contents.size())) {
+    // Observe the activation client of the root window of the browser's aura
+    // window if this is the first browser matching it (there is no other
+    // tracked browser matching it).
+    auto* activation_client =
+        GetActivationClientWithTabStripModel(tab_strip_model);
+    if (!activation_client_observations_.IsObservingSource(activation_client))
+      activation_client_observations_.AddObservation(activation_client);
+  }
+}
+
+void WebsiteMetrics::OnTabStripModelChangeRemove(
+    TabStripModel* tab_strip_model,
+    const TabStripModelChange::Remove& remove,
+    const TabStripSelectionChange& selection) {
+  // Last tab detached.
+  if (tab_strip_model->count() == 0) {
+    // Unobserve the activation client of the root window of the browser's aura
+    // window if the last browser using it was just removed.
+    auto* activation_client =
+        GetActivationClientWithTabStripModel(tab_strip_model);
+    if (activation_client_observations_.IsObservingSource(activation_client))
+      activation_client_observations_.RemoveObservation(activation_client);
+  }
+}
+
+}  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/website_metrics.h b/chrome/browser/apps/app_service/metrics/website_metrics.h
new file mode 100644
index 0000000..f61c4fa0
--- /dev/null
+++ b/chrome/browser/apps/app_service/metrics/website_metrics.h
@@ -0,0 +1,65 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_APPS_APP_SERVICE_METRICS_WEBSITE_METRICS_H_
+#define CHROME_BROWSER_APPS_APP_SERVICE_METRICS_WEBSITE_METRICS_H_
+
+#include "base/scoped_multi_source_observation.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/browser_tab_strip_tracker.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
+#include "ui/wm/public/activation_change_observer.h"
+#include "ui/wm/public/activation_client.h"
+
+class Browser;
+
+namespace apps {
+
+// WebsiteMetrics monitors creation/deletion of Browser and its
+// TabStripModel to record the website usage time metrics.
+class WebsiteMetrics : public BrowserListObserver,
+                       public TabStripModelObserver,
+                       public wm::ActivationChangeObserver {
+ public:
+  WebsiteMetrics();
+
+  WebsiteMetrics(const WebsiteMetrics&) = delete;
+  WebsiteMetrics& operator=(const WebsiteMetrics&) = delete;
+
+  ~WebsiteMetrics() override;
+
+  // BrowserListObserver overrides:
+  void OnBrowserAdded(Browser* browser) override;
+  void OnBrowserRemoved(Browser* browser) override;
+
+  // TabStripModelObserver overrides:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override;
+
+  // wm::ActivationChangeObserver overrides:
+  void OnWindowActivated(ActivationReason reason,
+                         aura::Window* gained_active,
+                         aura::Window* lost_active) override;
+
+ private:
+  void OnTabStripModelChangeInsert(TabStripModel* tab_strip_model,
+                                   const TabStripModelChange::Insert& insert,
+                                   const TabStripSelectionChange& selection);
+  void OnTabStripModelChangeRemove(TabStripModel* tab_strip_model,
+                                   const TabStripModelChange::Remove& remove,
+                                   const TabStripSelectionChange& selection);
+
+  BrowserTabStripTracker browser_tab_strip_tracker_;
+
+  // A set of observed activation clients for all browser's windows.
+  base::ScopedMultiSourceObservation<wm::ActivationClient,
+                                     wm::ActivationChangeObserver>
+      activation_client_observations_{this};
+};
+
+}  // namespace apps
+
+#endif  // CHROME_BROWSER_APPS_APP_SERVICE_METRICS_WEBSITE_METRICS_H_
diff --git a/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
index a82b953..079d4f3 100644
--- a/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
+++ b/chrome/browser/apps/intent_helper/supported_links_infobar_delegate_browsertest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
+#include "chrome/browser/web_applications/web_app_utils.h"
 #include "components/infobars/content/content_infobar_manager.h"
 #include "components/infobars/core/infobar.h"
 #include "components/services/app_service/public/cpp/features.h"
@@ -95,8 +96,11 @@
   EXPECT_TRUE(infobar);
   GetDelegate(infobar)->Accept();
 
-  if (!base::FeatureList::IsEnabled(apps::kAppServicePreferredAppsWithoutMojom))
+  if (web_app::IsWebAppsCrosapiEnabled() ||
+      !base::FeatureList::IsEnabled(
+          apps::kAppServicePreferredAppsWithoutMojom)) {
     WaitForPreferredAppUpdate();
+  }
 
   ASSERT_TRUE(
       app_service_proxy()->PreferredAppsList().IsPreferredAppForSupportedLinks(
@@ -118,8 +122,11 @@
   }
 
   app_service_proxy()->SetSupportedLinksPreference(test_web_app_id());
-  if (!base::FeatureList::IsEnabled(apps::kAppServicePreferredAppsWithoutMojom))
+  if (web_app::IsWebAppsCrosapiEnabled() ||
+      !base::FeatureList::IsEnabled(
+          apps::kAppServicePreferredAppsWithoutMojom)) {
     WaitForPreferredAppUpdate();
+  }
 
   Browser* browser = OpenTestWebApp();
   auto* contents = browser->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/ash/app_mode/kiosk_app_data_base.cc b/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
index 52c47b3..5e20ed85 100644
--- a/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
+++ b/chrome/browser/ash/app_mode/kiosk_app_data_base.cc
@@ -124,7 +124,7 @@
 }
 
 void KioskAppDataBase::DecodeIcon() {
-  DCHECK(!icon_path_.empty());
+  DLOG_IF(ERROR, icon_path_.empty()) << "Icon path is empty";
   kiosk_app_icon_loader_ = std::make_unique<KioskAppIconLoader>(this);
   kiosk_app_icon_loader_->Start(icon_path_);
 }
diff --git a/chrome/browser/ash/arc/input_method_manager/OWNERS b/chrome/browser/ash/arc/input_method_manager/OWNERS
index 2c672f3..a7058d7 100644
--- a/chrome/browser/ash/arc/input_method_manager/OWNERS
+++ b/chrome/browser/ash/arc/input_method_manager/OWNERS
@@ -1 +1,2 @@
+hirokisato@chromium.org
 yhanada@chromium.org
diff --git a/chrome/browser/ash/arc/input_overlay/constants.h b/chrome/browser/ash/arc/input_overlay/constants.h
index 44e192f4..57f3bf1 100644
--- a/chrome/browser/ash/arc/input_overlay/constants.h
+++ b/chrome/browser/ash/arc/input_overlay/constants.h
@@ -20,6 +20,9 @@
   // Display overlay can receive events and action labels can be focused. It
   // shows input mapping in edit mode.
   kEdit,
+  // Display overlay can receive events. This is the mode before entering into
+  // |kMenu|.
+  kPreMenu,
   // Display overlay can receive events but action labels can't be focused.
   // It shows expanded menu and input mapping as in view mode.
   kMenu,
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
index 834d00ba..40ab7ad 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.cc
@@ -164,7 +164,6 @@
   menu_entry->SetSize(gfx::Size(kMenuEntrySize, kMenuEntrySize));
   menu_entry->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
   menu_entry->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
-  menu_entry->SetInstallFocusRingOnFocus(false);
   // TODO(djacobo): Set proper positioning based on specs and responding to
   // resize.
   menu_entry->SetPosition(CalculateMenuEntryPosition());
@@ -197,6 +196,20 @@
   menu_entry_->SetVisible(false);
 }
 
+void DisplayOverlayController::FocusOnMenuEntry() {
+  if (!menu_entry_)
+    return;
+  menu_entry_->RequestFocus();
+}
+
+void DisplayOverlayController::ClearFocusOnMenuEntry() {
+  if (!menu_entry_)
+    return;
+  auto* focus_manager = menu_entry_->GetFocusManager();
+  if (focus_manager)
+    focus_manager->ClearFocus();
+}
+
 void DisplayOverlayController::RemoveInputMenuView() {
   if (!input_menu_view_)
     return;
@@ -236,6 +249,13 @@
 
   edit_finish_view_ = parent_view->AddChildView(
       EditFinishView::BuildView(this, parent_view->size()));
+
+  // Since |input_menu_view_| is removed when adding |edit_finish_view_| and
+  // |FocusManager| lost the focused view. Set |edit_finish_view_| explicitly
+  // as the focused view so Tab traversal key can work as expected.
+  auto* focus_manager = edit_finish_view_->GetFocusManager();
+  if (focus_manager)
+    focus_manager->SetFocusedView(edit_finish_view_);
 }
 
 void DisplayOverlayController::RemoveEditFinishView() {
@@ -325,6 +345,7 @@
       RemoveNudgeView();
       AddInputMappingView(overlay_widget);
       AddMenuEntryView(overlay_widget);
+      ClearFocusOnMenuEntry();
       if (touch_injector_->first_launch())
         AddNudgeView(overlay_widget);
       overlay_widget->GetNativeWindow()->SetEventTargetingPolicy(
@@ -339,6 +360,12 @@
       overlay_widget->GetNativeWindow()->SetEventTargetingPolicy(
           aura::EventTargetingPolicy::kTargetAndDescendants);
       break;
+    case DisplayMode::kPreMenu:
+      RemoveNudgeView();
+      overlay_widget->GetNativeWindow()->SetEventTargetingPolicy(
+          aura::EventTargetingPolicy::kTargetAndDescendants);
+      FocusOnMenuEntry();
+      break;
     case DisplayMode::kMenu:
       overlay_widget->GetNativeWindow()->SetEventTargetingPolicy(
           aura::EventTargetingPolicy::kTargetAndDescendants);
@@ -360,10 +387,10 @@
 
 absl::optional<gfx::Rect>
 DisplayOverlayController::GetOverlayMenuEntryBounds() {
-  if (!menu_entry_)
+  if (!menu_entry_ || !menu_entry_->GetVisible())
     return absl::nullopt;
 
-  return absl::optional<gfx::Rect>(menu_entry_->bounds());
+  return absl::optional<gfx::Rect>(menu_entry_->GetBoundsInScreen());
 }
 
 void DisplayOverlayController::AddActionEditMenu(ActionView* anchor,
diff --git a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
index fc43e51..e6f1740 100644
--- a/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
+++ b/chrome/browser/ash/arc/input_overlay/display_overlay_controller.h
@@ -49,7 +49,7 @@
 
   void OnWindowBoundsChanged();
   void SetDisplayMode(DisplayMode mode);
-  // Get the bounds of |overlay_menu_entry_| in contents view.
+  // Get the bounds of |menu_entry_| in screen coordinates
   absl::optional<gfx::Rect> GetOverlayMenuEntryBounds();
 
   void AddActionEditMenu(ActionView* anchor, ActionType action_type);
@@ -100,6 +100,8 @@
   void AddMenuEntryView(views::Widget* overlay_widget);
   void RemoveMenuEntryView();
   void OnMenuEntryPressed();
+  void FocusOnMenuEntry();
+  void ClearFocusOnMenuEntry();
   void RemoveInputMenuView();
 
   void AddInputMappingView(views::Widget* overlay_widget);
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.cc b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
index 1132f231..b7774a9 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector.cc
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector.cc
@@ -259,6 +259,12 @@
   }
 }
 
+void TouchInjector::CleanupTouchEvents() {
+  if (is_mouse_locked_)
+    FlipMouseLockFlag();
+  DispatchTouchReleaseEvent();
+}
+
 void TouchInjector::DispatchTouchCancelEvent() {
   for (auto& action : actions_) {
     auto cancel = action->GetTouchCanceledEvent();
@@ -372,37 +378,37 @@
     DispatchTouchReleaseEventOnMouseUnLock();
 }
 
-bool TouchInjector::MenuAnchorPressed(const ui::Event& event,
-                                      const gfx::RectF& content_bounds) {
-  if (!event.IsMouseEvent() && !event.IsTouchEvent())
+bool TouchInjector::LocatedEventOnMenuEntry(const ui::Event& event,
+                                            const gfx::RectF& content_bounds,
+                                            bool press_required) {
+  if (!event.IsLocatedEvent())
     return false;
 
+  auto event_location = event.AsLocatedEvent()->root_location();
   auto menu_anchor_bounds =
       display_overlay_controller_->GetOverlayMenuEntryBounds();
   if (!menu_anchor_bounds) {
-    DCHECK(display_mode_ != DisplayMode::kView);
+    DCHECK(display_mode_ != DisplayMode::kView &&
+           display_mode_ != DisplayMode::kPreMenu);
     return false;
   }
 
-  auto menu_anchor_bounds_in_root =
-      gfx::RectF(menu_anchor_bounds->x() + content_bounds.x(),
-                 menu_anchor_bounds->y() + content_bounds.y(),
-                 menu_anchor_bounds->width(), menu_anchor_bounds->height());
+  if (!press_required)
+    return menu_anchor_bounds->Contains(event_location);
 
-  auto location = gfx::Point(event.AsLocatedEvent()->root_location());
-  target_window_->GetHost()->ConvertPixelsToDIP(&location);
-  auto location_f = gfx::PointF(location);
+  if (!event.IsMouseEvent() && !event.IsTouchEvent())
+    return false;
 
   if (event.IsMouseEvent()) {
     auto* mouse = event.AsMouseEvent();
     if (mouse->type() == ui::ET_MOUSE_PRESSED &&
-        menu_anchor_bounds_in_root.Contains(location_f)) {
+        menu_anchor_bounds->Contains(event_location)) {
       return true;
     }
   } else {
     auto* touch = event.AsTouchEvent();
     if (touch->type() == ui::ET_TOUCH_PRESSED &&
-        menu_anchor_bounds_in_root.Contains(location_f)) {
+        menu_anchor_bounds->Contains(event_location)) {
       return true;
     }
   }
@@ -413,17 +419,49 @@
     const ui::Event& event,
     const ui::EventRewriter::Continuation continuation) {
   continuation_ = continuation;
-
   auto bounds = CalculateWindowContentBounds(target_window_);
+
+  // This is for Tab key as Accessibility requirement.
+  // - For key event, Tab key is used to enter into the |kPreMenu| mode. And any
+  // keys, except Space and Enter keys, are used to exit the |kPreMenu| and
+  // enter into the |kView| mode, and continue events in |kView| mode.
+  // - For any located events in |kPreMenu| mode, if it doesn't happen on the
+  // menu entry button, then it enters into the |kView| mode and continues
+  // events in |kView| mode.
+  if (display_mode_ == DisplayMode::kView && event.IsKeyEvent() &&
+      views::FocusManager::IsTabTraversalKeyEvent(*(event.AsKeyEvent()))) {
+    if (event.AsKeyEvent()->type() == ui::ET_KEY_PRESSED) {
+      CleanupTouchEvents();
+      display_overlay_controller_->SetDisplayMode(DisplayMode::kPreMenu);
+    }
+    return DiscardEvent(continuation);
+  } else if (display_mode_ == DisplayMode::kPreMenu) {
+    if (event.IsKeyEvent()) {
+      auto* key_event = event.AsKeyEvent();
+      if (key_event->key_code() != ui::KeyboardCode::VKEY_SPACE &&
+          key_event->key_code() != ui::KeyboardCode::VKEY_RETURN &&
+          key_event->type() == ui::ET_KEY_PRESSED) {
+        display_overlay_controller_->SetDisplayMode(DisplayMode::kView);
+      } else {
+        return SendEvent(continuation, &event);
+      }
+    } else if (LocatedEventOnMenuEntry(event, bounds,
+                                       /*press_required=*/false)) {
+      return SendEvent(continuation, &event);
+    } else {
+      display_overlay_controller_->SetDisplayMode(DisplayMode::kView);
+    }
+  }
+
+  if (display_mode_ != DisplayMode::kView)
+    return SendEvent(continuation, &event);
+
   // |display_overlay_controller_| is null for unittest.
-  if (display_overlay_controller_ && display_mode_ == DisplayMode::kView &&
-      MenuAnchorPressed(event, bounds)) {
+  if (display_overlay_controller_ &&
+      LocatedEventOnMenuEntry(event, bounds, /*press_required=*/true)) {
     // Release all active touches when the display mode is changed from |kView|
     // to |kMenu|.
-    if (is_mouse_locked_)
-      FlipMouseLockFlag();
-    DispatchTouchReleaseEvent();
-
+    CleanupTouchEvents();
     display_overlay_controller_->SetDisplayMode(DisplayMode::kMenu);
     return SendEvent(continuation, &event);
   }
@@ -431,9 +469,6 @@
   if (text_input_active_)
     return SendEvent(continuation, &event);
 
-  if (display_mode_ != DisplayMode::kView)
-    return SendEvent(continuation, &event);
-
   if (!touch_injector_enable_)
     return SendEvent(continuation, &event);
 
diff --git a/chrome/browser/ash/arc/input_overlay/touch_injector.h b/chrome/browser/ash/arc/input_overlay/touch_injector.h
index a4be1a2..8da18a9 100644
--- a/chrome/browser/ash/arc/input_overlay/touch_injector.h
+++ b/chrome/browser/ash/arc/input_overlay/touch_injector.h
@@ -143,6 +143,9 @@
 
   class KeyCommand;
 
+  // Clean up active touch events before entering into other mode from |kView|
+  // mode.
+  void CleanupTouchEvents();
   // If the window is destroying or focusing out, releasing the active touch
   // event.
   void DispatchTouchCancelEvent();
@@ -158,9 +161,11 @@
   void ParseMouseLock(const base::Value& value);
 
   void FlipMouseLockFlag();
-  // Check if the event located on menu icon.
-  bool MenuAnchorPressed(const ui::Event& event,
-                         const gfx::RectF& content_bounds);
+  // Check if the event located on menu entry. |press_required| tells whether or
+  // not a mouse press or touch press is required.
+  bool LocatedEventOnMenuEntry(const ui::Event& event,
+                               const gfx::RectF& content_bounds,
+                               bool press_required);
 
   // Takes valid touch events and overrides their ids with an id managed by the
   // TouchIdManager.
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
index a4b465d..28e6328 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_label.cc
@@ -190,8 +190,8 @@
 }
 
 void ActionLabel::SetDisplayMode(DisplayMode mode) {
-  DCHECK(mode != DisplayMode::kMenu);
-  if (mode == DisplayMode::kMenu)
+  DCHECK(mode != DisplayMode::kMenu && mode != DisplayMode::kPreMenu);
+  if (mode == DisplayMode::kMenu || mode == DisplayMode::kPreMenu)
     return;
 
   switch (mode) {
@@ -213,7 +213,7 @@
       SetToEditError();
       break;
     case DisplayMode::kRestore:
-      if (!ClearFocus())
+      if (HasFocus())
         SetToEditDefault();
       break;
     default:
@@ -253,13 +253,13 @@
 void ActionLabel::OnFocus() {
   SetToEditFocus();
   LabelButton::OnFocus();
-  auto* parent_view = static_cast<ActionView*>(parent());
-  parent_view->ShowInfoMsg(kEditInfoMessage, this);
+  static_cast<ActionView*>(parent())->ShowInfoMsg(kEditInfoMessage, this);
 }
 
 void ActionLabel::OnBlur() {
   SetToEditDefault();
   LabelButton::OnBlur();
+  static_cast<ActionView*>(parent())->RemoveMessage();
 }
 
 bool ActionLabel::ClearFocus() {
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
index 5a0171e..a415d2f 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.cc
@@ -48,8 +48,14 @@
 ActionView::~ActionView() = default;
 
 void ActionView::SetDisplayMode(DisplayMode mode, ActionLabel* editing_label) {
-  DCHECK(mode != DisplayMode::kEducation);
-  if ((!editable_ && mode == DisplayMode::kEdit) || mode == DisplayMode::kMenu)
+  DCHECK(mode != DisplayMode::kEducation && mode != DisplayMode::kMenu &&
+         mode != DisplayMode::kPreMenu);
+  if (mode == DisplayMode::kEducation || mode == DisplayMode::kMenu ||
+      mode == DisplayMode::kPreMenu) {
+    return;
+  }
+
+  if (!editable_ && mode == DisplayMode::kEdit)
     return;
   if (mode == DisplayMode::kView) {
     RemoveEditButton();
@@ -109,6 +115,10 @@
                                               MessageType::kInfo);
 }
 
+void ActionView::RemoveMessage() {
+  display_overlay_controller_->RemoveEditMessage();
+}
+
 void ActionView::ChangeBinding(Action* action,
                                ActionLabel* action_label,
                                std::unique_ptr<InputElement> input_element) {
diff --git a/chrome/browser/ash/arc/input_overlay/ui/action_view.h b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
index 4354d3c..05815940 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/action_view.h
+++ b/chrome/browser/ash/arc/input_overlay/ui/action_view.h
@@ -65,6 +65,7 @@
   // Show info/edu message.
   void ShowInfoMsg(const base::StringPiece& message,
                    ActionLabel* editing_label);
+  void RemoveMessage();
   // Change binding for |action| binding to |input_element| and set
   // |kEditedSuccess| on |action_label| if |action_label| is not nullptr.
   // Otherwise, set |kEditedSuccess| to all |ActionLabel|.
diff --git a/chrome/browser/ash/arc/input_overlay/ui/edit_finish_view.cc b/chrome/browser/ash/arc/input_overlay/ui/edit_finish_view.cc
index 2e632dad..e12ea12 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/edit_finish_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/edit_finish_view.cc
@@ -135,6 +135,8 @@
   if (!display_overlay_controller_)
     return;
   display_overlay_controller_->OnCustomizeRestore();
+  // Take the focus from |ActionLabel|.
+  reset_button_->RequestFocus();
 }
 
 void EditFinishView::OnSaveButtonPressed() {
diff --git a/chrome/browser/ash/arc/input_overlay/ui/input_mapping_view.cc b/chrome/browser/ash/arc/input_overlay/ui/input_mapping_view.cc
index a1b04cf..10cde760 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/input_mapping_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/input_mapping_view.cc
@@ -31,10 +31,12 @@
 InputMappingView::~InputMappingView() = default;
 
 void InputMappingView::SetDisplayMode(const DisplayMode mode) {
-  if (current_display_mode_ == mode)
+  DCHECK(mode != DisplayMode::kEducation);
+  if (current_display_mode_ == mode || mode == DisplayMode::kMenu ||
+      mode == DisplayMode::kPreMenu) {
     return;
+  }
   switch (mode) {
-    case DisplayMode::kMenu:
     case DisplayMode::kView:
       SetBackground(nullptr);
       break;
diff --git a/chrome/browser/ash/arc/input_overlay/ui/message_view.cc b/chrome/browser/ash/arc/input_overlay/ui/message_view.cc
index 3c1e655..05e0686 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/message_view.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/message_view.cc
@@ -59,6 +59,7 @@
       color_utils::GetResultingPaintColor(kForegroundColor, kBackgroundColor),
       kCornerRadius));
   SetBorder(views::CreateEmptyBorder(gfx::Insets::VH(0, kSideInset)));
+  SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
 
   SetText(base::UTF8ToUTF16(message));
   SetEnabledTextColors(kTextColor);
diff --git a/chrome/browser/ash/file_manager/restore_io_task.cc b/chrome/browser/ash/file_manager/restore_io_task.cc
index b1e927f6..588a1600 100644
--- a/chrome/browser/ash/file_manager/restore_io_task.cc
+++ b/chrome/browser/ash/file_manager/restore_io_task.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/file_manager/restore_io_task.h"
 
 #include "base/callback.h"
+#include "base/files/file_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 
@@ -14,8 +15,11 @@
 RestoreIOTask::RestoreIOTask(
     std::vector<storage::FileSystemURL> file_urls,
     Profile* profile,
-    scoped_refptr<storage::FileSystemContext> file_system_context)
-    : file_system_context_(file_system_context) {
+    scoped_refptr<storage::FileSystemContext> file_system_context,
+    const base::FilePath base_path)
+    : file_system_context_(file_system_context),
+      profile_(profile),
+      base_path_(base_path) {
   progress_.state = State::kQueued;
   progress_.type = OperationType::kRestore;
   progress_.bytes_transferred = 0;
@@ -44,6 +48,41 @@
                             IOTask::CompleteCallback complete_callback) {
   progress_callback_ = std::move(progress_callback);
   complete_callback_ = std::move(complete_callback);
+
+  progress_.state = State::kInProgress;
+
+  ValidateTrashInfo(0);
+}
+
+// Calls the completion callback for the task. `progress_` should not be
+// accessed after calling this.
+void RestoreIOTask::Complete(State state) {
+  progress_.state = state;
+  base::SequencedTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(complete_callback_), std::move(progress_)));
+}
+
+void RestoreIOTask::ValidateTrashInfo(size_t idx) {
+  const base::FilePath& file_path = progress_.sources[idx].url.path();
+  if (file_path.FinalExtension() != ".trashinfo") {
+    progress_.sources[idx].error = base::File::FILE_ERROR_INVALID_URL;
+    Complete(State::kError);
+    return;
+  }
+
+  // TODO(b/231830250): Add in logic to ensure the trash location is parented at
+  // an enabled trash location.
+
+  ParseTrashInfoFile(idx, file_path);
+}
+
+void RestoreIOTask::ParseTrashInfoFile(
+    size_t idx,
+    const base::FilePath& trash_info_file_path) {
+  // TODO(b/231830250): Add parsing logic here on a base::MayBlock thread to
+  // enable restoration.
+  Complete(State::kSuccess);
 }
 
 void RestoreIOTask::Cancel() {
diff --git a/chrome/browser/ash/file_manager/restore_io_task.h b/chrome/browser/ash/file_manager/restore_io_task.h
index b1064ce..1f9dae3 100644
--- a/chrome/browser/ash/file_manager/restore_io_task.h
+++ b/chrome/browser/ash/file_manager/restore_io_task.h
@@ -30,7 +30,8 @@
  public:
   RestoreIOTask(std::vector<storage::FileSystemURL> file_urls,
                 Profile* profile,
-                scoped_refptr<storage::FileSystemContext> file_system_context);
+                scoped_refptr<storage::FileSystemContext> file_system_context,
+                const base::FilePath base_path);
   ~RestoreIOTask() override;
 
   // Starts restore trask.
@@ -39,7 +40,30 @@
   void Cancel() override;
 
  private:
+  // Finalises the RestoreIOTask with the `state`.
+  void Complete(State state);
+
+  // Ensure the metadata file conforms to the following:
+  //   - Has a .trashinfo suffix
+  // TODO(b/231830250): Implement the remaining validations:
+  //   - Resides in an enabled trash directory
+  //   - The file resides in the info directory
+  //   - Has an identical item in the files directory with no .trashinfo suffix
+  void ValidateTrashInfo(size_t idx);
+
+  // Parse the .trashinfo file and ensure it conforms to the XDG specification
+  // before restoring the file.
+  // TODO(b/231830250): Finish implementation of this method.
+  void ParseTrashInfoFile(size_t idx,
+                          const base::FilePath& trash_info_file_path);
+
   scoped_refptr<storage::FileSystemContext> file_system_context_;
+  raw_ptr<Profile> profile_;
+
+  // Represents the parent path that all the source URLs descend from. Used to
+  // work around the fact `FileSystemOperationRunner` requires relative paths
+  // only in testing.
+  base::FilePath base_path_;
 
   // Stores the id of the restore operation if one is in progress. Used so the
   // restore can be cancelled.
diff --git a/chrome/browser/ash/file_manager/restore_io_task_unittest.cc b/chrome/browser/ash/file_manager/restore_io_task_unittest.cc
new file mode 100644
index 0000000..22503575
--- /dev/null
+++ b/chrome/browser/ash/file_manager/restore_io_task_unittest.cc
@@ -0,0 +1,97 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/file_manager/restore_io_task.h"
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/rand_util.h"
+#include "base/run_loop.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "chrome/browser/ash/file_manager/path_util.h"
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "storage/browser/file_system/file_system_context.h"
+#include "storage/browser/file_system/file_system_url.h"
+#include "storage/browser/test/test_file_system_context.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
+
+namespace file_manager {
+namespace io_task {
+namespace {
+
+using ::base::test::RunClosure;
+using ::testing::_;
+using ::testing::Field;
+
+constexpr size_t kTestFileSize = 32;
+
+class RestoreIOTaskTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+    profile_ =
+        std::make_unique<TestingProfile>(base::FilePath(temp_dir_.GetPath()));
+
+    file_system_context_ = storage::CreateFileSystemContextForTesting(
+        nullptr, temp_dir_.GetPath());
+  }
+
+  storage::FileSystemURL CreateFileSystemURL(
+      const base::FilePath& absolute_path) {
+    std::string relative_path = absolute_path.value();
+    EXPECT_TRUE(file_manager::util::ReplacePrefix(
+        &relative_path, temp_dir_.GetPath().AsEndingWithSeparator().value(),
+        ""));
+
+    return file_system_context_->CreateCrackedFileSystemURL(
+        kTestStorageKey, storage::kFileSystemTypeTest,
+        base::FilePath::FromUTF8Unsafe(relative_path));
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<TestingProfile> profile_;
+  const blink::StorageKey kTestStorageKey =
+      blink::StorageKey::CreateFromStringForTesting("chrome-extension://abc");
+
+  base::ScopedTempDir temp_dir_;
+  scoped_refptr<storage::FileSystemContext> file_system_context_;
+};
+
+TEST_F(RestoreIOTaskTest, URLsWithInvalidSuffixShouldError) {
+  std::string foo_contents = base::RandBytesAsString(kTestFileSize);
+  const base::FilePath file_path = temp_dir_.GetPath().Append("foo.txt");
+  ASSERT_TRUE(base::WriteFile(file_path, foo_contents));
+
+  base::RunLoop run_loop;
+  std::vector<storage::FileSystemURL> source_urls = {
+      CreateFileSystemURL(file_path),
+  };
+
+  base::MockRepeatingCallback<void(const ProgressStatus&)> progress_callback;
+  base::MockOnceCallback<void(ProgressStatus)> complete_callback;
+
+  // Progress callback should not be called as the suffix doesn't end in
+  // .trashinfo.
+  EXPECT_CALL(progress_callback, Run(_)).Times(0);
+
+  // We should get one complete callback when the verifiation of the suffix
+  // fails.
+  EXPECT_CALL(complete_callback,
+              Run(Field(&ProgressStatus::state, State::kError)))
+      .WillOnce(RunClosure(run_loop.QuitClosure()));
+
+  RestoreIOTask task(source_urls, profile_.get(), file_system_context_,
+                     temp_dir_.GetPath());
+  task.Execute(progress_callback.Get(), complete_callback.Get());
+  run_loop.Run();
+}
+
+}  // namespace
+}  // namespace io_task
+}  // namespace file_manager
diff --git a/chrome/browser/ash/login/login_ui_browsertest.cc b/chrome/browser/ash/login/login_ui_browsertest.cc
index 69cfe92d..b1b523b 100644
--- a/chrome/browser/ash/login/login_ui_browsertest.cc
+++ b/chrome/browser/ash/login/login_ui_browsertest.cc
@@ -4,7 +4,9 @@
 
 #include "ash/constants/ash_pref_names.h"
 #include "ash/constants/ash_switches.h"
+#include "ash/login/login_screen_controller.h"
 #include "ash/public/cpp/login_screen_test_api.h"
+#include "ash/shell.h"
 #include "base/command_line.h"
 #include "chrome/browser/ash/login/lock/screen_locker_tester.h"
 #include "chrome/browser/ash/login/login_manager_test.h"
@@ -12,6 +14,7 @@
 #include "chrome/browser/ash/login/startup_utils.h"
 #include "chrome/browser/ash/login/test/device_state_mixin.h"
 #include "chrome/browser/ash/login/test/js_checker.h"
+#include "chrome/browser/ash/login/test/kiosk_apps_mixin.h"
 #include "chrome/browser/ash/login/test/local_state_mixin.h"
 #include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
@@ -21,6 +24,7 @@
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
 #include "chrome/browser/ash/login/wizard_controller.h"
+#include "chrome/browser/ash/policy/core/device_policy_cros_browser_test.h"
 #include "chrome/browser/ash/settings/scoped_testing_cros_settings.h"
 #include "chrome/browser/ash/settings/stub_cros_settings_provider.h"
 #include "chrome/browser/browser_process.h"
@@ -35,6 +39,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
 #include "content/public/test/browser_test.h"
+#include "net/dns/mock_host_resolver.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes.h"
@@ -554,4 +559,90 @@
 
 INSTANTIATE_TEST_SUITE_P(All, SshWarningTest, ::testing::Bool());
 
+namespace {
+
+// This is the constant that exists on the server side. It corresponds to
+// the type of enrollment license.
+constexpr char kKioskSkuName[] = "GOOGLE.CHROME_KIOSK_ANNUAL";
+
+}  // namespace
+
+class KioskSkuLoginScreenVisibilityTest
+    : public MixinBasedInProcessBrowserTest {
+ public:
+  KioskSkuLoginScreenVisibilityTest() : MixinBasedInProcessBrowserTest() {
+    device_state_.set_skip_initial_policy_setup(true);
+  }
+  ~KioskSkuLoginScreenVisibilityTest() override = default;
+  KioskSkuLoginScreenVisibilityTest(const KioskSkuLoginScreenVisibilityTest&) =
+      delete;
+  void operator=(const KioskSkuLoginScreenVisibilityTest&) = delete;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kLoginManager);
+    command_line->AppendSwitch(switches::kForceLoginManagerInTests);
+    command_line->AppendSwitch(
+        switches::kDisableOOBEChromeVoxHintTimerForTesting);
+
+    MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+
+ protected:
+  policy::DevicePolicyCrosTestHelper* policy_helper() {
+    return &policy_helper_;
+  }
+
+ private:
+  ash::DeviceStateMixin device_state_{
+      &mixin_host_,
+      ash::DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
+  policy::DevicePolicyCrosTestHelper policy_helper_;
+};
+
+// Verifies that shelf buttons of Guest mode and Add user are shown, and kiosk
+// instruction bubble is hidden without kiosk SKU.
+IN_PROC_BROWSER_TEST_F(KioskSkuLoginScreenVisibilityTest, WithoutKioskSku) {
+  Shell::Get()->login_screen_controller()->ShowLoginScreen();
+
+  EXPECT_TRUE(LoginScreenTestApi::IsLoginShelfShown());
+  EXPECT_TRUE(LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_TRUE(LoginScreenTestApi::IsAddUserButtonShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsKioskInstructionBubbleShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsKioskDefaultMessageShown());
+}
+
+// Verifies that shelf buttons of Guest mode and Add user are hidden, kiosk
+// instruction bubble is hidden, and kiosk
+// default message is shown without kiosk apps.
+IN_PROC_BROWSER_TEST_F(KioskSkuLoginScreenVisibilityTest, WithoutApps) {
+  Shell::Get()->login_screen_controller()->ShowLoginScreen();
+  policy_helper()->device_policy()->policy_data().set_license_sku(
+      kKioskSkuName);
+  policy_helper()->RefreshPolicyAndWaitUntilDeviceCloudPolicyUpdated();
+
+  EXPECT_TRUE(LoginScreenTestApi::IsLoginShelfShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsAddUserButtonShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsKioskInstructionBubbleShown());
+  EXPECT_TRUE(LoginScreenTestApi::IsKioskDefaultMessageShown());
+}
+
+// Verifies that shelf buttons of Guest mode and Add user are hidden, kiosk
+// instruction bubble is shown, and kiosk
+// default message is hidden with kiosk apps.
+IN_PROC_BROWSER_TEST_F(KioskSkuLoginScreenVisibilityTest, WithApps) {
+  Shell::Get()->login_screen_controller()->ShowLoginScreen();
+  policy_helper()->device_policy()->policy_data().set_license_sku(
+      kKioskSkuName);
+  KioskAppsMixin::AppendKioskAccount(
+      &policy_helper()->device_policy()->payload());
+  policy_helper()->RefreshPolicyAndWaitUntilDeviceCloudPolicyUpdated();
+
+  EXPECT_TRUE(LoginScreenTestApi::IsLoginShelfShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsGuestButtonShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsAddUserButtonShown());
+  EXPECT_TRUE(LoginScreenTestApi::IsKioskInstructionBubbleShown());
+  EXPECT_FALSE(LoginScreenTestApi::IsKioskDefaultMessageShown());
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
index 9cc0602..ad208ae9 100644
--- a/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
+++ b/chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.cc
@@ -34,6 +34,8 @@
 #include "content/public/browser/browser_context.h"
 #include "extensions/browser/api/feedback_private/feedback_private_api.h"
 #include "extensions/browser/api/feedback_private/feedback_service.h"
+#include "mojo/public/cpp/base/big_buffer.h"
+#include "mojo/public/mojom/base/safe_base_name.mojom.h"
 #include "net/base/network_change_notifier.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/aura/window.h"
@@ -44,6 +46,11 @@
 
 namespace {
 
+using ::ash::os_feedback_ui::mojom::AttachedFilePtr;
+using ::ash::os_feedback_ui::mojom::SendReportStatus;
+using extensions::FeedbackParams;
+using extensions::FeedbackPrivateAPI;
+
 feedback::FeedbackUploader* GetFeedbackUploaderForContext(
     content::BrowserContext* context) {
   return feedback::FeedbackUploaderFactoryChrome::GetForBrowserContext(context);
@@ -57,11 +64,26 @@
   return nullptr;
 }
 
-}  // namespace
+constexpr std::size_t MAX_ATTACHED_FILE_SIZE_BYTES = 10 * 1024 * 1024;
 
-using ::ash::os_feedback_ui::mojom::SendReportStatus;
-using extensions::FeedbackParams;
-using extensions::FeedbackPrivateAPI;
+bool ShouldAddAttachment(const AttachedFilePtr& attached_file) {
+  if (!(attached_file && attached_file->file_data.data())) {
+    // Does not have data.
+    return false;
+  }
+  if (attached_file->file_name.path().empty()) {
+    // The file name is empty.
+    return false;
+  }
+  if (attached_file->file_data.size() > MAX_ATTACHED_FILE_SIZE_BYTES) {
+    LOG(WARNING) << "Can't upload file larger than 10 MB. File size: "
+                 << attached_file->file_data.size();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
 
 ChromeOsFeedbackDelegate::ChromeOsFeedbackDelegate(Profile* profile)
     : ChromeOsFeedbackDelegate(profile,
@@ -150,6 +172,19 @@
         std::string(png_data->front_as<char>(), png_data->size()));
   }
 
+  const AttachedFilePtr& attached_file = report->attached_file;
+  if (ShouldAddAttachment(attached_file)) {
+    feedback_data->set_attached_filename(
+        attached_file->file_name.path().AsUTF8Unsafe());
+    const std::string file_data(
+        reinterpret_cast<const char*>(attached_file->file_data.data()),
+        attached_file->file_data.size());
+    // Compress attached file and add to |feedback_data|. The operation is done
+    // by posting a task to thread pool. The |feedback_data| will manage waiting
+    // for all tasks to complete.
+    feedback_data->AttachAndCompressFileData(std::move(file_data));
+  }
+
   feedback_service_->SendFeedback(
       feedback_params, feedback_data,
       base::BindOnce(&ChromeOsFeedbackDelegate::OnSendFeedbackDone,
diff --git a/chrome/browser/autofill/android/OWNERS b/chrome/browser/autofill/android/OWNERS
new file mode 100644
index 0000000..731670ee
--- /dev/null
+++ b/chrome/browser/autofill/android/OWNERS
@@ -0,0 +1 @@
+ file://components/autofill/android/OWNERS
diff --git a/chrome/browser/autofill/autofill_context_menu_manager.cc b/chrome/browser/autofill/autofill_context_menu_manager.cc
index 54fecf7c..f59d25f 100644
--- a/chrome/browser/autofill/autofill_context_menu_manager.cc
+++ b/chrome/browser/autofill/autofill_context_menu_manager.cc
@@ -7,6 +7,10 @@
 #include <string>
 
 #include "chrome/app/chrome_command_ids.h"
+#include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/common/autofill_features.h"
 
 namespace autofill {
 
@@ -18,17 +22,63 @@
 static constexpr int kAutofillContextCustomLast =
     IDC_CONTENT_CONTEXT_AUTOFILL_CUSTOM_LAST;
 
+// Field types that are supposed to be shown in the menu. |UNKNOWN_TYPE|
+// denotes a separator needs to be added.
+static constexpr std::array kAddressFieldTypesToShow = {
+    NAME_FULL,
+    UNKNOWN_TYPE,
+    ADDRESS_HOME_STREET_ADDRESS,
+    ADDRESS_HOME_CITY,
+    ADDRESS_HOME_ZIP,
+    UNKNOWN_TYPE,
+    PHONE_HOME_WHOLE_NUMBER,
+    EMAIL_ADDRESS};
+
+// Field types that are supposed to be shown in the menu. |UNKNOWN_TYPE|
+// denotes a separator needs to be added.
+static constexpr std::array kCardFieldTypesToShow = {
+    CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, UNKNOWN_TYPE,
+    CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR};
+
 }  // namespace
 
 // static
+AutofillContextMenuManager::CommandId
+AutofillContextMenuManager::ConvertToAutofillCustomCommandId(int offset) {
+  return AutofillContextMenuManager::CommandId(kAutofillContextCustomFirst +
+                                               offset);
+}
+
+// static
 bool AutofillContextMenuManager::IsAutofillCustomCommandId(
     CommandId command_id) {
   return command_id.value() >= kAutofillContextCustomFirst &&
          command_id.value() <= kAutofillContextCustomLast;
 }
 
-void AutofillContextMenuManager::AppendTopLevelItems() {
-  // TODO(crbug.com/1325811): Implement
+AutofillContextMenuManager::AutofillContextMenuManager(
+    PersonalDataManager* personal_data_manager,
+    ui::SimpleMenuModel::Delegate* delegate,
+    ui::SimpleMenuModel* menu_model)
+    : personal_data_manager_(personal_data_manager),
+      menu_model_(menu_model),
+      delegate_(delegate) {}
+
+AutofillContextMenuManager::~AutofillContextMenuManager() {
+  cached_menu_models_.clear();
+}
+
+void AutofillContextMenuManager::AppendItems() {
+  if (!base::FeatureList::IsEnabled(
+          features::kAutofillShowManualFallbackInContextMenu)) {
+    return;
+  }
+
+  DCHECK(personal_data_manager_);
+  DCHECK(menu_model_);
+
+  AppendAddressItems();
+  AppendCreditCardItems();
 }
 
 bool AutofillContextMenuManager::IsCommandIdChecked(
@@ -53,4 +103,175 @@
   // TODO(crbug.com/1325811): Implement.
 }
 
+void AutofillContextMenuManager::AppendAddressItems() {
+  std::vector<AutofillProfile*> address_profiles =
+      personal_data_manager_->GetProfiles();
+  if (address_profiles.empty()) {
+    return;
+  }
+
+  // Used to create menu model for storing address description. Would be
+  // attached to the top level "Fill Address Info" item in the context menu.
+  ui::SimpleMenuModel* profile_menu = new ui::SimpleMenuModel(delegate_);
+  cached_menu_models_.push_back(base::WrapUnique(profile_menu));
+
+  // True if a row is added in the address section.
+  bool address_added = false;
+
+  for (const AutofillProfile* profile : address_profiles) {
+    // Creates a menu model for storing address details. Would be attached
+    // to the address description menu item.
+    ui::SimpleMenuModel* address_details_submenu =
+        new ui::SimpleMenuModel(delegate_);
+    cached_menu_models_.push_back(base::WrapUnique(address_details_submenu));
+
+    // Create a submenu for each address profile with their details.
+    CreateSubMenuWithData(profile, kAddressFieldTypesToShow,
+                          address_details_submenu);
+
+    // Add a menu item showing address profile description. Hovering over it
+    // opens a submenu with the address details.
+    absl::optional<CommandId> profile_menu_id =
+        GetNextAvailableAutofillCommandId();
+    if (profile_menu_id) {
+      address_added = true;
+      profile_menu->AddSubMenu(profile_menu_id->value(),
+                               GetProfileDescription(*profile),
+                               address_details_submenu);
+    }
+  }
+
+  // Add a menu option to suggest filling address in the context menu.
+  // Hovering over it opens a submenu suggesting all the address profiles
+  // stored in the profile.
+  absl::optional<CommandId> address_menu_id =
+      GetNextAvailableAutofillCommandId();
+  if (address_menu_id && address_added) {
+    // TODO(crbug.com/1325811): Use i18n string.
+    menu_model_->AddSubMenu(address_menu_id->value(), u"Fill Address Info",
+                            profile_menu);
+  }
+}
+
+void AutofillContextMenuManager::AppendCreditCardItems() {
+  std::vector<CreditCard*> cards = personal_data_manager_->GetCreditCards();
+  if (cards.empty()) {
+    return;
+  }
+
+  // Used to create menu model for storing credit card description. Would be
+  // attached to the top level "Fill Payment" item in the context menu.
+  ui::SimpleMenuModel* card_submenu = new ui::SimpleMenuModel(delegate_);
+  cached_menu_models_.push_back(base::WrapUnique(card_submenu));
+
+  // True if a row is added in the credit card section.
+  bool card_added = false;
+
+  for (const CreditCard* card : cards) {
+    // Creates a menu model for storing credit card details. Would be attached
+    // to the credit card description menu item.
+    ui::SimpleMenuModel* card_details_submenu =
+        new ui::SimpleMenuModel(delegate_);
+    cached_menu_models_.push_back(base::WrapUnique(card_details_submenu));
+
+    // Create a submenu for each credit card with their details.
+    CreateSubMenuWithData(card, kCardFieldTypesToShow, card_details_submenu);
+
+    // Add a menu item showing credit card  description. Hovering over it
+    // opens a submenu with the credit card details.
+    absl::optional<CommandId> submenu_id = GetNextAvailableAutofillCommandId();
+    if (submenu_id) {
+      card_added = true;
+      card_submenu->AddSubMenu(submenu_id->value(),
+                               card->CardIdentifierStringForAutofillDisplay(),
+                               card_details_submenu);
+    }
+  }
+
+  // Add a menu option to suggest filling a credit card in the context menu.
+  // Hovering over it opens a submenu suggesting all the credit cards
+  // stored in the profile.
+  absl::optional<CommandId> card_submenu_id =
+      GetNextAvailableAutofillCommandId();
+  if (card_submenu_id && card_added) {
+    // TODO(crbug.com/1325811): Use i18n string.
+    menu_model_->AddSubMenu(card_submenu_id->value(), u"Fill Payment",
+                            card_submenu);
+  }
+}
+
+void AutofillContextMenuManager::CreateSubMenuWithData(
+    absl::variant<const AutofillProfile*, const CreditCard*>
+        profile_or_credit_card,
+    base::span<const ServerFieldType> field_types_to_show,
+    ui::SimpleMenuModel* menu_model) {
+  // True when an item is added to the context menu which is not a separator.
+  bool is_separator_required = false;
+
+  // True when a `UNKNOWN_TYPE` is seen in `field_types_to_show`.
+  bool was_prev_unknown = false;
+
+  for (const ServerFieldType type : field_types_to_show) {
+    if (type == UNKNOWN_TYPE) {
+      was_prev_unknown = true;
+      continue;
+    }
+
+    std::u16string value = absl::visit(
+        [type](const auto& alternative) {
+          return alternative->GetRawInfo(type);
+        },
+        profile_or_credit_card);
+
+    if (!value.empty()) {
+      absl::optional<CommandId> value_menu_id =
+          GetNextAvailableAutofillCommandId();
+      if (value_menu_id) {
+        if (was_prev_unknown && is_separator_required) {
+          menu_model->AddSeparator(ui::NORMAL_SEPARATOR);
+          is_separator_required = false;
+        }
+
+        // Create a menu item with the address/credit card details and attach
+        // to the model.
+        menu_model->AddItem(value_menu_id->value(), value);
+        was_prev_unknown = false;
+        is_separator_required = true;
+      }
+    }
+  }
+}
+
+absl::optional<AutofillContextMenuManager::CommandId>
+AutofillContextMenuManager::GetNextAvailableAutofillCommandId() {
+  int max_index = kAutofillContextCustomLast - kAutofillContextCustomFirst;
+
+  if (count_of_items_added_to_menu_model_ >= max_index)
+    return absl::nullopt;
+
+  return ConvertToAutofillCustomCommandId(
+      count_of_items_added_to_menu_model_++);
+}
+
+std::u16string AutofillContextMenuManager::GetProfileDescription(
+    const AutofillProfile& profile) {
+  // All user-visible fields.
+  static constexpr ServerFieldType kDetailsFields[] = {
+      NAME_FULL,
+      ADDRESS_HOME_LINE1,
+      ADDRESS_HOME_LINE2,
+      ADDRESS_HOME_DEPENDENT_LOCALITY,
+      ADDRESS_HOME_CITY,
+      ADDRESS_HOME_STATE,
+      ADDRESS_HOME_ZIP,
+      EMAIL_ADDRESS,
+      PHONE_HOME_WHOLE_NUMBER,
+      COMPANY_NAME,
+      ADDRESS_HOME_COUNTRY};
+
+  return profile.ConstructInferredLabel(
+      kDetailsFields, std::size(kDetailsFields),
+      /*num_fields_to_include=*/2, personal_data_manager_->app_locale());
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/autofill/autofill_context_menu_manager.h b/chrome/browser/autofill/autofill_context_menu_manager.h
index afc1ecf..d24539aa 100644
--- a/chrome/browser/autofill/autofill_context_menu_manager.h
+++ b/chrome/browser/autofill/autofill_context_menu_manager.h
@@ -5,7 +5,12 @@
 #ifndef CHROME_BROWSER_AUTOFILL_AUTOFILL_CONTEXT_MENU_MANAGER_H_
 #define CHROME_BROWSER_AUTOFILL_AUTOFILL_CONTEXT_MENU_MANAGER_H_
 
+#include "base/containers/span.h"
+#include "base/memory/raw_ptr.h"
 #include "base/types/strong_alias.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/base/models/simple_menu_model.h"
 
 namespace content {
 class RenderFrameHost;
@@ -14,6 +19,10 @@
 
 namespace autofill {
 
+class AutofillProfile;
+class CreditCard;
+class PersonalDataManager;
+
 // `AutofillContextMenuManager` is responsible for adding/executing Autofill
 // related context menu items. `RenderViewContextMenu` is intended to own and
 // control the lifetime of `AutofillContextMenuManager`.
@@ -26,25 +35,65 @@
   // `IDC_CONTENT_CONTEXT_AUTOFILL_CUSTOM_LAST`.
   using CommandId = base::StrongAlias<class CommandIdTag, int>;
 
+  // Convert a command ID so that it fits within the range for
+  // autofill context menu. `offset` is the count of the items added to the
+  // autofill context menu.
+  static CommandId ConvertToAutofillCustomCommandId(int offset);
+
   // Returns true if the given id is one generated for autofill context menu.
   static bool IsAutofillCustomCommandId(CommandId command_id);
 
-  AutofillContextMenuManager() = default;
+  AutofillContextMenuManager(PersonalDataManager* personal_data_manager,
+                             ui::SimpleMenuModel::Delegate* delegate,
+                             ui::SimpleMenuModel* menu_model);
+  ~AutofillContextMenuManager();
   AutofillContextMenuManager(const AutofillContextMenuManager&) = delete;
   AutofillContextMenuManager& operator=(const AutofillContextMenuManager&) =
       delete;
 
   // Adds items such as "Addresses"/"Credit Cards"/"Passwords" to the top level
   // of the context menu.
-  void AppendTopLevelItems();
+  void AppendItems();
 
-  // |AutofillContextMenuManager| specific, called from |RenderViewContextMenu|.
+  // `AutofillContextMenuManager` specific, called from `RenderViewContextMenu`.
   bool IsCommandIdChecked(CommandId command_id) const;
   bool IsCommandIdVisible(CommandId command_id) const;
   bool IsCommandIdEnabled(CommandId command_id) const;
   void ExecuteCommand(CommandId command_id,
                       content::WebContents* web_contents,
                       content::RenderFrameHost* render_frame_host);
+
+ private:
+  // Adds address items to the context menu.
+  void AppendAddressItems();
+
+  // Adds credit card items to the context menu.
+  void AppendCreditCardItems();
+
+  // Fetches value from `profile_or_credit_card` based on the type from
+  // `field_types_to_show` and adds them to the `menu_model`.
+  void CreateSubMenuWithData(
+      absl::variant<const AutofillProfile*, const CreditCard*>
+          profile_or_credit_card,
+      base::span<const ServerFieldType> field_types_to_show,
+      ui::SimpleMenuModel* menu_model);
+
+  // Returns a description for the given `profile`.
+  std::u16string GetProfileDescription(const AutofillProfile& profile);
+
+  // Returns the next available command id for adding an item to the context
+  // menu for Autofill.
+  absl::optional<CommandId> GetNextAvailableAutofillCommandId();
+
+  raw_ptr<PersonalDataManager> personal_data_manager_;
+  raw_ptr<ui::SimpleMenuModel> menu_model_;
+  raw_ptr<ui::SimpleMenuModel::Delegate> delegate_;
+
+  // Stores the count of items added to the context menu from Autofill.
+  int count_of_items_added_to_menu_model_ = 0;
+
+  // Keep track of and clean up menu models for submenus.
+  std::vector<std::unique_ptr<ui::SimpleMenuModel>> cached_menu_models_;
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/autofill/autofill_context_menu_manager_unittest.cc b/chrome/browser/autofill/autofill_context_menu_manager_unittest.cc
new file mode 100644
index 0000000..711c503d
--- /dev/null
+++ b/chrome/browser/autofill/autofill_context_menu_manager_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/autofill/autofill_context_menu_manager.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
+#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/test_personal_data_manager.h"
+#include "components/autofill/core/common/autofill_features.h"
+
+namespace autofill {
+
+class AutofillContextMenuManagerTest : public ChromeRenderViewHostTestHarness {
+ public:
+  AutofillContextMenuManagerTest() {
+    feature_.InitAndEnableFeature(
+        features::kAutofillShowManualFallbackInContextMenu);
+  }
+
+  AutofillContextMenuManagerTest(const AutofillContextMenuManagerTest&) =
+      delete;
+  AutofillContextMenuManagerTest& operator=(
+      const AutofillContextMenuManagerTest&) = delete;
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    PersonalDataManagerFactory::GetInstance()->SetTestingFactory(
+        profile(), BrowserContextKeyedServiceFactory::TestingFactory());
+
+    personal_data_manager_ = std::make_unique<TestPersonalDataManager>();
+    personal_data_manager_->SetPrefService(profile()->GetPrefs());
+    menu_model_ = std::make_unique<ui::SimpleMenuModel>(nullptr);
+
+    personal_data_manager_->AddProfile(test::GetFullProfile());
+    personal_data_manager_->AddCreditCard(test::GetCreditCard());
+
+    autofill_context_menu_manager_ =
+        std::make_unique<AutofillContextMenuManager>(
+            personal_data_manager_.get(), nullptr, menu_model_.get());
+  }
+
+  void TearDown() override {
+    personal_data_manager_.reset();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+ protected:
+  ui::SimpleMenuModel* menu_model() const { return menu_model_.get(); }
+
+  AutofillContextMenuManager* autofill_context_menu_manager() const {
+    return autofill_context_menu_manager_.get();
+  }
+
+ private:
+  std::unique_ptr<TestPersonalDataManager> personal_data_manager_;
+  std::unique_ptr<ui::SimpleMenuModel> menu_model_;
+  std::unique_ptr<AutofillContextMenuManager> autofill_context_menu_manager_;
+  base::test::ScopedFeatureList feature_;
+};
+
+// Tests that the Autofill context menu is correctly set up.
+TEST_F(AutofillContextMenuManagerTest, AutofillContextMenuContents) {
+  autofill_context_menu_manager()->AppendItems();
+
+  // Check for top level menu with autofill options.
+  ASSERT_EQ(2, menu_model()->GetItemCount());
+  ASSERT_EQ(u"Fill Address Info", menu_model()->GetLabelAt(0));
+  ASSERT_EQ(u"Fill Payment", menu_model()->GetLabelAt(1));
+  ASSERT_EQ(menu_model()->GetTypeAt(0), ui::MenuModel::ItemType::TYPE_SUBMENU);
+  ASSERT_EQ(menu_model()->GetTypeAt(1), ui::MenuModel::ItemType::TYPE_SUBMENU);
+
+  // Check for submenu with address descriptions.
+  auto* address_menu_model = menu_model()->GetSubmenuModelAt(0);
+  ASSERT_EQ(address_menu_model->GetItemCount(), 1);
+  ASSERT_EQ(u"John H. Doe, 666 Erebus St.", address_menu_model->GetLabelAt(0));
+  ASSERT_EQ(address_menu_model->GetTypeAt(0),
+            ui::MenuModel::ItemType::TYPE_SUBMENU);
+
+  // Check for submenu with address details.
+  auto* address_details_submenu = address_menu_model->GetSubmenuModelAt(0);
+  ASSERT_EQ(address_details_submenu->GetItemCount(), 6);
+  ASSERT_EQ(u"666 Erebus St.\nApt 8", address_details_submenu->GetLabelAt(0));
+  ASSERT_EQ(u"Elysium", address_details_submenu->GetLabelAt(1));
+  ASSERT_EQ(u"91111", address_details_submenu->GetLabelAt(2));
+  ASSERT_EQ(u"", address_details_submenu->GetLabelAt(3));
+  ASSERT_EQ(u"16502111111", address_details_submenu->GetLabelAt(4));
+  ASSERT_EQ(u"johndoe@hades.com", address_details_submenu->GetLabelAt(5));
+
+  // Check for submenu with credit card descriptions.
+  auto* card_menu_model = menu_model()->GetSubmenuModelAt(1);
+  ASSERT_EQ(card_menu_model->GetItemCount(), 1);
+  ASSERT_EQ(
+      u"Visa  "
+      u"\x202A\x2022\x2060\x2006\x2060\x2022\x2060\x2006\x2060\x2022\x2060"
+      u"\x2006\x2060\x2022\x2060\x2006\x2060"
+      u"1111\x202C",
+      card_menu_model->GetLabelAt(0));
+  ASSERT_EQ(card_menu_model->GetTypeAt(0),
+            ui::MenuModel::ItemType::TYPE_SUBMENU);
+
+  // Check for submenu with credit card details.
+  auto* card_details_submenu = card_menu_model->GetSubmenuModelAt(0);
+  ASSERT_EQ(card_details_submenu->GetItemCount(), 5);
+  ASSERT_EQ(u"Test User", card_details_submenu->GetLabelAt(0));
+  ASSERT_EQ(u"4111111111111111", card_details_submenu->GetLabelAt(1));
+  ASSERT_EQ(u"", card_details_submenu->GetLabelAt(2));
+  ASSERT_EQ(card_details_submenu->GetLabelAt(3),
+            base::ASCIIToUTF16(test::NextMonth().c_str()));
+  ASSERT_EQ(card_details_submenu->GetLabelAt(4),
+            base::ASCIIToUTF16(test::NextYear().c_str()).substr(2));
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc
index 2a94cc12..2f68ab8 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.cc
+++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -1304,7 +1304,7 @@
 }
 
 bool TestRecipeReplayer::ExecuteForceLoadPage(base::Value::Dict action) {
-  bool should_force = action.FindInt("force").value_or(false);
+  bool should_force = action.FindBool("force").value_or(false);
   if (!should_force) {
     return true;
   }
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
index ad4af3c..30c0c57a 100644
--- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
+++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.cc
@@ -25,7 +25,7 @@
 
 void ApcExternalActionDelegate::OnActionRequested(
     const autofill_assistant::external::Action& action_info,
-    base::OnceCallback<void()> start_dom_checks_callback,
+    base::OnceCallback<void(DomUpdateCallback)> start_dom_checks_callback,
     base::OnceCallback<void(const autofill_assistant::external::Result& result)>
         end_action_callback) {
   autofill_assistant::external::Result result;
diff --git a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
index 0a4544b..34a973d 100644
--- a/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
+++ b/chrome/browser/autofill_assistant/password_change/apc_external_action_delegate.h
@@ -33,7 +33,7 @@
   // ExternalActionDelegate
   void OnActionRequested(
       const autofill_assistant::external::Action& action_info,
-      base::OnceCallback<void()> start_dom_checks_callback,
+      base::OnceCallback<void(DomUpdateCallback)> start_dom_checks_callback,
       base::OnceCallback<void(const autofill_assistant::external::Result&
                                   result)> end_action_callback) override;
   void OnInterruptStarted() override;
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 15fbb5b..0af7b9b2 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -3514,6 +3514,7 @@
     "../ash/file_manager/guest_os_file_tasks_unittest.cc",
     "../ash/file_manager/io_task_controller_unittest.cc",
     "../ash/file_manager/path_util_unittest.cc",
+    "../ash/file_manager/restore_io_task_unittest.cc",
     "../ash/file_manager/speedometer_unittest.cc",
     "../ash/file_manager/trash_io_task_unittest.cc",
     "../ash/file_manager/url_util_unittest.cc",
diff --git a/chrome/browser/content_settings/content_settings_browsertest.cc b/chrome/browser/content_settings/content_settings_browsertest.cc
index 5dac0d2..72ac3f8 100644
--- a/chrome/browser/content_settings/content_settings_browsertest.cc
+++ b/chrome/browser/content_settings/content_settings_browsertest.cc
@@ -1722,6 +1722,36 @@
       https_server_.GetURL("b.test", "/fenced_frames/page_with_script.html");
   const GURL other_main_url = https_server_.GetURL("c.test", "/empty.html");
 
+  auto NavigatePrimaryPageAndAddFencedFrame =
+      [&]() -> content::RenderFrameHost* {
+    EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
+    EXPECT_FALSE(GetWebContents()->GetPrimaryMainFrame()->IsErrorDocument());
+    EXPECT_EQ(GetWebContents()->GetLastCommittedURL(), main_url);
+    content::RenderFrameHost* fenced_frame =
+        fenced_frame_test_helper().CreateFencedFrame(
+            GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
+    EXPECT_NE(fenced_frame, nullptr);
+    return fenced_frame;
+  };
+
+  auto ExpectScriptBlocked = [&](content::RenderFrameHost* fenced_frame) {
+    ui_test_utils::WaitForViewVisibility(
+        browser(), VIEW_ID_CONTENT_SETTING_JAVASCRIPT, true);
+    auto* main_pscs = PageSpecificContentSettings::GetForFrame(
+        GetWebContents()->GetPrimaryMainFrame());
+    auto* ff_pscs = PageSpecificContentSettings::GetForFrame(fenced_frame);
+    // Script should have been blocked in the fenced frame (and reflected in
+    // the PSCS of the primary page as well).
+    EXPECT_TRUE(ff_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+    EXPECT_TRUE(main_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  };
+
+  auto ExpectScriptAllowed = [&](content::RenderFrameHost* fenced_frame) {
+    EXPECT_EQ(1, EvalJs(fenced_frame, "(async () => { return 1; })();"));
+    auto* ff_pscs = PageSpecificContentSettings::GetForFrame(fenced_frame);
+    EXPECT_FALSE(ff_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  };
+
   // Block script in (a.test, b.test).
   auto* map =
       HostContentSettingsMapFactory::GetForProfile(browser()->profile());
@@ -1729,52 +1759,25 @@
       ContentSettingsPattern::FromURL(main_url),
       ContentSettingsPattern::FromURL(fenced_frame_url),
       ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_BLOCK);
-
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));
-  ASSERT_FALSE(GetWebContents()->GetPrimaryMainFrame()->IsErrorDocument());
-  ASSERT_EQ(GetWebContents()->GetLastCommittedURL(), main_url);
-  auto* main_pscs = PageSpecificContentSettings::GetForFrame(
-      GetWebContents()->GetPrimaryMainFrame());
-
   content::RenderFrameHost* fenced_frame =
-      fenced_frame_test_helper().CreateFencedFrame(
-          GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
-  ASSERT_NE(fenced_frame, nullptr);
-  auto* ff_pscs = PageSpecificContentSettings::GetForFrame(fenced_frame);
-
-  // Script should have been blocked in the fenced frame (and reflected in the
-  // PSCS of the primary page as well).
-  EXPECT_TRUE(ff_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
-  EXPECT_TRUE(main_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+      NavigatePrimaryPageAndAddFencedFrame();
+  ExpectScriptBlocked(fenced_frame);
 
   // Allow script in (a.test, b.test).
   map->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURL(main_url),
       ContentSettingsPattern::FromURL(fenced_frame_url),
       ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_ALLOW);
-  content::RenderFrameHost* fenced_frame_two =
-      fenced_frame_test_helper().CreateFencedFrame(
-          GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
-  ASSERT_NE(fenced_frame_two, nullptr);
-  auto* ff_two_pscs =
-      PageSpecificContentSettings::GetForFrame(fenced_frame_two);
-  // Script should not have been blocked in newly added fenced frame.
-  EXPECT_FALSE(ff_two_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
+  ExpectScriptAllowed(fenced_frame);
 
   // Block script in (c.test, b.test).
   map->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURL(other_main_url),
       ContentSettingsPattern::FromURL(fenced_frame_url),
       ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_BLOCK);
-  content::RenderFrameHost* fenced_frame_three =
-      fenced_frame_test_helper().CreateFencedFrame(
-          GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
-  ASSERT_NE(fenced_frame_three, nullptr);
-  auto* ff_three_pscs =
-      PageSpecificContentSettings::GetForFrame(fenced_frame_three);
-  // Script should not have been blocked in newly added fenced frame.
-  EXPECT_FALSE(
-      ff_three_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
+  ExpectScriptAllowed(fenced_frame);
 
   // Block script in (*, b.test) - this should not have any effect as the
   // (a.test, b.test) rule is a narrower rule and should have precedence.
@@ -1782,14 +1785,8 @@
       ContentSettingsPattern::Wildcard(),
       ContentSettingsPattern::FromURL(fenced_frame_url),
       ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_BLOCK);
-  content::RenderFrameHost* fenced_frame_four =
-      fenced_frame_test_helper().CreateFencedFrame(
-          GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
-  ASSERT_NE(fenced_frame_four, nullptr);
-  auto* ff_four_pscs =
-      PageSpecificContentSettings::GetForFrame(fenced_frame_four);
-  // Script should not have been blocked in newly added fenced frame.
-  EXPECT_FALSE(ff_four_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
+  ExpectScriptAllowed(fenced_frame);
 
   // Remove (a.test, b.test) rule - (*, b.test) rule should now be the narrowest
   // and will be applied.
@@ -1797,14 +1794,8 @@
       ContentSettingsPattern::FromURL(main_url),
       ContentSettingsPattern::FromURL(fenced_frame_url),
       ContentSettingsType::JAVASCRIPT, ContentSetting::CONTENT_SETTING_DEFAULT);
-  content::RenderFrameHost* fenced_frame_five =
-      fenced_frame_test_helper().CreateFencedFrame(
-          GetWebContents()->GetPrimaryMainFrame(), fenced_frame_url);
-  ASSERT_NE(fenced_frame_five, nullptr);
-  auto* ff_five_pscs =
-      PageSpecificContentSettings::GetForFrame(fenced_frame_five);
-  // Script should have been blocked in newly added fenced frame.
-  EXPECT_TRUE(ff_five_pscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  fenced_frame = NavigatePrimaryPageAndAddFencedFrame();
+  ExpectScriptBlocked(fenced_frame);
 }
 
 class ContentSettingsWorkerModulesWithFencedFrameBrowserTest
diff --git a/chrome/browser/extensions/api/preference/preference_api.cc b/chrome/browser/extensions/api/preference/preference_api.cc
index e28d34c9..3917895c 100644
--- a/chrome/browser/extensions/api/preference/preference_api.cc
+++ b/chrome/browser/extensions/api/preference/preference_api.cc
@@ -462,6 +462,21 @@
     : profile_(profile) {
   registrar_.Init(profile_->GetPrefs());
   for (const auto& pref : kPrefMapping) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    crosapi::mojom::PrefPath pref_path =
+        PrefMapping::GetInstance()->GetPrefPathForPrefName(pref.browser_pref);
+    if (pref_path != crosapi::mojom::PrefPath::kUnknown) {
+      // Extension-controlled pref with the real value to watch in ash.
+      // This base::Unretained() is safe because PreferenceEventRouter owns
+      // the corresponding observer.
+      extension_pref_observers_.push_back(std::make_unique<CrosapiPrefObserver>(
+          pref_path,
+          base::BindRepeating(&PreferenceEventRouter::OnAshPrefChanged,
+                              base::Unretained(this), pref_path,
+                              pref.extension_pref, pref.browser_pref)));
+      continue;
+    }
+#endif
     registrar_.Add(
         pref.browser_pref,
         base::BindRepeating(&PreferenceEventRouter::OnPrefChanged,
@@ -478,6 +493,64 @@
 
 PreferenceEventRouter::~PreferenceEventRouter() = default;
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+void PreferenceEventRouter::OnAshPrefChanged(crosapi::mojom::PrefPath pref_path,
+                                             const std::string& extension_pref,
+                                             const std::string& browser_pref,
+                                             base::Value value) {
+  // This pref should be read from ash.
+  // We can only get here via callback from ash. So there should be a
+  // LacrosService.
+  auto* lacros_service = chromeos::LacrosService::Get();
+  DCHECK(lacros_service);
+
+  // It's not sufficient to have the new state of the pref - we also need
+  // information about what just set it. So call Ash again to get information
+  // about the control state.
+  lacros_service->GetRemote<crosapi::mojom::Prefs>()
+      ->GetExtensionPrefWithControl(
+          pref_path, base::BindOnce(&PreferenceEventRouter::OnAshGetSuccess,
+                                    weak_factory_.GetWeakPtr(), browser_pref));
+}
+
+void PreferenceEventRouter::OnAshGetSuccess(
+    const std::string& browser_pref,
+    absl::optional<::base::Value> opt_value,
+    crosapi::mojom::PrefControlState control_state) {
+  bool incognito = false;
+
+  std::string event_name;
+  APIPermissionID permission = APIPermissionID::kInvalid;
+  bool found_event = PrefMapping::GetInstance()->FindEventForBrowserPref(
+      browser_pref, &event_name, &permission);
+  DCHECK(found_event);
+
+  base::ListValue args;
+  PrefTransformerInterface* transformer =
+      PrefMapping::GetInstance()->FindTransformerForBrowserPref(browser_pref);
+
+  base::Value* pref_value = &opt_value.value();
+  std::unique_ptr<base::Value> transformed_value =
+      transformer->BrowserToExtensionPref(pref_value, incognito);
+  if (!transformed_value) {
+    LOG(ERROR) << ErrorUtils::FormatErrorMessage(kConversionErrorMessage,
+                                                 browser_pref);
+    return;
+  }
+
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey(extensions::preference_api_constants::kValue,
+              std::move(*transformed_value));
+  args.Append(std::move(dict));
+
+  events::HistogramValue histogram_value =
+      events::TYPES_CHROME_SETTING_ON_CHANGE;
+  extensions::preference_helpers::DispatchEventToExtensionsWithAshControlState(
+      profile_, histogram_value, event_name, &args, permission, incognito,
+      browser_pref, control_state);
+}
+#endif
+
 void PreferenceEventRouter::OnPrefChanged(PrefService* pref_service,
                                           const std::string& browser_pref) {
   bool incognito = (pref_service != profile_->GetPrefs());
@@ -861,19 +934,10 @@
   ::base::Value* pref_value = &opt_value.value();
 
   std::string level_of_control;
-  switch (control_state) {
-    case crosapi::mojom::PrefControlState::kNotExtensionControllable:
-      level_of_control = preference_helpers::kNotControllable;
-      break;
-    case crosapi::mojom::PrefControlState::kLacrosExtensionControllable:
-      level_of_control = preference_helpers::kControllableByThisExtension;
-      break;
-    case crosapi::mojom::PrefControlState::kLacrosExtensionControlled:
-    default:
-      level_of_control = extensions::preference_helpers::GetLevelOfControl(
-          profile, extension_id(), cached_browser_pref_, incognito);
-      break;
-  }
+  level_of_control =
+      extensions::preference_helpers::GetLevelOfControlWithAshControlState(
+          control_state, profile, extension_id(), cached_browser_pref_,
+          incognito);
 
   base::Value result(base::Value::Type::DICTIONARY);
 
diff --git a/chrome/browser/extensions/api/preference/preference_api.h b/chrome/browser/extensions/api/preference/preference_api.h
index aad13528..5c0ce98 100644
--- a/chrome/browser/extensions/api/preference/preference_api.h
+++ b/chrome/browser/extensions/api/preference/preference_api.h
@@ -23,7 +23,9 @@
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chromeos/crosapi/mojom/prefs.mojom-shared.h"
 #include "chromeos/crosapi/mojom/prefs.mojom.h"
+#include "chromeos/lacros/crosapi_pref_observer.h"
 #include "chromeos/lacros/lacros_service.h"
+#include "components/prefs/pref_service.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #endif
 
@@ -59,11 +61,33 @@
   PrefChangeRegistrar registrar_;
   std::unique_ptr<PrefChangeRegistrar> incognito_registrar_;
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // Callback for extension-controlled prefs where the underlying pref lives
+  // in ash. An event fires when the value of the pref in ash changes.
+  void OnAshPrefChanged(crosapi::mojom::PrefPath pref_path,
+                        const std::string& extension_pref,
+                        const std::string& browser_pref,
+                        base::Value value);
+
+  // Second callback to return additional detail about the extension-controlled
+  // pref.
+  void OnAshGetSuccess(const std::string& browser_pref,
+                       absl::optional<::base::Value> opt_value,
+                       crosapi::mojom::PrefControlState control_state);
+
+  std::vector<std::unique_ptr<crosapi::mojom::PrefObserver>>
+      extension_pref_observers_;
+#endif
+
   // Weak, owns us (transitively via ExtensionService).
   raw_ptr<Profile> profile_;
 
   base::ScopedMultiSourceObservation<Profile, ProfileObserver>
       observed_profiles_{this};
+
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  base::WeakPtrFactory<PreferenceEventRouter> weak_factory_{this};
+#endif
 };
 
 // The class containing the implementation for extension-controlled preference
diff --git a/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc b/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
index 18fad2f..667d229b 100644
--- a/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
+++ b/chrome/browser/extensions/api/preference/preference_api_lacros_browsertest.cc
@@ -14,6 +14,7 @@
 #include "chrome/common/pref_names.h"
 #include "chromeos/crosapi/mojom/prefs.mojom-test-utils.h"
 #include "chromeos/lacros/lacros_service.h"
+#include "chromeos/lacros/lacros_test_helper.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/prefs/pref_service.h"
@@ -147,3 +148,16 @@
   }
   CheckPreferencesCleared();
 }
+
+IN_PROC_BROWSER_TEST_P(ExtensionPreferenceApiLacrosBrowserTest, OnChange) {
+  if (!IsServiceAvailable()) {
+    return;
+  }
+  if (!chromeos::IsAshVersionAtLeastForTesting(base::Version({104, 0, 5109}))) {
+    LOG(WARNING) << "Ash is too old, skipping the test.";
+    return;
+  }
+  EXPECT_TRUE(RunExtensionTest("preference/onchange_lacros", {},
+                               {.allow_in_incognito = false}))
+      << message_;
+}
diff --git a/chrome/browser/extensions/api/preference/preference_helpers.cc b/chrome/browser/extensions/api/preference/preference_helpers.cc
index 526b060..2e311fc 100644
--- a/chrome/browser/extensions/api/preference/preference_helpers.cc
+++ b/chrome/browser/extensions/api/preference/preference_helpers.cc
@@ -34,6 +34,12 @@
 
 }  // namespace
 
+using LevelOfControlGetter =
+    base::RepeatingCallback<const char*(Profile*,
+                                        const std::string& extension_id,
+                                        const std::string& browser_pref,
+                                        bool incognito)>;
+
 bool StringToScope(const std::string& s,
                    ExtensionPrefsScope* scope) {
   if (s == kRegular)
@@ -89,13 +95,14 @@
   return kControlledByOtherExtensions;
 }
 
-void DispatchEventToExtensions(Profile* profile,
-                               events::HistogramValue histogram_value,
-                               const std::string& event_name,
-                               base::ListValue* args,
-                               mojom::APIPermissionID permission,
-                               bool incognito,
-                               const std::string& browser_pref) {
+void DispatchEventToExtensionsImpl(Profile* profile,
+                                   events::HistogramValue histogram_value,
+                                   const std::string& event_name,
+                                   base::ListValue* args,
+                                   mojom::APIPermissionID permission,
+                                   bool incognito,
+                                   const std::string& browser_pref,
+                                   const LevelOfControlGetter level_getter) {
   EventRouter* router = EventRouter::Get(profile);
   if (!router || !router->HasEventListener(event_name))
     return;
@@ -112,7 +119,8 @@
       DCHECK(args_list[0].is_dict());
 
       std::string level_of_control =
-          GetLevelOfControl(profile, extension->id(), browser_pref, incognito);
+          level_getter.Run(profile, extension->id(), browser_pref, incognito);
+
       args_list[0].SetStringKey(kLevelOfControlKey, level_of_control);
 
       // If the extension is in incognito split mode,
@@ -152,5 +160,53 @@
   }
 }
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+void DispatchEventToExtensionsWithAshControlState(
+    Profile* profile,
+    events::HistogramValue histogram_value,
+    const std::string& event_name,
+    base::ListValue* args,
+    mojom::APIPermissionID permission,
+    bool incognito,
+    const std::string& browser_pref,
+    crosapi::mojom::PrefControlState control_state) {
+  DispatchEventToExtensionsImpl(
+      profile, histogram_value, event_name, args, permission, incognito,
+      browser_pref,
+      base::BindRepeating(&GetLevelOfControlWithAshControlState,
+                          control_state));
+}
+
+const char* GetLevelOfControlWithAshControlState(
+    crosapi::mojom::PrefControlState control_state,
+    Profile* profile,
+    const std::string& extension_id,
+    const std::string& browser_pref,
+    bool incognito) {
+  switch (control_state) {
+    case crosapi::mojom::PrefControlState::kNotExtensionControllable:
+      return preference_helpers::kNotControllable;
+    case crosapi::mojom::PrefControlState::kLacrosExtensionControllable:
+      return preference_helpers::kControllableByThisExtension;
+    case crosapi::mojom::PrefControlState::kLacrosExtensionControlled:
+    case crosapi::mojom::PrefControlState::kNotExtensionControlledPrefPath:
+    case crosapi::mojom::PrefControlState::kDefaultUnknown:
+      return extensions::preference_helpers::GetLevelOfControl(
+          profile, extension_id, browser_pref, incognito);
+  }
+}  // BUILDFLAG(IS_CHROMEOS_LACROS)
+#endif
+
+void DispatchEventToExtensions(Profile* profile,
+                               events::HistogramValue histogram_value,
+                               const std::string& event_name,
+                               base::ListValue* args,
+                               mojom::APIPermissionID permission,
+                               bool incognito,
+                               const std::string& browser_pref) {
+  DispatchEventToExtensionsImpl(profile, histogram_value, event_name, args,
+                                permission, incognito, browser_pref,
+                                base::BindRepeating(GetLevelOfControl));
+}
 }  // namespace preference_helpers
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/preference/preference_helpers.h b/chrome/browser/extensions/api/preference/preference_helpers.h
index 202b7926..94f2ade4 100644
--- a/chrome/browser/extensions/api/preference/preference_helpers.h
+++ b/chrome/browser/extensions/api/preference/preference_helpers.h
@@ -7,11 +7,17 @@
 
 #include <string>
 
+#include "build/chromeos_buildflags.h"
 #include "extensions/browser/extension_event_histogram_value.h"
 #include "extensions/browser/extension_prefs_scope.h"
 #include "extensions/common/mojom/api_permission_id.mojom-shared.h"
 #include "extensions/common/permissions/permission_set.h"
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#include "chromeos/crosapi/mojom/prefs.mojom-shared.h"
+#include "chromeos/crosapi/mojom/prefs.mojom.h"
+#endif
+
 class PrefService;
 class Profile;
 
@@ -40,6 +46,30 @@
     const std::string& browser_pref,
     bool incognito);
 
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+// As GetLevelOfControl, but additionally considers the ash state of the pref.
+// This is relevant when the underlying preference lives in ash. Note that when
+// the ash state is kDefaultUnknown this is equivalent to GetLevelOfControl.
+const char* GetLevelOfControlWithAshControlState(
+    crosapi::mojom::PrefControlState control_state,
+    Profile* profile,
+    const std::string& extension_id,
+    const std::string& browser_pref,
+    bool incognito);
+
+// As DispatchEventToExtensions, but additionally considers the ash state of the
+// pref.
+void DispatchEventToExtensionsWithAshControlState(
+    Profile* profile,
+    events::HistogramValue histogram_value,
+    const std::string& event_name,
+    base::ListValue* args,
+    mojom::APIPermissionID permission,
+    bool incognito,
+    const std::string& browser_pref,
+    crosapi::mojom::PrefControlState control_state);
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
 // Dispatches |event_name| to extensions listening to the event and holding
 // |permission|. |args| is passed as arguments to the event listener.  A
 // key-value for the level of control the extension has over |browser_pref| is
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index c598e75..9d57820 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -796,6 +796,8 @@
           settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_allowlist)[::ash::prefs::kPowerQuickDimEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
+  (*s_allowlist)[ash::prefs::kUserCameraAllowed] =
+      settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
 #else
   // System settings.
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 15fe0491..c246cba 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4973,8 +4973,7 @@
 
 const char kFilesArchivemount2Name[] = "Archivemount in Files App (2nd Tier)";
 const char kFilesArchivemount2Description[] =
-    "Enable mounting additional archive formats in File Manager. This has no "
-    "effect unless #files-archivemount is also enabled.";
+    "Enable mounting additional archive formats in File Manager.";
 
 const char kFilesExtractArchiveName[] = "Extract archive in Files app";
 const char kFilesExtractArchiveDescription[] =
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 74c5fe0..60f80543 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -816,7 +816,7 @@
 
 const base::Feature kTrustedWebActivityNotificationPermissionDelegation{
     "TrustedWebActivityNotificationPermissionDelegation",
-    base::FEATURE_DISABLED_BY_DEFAULT};
+    base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kTrustedWebActivityPostMessage{
     "TrustedWebActivityPostMessage", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
index 9dbecb0..3c78492 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -99,7 +99,7 @@
                     .put(ChromeFeatureList.TAB_STRIP_IMPROVEMENTS, false)
                     .put(ChromeFeatureList.BACK_GESTURE_REFACTOR, false)
                     .put(ChromeFeatureList.TRUSTED_WEB_ACTIVITY_NOTIFICATION_PERMISSION_DELEGATION,
-                            false)
+                            true)
                     .put(ChromeFeatureList.CREATE_SAFEBROWSING_ON_STARTUP, false)
                     .build();
 
diff --git a/chrome/browser/media/webrtc/conditional_focus_browsertest.cc b/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
index a3345a38..cc4f289 100644
--- a/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
+++ b/chrome/browser/media/webrtc/conditional_focus_browsertest.cc
@@ -276,7 +276,7 @@
   // Test.
   CallFocusAndExpectError(
       "InvalidStateError: Failed to execute 'focus' on "
-      "'FocusableMediaStreamTrack': Method may only be called once.");
+      "'BrowserCaptureMediaStreamTrack': Method may only be called once.");
 }
 
 IN_PROC_BROWSER_TEST_F(ConditionalFocusBrowserTest,
@@ -291,7 +291,7 @@
   EXPECT_EQ(
       script_result,
       "InvalidStateError: Failed to execute 'focus' on "
-      "'FocusableMediaStreamTrack': Method may not be invoked on clones.");
+      "'BrowserCaptureMediaStreamTrack': Method may not be invoked on clones.");
 }
 
 IN_PROC_BROWSER_TEST_F(ConditionalFocusBrowserTest,
@@ -302,8 +302,8 @@
           /*on_correct_microtask=*/false,
           /*expected_result=*/
           "InvalidStateError: Failed to execute 'focus' on "
-          "'FocusableMediaStreamTrack': The microtask on which the Promise was "
-          "settled has terminated.");
+          "'BrowserCaptureMediaStreamTrack': The microtask on which the "
+          "Promise was settled has terminated.");
 }
 
 #endif  //  !BUILDFLAG(IS_CHROMEOS_LACROS)
diff --git a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
index e36683c..48e753b 100644
--- a/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_getdisplaymedia_browsertest.cc
@@ -754,18 +754,30 @@
     WebRtcTestBase::SetUpCommandLine(command_line);
 
     std::vector<std::string> enabled_blink_features;
+    std::vector<std::string> disabled_blink_features;
+
     if (conditional_focus_enabled_) {
       enabled_blink_features.push_back("ConditionalFocus");
     }
+
     if (region_capture_enabled_) {
       enabled_blink_features.push_back("RegionCapture");
+    } else {
+      disabled_blink_features.push_back("RegionCapture");
     }
+
     if (!enabled_blink_features.empty()) {
       command_line->AppendSwitchASCII(
           switches::kEnableBlinkFeatures,
           base::JoinString(enabled_blink_features, ","));
     }
 
+    if (!disabled_blink_features.empty()) {
+      command_line->AppendSwitchASCII(
+          switches::kDisableBlinkFeatures,
+          base::JoinString(disabled_blink_features, ","));
+    }
+
     command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
     command_line->AppendSwitchASCII(
         switches::kUseFakeDeviceForMediaStream,
@@ -810,9 +822,8 @@
                    : conditional_focus_enabled_ ? "FocusableMediaStreamTrack"
                                                 : "MediaStreamTrack";
       case DisplaySurfaceType::kWindow:
-        return conditional_focus_enabled_ || region_capture_enabled_
-                   ? "FocusableMediaStreamTrack"
-                   : "MediaStreamTrack";
+        return conditional_focus_enabled_ ? "FocusableMediaStreamTrack"
+                                          : "MediaStreamTrack";
       case DisplaySurfaceType::kScreen:
         return "MediaStreamTrack";
     }
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn
index ed76813..221616e 100644
--- a/chrome/browser/password_manager/android/BUILD.gn
+++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -181,6 +181,7 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//chrome/android:chrome_java",
     "//chrome/browser/flags:java",
     "//chrome/browser/loading_modal/android:java",
     "//chrome/browser/profiles/android:java",
diff --git a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
index 9b4a5df..c8165a2 100644
--- a/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
+++ b/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/PasswordManagerHelperTest.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.password_manager;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
@@ -11,18 +14,20 @@
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.os.Bundle;
 
 import com.google.android.gms.common.api.ApiException;
 import com.google.android.gms.common.api.CommonStatusCodes;
 import com.google.android.gms.common.api.Status;
 import com.google.common.base.Optional;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -47,12 +52,15 @@
 import org.chromium.chrome.browser.password_manager.CredentialManagerLauncher.CredentialManagerError;
 import org.chromium.chrome.browser.password_manager.PasswordCheckupClientHelper.PasswordCheckBackendException;
 import org.chromium.chrome.browser.password_manager.PasswordManagerHelper.PasswordCheckOperation;
+import org.chromium.chrome.browser.password_manager.settings.PasswordSettings;
 import org.chromium.chrome.browser.sync.SyncService;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.base.GoogleServiceAuthError;
+import org.chromium.components.signin.base.GoogleServiceAuthError.State;
 import org.chromium.components.sync.ModelType;
 import org.chromium.ui.modaldialog.ModalDialogManager;
 
@@ -143,15 +151,15 @@
     @Test
     public void testSyncCheckFeatureNotEnabled() {
         when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(false);
-        Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
-        Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
+        assertFalse(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
+        assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
                 mSyncServiceMock));
     }
 
     @Test
     public void testSyncCheckNoSyncConsent() {
         when(mSyncServiceMock.hasSyncConsent()).thenReturn(false);
-        Assert.assertFalse(
+        assertFalse(
                 PasswordManagerHelper.isSyncingPasswordsWithNoCustomPassphrase(mSyncServiceMock));
     }
 
@@ -159,8 +167,8 @@
     public void testSyncPasswordsDisabled() {
         when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
         when(mSyncServiceMock.getChosenDataTypes()).thenReturn(Collections.EMPTY_SET);
-        Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
-        Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
+        assertFalse(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
+        assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
                 mSyncServiceMock));
     }
 
@@ -169,7 +177,7 @@
         when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
         when(mSyncServiceMock.getChosenDataTypes())
                 .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
-        Assert.assertTrue(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
+        assertTrue(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
     }
 
     @Test
@@ -179,8 +187,8 @@
                 .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
         when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
         when(mSyncServiceMock.isUsingExplicitPassphrase()).thenReturn(true);
-        Assert.assertTrue(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
-        Assert.assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
+        assertTrue(PasswordManagerHelper.hasChosenToSyncPasswords(mSyncServiceMock));
+        assertFalse(PasswordManagerHelper.hasChosenToSyncPasswordsWithNoCustomPassphrase(
                 mSyncServiceMock));
     }
 
@@ -191,7 +199,7 @@
                 .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
         when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
         when(mSyncServiceMock.isUsingExplicitPassphrase()).thenReturn(false);
-        Assert.assertTrue(
+        assertTrue(
                 PasswordManagerHelper.isSyncingPasswordsWithNoCustomPassphrase(mSyncServiceMock));
     }
 
@@ -202,12 +210,72 @@
                 .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
         when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
         when(mSyncServiceMock.isUsingExplicitPassphrase()).thenReturn(true);
-        Assert.assertFalse(
+        assertFalse(
                 PasswordManagerHelper.isSyncingPasswordsWithNoCustomPassphrase(mSyncServiceMock));
     }
 
     @Test
     @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testCanUseUpmCheckup() {
+        SyncService.overrideForTests(mSyncServiceMock);
+        when(mSyncServiceMock.getChosenDataTypes())
+                .thenReturn(CollectionUtil.newHashSet(ModelType.PASSWORDS));
+        when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
+        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
+        when(mSyncServiceMock.hasSyncConsent()).thenReturn(true);
+
+        assertTrue(PasswordManagerHelper.canUseUpmCheckup());
+        SyncService.resetForTests();
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testCanNotUseUpmCheckupWithoutPasswordType() {
+        SyncService.overrideForTests(mSyncServiceMock);
+        when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(true);
+        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
+        when(mSyncServiceMock.hasSyncConsent()).thenReturn(true);
+
+        assertFalse(PasswordManagerHelper.canUseUpmCheckup());
+        SyncService.resetForTests();
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testCanNotUseUpmCheckupWithoutSyncService() {
+        SyncService.overrideForTests(mSyncServiceMock);
+        when(mSyncServiceMock.isEngineInitialized()).thenReturn(false);
+        when(mSyncServiceMock.hasSyncConsent()).thenReturn(true);
+
+        assertFalse(PasswordManagerHelper.canUseUpmCheckup());
+        SyncService.resetForTests();
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testCanNotUseUpmCheckupWithoutSyncConsent() {
+        SyncService.overrideForTests(mSyncServiceMock);
+        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
+        when(mSyncServiceMock.hasSyncConsent()).thenReturn(false);
+
+        assertFalse(PasswordManagerHelper.canUseUpmCheckup());
+        SyncService.resetForTests();
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testCanNotUseUpmCheckupWithAuthError() {
+        SyncService.overrideForTests(mSyncServiceMock);
+        when(mSyncServiceMock.isEngineInitialized()).thenReturn(true);
+        when(mSyncServiceMock.hasSyncConsent()).thenReturn(true);
+        when(mSyncServiceMock.getAuthError()).thenReturn(State.INVALID_GAIA_CREDENTIALS);
+
+        assertFalse(PasswordManagerHelper.canUseUpmCheckup());
+        SyncService.resetForTests();
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
     public void testLaunchesCredentialManagerSync() {
         chooseToSyncPasswordsWithoutCustomPassphrase();
 
@@ -222,6 +290,22 @@
 
     @Test
     @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testShowPasswordSettingsNoSyncLaunchesOldUI() {
+        when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(false);
+        Context mockContext = mock(Context.class);
+
+        PasswordManagerHelper.showPasswordSettings(mockContext,
+                ManagePasswordsReferrer.CHROME_SETTINGS, mSettingsLauncherMock,
+                mCredentialManagerLauncherMock, mSyncServiceMock, mModalDialogManagerSupplier);
+
+        verify(mockContext).startActivity(any());
+        verify(mSettingsLauncherMock)
+                .createSettingsActivityIntent(
+                        eq(mockContext), eq(PasswordSettings.class.getName()), any(Bundle.class));
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
     public void testRecordsSuccessMetricsForAccountIntent() {
         chooseToSyncPasswordsWithoutCustomPassphrase();
         setUpSuccessfulIntentFetchingForAccount();
@@ -229,16 +313,16 @@
         PasswordManagerHelper.showPasswordSettings(ContextUtils.getApplicationContext(),
                 ManagePasswordsReferrer.CHROME_SETTINGS, mSettingsLauncherMock,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mModalDialogManagerSupplier);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_GET_INTENT_LATENCY_HISTOGRAM, 0));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_GET_INTENT_SUCCESS_HISTOGRAM, 1));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         ACCOUNT_GET_INTENT_ERROR_HISTOGRAM));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM, 1));
     }
@@ -253,16 +337,16 @@
                 ManagePasswordsReferrer.CHROME_SETTINGS, mSettingsLauncherMock,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mModalDialogManagerSupplier);
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_GET_INTENT_ERROR_HISTOGRAM, CredentialManagerError.API_ERROR));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_GET_INTENT_SUCCESS_HISTOGRAM, 0));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         ACCOUNT_GET_INTENT_LATENCY_HISTOGRAM));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         ACCOUNT_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM));
     }
@@ -278,21 +362,38 @@
                 ManagePasswordsReferrer.CHROME_SETTINGS, mSettingsLauncherMock,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mModalDialogManagerSupplier);
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_GET_INTENT_LATENCY_HISTOGRAM, 0));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_GET_INTENT_SUCCESS_HISTOGRAM, 1));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         ACCOUNT_GET_INTENT_ERROR_HISTOGRAM));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         ACCOUNT_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM, 0));
     }
 
     @Test
+    @DisableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
+    public void testNoSyncShowPasswordCheckupImmediatelyReturns() {
+        SyncService.overrideForTests(mSyncServiceMock);
+        when(mSyncServiceMock.isSyncFeatureEnabled()).thenReturn(false);
+        when(mSyncServiceMock.hasSyncConsent()).thenReturn(false);
+        setUpSuccessfulIntentFetchingForAccount();
+
+        PasswordManagerHelper.showPasswordCheckup(ContextUtils.getApplicationContext(),
+                PasswordCheckReferrer.SAFETY_CHECK, mPasswordCheckupClientHelperMock,
+                mSyncServiceMock, mModalDialogManagerSupplier);
+
+        verify(mCredentialManagerLauncherMock, times(0))
+                .getCredentialManagerIntentForAccount(eq(ManagePasswordsReferrer.CHROME_SETTINGS),
+                        eq(TEST_EMAIL_ADDRESS), any(Callback.class), any(Callback.class));
+    }
+
+    @Test
     @EnableFeatures(ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID)
     public void testLaunchesPasswordCheckupSync() {
         chooseToSyncPasswordsWithoutCustomPassphrase();
@@ -342,7 +443,7 @@
                 mSyncServiceMock, mModalDialogManagerSupplier);
         checkPasswordCheckupSuccessHistogramsForOperation(
                 PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM, 1));
     }
@@ -361,7 +462,7 @@
         checkPasswordCheckupFailureHistogramsForOperation(
                 PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT,
                 CredentialManagerError.UNCATEGORIZED, OptionalInt.empty());
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM));
     }
@@ -381,7 +482,7 @@
                 CredentialManagerError.API_ERROR,
                 OptionalInt.of(CommonStatusCodes.DEVELOPER_ERROR));
 
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM));
     }
@@ -485,7 +586,7 @@
                 mSyncServiceMock, mModalDialogManagerSupplier);
         checkPasswordCheckupSuccessHistogramsForOperation(
                 PasswordCheckOperation.GET_PASSWORD_CHECKUP_INTENT);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         PASSWORD_CHECKUP_LAUNCH_CREDENTIAL_MANAGER_SUCCESS_HISTOGRAM, 0));
     }
@@ -706,7 +807,7 @@
 
         verify(mLoadingModalDialogCoordinator).dismiss();
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.NOT_SHOWN_LOADED));
@@ -725,7 +826,7 @@
         PasswordManagerHelper.launchTheCredentialManager(ManagePasswordsReferrer.CHROME_SETTINGS,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_LOADED));
@@ -747,12 +848,12 @@
         PasswordManagerHelper.launchTheCredentialManager(ManagePasswordsReferrer.CHROME_SETTINGS,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM));
 
         mLoadingDialogCoordinatorObserver.onDismissable();
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_LOADED));
@@ -773,7 +874,7 @@
         PasswordManagerHelper.launchTheCredentialManager(ManagePasswordsReferrer.CHROME_SETTINGS,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_CANCELLED));
@@ -791,7 +892,7 @@
         PasswordManagerHelper.launchTheCredentialManager(ManagePasswordsReferrer.CHROME_SETTINGS,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM));
 
@@ -799,7 +900,7 @@
                 .thenReturn(LoadingModalDialogCoordinator.State.CANCELLED);
         mLoadingDialogCoordinatorObserver.onDismissedWithState(
                 LoadingModalDialogCoordinator.State.CANCELLED);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_CANCELLED));
@@ -816,7 +917,7 @@
         PasswordManagerHelper.launchTheCredentialManager(ManagePasswordsReferrer.CHROME_SETTINGS,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_TIMED_OUT));
@@ -834,7 +935,7 @@
         PasswordManagerHelper.launchTheCredentialManager(ManagePasswordsReferrer.CHROME_SETTINGS,
                 mCredentialManagerLauncherMock, mSyncServiceMock, mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM));
 
@@ -842,7 +943,7 @@
                 .thenReturn(LoadingModalDialogCoordinator.State.TIMED_OUT);
         mLoadingDialogCoordinatorObserver.onDismissedWithState(
                 LoadingModalDialogCoordinator.State.TIMED_OUT);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_TIMED_OUT));
@@ -862,7 +963,7 @@
 
         verify(mLoadingModalDialogCoordinator).dismiss();
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.NOT_SHOWN_LOADED));
@@ -883,7 +984,7 @@
 
         verify(mLoadingModalDialogCoordinator).dismiss();
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_CREDENTIAL_MANAGER_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.NOT_SHOWN_LOADED));
@@ -903,7 +1004,7 @@
 
         verify(mLoadingModalDialogCoordinator).dismiss();
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.NOT_SHOWN_LOADED));
@@ -925,7 +1026,7 @@
         ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
         verify(mLoadingModalDialogCoordinator).dismiss();
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_LOADED));
@@ -945,12 +1046,12 @@
                 mPasswordCheckupClientHelperMock, Optional.of(TEST_EMAIL_ADDRESS),
                 mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM));
 
         mLoadingDialogCoordinatorObserver.onDismissable();
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_LOADED));
@@ -971,7 +1072,7 @@
                 mPasswordCheckupClientHelperMock, Optional.of(TEST_EMAIL_ADDRESS),
                 mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_CANCELLED));
@@ -990,7 +1091,7 @@
                 mPasswordCheckupClientHelperMock, Optional.of(TEST_EMAIL_ADDRESS),
                 mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM));
 
@@ -998,7 +1099,7 @@
                 .thenReturn(LoadingModalDialogCoordinator.State.CANCELLED);
         mLoadingDialogCoordinatorObserver.onDismissedWithState(
                 LoadingModalDialogCoordinator.State.CANCELLED);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_CANCELLED));
@@ -1016,7 +1117,7 @@
                 mPasswordCheckupClientHelperMock, Optional.of(TEST_EMAIL_ADDRESS),
                 mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_TIMED_OUT));
@@ -1035,7 +1136,7 @@
                 mPasswordCheckupClientHelperMock, Optional.of(TEST_EMAIL_ADDRESS),
                 mLoadingModalDialogCoordinator);
 
-        Assert.assertEquals(0,
+        assertEquals(0,
                 RecordHistogram.getHistogramTotalCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM));
 
@@ -1043,7 +1144,7 @@
                 .thenReturn(LoadingModalDialogCoordinator.State.TIMED_OUT);
         mLoadingDialogCoordinatorObserver.onDismissedWithState(
                 LoadingModalDialogCoordinator.State.TIMED_OUT);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.SHOWN_TIMED_OUT));
@@ -1065,7 +1166,7 @@
 
         verify(mLoadingModalDialogCoordinator).dismiss();
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.NOT_SHOWN_LOADED));
@@ -1086,7 +1187,7 @@
 
         verify(mLoadingModalDialogCoordinator).dismiss();
 
-        Assert.assertEquals(1,
+        assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         LOADING_DIALOG_PASSWORD_CHECKUP_HISTOGRAM,
                         PasswordManagerHelper.LoadingDialogOutcome.NOT_SHOWN_LOADED));
@@ -1216,18 +1317,18 @@
             @PasswordCheckOperation int operation) {
         final String nameWithSuffix = PASSWORD_CHECKUP_HISTOGRAM_BASE + "."
                 + getPasswordCheckupHistogramSuffixForOperation(operation);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".Success", 1));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".Latency", 0));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 ShadowRecordHistogram.getHistogramTotalCountForTesting(
                         nameWithSuffix + ".ErrorLatency"));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 ShadowRecordHistogram.getHistogramTotalCountForTesting(nameWithSuffix + ".Error"));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 ShadowRecordHistogram.getHistogramTotalCountForTesting(
                         nameWithSuffix + ".ApiError"));
     }
@@ -1236,24 +1337,24 @@
             @PasswordCheckOperation int operation, int errorCode, OptionalInt apiErrorCode) {
         final String nameWithSuffix = PASSWORD_CHECKUP_HISTOGRAM_BASE + "."
                 + getPasswordCheckupHistogramSuffixForOperation(operation);
-        Assert.assertEquals(1,
+        assertEquals(1,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".Success", 0));
-        Assert.assertEquals(0,
+        assertEquals(0,
                 ShadowRecordHistogram.getHistogramTotalCountForTesting(
                         nameWithSuffix + ".Latency"));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".ErrorLatency", 0));
-        Assert.assertEquals(1,
+        assertEquals(1,
                 ShadowRecordHistogram.getHistogramValueCountForTesting(
                         nameWithSuffix + ".Error", errorCode));
         apiErrorCode.ifPresentOrElse(apiError
-                -> Assert.assertEquals(1,
+                -> assertEquals(1,
                         ShadowRecordHistogram.getHistogramValueCountForTesting(
                                 nameWithSuffix + ".APIError", apiError)),
                 ()
-                        -> Assert.assertEquals(0,
+                        -> assertEquals(0,
                                 ShadowRecordHistogram.getHistogramTotalCountForTesting(
                                         nameWithSuffix + ".APIError")));
     }
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
index 1b4ca5a4..2e79398c 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
@@ -230,13 +230,15 @@
 }
 
 void RecordHandlerImpl::ReportUploader::HandleSuccessfulUpload() {
+  // {{{Note}}} ERP Response Payload Overview
+  //
   //  {
   //    "lastSucceedUploadedRecord": ... // SequenceInformation proto
   //    "firstFailedUploadedRecord": {
   //      "failedUploadedRecord": ... // SequenceInformation proto
   //      "failureStatus": ... // Status proto
-  //    }
-  //    "forceConfirm": true  // if present, flag that lastSucceedUploadedRecord
+  //    },
+  //    "forceConfirm": true, // if present, flag that lastSucceedUploadedRecord
   //                          // is to be accepted unconditionally by client
   //    "encryptionSettings": ... // EncryptionSettings proto
   //  }
diff --git a/chrome/browser/policy/messaging_layer/util/test_request_payload.h b/chrome/browser/policy/messaging_layer/util/test_request_payload.h
index 6795362..e10bddb8 100644
--- a/chrome/browser/policy/messaging_layer/util/test_request_payload.h
+++ b/chrome/browser/policy/messaging_layer/util/test_request_payload.h
@@ -166,6 +166,9 @@
 // malformed for a particular test case). To use this class, call
 // CreateDataUpload() or CreateEmpty() to create an instance. Adapt matchers by
 // calling AppendMatcher() or RemoveMatcher().
+//
+// For the document of what response payload should look like, search for
+// "{{{Note}}} ERP Request Payload Overview" in the codebase.
 template <class T = base::Value::Dict>
 class RequestValidityMatcherBuilder {
  public:
diff --git a/chrome/browser/policy/messaging_layer/util/test_response_payload.h b/chrome/browser/policy/messaging_layer/util/test_response_payload.h
index 669161d..68dcd725 100644
--- a/chrome/browser/policy/messaging_layer/util/test_response_payload.h
+++ b/chrome/browser/policy/messaging_layer/util/test_response_payload.h
@@ -22,6 +22,9 @@
 //                                       .SetSuccess(false)
 //                                       .SetForceConfirm(true)
 //                                       .Build();
+//
+// For the document of what response payload should look like, search for
+// "{{{Note}}} ERP Response Payload Overview" in the codebase.
 class ResponseBuilder {
  public:
   ResponseBuilder() = default;
diff --git a/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc b/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
index 54ced27c3..cd1e534 100644
--- a/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
+++ b/chrome/browser/policy/networking/device_network_configuration_updater_ash.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chromeos/components/onc/onc_parsed_certificates.h"
 #include "chromeos/components/onc/onc_utils.h"
-#include "chromeos/components/onc/variable_expander.h"
 #include "chromeos/network/managed_network_configuration_handler.h"
 #include "chromeos/network/network_device_handler.h"
 #include "chromeos/system/statistics_provider.h"
@@ -128,12 +127,10 @@
   substitutions[::onc::substitutes::kDeviceAssetId] =
       device_asset_id_fetcher_.Run();
 
-  chromeos::VariableExpander variable_expander(std::move(substitutions));
-  chromeos::onc::ExpandStringsInNetworks(variable_expander,
-                                         network_configs_onc);
-
+  network_config_handler_->SetProfileWideVariableExpansions(
+      /*username_hash=*/std::string(), std::move(substitutions));
   network_config_handler_->SetPolicy(
-      onc_source_, std::string() /* no username hash */, *network_configs_onc,
+      onc_source_, /*username_hash=*/std::string(), *network_configs_onc,
       *global_network_config);
 }
 
diff --git a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
index 30a297e..ba12f89c 100644
--- a/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
+++ b/chrome/browser/policy/networking/network_configuration_updater_ash_unittest.cc
@@ -54,6 +54,7 @@
 using testing::_;
 using testing::AnyNumber;
 using testing::AtLeast;
+using testing::Eq;
 using testing::Mock;
 using testing::Ne;
 using testing::Return;
@@ -348,6 +349,9 @@
 
     certificate_importer_ = new FakeCertificateImporter;
     client_certificate_importer_owned_.reset(certificate_importer_);
+
+    EXPECT_CALL(network_config_handler_, SetProfileWideVariableExpansions(_, _))
+        .Times(AnyNumber());
   }
 
   base::Value* GetExpectedFakeNetworkConfigs(::onc::ONCSource source) {
@@ -356,13 +360,6 @@
     fake_network_configs_ =
         fake_toplevel_onc.FindKey(onc::toplevel_config::kNetworkConfigurations)
             ->Clone();
-    if (source == ::onc::ONC_SOURCE_DEVICE_POLICY) {
-      std::string expected_identity =
-          std::string(kFakeSerialNumber) + "-" + std::string(kFakeAssetId);
-      SetExpectedValueInNetworkConfig(
-          &fake_network_configs_, "{guid-for-wifi-with-device-exp}",
-          {"WiFi", "EAP", "Identity"}, base::Value(expected_identity));
-    }
     return &fake_network_configs_;
   }
 
@@ -449,21 +446,6 @@
   std::unique_ptr<NetworkConfigurationUpdater> network_configuration_updater_;
 
  private:
-  void SetExpectedValueInNetworkConfig(
-      base::Value* network_configs,
-      base::StringPiece guid,
-      std::initializer_list<base::StringPiece> path,
-      base::Value value) {
-    for (base::Value& network_config : network_configs->GetListDeprecated()) {
-      const base::Value* guid_value =
-          network_config.FindKey(::onc::network_config::kGUID);
-      if (!guid_value || guid_value->GetString() != guid)
-        continue;
-      network_config.SetPath(path, std::move(value));
-      break;
-    }
-  }
-
   base::Value fake_network_configs_;
   base::Value fake_global_network_config_{base::Value::Type::DICTIONARY};
   chromeos::ScopedFakeSessionManagerClient scoped_session_manager_client_;
@@ -577,6 +559,8 @@
   MarkPolicyProviderInitialized();
 
   Mock::VerifyAndClearExpectations(&network_config_handler_);
+  EXPECT_CALL(network_config_handler_, SetProfileWideVariableExpansions(_, _))
+      .Times(AnyNumber());
   EXPECT_EQ(0u, certificate_importer_->GetAndResetImportCount());
 
   certificate_importer_->SetExpectedONCClientCertificates(
@@ -588,7 +572,11 @@
   EXPECT_EQ(1u, certificate_importer_->GetAndResetImportCount());
 }
 
-TEST_F(NetworkConfigurationUpdaterAshTest, ReplaceDeviceOncPlaceholders) {
+TEST_F(NetworkConfigurationUpdaterAshTest, SetDeviceVariableExpansions) {
+  Mock::VerifyAndClearExpectations(&network_config_handler_);
+  const base::flat_map<std::string, std::string> kExpectedExpansions = {
+      {"DEVICE_ASSET_ID", kFakeAssetId},
+      {"DEVICE_SERIAL_NUMBER", kFakeSerialNumber}};
   PolicyMap policy;
   policy.Set(key::kDeviceOpenNetworkConfiguration, POLICY_LEVEL_MANDATORY,
              POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD, base::Value(kFakeONC),
@@ -597,14 +585,41 @@
 
   ::onc::ONCSource source = onc::ONC_SOURCE_DEVICE_POLICY;
   EXPECT_CALL(network_config_handler_,
-              SetPolicy(source, std::string(),
+              SetPolicy(source, /*userhash=*/std::string(),
                         IsEqualTo(GetExpectedFakeNetworkConfigs(source)),
                         IsEqualTo(GetExpectedFakeGlobalNetworkConfig())));
+  EXPECT_CALL(network_config_handler_,
+              SetProfileWideVariableExpansions(/*userhash=*/std::string(),
+                                               Eq(kExpectedExpansions)));
 
   CreateNetworkConfigurationUpdaterForDevicePolicy();
   MarkPolicyProviderInitialized();
 }
 
+TEST_F(NetworkConfigurationUpdaterAshTest, SetUserVariableExpansions) {
+  Mock::VerifyAndClearExpectations(&network_config_handler_);
+  const base::flat_map<std::string, std::string> kExpectedExpansions = {
+      {"LOGIN_EMAIL", kFakeUserEmail}, {"LOGIN_ID", kFakeUserEmail}};
+  PolicyMap policy;
+  policy.Set(key::kOpenNetworkConfiguration, POLICY_LEVEL_MANDATORY,
+             POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(kFakeONC),
+             nullptr);
+  UpdateProviderPolicy(policy);
+
+  ::onc::ONCSource source = onc::ONC_SOURCE_USER_POLICY;
+  EXPECT_CALL(network_config_handler_,
+              SetPolicy(source, kFakeUsernameHash,
+                        IsEqualTo(GetExpectedFakeNetworkConfigs(source)),
+                        IsEqualTo(GetExpectedFakeGlobalNetworkConfig())));
+  EXPECT_CALL(network_config_handler_,
+              SetProfileWideVariableExpansions(/*userhash=*/kFakeUsernameHash,
+                                               Eq(kExpectedExpansions)));
+
+  CreateNetworkConfigurationUpdaterForUserPolicy(
+      /*set_client_cert_importer=*/false);
+  MarkPolicyProviderInitialized();
+}
+
 TEST(UserNetworkConfigurationStaticsTest, TestHasWebTrustedCertsNo) {
   const char kONCWithoutWebTrustedCert[] = R"(
     { "Certificates": [
@@ -758,6 +773,8 @@
   CreateNetworkConfigurationUpdater();
 
   Mock::VerifyAndClearExpectations(&network_config_handler_);
+  EXPECT_CALL(network_config_handler_, SetProfileWideVariableExpansions(_, _))
+      .Times(AnyNumber());
   EXPECT_EQ(0u, certificate_importer_->GetAndResetImportCount());
 
   EXPECT_CALL(
@@ -804,6 +821,8 @@
   MarkPolicyProviderInitialized();
 
   Mock::VerifyAndClearExpectations(&network_config_handler_);
+  EXPECT_CALL(network_config_handler_, SetProfileWideVariableExpansions(_, _))
+      .Times(AnyNumber());
   // The certificate importer is only called if the certificates changes. An
   // empty policy does not count.
   EXPECT_EQ(0u, certificate_importer_->GetAndResetImportCount());
@@ -822,6 +841,8 @@
              POLICY_SOURCE_CLOUD, base::Value(kFakeONC), nullptr);
   UpdateProviderPolicy(policy);
   Mock::VerifyAndClearExpectations(&network_config_handler_);
+  EXPECT_CALL(network_config_handler_, SetProfileWideVariableExpansions(_, _))
+      .Times(AnyNumber());
   EXPECT_EQ(ExpectedImportCertificatesCallCount(),
             certificate_importer_->GetAndResetImportCount());
 
diff --git a/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc b/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc
index 621979cd..1fd7027 100644
--- a/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc
+++ b/chrome/browser/policy/networking/user_network_configuration_updater_ash.cc
@@ -112,6 +112,7 @@
     : UserNetworkConfigurationUpdater(policy_service),
       user_(&user),
       network_config_handler_(network_config_handler) {
+  DCHECK(user_);
   // The updater is created with |client_certificate_importer_| unset and is
   // responsible for creating it. This requires |GetNSSCertDatabaseForProfile|
   // call, which is not safe before the profile initialization is finalized.
@@ -127,6 +128,13 @@
   // initialized in tests.
   if (chromeos::NetworkCertLoader::IsInitialized())
     chromeos::NetworkCertLoader::Get()->SetUserPolicyCertificateProvider(this);
+
+  // Set profile-wide expansions for policy networks (i.e. those that apply to
+  // all networks in this profile). Note that this does currently not apply
+  // user-imported networks (through chrome://network) because those currently
+  // don't use the ManagedNetworkConfigurationHandler (b/235297258).
+  network_config_handler_->SetProfileWideVariableExpansions(
+      user.username_hash(), chromeos::onc::GetVariableExpansionsForUser(&user));
 }
 
 void UserNetworkConfigurationUpdaterAsh::ImportClientCertificates() {
@@ -145,8 +153,6 @@
     base::DictionaryValue* global_network_config) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(user_);
-  chromeos::onc::ExpandStringPlaceholdersInNetworksForUser(user_,
-                                                           network_configs_onc);
 
   // Call on UserSessionManager to send the user's password to session manager
   // if the password substitution variable exists in the ONC.
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 20ae83b..ef28a9d 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_delegate.h"
 #include "chrome/browser/ash/system_web_apps/types/system_web_app_type.h"
 #include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
+#include "chrome/browser/autofill/personal_data_manager_factory.h"
 #include "chrome/browser/browser_features.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h"
@@ -707,7 +708,12 @@
       protocol_handler_registry_(
           ProtocolHandlerRegistryFactory::GetForBrowserContext(GetProfile())),
       accessibility_labels_submenu_model_(this),
-      embedder_web_contents_(GetWebContentsToUse(source_web_contents_)) {
+      embedder_web_contents_(GetWebContentsToUse(source_web_contents_)),
+      autofill_context_menu_manager_(
+          autofill::PersonalDataManagerFactory::GetForProfile(
+              GetProfile()->GetOriginalProfile()),
+          this,
+          &menu_model_) {
   if (!g_custom_id_ranges_initialized) {
     g_custom_id_ranges_initialized = true;
     SetContentCustomCommandIdRange(IDC_CONTENT_CONTEXT_CUSTOM_FIRST,
@@ -925,7 +931,7 @@
 
   if (content_type_->SupportsGroup(
           ContextMenuContentType::ITEM_GROUP_AUTOFILL)) {
-    autofill_context_menu_manager_.AppendTopLevelItems();
+    autofill_context_menu_manager_.AppendItems();
     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
   }
 
diff --git a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
index 16c054d7..65db83a 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/consolidated_consent.js
@@ -222,6 +222,10 @@
       this.$.loadedContent.classList.remove('landscape-header-aligned');
       this.$.loadedContent.classList.add('landscape-vertical-centered');
     }
+
+    // Call updateLocalizedContent() to ensure that the listeners of the click
+    // events on the ToS links are added.
+    this.updateLocalizedContent();
   }
 
   applyOobeConfiguration_() {
diff --git a/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js b/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js
index 9260099a..b6d7cb4 100644
--- a/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js
+++ b/chrome/browser/resources/chromeos/login/screens/common/guest_tos.js
@@ -89,6 +89,10 @@
         false /* clear_anchors */);
     this.loadEulaWebview_(
         this.$.guestTosCrosEulaWebview, crosEulaUrl, true /* clear_anchors */);
+
+    // Call updateLocalizedContent() to ensure that the listeners of the click
+    // events on the ToS links are added.
+    this.updateLocalizedContent();
   }
 
   /** Initial UI State for screen */
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 3708db2..716df35 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -610,6 +610,7 @@
     "chromeos/os_printing_page/os_printing_page.js",
     "chromeos/os_privacy_page/os_privacy_page.js",
     "chromeos/os_privacy_page/peripheral_data_access_protection_dialog.js",
+    "chromeos/os_privacy_page/privacy_hub_page.js",
     "chromeos/os_privacy_page/smart_privacy_page.js",
     "chromeos/os_reset_page/os_powerwash_dialog_esim_item.js",
     "chromeos/os_reset_page/os_powerwash_dialog.js",
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.js b/chrome/browser/resources/settings/chromeos/lazy_load.js
index e4599c0a..e1e6efc 100644
--- a/chrome/browser/resources/settings/chromeos/lazy_load.js
+++ b/chrome/browser/resources/settings/chromeos/lazy_load.js
@@ -52,6 +52,7 @@
 import './os_printing_page/os_printing_page.js';
 import './os_privacy_page/os_privacy_page.js';
 import './os_privacy_page/peripheral_data_access_protection_dialog.js';
+import './os_privacy_page/privacy_hub_page.js';
 import './os_privacy_page/smart_privacy_page.js';
 import './os_reset_page/os_powerwash_dialog.js';
 import './os_reset_page/os_powerwash_dialog_esim_item.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js
index 3dc28cf5..a471898f 100644
--- a/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js
+++ b/chrome/browser/resources/settings/chromeos/os_languages_page/input_method_util.js
@@ -169,9 +169,6 @@
       {name: OptionType.ENABLE_DOUBLE_SPACE_PERIOD},
       {name: OptionType.EDIT_USER_DICT}
     ],
-    basic: [],
-    advanced: [],
-    suggestions: [],
   },
   [SettingsType.ZHUYIN_SETTINGS]: {
     physicalKeyboard: [
@@ -179,20 +176,12 @@
       {name: OptionType.ZHUYIN_SELECT_KEYS},
       {name: OptionType.ZHUYIN_PAGE_SIZE},
     ],
-    virtualKeyboard: [],
-    basic: [],
-    advanced: [],
-    suggestions: [],
   },
   [SettingsType.KOREAN_SETTINGS]: {
     basic: [
       {name: OptionType.KOREAN_KEYBOARD_LAYOUT},
       {name: OptionType.KOREAN_ENABLE_SYLLABLE_INPUT},
     ],
-    virtualKeyboard: [],
-    advanced: [],
-    physicalKeyboard: [],
-    suggestions: [],
   },
   [SettingsType.PINYIN_FUZZY_SETTINGS]: {
     advanced: [{
@@ -212,10 +201,6 @@
         OptionType.PINYIN_Z_ZH,
       ]
     }],
-    virtualKeyboard: [],
-    basic: [],
-    physicalKeyboard: [],
-    suggestions: [],
   },
   [SettingsType.PINYIN_SETTINGS]: {
     physicalKeyboard: [
@@ -227,34 +212,19 @@
       {name: OptionType.PINYIN_CHINESE_PUNCTUATION},
     ],
     advanced: [{name: OptionType.EDIT_USER_DICT}],
-    basic: [],
-    virtualKeyboard: [],
-    suggestions: [],
   },
   [SettingsType.BASIC_SETTINGS]: {
-    physicalKeyboard: [],
     virtualKeyboard: [
       {name: OptionType.ENABLE_SOUND_ON_KEYPRESS},
     ],
-    basic: [],
-    advanced: [],
-    suggestions: [],
   },
   [SettingsType.ENGLISH_SOUTH_AFRICA_SETTINGS]: {
-    physicalKeyboard: [],
     virtualKeyboard: [
       {name: OptionType.ENABLE_SOUND_ON_KEYPRESS},
       {name: OptionType.VIRTUAL_KEYBOARD_ENABLE_CAPITALIZATION},
     ],
-    basic: [],
-    advanced: [],
-    suggestions: [],
   },
   [SettingsType.SUGGESTION_SETTINGS]: {
-    physicalKeyboard: [],
-    virtualKeyboard: [],
-    basic: [],
-    advanced: [],
     suggestions:
         [{name: OptionType.PHYSICAL_KEYBOARD_ENABLE_PREDICTIVE_WRITING}],
   },
@@ -299,16 +269,17 @@
     virtualKeyboard: [],
     suggestions: []
   };
+
   const inputMethodSettings = getInputMethodSettings(predictiveWritingEnabled);
   const engineSettings = inputMethodSettings[engineId];
   if (engineSettings) {
     engineSettings.forEach((settingType) => {
       const settings = Settings[settingType];
-      options.basic.push(...settings.basic);
-      options.advanced.push(...settings.advanced);
-      options.physicalKeyboard.push(...settings.physicalKeyboard);
-      options.virtualKeyboard.push(...settings.virtualKeyboard);
-      options.suggestions.push(...settings.suggestions);
+      options.basic.push(...(settings.basic || []));
+      options.advanced.push(...(settings.advanced || []));
+      options.physicalKeyboard.push(...(settings.physicalKeyboard || []));
+      options.virtualKeyboard.push(...(settings.virtualKeyboard || []));
+      options.suggestions.push(...(settings.suggestions || []));
     });
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn
index ef483f9..d732106 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn
@@ -12,6 +12,7 @@
   deps = [
     ":os_privacy_page",
     ":peripheral_data_access_protection_dialog",
+    ":privacy_hub_page",
     ":smart_privacy_page",
   ]
 }
@@ -64,10 +65,18 @@
   ]
 }
 
+js_library("privacy_hub_page") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:load_time_data.m",
+  ]
+}
+
 html_to_js("web_components") {
   js_files = [
     "os_privacy_page.js",
     "peripheral_data_access_protection_dialog.js",
+    "privacy_hub_page.js",
     "smart_privacy_page.js",
   ]
 }
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html
index 015782dcc8..83939dd1 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.html
@@ -41,6 +41,14 @@
         </cr-link-row>
         <div class="hr"></div>
       </template>
+      <template is="dom-if" if="[[showPrivacyHub_]]" restamp>
+        <cr-link-row id="privacyHubSubpageTrigger"
+            on-click="onPrivacyHub_"
+            label="$i18n{privacyHubTitle}"
+            role-description="$i18n{subpageArrowRoleDescription}">
+        </cr-link-row>
+        <div class="hr"></div>
+      </template>
     </template>
 
 <if expr="_google_chrome">
@@ -160,6 +168,12 @@
         </settings-smart-privacy-page>
       </settings-subpage>
     </template>
+    <template is="dom-if" route-path="/osPrivacy/privacyHub">
+      <settings-subpage page-title="$i18n{privacyHubTitle}">
+        <settings-privacy-hub-page prefs="{{prefs}}">
+        </settings-privacy-hub-page>
+      </settings-subpage>
+    </template>
   </template>
 
 </settings-animated-pages>
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
index 3b2db5e..5fb4fe7 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
@@ -213,6 +213,18 @@
         },
       },
 
+      /**
+       * Whether privacy hub should be displayed.
+       * @private
+       */
+      showPrivacyHub_: {
+        type: Boolean,
+        readOnly: true,
+        value: function() {
+          return loadTimeData.getBoolean('showPrivacyHub');
+        },
+      },
+
       // <if expr="_google_chrome">
       /**
        * The preference controlling the current user's metrics consent. This
@@ -367,6 +379,11 @@
   }
 
   /** @private */
+  onPrivacyHub_() {
+    Router.getInstance().navigateTo(routes.PRIVACY_HUB);
+  }
+
+  /** @private */
   onAuthTokenChanged_() {
     if (this.authToken_ === undefined) {
       this.setModes_ = undefined;
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.html b/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.html
new file mode 100644
index 0000000..5c1a986
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.html
@@ -0,0 +1,6 @@
+<settings-toggle-button
+    pref="{{prefs.ash.user.camera_allowed}}"
+    id="cameraToggle"
+    label="$i18n{cameraToggleTitle}"
+    deep-link-focus-id$="[[Setting.kCameraOnOff]]">
+</settings-toggle-button>
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.js b/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.js
new file mode 100644
index 0000000..89af9daf
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/privacy_hub_page.js
@@ -0,0 +1,82 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * 'os-settings-privacy-hub-page' contains privacy hub configurations.
+ */
+
+import '../../controls/settings_toggle_button.js';
+
+import {assert} from 'chrome://resources/js/assert.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {Route} from '../../router.js';
+import {DeepLinkingBehavior, DeepLinkingBehaviorInterface} from '../deep_linking_behavior.js';
+import {routes} from '../os_route.js';
+import {RouteObserverBehavior, RouteObserverBehaviorInterface} from '../route_observer_behavior.js';
+
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {DeepLinkingBehaviorInterface}
+ * @implements {RouteObserverBehaviorInterface}
+ */
+const SettingsPrivacyHubPageBase = mixinBehaviors(
+    [DeepLinkingBehavior, RouteObserverBehavior], PolymerElement);
+
+/** @polymer */
+class SettingsPrivacyHubPage extends SettingsPrivacyHubPageBase {
+  static get is() {
+    return 'settings-privacy-hub-page';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  /** @override */
+  ready() {
+    super.ready();
+    assert(loadTimeData.getBoolean('showPrivacyHub'));
+  }
+
+  static get properties() {
+    return {
+      /**
+       * Preferences state.
+       */
+      prefs: {
+        type: Object,
+        notify: true,
+      },
+
+      /**
+       * Used by DeepLinkingBehavior to focus this page's deep links.
+       * @type {!Set<!chromeos.settings.mojom.Setting>}
+       */
+      supportedSettingIds: {
+        type: Object,
+        value: () => new Set([
+          chromeos.settings.mojom.Setting.kCameraOnOff,
+        ]),
+      },
+    };
+  }
+
+  /**
+   * RouteObserverBehavior
+   * @param {!Route} route
+   */
+  currentRouteChanged(route) {
+    // Does not apply to this page.
+    if (route !== routes.PRIVACY_HUB) {
+      return;
+    }
+    this.attemptDeepLink();
+  }
+}
+
+customElements.define(SettingsPrivacyHubPage.is, SettingsPrivacyHubPage);
diff --git a/chrome/browser/resources/settings/chromeos/os_route.js b/chrome/browser/resources/settings/chromeos/os_route.js
index 06063bc..690202de 100644
--- a/chrome/browser/resources/settings/chromeos/os_route.js
+++ b/chrome/browser/resources/settings/chromeos/os_route.js
@@ -279,6 +279,8 @@
       Subpage.kManageOtherPeopleV2);
   r.SMART_PRIVACY = createSubpage(
       r.OS_PRIVACY, mojom.SMART_PRIVACY_SUBPAGE_PATH, Subpage.kSmartPrivacy);
+  r.PRIVACY_HUB = createSubpage(
+      r.OS_PRIVACY, mojom.PRIVACY_HUB_SUBPAGE_PATH, Subpage.kPrivacyHub);
 
   // Languages and Input section.
   r.OS_LANGUAGES = createSection(
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_routes.js b/chrome/browser/resources/settings/chromeos/os_settings_routes.js
index f638e9f..e5302aa 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_routes.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_routes.js
@@ -84,6 +84,7 @@
  *   POINTERS: !Route,
  *   POWER: !Route,
  *   PRIVACY: !Route,
+ *   PRIVACY_HUB: !Route,
  *   SEARCH: !Route,
  *   SEARCH_SUBPAGE: !Route,
  *   SMART_LOCK: !Route,
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 03bc5f3..068713a 100644
--- a/chrome/browser/resources/settings/people_page/sync_account_control.ts
+++ b/chrome/browser/resources/settings/people_page/sync_account_control.ts
@@ -168,23 +168,12 @@
   }
 
   /**
-   * Records the following user actions:
-   * - Signin_Impression_FromSettings and
-   * - Signin_ImpressionWithAccount_FromSettings
-   * - Signin_ImpressionWithNoAccount_FromSettings
+   * Records Signin_Impression_FromSettings user action.
    */
   recordImpressionUserActions_() {
     assert(!this.syncStatus.signedIn);
-    assert(this.shownAccount_ !== undefined);
 
     chrome.metricsPrivate.recordUserAction('Signin_Impression_FromSettings');
-    if (this.shownAccount_) {
-      chrome.metricsPrivate.recordUserAction(
-          'Signin_ImpressionWithAccount_FromSettings');
-    } else {
-      chrome.metricsPrivate.recordUserAction(
-          'Signin_ImpressionWithNoAccount_FromSettings');
-    }
   }
 
   private computeSignedIn_(): boolean {
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.html b/chrome/browser/resources/settings/privacy_page/cookies_page.html
index ec09ee20..4830f1a 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.html
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.html
@@ -135,8 +135,8 @@
     <settings-toggle-button id="clearOnExit" class="hr"
         pref="{{prefs.generated.cookie_session_only}}"
         label="$i18n{cookiePageClearOnExit}"
-<if expr="not chromeos_ash and not chromeos_lacros">
-        sub-label="$i18n{cookiePageClearOnExitDesc}"
+<if expr="not chromeos_ash">
+        sub-label="[[getClearOnExitSubLabel_()]]"
 </if>
         on-settings-boolean-control-change="onClearOnExitChange_">
     </settings-toggle-button>
diff --git a/chrome/browser/resources/settings/privacy_page/cookies_page.ts b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
index f6119b30..0d268f9 100644
--- a/chrome/browser/resources/settings/privacy_page/cookies_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/cookies_page.ts
@@ -196,6 +196,18 @@
     }
   }
 
+  // <if expr="not chromeos_ash">
+  private getClearOnExitSubLabel_(): string {
+    // <if expr="chromeos_lacros">
+    if (loadTimeData.getBoolean('isSecondaryUser')) {
+      return '';
+    }
+    // </if>
+
+    return this.i18n('cookiePageClearOnExitDesc');
+  }
+  // </if>
+
   private getSiteDataLabel_(): string {
     return this.enableConsolidatedSiteStorageControls_ ?
         this.i18n('cookiePageAllSitesLink') :
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_dialog.html b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_dialog.html
index 589356f..10651c33 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_dialog.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_guide/privacy_guide_dialog.html
@@ -10,7 +10,7 @@
   }
 
   #dialog {
-    background-color: var(--md-background-color);
+    background-color: var(--cr-card-background-color);
     border: 0;
     display: block;
     height: 100vh;
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_guide_promo.html b/chrome/browser/resources/settings/privacy_page/privacy_guide_promo.html
index 2098d4b..481d2e3 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_guide_promo.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_guide_promo.html
@@ -26,13 +26,12 @@
 </style>
 <div id="wrapper">
   <div id="controlsColumn">
-    <h2 id="title" aria-hidden="true">$i18n{privacyGuidePromoHeader}</h2>
-    <div id="bodyText" class="cr-secondary-text" aria-hidden="true">
+    <h2 id="title">$i18n{privacyGuidePromoHeader}</h2>
+    <div id="bodyText" class="cr-secondary-text">
       $i18n{privacyGuidePromoBody}
     </div>
     <cr-button class="action-button" id="startButton" role="button"
-        aria-labelledby="title" aria-describedby="bodyText"
-        on-click="onPrivacyGuideStartClick_">
+        aria-describedby="title bodyText" on-click="onPrivacyGuideStartClick_">
       $i18n{privacyGuidePromoStartButton}
     </cr-button>
     <cr-button id="noThanksButton" role="button"
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index b19b9cd..7955702 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -307,7 +307,7 @@
 
     // Display a re-authentication dialog.
     signin_ui_util::ShowReauthForAccount(
-        browser, manage_accounts_params.email,
+        profile, manage_accounts_params.email,
         signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN);
     return;
   }
@@ -325,7 +325,7 @@
       // account is in error, as the reconcilor cannot generate the cookie until
       // the primary account is fixed. Display a reauth dialog instead.
       signin_ui_util::ShowReauthForPrimaryAccountWithAuthError(
-          browser, signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN);
+          profile, signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN);
       return;
     }
 
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninChecker.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninChecker.java
index 3b92b05..8d2b9cd 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninChecker.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninChecker.java
@@ -144,7 +144,6 @@
     private void onChildAccountStatusReady(boolean isChild, @Nullable Account childAccount) {
         if (isChild) {
             assert childAccount != null;
-            mSigninManager.onFirstRunCheckDone();
             mSigninManager.runAfterOperationInProgress(() -> {
                 final boolean forceSync = !ChromeFeatureList.isEnabled(
                         ChromeFeatureList.ALLOW_SYNC_OFF_FOR_CHILD_ACCOUNTS);
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java
index e2822577..5ab9828 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/SigninManager.java
@@ -87,13 +87,6 @@
     IdentityManager getIdentityManager();
 
     /**
-     * Notifies the SigninManager that the First Run check has completed.
-     *
-     * The user will be allowed to sign-in once this is signaled.
-     */
-    void onFirstRunCheckDone();
-
-    /**
      * Returns true if sign in can be started now.
      */
     boolean isSigninAllowed();
diff --git a/chrome/browser/signin/signin_ui_delegate.cc b/chrome/browser/signin/signin_ui_delegate.cc
index 56120fe1d..bca33b9 100644
--- a/chrome/browser/signin/signin_ui_delegate.cc
+++ b/chrome/browser/signin/signin_ui_delegate.cc
@@ -5,13 +5,11 @@
 #include "chrome/browser/signin/signin_ui_delegate.h"
 
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 
 namespace signin_ui_util {
 
 void SigninUiDelegate::ShowTurnSyncOnUI(
-    Browser* browser,
     Profile* profile,
     signin_metrics::AccessPoint access_point,
     signin_metrics::PromoAction promo_action,
@@ -20,19 +18,14 @@
     TurnSyncOnHelper::SigninAbortedMode signin_aborted_mode) {
   // TurnSyncOnHelper is suicidal (it will delete itself once it finishes
   // enabling sync).
-  new TurnSyncOnHelper(profile, EnsureBrowser(browser, profile), access_point,
+  new TurnSyncOnHelper(profile, EnsureBrowser(profile), access_point,
                        promo_action, signin_reason, account_id,
                        signin_aborted_mode);
 }
 
 // static
-Browser* SigninUiDelegate::EnsureBrowser(Browser* browser, Profile* profile) {
-  DCHECK(!browser || browser->profile() == profile);
+Browser* SigninUiDelegate::EnsureBrowser(Profile* profile) {
   DCHECK(profile);
-
-  if (browser)
-    return browser;
-
   chrome::ScopedTabbedBrowserDisplayer displayer(profile);
   return displayer.browser();
 }
diff --git a/chrome/browser/signin/signin_ui_delegate.h b/chrome/browser/signin/signin_ui_delegate.h
index 7e5f6493..da32115b 100644
--- a/chrome/browser/signin/signin_ui_delegate.h
+++ b/chrome/browser/signin/signin_ui_delegate.h
@@ -23,26 +23,20 @@
 class SigninUiDelegate {
  public:
   // Displays a sign-in prompt to the user.
-  // `browser` might be null. In that case, the delegate will find an existing
-  // suitable window for `profile` or create a new one if needed.
   // `enable_sync` indicates whether the sync should be enabled after the user
   // successfully signs in.
-  virtual void ShowSigninUI(Browser* browser,
-                            Profile* profile,
+  virtual void ShowSigninUI(Profile* profile,
                             bool enable_sync,
                             signin_metrics::AccessPoint access_point,
                             signin_metrics::PromoAction promo_action) = 0;
 
   // Displays a reauth prompt to the user for an account with indicated `email`.
   // This account should be already known to Chrome.
-  // `browser` might be null. In that case, the delegate will find an existing
-  // suitable window for `profile` or create a new one if needed.
   // `enable_sync` indicates whether the sync should be enabled after the user
   // successfully re-authenticates.
   // Note: if sync is enabled, `enable_sync` has to be false, as it's not valid
   // to start a new sync setup flow when sync is already enabled.
-  virtual void ShowReauthUI(Browser* browser,
-                            Profile* profile,
+  virtual void ShowReauthUI(Profile* profile,
                             const std::string& email,
                             bool enable_sync,
                             signin_metrics::AccessPoint access_point,
@@ -51,10 +45,7 @@
   // Displays a sync confirmation dialog to the user for an account with
   // identified by `account_id`. Account must be a valid (have no auth error)
   // account added to `profile`.
-  // `browser` might be null. In that case, the delegate will find an existing
-  // suitable window for `profile` or create a new one if needed.
   virtual void ShowTurnSyncOnUI(
-      Browser* browser,
       Profile* profile,
       signin_metrics::AccessPoint access_point,
       signin_metrics::PromoAction promo_action,
@@ -63,7 +54,7 @@
       TurnSyncOnHelper::SigninAbortedMode signin_aborted_mode);
 
  protected:
-  static Browser* EnsureBrowser(Browser* browser, Profile* profile);
+  static Browser* EnsureBrowser(Profile* profile);
 };
 
 }  // namespace signin_ui_util
diff --git a/chrome/browser/signin/signin_ui_delegate_impl_dice.cc b/chrome/browser/signin/signin_ui_delegate_impl_dice.cc
index 90edfaf2..73b5707 100644
--- a/chrome/browser/signin/signin_ui_delegate_impl_dice.cc
+++ b/chrome/browser/signin/signin_ui_delegate_impl_dice.cc
@@ -34,26 +34,23 @@
 }  // namespace
 
 void SigninUiDelegateImplDice::ShowSigninUI(
-    Browser* browser,
     Profile* profile,
     bool enable_sync,
     signin_metrics::AccessPoint access_point,
     signin_metrics::PromoAction promo_action) {
-  ShowDiceTab(EnsureBrowser(browser, profile), /*email=*/std::string(),
-              enable_sync, access_point, promo_action);
+  ShowDiceTab(EnsureBrowser(profile), /*email=*/std::string(), enable_sync,
+              access_point, promo_action);
 }
 
 void SigninUiDelegateImplDice::ShowReauthUI(
-    Browser* browser,
     Profile* profile,
     const std::string& email,
     bool enable_sync,
     signin_metrics::AccessPoint access_point,
     signin_metrics::PromoAction promo_action) {
-  DCHECK(!browser || browser->profile() == profile);
   DCHECK(profile);
 
-  ShowDiceTab(EnsureBrowser(browser, profile), email, enable_sync, access_point,
+  ShowDiceTab(EnsureBrowser(profile), email, enable_sync, access_point,
               promo_action);
 }
 
diff --git a/chrome/browser/signin/signin_ui_delegate_impl_dice.h b/chrome/browser/signin/signin_ui_delegate_impl_dice.h
index 3621298..88f7231 100644
--- a/chrome/browser/signin/signin_ui_delegate_impl_dice.h
+++ b/chrome/browser/signin/signin_ui_delegate_impl_dice.h
@@ -13,13 +13,11 @@
 class SigninUiDelegateImplDice : public SigninUiDelegate {
  public:
   // SigninUiDelegate:
-  void ShowSigninUI(Browser* browser,
-                    Profile* profile,
+  void ShowSigninUI(Profile* profile,
                     bool enable_sync,
                     signin_metrics::AccessPoint access_point,
                     signin_metrics::PromoAction promo_action) override;
-  void ShowReauthUI(Browser* browser,
-                    Profile* profile,
+  void ShowReauthUI(Profile* profile,
                     const std::string& email,
                     bool enable_sync,
                     signin_metrics::AccessPoint access_point,
diff --git a/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc b/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc
index 6655b38..52ea6db47 100644
--- a/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc
+++ b/chrome/browser/signin/signin_ui_delegate_impl_lacros.cc
@@ -82,7 +82,6 @@
 }  // namespace
 
 void SigninUiDelegateImplLacros::ShowSigninUI(
-    Browser* browser,
     Profile* profile,
     bool enable_sync,
     signin_metrics::AccessPoint access_point,
@@ -93,7 +92,6 @@
                      // base::Unretained() is fine because
                      // SigninUiDelegateImplLacros is a singleton.
                      base::Unretained(this), enable_sync, /*is_reauth=*/false,
-                     browser ? browser->AsWeakPtr() : nullptr,
                      profile->GetPath(), access_point, promo_action);
   signin_manager->StartLacrosSigninFlow(
       profile->GetPath(),
@@ -105,7 +103,6 @@
 }
 
 void SigninUiDelegateImplLacros::ShowReauthUI(
-    Browser* browser,
     Profile* profile,
     const std::string& email,
     bool enable_sync,
@@ -120,7 +117,6 @@
                      base::Unretained(this), enable_sync,
                      account_reconcilor->GetConsistencyCookieManager()
                          ->CreateScopedAccountUpdate(),
-                     browser ? browser->AsWeakPtr() : nullptr,
                      profile->GetPath(), access_point, promo_action, email);
   account_manager::AccountManagerFacade* account_manager_facade =
       ::GetAccountManagerFacade(profile->GetPath().value());
@@ -132,7 +128,6 @@
 void SigninUiDelegateImplLacros::OnAccountAdded(
     bool enable_sync,
     bool is_reauth,
-    base::WeakPtr<Browser> browser_weak,
     const base::FilePath& profile_path,
     signin_metrics::AccessPoint access_point,
     signin_metrics::PromoAction promo_action,
@@ -145,11 +140,11 @@
   if (!profile)
     return;
 
-  Browser* browser = EnsureBrowser(browser_weak.get(), profile);
+  Browser* browser = EnsureBrowser(profile);
   if (!browser)
     return;
 
-  ShowTurnSyncOnUI(browser, profile, access_point, promo_action,
+  ShowTurnSyncOnUI(profile, access_point, promo_action,
                    is_reauth ? signin_metrics::Reason::kReauthentication
                              : signin_metrics::Reason::kSigninPrimaryAccount,
                    account_id,
@@ -161,7 +156,6 @@
 void SigninUiDelegateImplLacros::OnReauthComplete(
     bool enable_sync,
     signin::ConsistencyCookieManager::ScopedAccountUpdate&& update,
-    base::WeakPtr<Browser> browser_weak,
     const base::FilePath& profile_path,
     signin_metrics::AccessPoint access_point,
     signin_metrics::PromoAction promo_action,
@@ -173,8 +167,8 @@
 
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(profile);
-  OnAccountAdded(enable_sync, /*is_reauth=*/true, browser_weak, profile_path,
-                 access_point, promo_action,
+  OnAccountAdded(enable_sync, /*is_reauth=*/true, profile_path, access_point,
+                 promo_action,
                  identity_manager->FindExtendedAccountInfoByEmailAddress(email)
                      .account_id);
 }
diff --git a/chrome/browser/signin/signin_ui_delegate_impl_lacros.h b/chrome/browser/signin/signin_ui_delegate_impl_lacros.h
index 2c1b609..1e37ef4 100644
--- a/chrome/browser/signin/signin_ui_delegate_impl_lacros.h
+++ b/chrome/browser/signin/signin_ui_delegate_impl_lacros.h
@@ -32,14 +32,12 @@
   // Displays the Chrome account picker first, if the system has available
   // accounts. If the user chooses to add a new account or no existing accounts
   // are available, this function will display OS's add account flow.
-  void ShowSigninUI(Browser* browser,
-                    Profile* profile,
+  void ShowSigninUI(Profile* profile,
                     bool enable_sync,
                     signin_metrics::AccessPoint access_point,
                     signin_metrics::PromoAction promo_action) override;
   // Displays OS's reauth dialog.
-  void ShowReauthUI(Browser* browser,
-                    Profile* profile,
+  void ShowReauthUI(Profile* profile,
                     const std::string& email,
                     bool enable_sync,
                     signin_metrics::AccessPoint access_point,
@@ -48,7 +46,6 @@
  private:
   void OnAccountAdded(bool enable_sync,
                       bool is_reauth,
-                      base::WeakPtr<Browser> browser_weak,
                       const base::FilePath& profile_path,
                       signin_metrics::AccessPoint access_point,
                       signin_metrics::PromoAction promo_action,
@@ -57,7 +54,6 @@
   void OnReauthComplete(
       bool enable_sync,
       signin::ConsistencyCookieManager::ScopedAccountUpdate&& update,
-      base::WeakPtr<Browser> browser_weak,
       const base::FilePath& profile_path,
       signin_metrics::AccessPoint access_point,
       signin_metrics::PromoAction promo_action,
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc
index 6fd7acc..790ad105 100644
--- a/chrome/browser/signin/signin_ui_util.cc
+++ b/chrome/browser/signin/signin_ui_util.cc
@@ -200,31 +200,31 @@
 }
 
 void ShowReauthForPrimaryAccountWithAuthError(
-    Browser* browser,
+    Profile* profile,
     signin_metrics::AccessPoint access_point) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // On ChromeOS, sync errors are fixed by re-signing into the OS.
   NOTREACHED();
 #else
   signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(browser->profile());
+      IdentityManagerFactory::GetForProfile(profile);
   CoreAccountInfo primary_account_info =
       identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
   DCHECK(!primary_account_info.IsEmpty());
   DCHECK(identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
       primary_account_info.account_id));
-  ShowReauthForAccount(browser, primary_account_info.email, access_point);
+  ShowReauthForAccount(profile, primary_account_info.email, access_point);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
-void ShowReauthForAccount(Browser* browser,
+void ShowReauthForAccount(Profile* profile,
                           const std::string& email,
                           signin_metrics::AccessPoint access_point) {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Only `ACCESS_POINT_WEB_SIGNIN` is supported, because `kContentAreaReauth`
   // is hardcoded.
   DCHECK_EQ(access_point, signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN);
-  ::GetAccountManagerFacade(browser->profile()->GetPath().value())
+  ::GetAccountManagerFacade(profile->GetPath().value())
       ->ShowReauthAccountDialog(account_manager::AccountManagerFacade::
                                     AccountAdditionSource::kContentAreaReauth,
                                 email, base::OnceClosure());
@@ -232,7 +232,7 @@
   // Pass `false` for `enable_sync`, as this function is not expected to start a
   // sync setup flow after the reauth.
   GetSigninUiDelegate()->ShowReauthUI(
-      browser, browser->profile(), email,
+      profile, email,
       /*enable_sync=*/false, access_point,
       signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
 #endif
@@ -262,7 +262,7 @@
   if (email_hint.empty()) {
     // Add a new account.
     GetSigninUiDelegate()->ShowSigninUI(
-        nullptr, profile, enable_sync,
+        profile, enable_sync,
         signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS,
         signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
     return;
@@ -270,28 +270,26 @@
 
   // Re-authenticate an existing account.
   GetSigninUiDelegate()->ShowReauthUI(
-      nullptr, profile, email_hint, enable_sync,
+      profile, email_hint, enable_sync,
       signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS,
       signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 }
 
 void EnableSyncFromSingleAccountPromo(
-    Browser* browser,
+    Profile* profile,
     const CoreAccountInfo& account,
     signin_metrics::AccessPoint access_point) {
-  EnableSyncFromMultiAccountPromo(browser, account, access_point,
+  EnableSyncFromMultiAccountPromo(profile, account, access_point,
                                   /*is_default_promo_account=*/true);
 }
 
-void EnableSyncFromMultiAccountPromo(Browser* browser,
+void EnableSyncFromMultiAccountPromo(Profile* profile,
                                      const CoreAccountInfo& account,
                                      signin_metrics::AccessPoint access_point,
                                      bool is_default_promo_account) {
 #if BUILDFLAG(ENABLE_DICE_SUPPORT) || BUILDFLAG(IS_CHROMEOS_LACROS)
-  DCHECK(browser);
   DCHECK_NE(signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, access_point);
-  Profile* profile = browser->profile();
   DCHECK(!profile->IsOffTheRecord());
 
   signin::IdentityManager* identity_manager =
@@ -308,7 +306,7 @@
                   PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT
             : signin_metrics::PromoAction::
                   PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT;
-    GetSigninUiDelegate()->ShowSigninUI(browser, profile, /*enable_sync=*/true,
+    GetSigninUiDelegate()->ShowSigninUI(profile, /*enable_sync=*/true,
                                         access_point, new_account_promo_action);
     return;
   }
@@ -328,7 +326,7 @@
       identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
           account.account_id);
   if (needs_reauth_before_enable_sync) {
-    GetSigninUiDelegate()->ShowReauthUI(browser, profile, account.email,
+    GetSigninUiDelegate()->ShowReauthUI(profile, account.email,
                                         /*enable_sync=*/true, access_point,
                                         existing_account_promo_action);
     return;
@@ -339,7 +337,7 @@
   signin_metrics::RecordSigninUserActionForAccessPoint(
       access_point, existing_account_promo_action);
   GetSigninUiDelegate()->ShowTurnSyncOnUI(
-      browser, profile, access_point, existing_account_promo_action,
+      profile, access_point, existing_account_promo_action,
       signin_metrics::Reason::kSigninPrimaryAccount, account.account_id,
       TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT);
 #else
diff --git a/chrome/browser/signin/signin_ui_util.h b/chrome/browser/signin/signin_ui_util.h
index dc2cd41..83daf28 100644
--- a/chrome/browser/signin/signin_ui_util.h
+++ b/chrome/browser/signin/signin_ui_util.h
@@ -19,7 +19,6 @@
 
 struct AccountInfo;
 struct CoreAccountInfo;
-class Browser;
 class Profile;
 class ProfileAttributesEntry;
 class ProfileAttributesStorage;
@@ -45,11 +44,11 @@
 // Shows a reauth page/dialog to reauthanticate a primary account in error
 // state.
 void ShowReauthForPrimaryAccountWithAuthError(
-    Browser* browser,
+    Profile* profile,
     signin_metrics::AccessPoint access_point);
 
 // Shows a reauth page/dialog to reauthanticate an account.
-void ShowReauthForAccount(Browser* browser,
+void ShowReauthForAccount(Profile* profile,
                           const std::string& email,
                           signin_metrics::AccessPoint access_point);
 
@@ -66,7 +65,7 @@
 //   then it presents the Chrome sign-in page with |account.emil| prefilled.
 // * If token service has a valid refresh token for |account|, then it
 //   enables sync for |account|.
-void EnableSyncFromSingleAccountPromo(Browser* browser,
+void EnableSyncFromSingleAccountPromo(Profile* profile,
                                       const CoreAccountInfo& account,
                                       signin_metrics::AccessPoint access_point);
 
@@ -77,7 +76,7 @@
 //
 // |is_default_promo_account| is true if |account| corresponds to the default
 // account in the promo. It is ignored if |account| is empty.
-void EnableSyncFromMultiAccountPromo(Browser* browser,
+void EnableSyncFromMultiAccountPromo(Profile* profile,
                                      const CoreAccountInfo& account,
                                      signin_metrics::AccessPoint access_point,
                                      bool is_default_promo_account);
diff --git a/chrome/browser/signin/signin_ui_util_unittest.cc b/chrome/browser/signin/signin_ui_util_unittest.cc
index 7ea04fbb5..4832215 100644
--- a/chrome/browser/signin/signin_ui_util_unittest.cc
+++ b/chrome/browser/signin/signin_ui_util_unittest.cc
@@ -83,16 +83,14 @@
  public:
   MOCK_METHOD(void,
               ShowSigninUI,
-              (Browser * browser,
-               Profile* profile,
+              (Profile * profile,
                bool enable_sync,
                signin_metrics::AccessPoint access_point,
                signin_metrics::PromoAction promo_action),
               ());
   MOCK_METHOD(void,
               ShowReauthUI,
-              (Browser * browser,
-               Profile* profile,
+              (Profile * profile,
                const std::string& email,
                bool enable_sync,
                signin_metrics::AccessPoint access_point,
@@ -100,8 +98,7 @@
               ());
   MOCK_METHOD(void,
               ShowTurnSyncOnUI,
-              (Browser * browser,
-               Profile* profile,
+              (Profile * profile,
                signin_metrics::AccessPoint access_point,
                signin_metrics::PromoAction promo_action,
                signin_metrics::Reason signin_reason,
@@ -116,8 +113,7 @@
  public:
   MOCK_METHOD(void,
               ShowTurnSyncOnUI,
-              (Browser * browser,
-               Profile* profile,
+              (Profile * profile,
                signin_metrics::AccessPoint access_point,
                signin_metrics::PromoAction promo_action,
                signin_metrics::Reason signin_reason,
@@ -152,7 +148,7 @@
 
   void EnableSync(const CoreAccountInfo& account_info,
                   bool is_default_promo_account) {
-    EnableSyncFromMultiAccountPromo(browser(), account_info, access_point_,
+    EnableSyncFromMultiAccountPromo(profile(), account_info, access_point_,
                                     is_default_promo_account);
   }
 
@@ -164,8 +160,8 @@
       TurnSyncOnHelper::SigninAbortedMode signin_aborted_mode) {
     EXPECT_CALL(
         mock_delegate_,
-        ShowTurnSyncOnUI(_, profile(), access_point, promo_action,
-                         signin_reason, account_id, signin_aborted_mode));
+        ShowTurnSyncOnUI(profile(), access_point, promo_action, signin_reason,
+                         account_id, signin_aborted_mode));
   }
 
   void ExpectNoSigninStartedHistograms(
@@ -481,7 +477,7 @@
       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
 
   signin_ui_util::ShowReauthForPrimaryAccountWithAuthError(
-      browser(),
+      profile(),
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
 
   // Verify that the active tab has the correct DICE sign-in URL.
@@ -611,14 +607,14 @@
                     bool enable_sync,
                     signin_metrics::AccessPoint access_point,
                     signin_metrics::PromoAction promo_action) {
-    EXPECT_CALL(mock_delegate_, ShowReauthUI(_, profile(), email, enable_sync,
+    EXPECT_CALL(mock_delegate_, ShowReauthUI(profile(), email, enable_sync,
                                              access_point, promo_action));
   }
 
   void ExpectAddAccount(bool enable_sync,
                         signin_metrics::AccessPoint access_point,
                         signin_metrics::PromoAction promo_action) {
-    EXPECT_CALL(mock_delegate_, ShowSigninUI(_, profile(), enable_sync,
+    EXPECT_CALL(mock_delegate_, ShowSigninUI(profile(), enable_sync,
                                              access_point, promo_action));
   }
 
@@ -630,8 +626,8 @@
       TurnSyncOnHelper::SigninAbortedMode signin_aborted_mode) {
     EXPECT_CALL(
         mock_delegate_,
-        ShowTurnSyncOnUI(_, profile(), access_point, promo_action,
-                         signin_reason, account_id, signin_aborted_mode));
+        ShowTurnSyncOnUI(profile(), access_point, promo_action, signin_reason,
+                         account_id, signin_aborted_mode));
   }
 
  protected:
@@ -660,7 +656,7 @@
         account_info.account_id,
         TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT);
     EnableSyncFromMultiAccountPromo(
-        browser(), account_info,
+        profile(), account_info,
         signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
         is_default_promo_account);
   }
@@ -682,7 +678,7 @@
                signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
                signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT);
   EnableSyncFromSingleAccountPromo(
-      browser(), account_info,
+      profile(), account_info,
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
 }
 
@@ -693,7 +689,7 @@
       signin_metrics::PromoAction::
           PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT);
   EnableSyncFromMultiAccountPromo(
-      browser(), CoreAccountInfo(),
+      profile(), CoreAccountInfo(),
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
       /*is_default_promo_account=*/false);
 }
@@ -709,7 +705,7 @@
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
       signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_EXISTING_ACCOUNT);
   EnableSyncFromMultiAccountPromo(
-      browser(), CoreAccountInfo(),
+      profile(), CoreAccountInfo(),
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
       /*is_default_promo_account=*/false);
 }
@@ -730,7 +726,7 @@
                signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
                signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
   ShowReauthForPrimaryAccountWithAuthError(
-      browser(),
+      profile(),
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
 }
 
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service.h b/chrome/browser/supervised_user/child_accounts/child_account_service.h
index 63bbdf6..9bfdc55 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service.h
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service.h
@@ -68,6 +68,7 @@
       const base::RepeatingCallback<void()>& callback);
 
  private:
+  friend class ChildAccountServiceTest;
   friend class ChildAccountServiceFactory;
   // Use |ChildAccountServiceFactory::GetForProfile(...)| to get an instance of
   // this service.
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc b/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
index 9eb7aa2b..6b578b81 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
@@ -4,10 +4,14 @@
 
 #include "chrome/browser/supervised_user/child_accounts/child_account_service.h"
 
+#include <vector>
+
 #include "base/bind.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
+#include "chrome/browser/supervised_user/child_accounts/family_info_fetcher.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/signin/public/base/list_accounts_test_utils.h"
 #include "components/signin/public/base/test_signin_client.h"
@@ -26,6 +30,8 @@
   return std::make_unique<TestSigninClient>(profile->GetPrefs());
 }
 
+}  // namespace
+
 class ChildAccountServiceTest : public ::testing::Test {
  public:
   ChildAccountServiceTest() = default;
@@ -34,8 +40,11 @@
     TestingProfile::Builder builder;
     builder.AddTestingFactory(ChromeSigninClientFactory::GetInstance(),
                               base::BindRepeating(&BuildTestSigninClient));
+    builder.SetIsSupervisedProfile();
     profile_ = IdentityTestEnvironmentProfileAdaptor::
         CreateProfileForIdentityTestEnvironment(builder);
+    child_account_service_ =
+        ChildAccountServiceFactory::GetForProfile(profile_.get());
   }
 
  protected:
@@ -54,9 +63,17 @@
         ->GetAccountsCookieMutator();
   }
 
+  // Assumes a successful response from a family info fetch with the given list
+  // of family members.
+  void OnGetFamilyMembersSuccess(
+      const std::vector<FamilyInfoFetcher::FamilyMember>& members) {
+    child_account_service_->OnGetFamilyMembersSuccess(members);
+  }
+
   content::BrowserTaskEnvironment task_environment_;
 
   std::unique_ptr<TestingProfile> profile_;
+  ChildAccountService* child_account_service_ = nullptr;
 };
 
 TEST_F(ChildAccountServiceTest, GetGoogleAuthState) {
@@ -65,19 +82,16 @@
 
   signin::SetListAccountsResponseNoAccounts(test_url_loader_factory);
 
-  ChildAccountService* child_account_service =
-      ChildAccountServiceFactory::GetForProfile(profile_.get());
-
   // Initial state should be PENDING.
   EXPECT_EQ(ChildAccountService::AuthState::PENDING,
-            child_account_service->GetGoogleAuthState());
+            child_account_service_->GetGoogleAuthState());
 
   // Wait until the response to the ListAccount request triggered by the call
   // above comes back.
   content::RunAllTasksUntilIdle();
 
   EXPECT_EQ(ChildAccountService::AuthState::NOT_AUTHENTICATED,
-            child_account_service->GetGoogleAuthState());
+            child_account_service_->GetGoogleAuthState());
 
   // A valid, signed-in account means authenticated.
   signin::SetListAccountsResponseOneAccountWithParams(
@@ -89,7 +103,7 @@
   accounts_cookie_mutator->TriggerCookieJarUpdate();
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(ChildAccountService::AuthState::AUTHENTICATED,
-            child_account_service->GetGoogleAuthState());
+            child_account_service_->GetGoogleAuthState());
 
   // An invalid (but signed-in) account means not authenticated.
   signin::SetListAccountsResponseOneAccountWithParams(
@@ -101,7 +115,7 @@
   accounts_cookie_mutator->TriggerCookieJarUpdate();
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(ChildAccountService::AuthState::NOT_AUTHENTICATED,
-            child_account_service->GetGoogleAuthState());
+            child_account_service_->GetGoogleAuthState());
 
   // A valid but not signed-in account means not authenticated.
   signin::SetListAccountsResponseOneAccountWithParams(
@@ -113,7 +127,24 @@
   accounts_cookie_mutator->TriggerCookieJarUpdate();
   content::RunAllTasksUntilIdle();
   EXPECT_EQ(ChildAccountService::AuthState::NOT_AUTHENTICATED,
-            child_account_service->GetGoogleAuthState());
+            child_account_service_->GetGoogleAuthState());
 }
 
-}  // namespace
+TEST_F(ChildAccountServiceTest, StartFetchingFamilyInfo) {
+  std::vector<FamilyInfoFetcher::FamilyMember> members;
+  members.push_back(FamilyInfoFetcher::FamilyMember(
+      "someObfuscatedGaiaId", FamilyInfoFetcher::HEAD_OF_HOUSEHOLD,
+      "Homer Simpson", "homer@simpson.com", "http://profile.url/homer",
+      "http://profile.url/homer/image"));
+  members.push_back(FamilyInfoFetcher::FamilyMember(
+      "anotherObfuscatedGaiaId", FamilyInfoFetcher::PARENT, "Marge Simpson",
+      /*profile_image_url=*/std::string(), "http://profile.url/marge",
+      /*email=*/std::string()));
+
+  OnGetFamilyMembersSuccess(members);
+
+  EXPECT_EQ("Homer Simpson", profile_->GetPrefs()->GetString(
+                                 prefs::kSupervisedUserCustodianName));
+  EXPECT_EQ("Marge Simpson", profile_->GetPrefs()->GetString(
+                                 prefs::kSupervisedUserSecondCustodianName));
+}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d724d97f..75592a8 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2844,8 +2844,16 @@
       "webui/nearby_share/nearby_share_dialog_ui.h",
       "webui/nearby_share/shared_resources.cc",
       "webui/nearby_share/shared_resources.h",
+      "webui/policy/status_provider/device_active_directory_policy_status_provider.cc",
+      "webui/policy/status_provider/device_active_directory_policy_status_provider.h",
       "webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.cc",
       "webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h",
+      "webui/policy/status_provider/device_local_account_policy_status_provider.cc",
+      "webui/policy/status_provider/device_local_account_policy_status_provider.h",
+      "webui/policy/status_provider/user_active_directory_policy_status_provider.cc",
+      "webui/policy/status_provider/user_active_directory_policy_status_provider.h",
+      "webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.cc",
+      "webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.h",
       "webui/settings/ash/app_management/app_management_uma.h",
       "webui/settings/ash/calculator/size_calculator.cc",
       "webui/settings/ash/calculator/size_calculator.h",
@@ -3300,6 +3308,8 @@
       "views/profiles/lacros_first_run_signed_in_flow_controller.h",
       "webui/policy/status_provider/device_policy_status_provider_lacros.cc",
       "webui/policy/status_provider/device_policy_status_provider_lacros.h",
+      "webui/policy/status_provider/user_policy_status_provider_lacros.cc",
+      "webui/policy/status_provider/user_policy_status_provider_lacros.h",
       "webui/signin/profile_picker_lacros_sign_in_provider.cc",
       "webui/signin/profile_picker_lacros_sign_in_provider.h",
       "window_sizer/window_sizer_chromeos.cc",
@@ -3815,6 +3825,8 @@
       "webui/conflicts/conflicts_handler.h",
       "webui/conflicts/conflicts_ui.cc",
       "webui/conflicts/conflicts_ui.h",
+      "webui/policy/status_provider/updater_status_provider.cc",
+      "webui/policy/status_provider/updater_status_provider.h",
       "webui/sandbox/sandbox_handler.cc",
       "webui/sandbox/sandbox_handler.h",
       "webui/settings/chrome_cleanup_handler_win.cc",
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninPromoController.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninPromoController.java
index ca74b67..3abc7b13 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninPromoController.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninPromoController.java
@@ -109,8 +109,6 @@
     private @Nullable ImpressionTracker mImpressionTracker;
     private final @AccessPoint int mAccessPoint;
     private final String mImpressionUserActionName;
-    private final String mImpressionWithAccountUserActionName;
-    private final String mImpressionWithNoAccountUserActionName;
     private final String mSigninWithDefaultUserActionName;
     private final String mSigninNotDefaultUserActionName;
     private final String mSigninNewAccountUserActionName;
@@ -275,10 +273,6 @@
         switch (mAccessPoint) {
             case SigninAccessPoint.BOOKMARK_MANAGER:
                 mImpressionUserActionName = "Signin_Impression_FromBookmarkManager";
-                mImpressionWithAccountUserActionName =
-                        "Signin_ImpressionWithAccount_FromBookmarkManager";
-                mImpressionWithNoAccountUserActionName =
-                        "Signin_ImpressionWithNoAccount_FromBookmarkManager";
                 mSigninWithDefaultUserActionName = "Signin_SigninWithDefault_FromBookmarkManager";
                 mSigninNotDefaultUserActionName = "Signin_SigninNotDefault_FromBookmarkManager";
                 // On Android, the promo does not have a button to add and account when there is
@@ -301,10 +295,6 @@
                 break;
             case SigninAccessPoint.NTP_CONTENT_SUGGESTIONS:
                 mImpressionUserActionName = "Signin_Impression_FromNTPContentSuggestions";
-                mImpressionWithAccountUserActionName =
-                        "Signin_ImpressionWithAccount_FromNTPContentSuggestions";
-                mImpressionWithNoAccountUserActionName =
-                        "Signin_ImpressionWithNoAccount_FromNTPContentSuggestions";
                 mSigninWithDefaultUserActionName =
                         "Signin_SigninWithDefault_FromNTPContentSuggestions";
                 mSigninNotDefaultUserActionName =
@@ -334,10 +324,6 @@
                 break;
             case SigninAccessPoint.RECENT_TABS:
                 mImpressionUserActionName = "Signin_Impression_FromRecentTabs";
-                mImpressionWithAccountUserActionName =
-                        "Signin_ImpressionWithAccount_FromRecentTabs";
-                mImpressionWithNoAccountUserActionName =
-                        "Signin_ImpressionWithNoAccount_FromRecentTabs";
                 mSigninWithDefaultUserActionName = "Signin_SigninWithDefault_FromRecentTabs";
                 mSigninNotDefaultUserActionName = "Signin_SigninNotDefault_FromRecentTabs";
                 // On Android, the promo does not have a button to add and account when there is
@@ -362,15 +348,12 @@
                 break;
             case SigninAccessPoint.SETTINGS:
                 mImpressionUserActionName = "Signin_Impression_FromSettings";
-                mImpressionWithAccountUserActionName = "Signin_ImpressionWithAccount_FromSettings";
                 mSigninWithDefaultUserActionName = "Signin_SigninWithDefault_FromSettings";
                 mSigninNotDefaultUserActionName = "Signin_SigninNotDefault_FromSettings";
                 // On Android, the promo does not have a button to add and account when there is
                 // already an account on the device. Always use the NoExistingAccount variant.
                 mSigninNewAccountUserActionName =
                         "Signin_SigninNewAccountNoExistingAccount_FromSettings";
-                mImpressionWithNoAccountUserActionName =
-                        "Signin_ImpressionWithNoAccount_FromSettings";
                 mSyncPromoDismissedPreferenceTracker =
                         ChromePreferenceKeys.SIGNIN_PROMO_SETTINGS_PERSONALIZED_DISMISSED;
                 if (ChromeFeatureList.isEnabled(
@@ -615,11 +598,6 @@
 
     private void recordSigninPromoImpression() {
         RecordUserAction.record(mImpressionUserActionName);
-        if (mProfileData == null) {
-            RecordUserAction.record(mImpressionWithNoAccountUserActionName);
-        } else {
-            RecordUserAction.record(mImpressionWithAccountUserActionName);
-        }
     }
 
     @VisibleForTesting
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java
index bec916f..7aa10ff 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/fre/SigninFirstRunMediator.java
@@ -190,7 +190,6 @@
         mModel.set(SigninFirstRunProperties.SHOW_SIGNIN_PROGRESS_SPINNER_WITH_TEXT, true);
         final SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
                 Profile.getLastUsedRegularProfile());
-        signinManager.onFirstRunCheckDone();
         signinManager.signin(
                 AccountUtils.createAccountFromName(mSelectedAccountName), new SignInCallback() {
                     @Override
diff --git a/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc b/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc
index 7691e51..17dcc55 100644
--- a/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc
+++ b/chrome/browser/ui/ash/arc_open_url_delegate_impl.cc
@@ -93,6 +93,8 @@
         {ChromePage::POINTEROVERLAY,
          chromeos::settings::mojom::kPointersSubpagePath},
         {ChromePage::POWER, chromeos::settings::mojom::kPowerSubpagePath},
+        {ChromePage::PRIVACYHUB,
+         chromeos::settings::mojom::kPrivacyHubSubpagePath},
         {ChromePage::SMARTPRIVACY,
          chromeos::settings::mojom::kSmartPrivacySubpagePath},
         {ChromePage::STORAGE, chromeos::settings::mojom::kStorageSubpagePath},
diff --git a/chrome/browser/ui/ash/arc_open_url_delegate_impl_browsertest.cc b/chrome/browser/ui/ash/arc_open_url_delegate_impl_browsertest.cc
index ffbd55df..00d797d 100644
--- a/chrome/browser/ui/ash/arc_open_url_delegate_impl_browsertest.cc
+++ b/chrome/browser/ui/ash/arc_open_url_delegate_impl_browsertest.cc
@@ -341,6 +341,9 @@
   TestOpenOSSettingsChromePage(
       ChromePage::MANAGEACCESSIBILITYTTS,
       base_url.Resolve(chromeos::settings::mojom::kTextToSpeechSubpagePath));
+  TestOpenOSSettingsChromePage(
+      ChromePage::PRIVACYHUB,
+      base_url.Resolve(chromeos::settings::mojom::kPrivacyHubSubpagePath));
 }
 
 void TestAllBrowserSettingPages(const GURL& base_url) {
diff --git a/chrome/browser/ui/ash/default_pinned_apps.cc b/chrome/browser/ui/ash/default_pinned_apps.cc
index 43eda618..4d567d26 100644
--- a/chrome/browser/ui/ash/default_pinned_apps.cc
+++ b/chrome/browser/ui/ash/default_pinned_apps.cc
@@ -9,12 +9,13 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/web_applications/web_app_id_constants.h"
 #include "chrome/common/extensions/extension_constants.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "extensions/common/constants.h"
 
 namespace {
 
-base::span<StaticAppId> GetDefaultPinnedApps() {
-  constexpr const char* kDefaultPinnedApps[] = {
+std::vector<StaticAppId> GetDefaultPinnedApps() {
+  std::vector<StaticAppId> app_ids{
       extension_misc::kGmailAppId,
       web_app::kGmailAppId,
 
@@ -36,12 +37,17 @@
 
       arc::kGooglePhotosAppId,
   };
-  return base::span<StaticAppId>(kDefaultPinnedApps,
-                                 std::size(kDefaultPinnedApps));
+
+  if (chromeos::features::IsCloudGamingDeviceEnabled()) {
+    app_ids.push_back(web_app::kCloudGamingPartnerPlatform);
+    app_ids.push_back(web_app::kStadiaAppId);
+  }
+
+  return app_ids;
 }
 
-base::span<StaticAppId> GetTabletFormFactorDefaultPinnedApps() {
-  constexpr const char* kTabletFormFactorDefaultPinnedApps[] = {
+std::vector<StaticAppId> GetTabletFormFactorDefaultPinnedApps() {
+  std::vector<StaticAppId> app_ids{
       arc::kGmailAppId,
 
       arc::kGoogleCalendarAppId,
@@ -52,13 +58,12 @@
 
       arc::kGooglePhotosAppId,
   };
-  return base::span<StaticAppId>(kTabletFormFactorDefaultPinnedApps,
-                                 std::size(kTabletFormFactorDefaultPinnedApps));
+  return app_ids;
 }
 
 }  // namespace
 
-base::span<StaticAppId> GetDefaultPinnedAppsForFormFactor() {
+std::vector<StaticAppId> GetDefaultPinnedAppsForFormFactor() {
   if (ash::switches::IsTabletFormFactor()) {
     return GetTabletFormFactorDefaultPinnedApps();
   }
diff --git a/chrome/browser/ui/ash/default_pinned_apps.h b/chrome/browser/ui/ash/default_pinned_apps.h
index 82eab10..d48c4c77 100644
--- a/chrome/browser/ui/ash/default_pinned_apps.h
+++ b/chrome/browser/ui/ash/default_pinned_apps.h
@@ -5,10 +5,10 @@
 #ifndef CHROME_BROWSER_UI_ASH_DEFAULT_PINNED_APPS_H_
 #define CHROME_BROWSER_UI_ASH_DEFAULT_PINNED_APPS_H_
 
-#include "base/containers/span.h"
+#include <vector>
 
-using StaticAppId = const char* const;
+using StaticAppId = const char*;
 
-base::span<StaticAppId> GetDefaultPinnedAppsForFormFactor();
+std::vector<StaticAppId> GetDefaultPinnedAppsForFormFactor();
 
 #endif  // CHROME_BROWSER_UI_ASH_DEFAULT_PINNED_APPS_H_
diff --git a/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc b/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc
index 991fda3..78c886d 100644
--- a/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.cc
@@ -16,37 +16,16 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/signin/public/identity_manager/account_info.h"
 
-BookmarkBubbleSignInDelegate::BookmarkBubbleSignInDelegate(Browser* browser)
-    : browser_(browser), profile_(browser->profile()->GetOriginalProfile()) {
-  if (profile_ != browser_->profile())
-    browser_ = nullptr;
+BookmarkBubbleSignInDelegate::BookmarkBubbleSignInDelegate(Profile* profile)
+    : profile_(profile->GetOriginalProfile()) {}
 
-  BrowserList::AddObserver(this);
-}
-
-BookmarkBubbleSignInDelegate::~BookmarkBubbleSignInDelegate() {
-  BrowserList::RemoveObserver(this);
-}
+BookmarkBubbleSignInDelegate::~BookmarkBubbleSignInDelegate() = default;
 
 void BookmarkBubbleSignInDelegate::OnEnableSync(const AccountInfo& account) {
-  EnsureBrowser();
   signin_ui_util::EnableSyncFromSingleAccountPromo(
-      browser_, account,
+      profile_, account,
       signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE);
 
   // TODO(msarda): Close the bookmarks bubble once the enable sync flow has
   // started.
 }
-
-void BookmarkBubbleSignInDelegate::OnBrowserRemoved(Browser* browser) {
-  if (browser == browser_)
-    browser_ = nullptr;
-}
-
-void BookmarkBubbleSignInDelegate::EnsureBrowser() {
-  if (!browser_) {
-    browser_ = chrome::FindLastActiveWithProfile(profile_);
-    if (!browser_)
-      browser_ = Browser::Create(Browser::CreateParams(profile_, true));
-  }
-}
diff --git a/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h b/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h
index bc6c68e3..3f9a343 100644
--- a/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h
+++ b/chrome/browser/ui/bookmarks/bookmark_bubble_sign_in_delegate.h
@@ -6,18 +6,15 @@
 #define CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_BUBBLE_SIGN_IN_DELEGATE_H_
 
 #include "base/memory/raw_ptr.h"
-#include "chrome/browser/ui/browser_list_observer.h"
 #include "chrome/browser/ui/sync/bubble_sync_promo_delegate.h"
 
-class Browser;
 class Profile;
 
 // Delegate of the bookmark bubble to load the sign in page in a browser
 // when the sign in link is clicked.
-class BookmarkBubbleSignInDelegate : public BubbleSyncPromoDelegate,
-                                     public BrowserListObserver {
+class BookmarkBubbleSignInDelegate : public BubbleSyncPromoDelegate {
  public:
-  explicit BookmarkBubbleSignInDelegate(Browser* browser);
+  explicit BookmarkBubbleSignInDelegate(Profile* profile);
 
   BookmarkBubbleSignInDelegate(const BookmarkBubbleSignInDelegate&) = delete;
   BookmarkBubbleSignInDelegate& operator=(const BookmarkBubbleSignInDelegate&) =
@@ -28,18 +25,8 @@
   // BubbleSyncPromoDelegate:
   void OnEnableSync(const AccountInfo& account) override;
 
-  // BrowserListObserver:
-  void OnBrowserRemoved(Browser* browser) override;
-
  private:
-  // Makes sure |browser_| points to a valid browser.
-  void EnsureBrowser();
-
-  // The browser in which the sign in page must be loaded.
-  raw_ptr<Browser> browser_;
-
-  // The profile associated with |browser_|.
-  raw_ptr<Profile> profile_;
+  const raw_ptr<Profile> profile_;
 };
 
 #endif  // CHROME_BROWSER_UI_BOOKMARKS_BOOKMARK_BUBBLE_SIGN_IN_DELEGATE_H_
diff --git a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
index c42fe48a..574e323 100644
--- a/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
+++ b/chrome/browser/ui/exclusive_access/fullscreen_controller_interactive_browsertest.cc
@@ -681,8 +681,8 @@
 
 // Tests FullscreenController support for fullscreen capability delegation.
 // https://wicg.github.io/capability-delegation/spec.html
-// See related wpt/fullscreen/api/delegate-request.https.sub.tentative.html
-// TODO(crbug.com/1326575): Test opener->popup etc. messaging; add WPT coverage.
+// TODO(crbug.com/1326575): Remove these tests after the feature launches, in
+// favor of the WPT coverage at wpt/html/capability-delegation/*.
 class FullscreenCapabilityDelegationFullscreenControllerInteractiveTest
     : public FullscreenControllerInteractiveTest,
       public testing::WithParamInterface<bool> {
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index 38ffc285..04993db7 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -649,9 +649,8 @@
 }
 
 void ManagePasswordsUIController::EnableSync(const AccountInfo& account) {
-  Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
   signin_ui_util::EnableSyncFromSingleAccountPromo(
-      browser, account,
+      Profile::FromBrowserContext(web_contents()->GetBrowserContext()), account,
       signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE);
 }
 
diff --git a/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc b/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc
index 1c3a3987..8103232 100644
--- a/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc
+++ b/chrome/browser/ui/passwords/password_manager_navigation_throttle.cc
@@ -7,7 +7,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "components/password_manager/core/browser/manage_passwords_referrer.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/page_navigator.h"
@@ -48,12 +47,7 @@
           url::Origin::Create(GURL(password_manager::kTestingReferrerURL)))
     return false;
 
-#if BUILDFLAG(IS_ANDROID)
-  return password_manager::features::UsesUnifiedPasswordManagerUi();
-#else
-  return base::FeatureList::IsEnabled(
-      password_manager::features::kUnifiedPasswordManagerDesktop);
-#endif
+  return true;
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc b/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc
index 493443f..13aaf95 100644
--- a/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc
+++ b/chrome/browser/ui/passwords/password_manager_navigation_throttle_unittest.cc
@@ -4,10 +4,8 @@
 #include "chrome/browser/ui/passwords/password_manager_navigation_throttle.h"
 
 #include "base/memory/raw_ptr.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/variations/scoped_variations_ids_provider.h"
 #include "content/public/browser/navigation_throttle.h"
 #include "content/public/browser/render_frame_host.h"
@@ -42,13 +40,6 @@
         ->InitializeRenderFrameIfNeeded();
     subframe_ = content::RenderFrameHostTester::For(main_rfh())
                     ->AppendChild("subframe");
-#if BUILDFLAG(IS_ANDROID)
-    feature_list_.InitAndEnableFeature(
-        password_manager::features::kUnifiedPasswordManagerAndroid);
-#else
-    feature_list_.InitAndEnableFeature(
-        password_manager::features::kUnifiedPasswordManagerDesktop);
-#endif
   }
 
   content::RenderFrameHost* subframe() const { return subframe_; }
@@ -64,7 +55,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList feature_list_;
   variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
       variations::VariationsIdsProvider::Mode::kUseSignedInState};
   raw_ptr<content::RenderFrameHost> subframe_ = nullptr;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc
index ace78cb..dbc7e12c 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_sign_in_delegate_browsertest.cc
@@ -63,7 +63,8 @@
 }
 
 void BookmarkBubbleSignInDelegateTest::SignInBrowser(Browser* browser) {
-  auto delegate = std::make_unique<BookmarkBubbleSignInDelegate>(browser);
+  auto delegate =
+      std::make_unique<BookmarkBubbleSignInDelegate>(browser->profile());
   delegate->OnEnableSync(AccountInfo());
 }
 
@@ -133,7 +134,7 @@
   int starting_tab_count = extra_browser->tab_strip_model()->count();
 
   std::unique_ptr<BubbleSyncPromoDelegate> delegate =
-      std::make_unique<BookmarkBubbleSignInDelegate>(browser());
+      std::make_unique<BookmarkBubbleSignInDelegate>(profile());
 
   BrowserList::SetLastActive(extra_browser);
 
diff --git a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
index 33d4d59..556d9ed 100644
--- a/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_installed_bubble_view.cc
@@ -245,7 +245,7 @@
 
 void ExtensionInstalledBubbleView::OnEnableSync(const AccountInfo& account) {
   signin_ui_util::EnableSyncFromSingleAccountPromo(
-      browser_, account,
+      browser_->profile(), account,
       signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE);
   GetWidget()->Close();
 }
diff --git a/chrome/browser/ui/views/page_info/page_info_main_view.cc b/chrome/browser/ui/views/page_info/page_info_main_view.cc
index 69b64c9..a621083 100644
--- a/chrome/browser/ui/views/page_info/page_info_main_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_main_view.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/page_info/page_info_main_view.h"
 
+#include "base/feature_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
@@ -16,6 +17,7 @@
 #include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/page_info/chosen_object_view.h"
 #include "chrome/browser/ui/views/page_info/page_info_history_controller.h"
+#include "chrome/browser/ui/views/page_info/page_info_hover_button.h"
 #include "chrome/browser/ui/views/page_info/page_info_navigation_handler.h"
 #include "chrome/browser/ui/views/page_info/page_info_security_content_view.h"
 #include "chrome/browser/ui/views/page_info/page_info_view_factory.h"
@@ -23,6 +25,7 @@
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/common/url_constants.h"
 #include "components/page_info/core/features.h"
+#include "components/page_info/page_info_ui_delegate.h"
 #include "components/permissions/permission_util.h"
 #include "components/privacy_sandbox/privacy_sandbox_features.h"
 #include "components/strings/grit/components_chromium_strings.h"
@@ -558,18 +561,35 @@
       ->SetOrientation(views::LayoutOrientation::kVertical);
   about_this_site_section->AddChildView(PageInfoViewFactory::CreateSeparator());
 
-  auto* about_this_site_button = about_this_site_section->AddChildView(
-      std::make_unique<PageInfoHoverButton>(
-          base::BindRepeating(&PageInfoNavigationHandler::OpenAboutThisSitePage,
-                              base::Unretained(navigation_handler_), info),
-          PageInfoViewFactory::GetAboutThisSiteIcon(),
-          IDS_PAGE_INFO_ABOUT_THIS_SITE_HEADER, std::u16string(),
-          PageInfoViewFactory::VIEW_ID_PAGE_INFO_ABOUT_THIS_SITE_BUTTON,
-          l10n_util::GetStringUTF16(IDS_PAGE_INFO_ABOUT_THIS_SITE_TOOLTIP),
-          base::UTF8ToUTF16(info.description().description()),
-          PageInfoViewFactory::GetOpenSubpageIcon()));
-  about_this_site_button->SetSubtitleMultiline(false);
+  PageInfoHoverButton* about_this_site_button = nullptr;
 
+  if (base::FeatureList::IsEnabled(page_info::kPageInfoAboutThisSiteMoreInfo)) {
+    about_this_site_button = about_this_site_section->AddChildView(
+        std::make_unique<PageInfoHoverButton>(
+            // TODO(crbug.com/1318000): Open side-panel instead.
+            base::BindRepeating(
+                &ChromePageInfoUiDelegate::OpenMoreAboutThisPageUrl,
+                base::Unretained(ui_delegate_), GURL(info.more_about().url())),
+            PageInfoViewFactory::GetAboutThisPageIcon(),
+            IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE, std::u16string(),
+            PageInfoViewFactory::VIEW_ID_PAGE_INFO_ABOUT_THIS_SITE_BUTTON,
+            l10n_util::GetStringUTF16(IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP),
+            base::UTF8ToUTF16(info.description().description()),
+            PageInfoViewFactory::GetLaunchIcon()));
+  } else {
+    about_this_site_button = about_this_site_section->AddChildView(
+        std::make_unique<PageInfoHoverButton>(
+            base::BindRepeating(
+                &PageInfoNavigationHandler::OpenAboutThisSitePage,
+                base::Unretained(navigation_handler_), info),
+            PageInfoViewFactory::GetAboutThisSiteIcon(),
+            IDS_PAGE_INFO_ABOUT_THIS_SITE_HEADER, std::u16string(),
+            PageInfoViewFactory::VIEW_ID_PAGE_INFO_ABOUT_THIS_SITE_BUTTON,
+            l10n_util::GetStringUTF16(IDS_PAGE_INFO_ABOUT_THIS_SITE_TOOLTIP),
+            base::UTF8ToUTF16(info.description().description()),
+            PageInfoViewFactory::GetOpenSubpageIcon()));
+  }
+  about_this_site_button->SetSubtitleMultiline(false);
   return about_this_site_section;
 }
 
diff --git a/chrome/browser/ui/views/page_info/page_info_navigation_handler.h b/chrome/browser/ui/views/page_info/page_info_navigation_handler.h
index b8c9f56a..9bfef2f 100644
--- a/chrome/browser/ui/views/page_info/page_info_navigation_handler.h
+++ b/chrome/browser/ui/views/page_info/page_info_navigation_handler.h
@@ -5,11 +5,12 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_NAVIGATION_HANDLER_H_
 #define CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_NAVIGATION_HANDLER_H_
 
-namespace page_info {
-namespace proto {
+#include "base/callback_forward.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+
+namespace page_info::proto {
 class SiteInfo;
-}
-}  // namespace page_info
+}  // namespace page_info::proto
 
 // An interface that provides methods to navigate between pages of the page
 // info. Note that `OpenMainPage` must update the set of ignored empty storage
diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.cc b/chrome/browser/ui/views/page_info/page_info_view_factory.cc
index 671179a..dc8e5317 100644
--- a/chrome/browser/ui/views/page_info/page_info_view_factory.cc
+++ b/chrome/browser/ui/views/page_info/page_info_view_factory.cc
@@ -430,6 +430,13 @@
 }
 
 // static
+const ui::ImageModel PageInfoViewFactory::GetAboutThisPageIcon() {
+  // TODO(crbug.com/1318000): Use globe icon.
+  return ui::ImageModel::FromVectorIcon(views::kInfoIcon, ui::kColorIcon,
+                                        GetIconSize());
+}
+
+// static
 const ui::ImageModel PageInfoViewFactory::GetHistoryIcon() {
   return ui::ImageModel::FromVectorIcon(vector_icons::kHistoryIcon,
                                         ui::kColorIcon, GetIconSize());
diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.h b/chrome/browser/ui/views/page_info/page_info_view_factory.h
index 49b239a1..fd726c3 100644
--- a/chrome/browser/ui/views/page_info/page_info_view_factory.h
+++ b/chrome/browser/ui/views/page_info/page_info_view_factory.h
@@ -112,6 +112,9 @@
   // Returns the icon for 'About this site' button.
   static const ui::ImageModel GetAboutThisSiteIcon();
 
+  // Returns the icon for 'About this page' button.
+  static const ui::ImageModel GetAboutThisPageIcon();
+
   // Returns the icon for the history button.
   static const ui::ImageModel GetHistoryIcon();
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 10c2f1c..ad74ce1 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -289,8 +289,9 @@
       chrome::ShowSettingsSubPage(browser(), chrome::kSignOutSubPage);
       break;
     case AvatarSyncErrorType::kUnrecoverableError: {
+      Profile* profile = browser()->profile();
       signin::IdentityManager* identity_manager =
-          IdentityManagerFactory::GetForProfile(browser()->profile());
+          IdentityManagerFactory::GetForProfile(profile);
       // This error means that the Sync engine failed to initialize. Shutdown
       // Sync engine by revoking sync consent.
       identity_manager->GetPrimaryAccountMutator()->RevokeSyncConsent(
@@ -299,7 +300,7 @@
       Hide();
       // Re-enable sync with the same primary account.
       signin_ui_util::EnableSyncFromSingleAccountPromo(
-          browser(),
+          profile,
           identity_manager->GetPrimaryAccountInfo(
               signin::ConsentLevel::kSignin),
           signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
@@ -308,7 +309,7 @@
     case AvatarSyncErrorType::kAuthError:
       Hide();
       signin_ui_util::ShowReauthForPrimaryAccountWithAuthError(
-          browser(),
+          browser()->profile(),
           signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
       break;
     case AvatarSyncErrorType::kUpgradeClientError:
@@ -340,7 +341,7 @@
     return;
   Hide();
   signin_ui_util::EnableSyncFromSingleAccountPromo(
-      browser(), account,
+      browser()->profile(), account,
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
 }
 
@@ -373,7 +374,7 @@
   Hide();
 
   signin_ui_util::EnableSyncFromSingleAccountPromo(
-      browser(), AccountInfo(),
+      browser()->profile(), AccountInfo(),
       signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
 }
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
index 263fecc..1741eb0 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc
@@ -645,8 +645,7 @@
    public:
     MOCK_METHOD(void,
                 ShowTurnSyncOnUI,
-                (Browser * browser,
-                 Profile* profile,
+                (Profile * profile,
                  signin_metrics::AccessPoint access_point,
                  signin_metrics::PromoAction promo_action,
                  signin_metrics::Reason signin_reason,
@@ -714,7 +713,7 @@
   EXPECT_CALL(
       mock_delegate_,
       ShowTurnSyncOnUI(
-          browser(), browser()->profile(),
+          browser()->profile(),
           signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
           signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT,
           signin_metrics::Reason::kReauthentication, account_info().account_id,
diff --git a/chrome/browser/ui/views/sync/bubble_sync_promo_view.cc b/chrome/browser/ui/views/sync/bubble_sync_promo_view.cc
index 87aae0b..16bda68 100644
--- a/chrome/browser/ui/views/sync/bubble_sync_promo_view.cc
+++ b/chrome/browser/ui/views/sync/bubble_sync_promo_view.cc
@@ -75,8 +75,6 @@
             /*use_account_name_as_title=*/true));
   }
   signin_metrics::RecordSigninImpressionUserActionForAccessPoint(access_point);
-  signin_metrics::RecordSigninImpressionWithAccountUserActionForAccessPoint(
-      access_point, !account.IsEmpty() /* with_account */);
 }
 
 BubbleSyncPromoView::~BubbleSyncPromoView() = default;
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index c7e1bf3..8499344e7 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -525,11 +525,12 @@
       GetPageActionIconView(PageActionIconType::kBookmarkStar);
 
   std::unique_ptr<BubbleSyncPromoDelegate> delegate;
+  Profile* profile = browser_->profile();
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
-  delegate = std::make_unique<BookmarkBubbleSignInDelegate>(browser_);
+  delegate = std::make_unique<BookmarkBubbleSignInDelegate>(profile);
 #endif
   BookmarkBubbleView::ShowBubble(anchor_view, bookmark_star_icon, observer,
-                                 std::move(delegate), browser_->profile(), url,
+                                 std::move(delegate), profile, url,
                                  already_bookmarked);
 }
 
diff --git a/chrome/browser/ui/webui/history/history_login_handler.cc b/chrome/browser/ui/webui/history/history_login_handler.cc
index 6cc94ef..6c0f910 100644
--- a/chrome/browser/ui/webui/history/history_login_handler.cc
+++ b/chrome/browser/ui/webui/history/history_login_handler.cc
@@ -63,11 +63,10 @@
 
 void HistoryLoginHandler::HandleStartSignInFlow(
     const base::ListValue* /*args*/) {
-  Browser* browser =
-      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
+  Profile* profile = Profile::FromWebUI(web_ui());
   signin_ui_util::EnableSyncFromSingleAccountPromo(
-      browser,
-      IdentityManagerFactory::GetForProfile(browser->profile())
-          ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin),
+      profile,
+      IdentityManagerFactory::GetForProfile(profile)->GetPrimaryAccountInfo(
+          signin::ConsentLevel::kSignin),
       signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
 }
diff --git a/chrome/browser/ui/webui/policy/policy_ui_handler.cc b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
index f46e9d6..1a9121fc 100644
--- a/chrome/browser/ui/webui/policy/policy_ui_handler.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
@@ -94,7 +94,11 @@
 #include "chrome/browser/ash/policy/off_hours/device_off_hours_controller.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.h"
 #include "chrome/browser/ui/webui/policy/status_provider/device_cloud_policy_status_provider_chromeos.h"
+#include "chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.h"
+#include "chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.h"
+#include "chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.h"
 #include "components/user_manager/user_manager.h"
 #else
 #include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
@@ -102,9 +106,9 @@
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
 #include "chrome/browser/ui/webui/policy/status_provider/device_policy_status_provider_lacros.h"
+#include "chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.h"
 #include "chromeos/crosapi/mojom/policy_service.mojom.h"
 #include "chromeos/lacros/lacros_service.h"
-#include "components/policy/core/common/policy_loader_lacros.h"
 #endif
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -120,394 +124,12 @@
 #include <DSRole.h>
 
 #include "chrome/browser/google/google_update_policy_fetcher_win.h"
+#include "chrome/browser/ui/webui/policy/status_provider/updater_status_provider.h"
 #include "chrome/install_static/install_util.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #endif  // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
-namespace em = enterprise_management;
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-// A cloud policy status provider for device account.
-class UserPolicyStatusProviderLacros : public policy::PolicyStatusProvider {
- public:
-  UserPolicyStatusProviderLacros(policy::PolicyLoaderLacros* loader,
-                                 Profile* profile);
-
-  UserPolicyStatusProviderLacros(const UserPolicyStatusProviderLacros&) =
-      delete;
-  UserPolicyStatusProviderLacros& operator=(
-      const UserPolicyStatusProviderLacros&) = delete;
-
-  ~UserPolicyStatusProviderLacros() override;
-
-  // CloudPolicyCoreStatusProvider implementation.
-  void GetStatus(base::DictionaryValue* dict) override;
-
- private:
-  raw_ptr<Profile> profile_;
-  raw_ptr<policy::PolicyLoaderLacros> loader_;
-};
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// A cloud policy status provider for user policy on Chrome OS.
-class UserCloudPolicyStatusProviderChromeOS
-    : public UserCloudPolicyStatusProvider {
- public:
-  explicit UserCloudPolicyStatusProviderChromeOS(policy::CloudPolicyCore* core,
-                                                 Profile* profile);
-
-  UserCloudPolicyStatusProviderChromeOS(
-      const UserCloudPolicyStatusProviderChromeOS&) = delete;
-  UserCloudPolicyStatusProviderChromeOS& operator=(
-      const UserCloudPolicyStatusProviderChromeOS&) = delete;
-
-  ~UserCloudPolicyStatusProviderChromeOS() override;
-
-  // CloudPolicyCoreStatusProvider implementation.
-  void GetStatus(base::DictionaryValue* dict) override;
-
- private:
-  Profile* profile_;
-};
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-// A cloud policy status provider that reads policy status from the policy core
-// associated with the device-local account specified by |user_id| at
-// construction time. The indirection via user ID and
-// DeviceLocalAccountPolicyService is necessary because the device-local account
-// may go away any time behind the scenes, at which point the status message
-// text will indicate CloudPolicyStore::STATUS_BAD_STATE.
-class DeviceLocalAccountPolicyStatusProvider
-    : public policy::PolicyStatusProvider,
-      public policy::DeviceLocalAccountPolicyService::Observer {
- public:
-  DeviceLocalAccountPolicyStatusProvider(
-      const std::string& user_id,
-      policy::DeviceLocalAccountPolicyService* service);
-
-  DeviceLocalAccountPolicyStatusProvider(
-      const DeviceLocalAccountPolicyStatusProvider&) = delete;
-  DeviceLocalAccountPolicyStatusProvider& operator=(
-      const DeviceLocalAccountPolicyStatusProvider&) = delete;
-
-  ~DeviceLocalAccountPolicyStatusProvider() override;
-
-  // PolicyStatusProvider implementation.
-  void GetStatus(base::DictionaryValue* dict) override;
-
-  // policy::DeviceLocalAccountPolicyService::Observer implementation.
-  void OnPolicyUpdated(const std::string& user_id) override;
-  void OnDeviceLocalAccountsChanged() override;
-
- private:
-  const std::string user_id_;
-  policy::DeviceLocalAccountPolicyService* service_;
-};
-
-// Provides status for Active Directory user policy.
-class UserActiveDirectoryPolicyStatusProvider
-    : public policy::PolicyStatusProvider,
-      public policy::CloudPolicyStore::Observer {
- public:
-  explicit UserActiveDirectoryPolicyStatusProvider(
-      policy::ActiveDirectoryPolicyManager* policy_manager,
-      Profile* profile);
-
-  UserActiveDirectoryPolicyStatusProvider(
-      const UserActiveDirectoryPolicyStatusProvider&) = delete;
-  UserActiveDirectoryPolicyStatusProvider& operator=(
-      const UserActiveDirectoryPolicyStatusProvider&) = delete;
-
-  ~UserActiveDirectoryPolicyStatusProvider() override;
-
-  // PolicyStatusProvider implementation.
-  void GetStatus(base::DictionaryValue* dict) override;
-
-  // policy::CloudPolicyStore::Observer implementation.
-  void OnStoreLoaded(policy::CloudPolicyStore* store) override;
-  void OnStoreError(policy::CloudPolicyStore* store) override;
-
- private:
-  policy::ActiveDirectoryPolicyManager* const policy_manager_;  // not owned.
-  Profile* profile_;
-};
-
-// Provides status for Device Active Directory policy.
-class DeviceActiveDirectoryPolicyStatusProvider
-    : public UserActiveDirectoryPolicyStatusProvider {
- public:
-  DeviceActiveDirectoryPolicyStatusProvider(
-      policy::ActiveDirectoryPolicyManager* policy_manager,
-      const std::string& enterprise_domain_manager);
-
-  DeviceActiveDirectoryPolicyStatusProvider(
-      const DeviceActiveDirectoryPolicyStatusProvider&) = delete;
-  DeviceActiveDirectoryPolicyStatusProvider& operator=(
-      const DeviceActiveDirectoryPolicyStatusProvider&) = delete;
-
-  ~DeviceActiveDirectoryPolicyStatusProvider() override = default;
-
-  // PolicyStatusProvider implementation.
-  void GetStatus(base::DictionaryValue* dict) override;
-
- private:
-  std::string enterprise_domain_manager_;
-};
-#endif
-
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-class UpdaterStatusProvider : public policy::PolicyStatusProvider {
- public:
-  UpdaterStatusProvider();
-  ~UpdaterStatusProvider() override = default;
-  void SetUpdaterStatus(std::unique_ptr<GoogleUpdateState> status);
-  void GetStatus(base::DictionaryValue* dict) override;
-
- private:
-  static std::string FetchActiveDirectoryDomain();
-  void OnDomainReceived(std::string domain);
-
-  std::unique_ptr<GoogleUpdateState> updater_status_;
-  std::string domain_;
-  base::WeakPtrFactory<UpdaterStatusProvider> weak_factory_{this};
-};
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
-UserPolicyStatusProviderLacros::UserPolicyStatusProviderLacros(
-    policy::PolicyLoaderLacros* loader,
-    Profile* profile)
-    : profile_(profile), loader_(loader) {}
-
-UserPolicyStatusProviderLacros::~UserPolicyStatusProviderLacros() = default;
-
-void UserPolicyStatusProviderLacros::GetStatus(base::DictionaryValue* dict) {
-  em::PolicyData* policy = loader_->GetPolicyData();
-  if (!policy)
-    return;
-  GetStatusFromPolicyData(policy, dict);
-  ExtractDomainFromUsername(dict);
-  GetUserAffiliationStatus(dict, profile_);
-
-  // Get last fetched time from policy, since we have no refresh scheduler here.
-  base::Time last_refresh_time =
-      policy && policy->has_timestamp()
-          ? base::Time::FromJavaTime(policy->timestamp())
-          : base::Time();
-  dict->SetStringKey("timeSinceLastRefresh",
-                     GetTimeSinceLastActionString(last_refresh_time));
-
-  const base::Time last_refresh_attempt_time = loader_->last_fetch_timestamp();
-  dict->SetStringKey("timeSinceLastFetchAttempt",
-                     GetTimeSinceLastActionString(last_refresh_attempt_time));
-
-  // TODO(https://crbug.com/1243869): Pass this information from Ash through
-  // Mojo. Assume no error for now.
-  dict->SetBoolKey("error", false);
-  dict->SetStringKey(
-      "status", FormatStoreStatus(
-                    policy::CloudPolicyStore::STATUS_OK,
-                    policy::CloudPolicyValidatorBase::Status::VALIDATION_OK));
-}
-#endif
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-UserCloudPolicyStatusProviderChromeOS::UserCloudPolicyStatusProviderChromeOS(
-    policy::CloudPolicyCore* core,
-    Profile* profile)
-    : UserCloudPolicyStatusProvider(core, profile) {
-  profile_ = profile;
-}
-
-UserCloudPolicyStatusProviderChromeOS::
-    ~UserCloudPolicyStatusProviderChromeOS() = default;
-
-void UserCloudPolicyStatusProviderChromeOS::GetStatus(
-    base::DictionaryValue* dict) {
-  if (!core_->store()->is_managed())
-    return;
-  UserCloudPolicyStatusProvider::GetStatus(dict);
-  GetUserAffiliationStatus(dict, profile_);
-  GetUserManager(dict, profile_);
-}
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-DeviceLocalAccountPolicyStatusProvider::DeviceLocalAccountPolicyStatusProvider(
-    const std::string& user_id,
-    policy::DeviceLocalAccountPolicyService* service)
-    : user_id_(user_id), service_(service) {
-  service_->AddObserver(this);
-}
-
-DeviceLocalAccountPolicyStatusProvider::
-    ~DeviceLocalAccountPolicyStatusProvider() {
-  service_->RemoveObserver(this);
-}
-
-void DeviceLocalAccountPolicyStatusProvider::GetStatus(
-    base::DictionaryValue* dict) {
-  const policy::DeviceLocalAccountPolicyBroker* broker =
-      service_->GetBrokerForUser(user_id_);
-  if (broker) {
-    policy::PolicyStatusProvider::GetStatusFromCore(broker->core(), dict);
-  } else {
-    dict->SetBoolKey("error", true);
-    dict->SetStringKey("status",
-                       policy::FormatStoreStatus(
-                           policy::CloudPolicyStore::STATUS_BAD_STATE,
-                           policy::CloudPolicyValidatorBase::VALIDATION_OK));
-    dict->SetStringKey("username", std::string());
-  }
-  ExtractDomainFromUsername(dict);
-  dict->SetBoolKey("publicAccount", true);
-}
-
-void DeviceLocalAccountPolicyStatusProvider::OnPolicyUpdated(
-    const std::string& user_id) {
-  if (user_id == user_id_)
-    NotifyStatusChange();
-}
-
-void DeviceLocalAccountPolicyStatusProvider::OnDeviceLocalAccountsChanged() {
-  NotifyStatusChange();
-}
-
-UserActiveDirectoryPolicyStatusProvider::
-    UserActiveDirectoryPolicyStatusProvider(
-        policy::ActiveDirectoryPolicyManager* policy_manager,
-        Profile* profile)
-    : policy_manager_(policy_manager) {
-  policy_manager_->store()->AddObserver(this);
-  profile_ = profile;
-}
-
-UserActiveDirectoryPolicyStatusProvider::
-    ~UserActiveDirectoryPolicyStatusProvider() {
-  policy_manager_->store()->RemoveObserver(this);
-}
-
-void UserActiveDirectoryPolicyStatusProvider::GetStatus(
-    base::DictionaryValue* dict) {
-  const em::PolicyData* policy = policy_manager_->store()->policy();
-  const std::string client_id = policy ? policy->device_id() : std::string();
-  const std::string username = policy ? policy->username() : std::string();
-  const std::u16string status =
-      policy::FormatStoreStatus(policy_manager_->store()->status(),
-                                policy_manager_->store()->validation_status());
-  dict->SetStringKey("status", status);
-  dict->SetStringKey("username", username);
-  dict->SetStringKey("clientId", client_id);
-
-  const base::TimeDelta refresh_interval =
-      policy_manager_->scheduler()->interval();
-  dict->SetStringKey(
-      "refreshInterval",
-      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
-                             ui::TimeFormat::LENGTH_SHORT, refresh_interval));
-
-  const base::Time last_refresh_time =
-      (policy && policy->has_timestamp())
-          ? base::Time::FromJavaTime(policy->timestamp())
-          : base::Time();
-  dict->SetStringKey("timeSinceLastRefresh",
-                     GetTimeSinceLastActionString(last_refresh_time));
-
-  const base::Time last_refresh_attempt_time =
-      policy_manager_->scheduler()->last_refresh_attempt();
-  dict->SetStringKey("timeSinceLastFetchAttempt",
-                     GetTimeSinceLastActionString(last_refresh_attempt_time));
-
-  // Check if profile is present. Note that profile is not present if object is
-  // an instance of DeviceActiveDirectoryPolicyStatusProvider that inherits from
-  // UserActiveDirectoryPolicyStatusProvider.
-  // TODO(b/182585903): Extend browser test to cover Active Directory case.
-  if (profile_) {
-    GetUserAffiliationStatus(dict, profile_);
-    GetUserManager(dict, profile_);
-  }
-}
-
-void UserActiveDirectoryPolicyStatusProvider::OnStoreLoaded(
-    policy::CloudPolicyStore* store) {
-  NotifyStatusChange();
-}
-
-void UserActiveDirectoryPolicyStatusProvider::OnStoreError(
-    policy::CloudPolicyStore* store) {
-  NotifyStatusChange();
-}
-
-DeviceActiveDirectoryPolicyStatusProvider::
-    DeviceActiveDirectoryPolicyStatusProvider(
-        policy::ActiveDirectoryPolicyManager* policy_manager,
-        const std::string& enterprise_domain_manager)
-    : UserActiveDirectoryPolicyStatusProvider(policy_manager, nullptr),
-      enterprise_domain_manager_(enterprise_domain_manager) {}
-
-void DeviceActiveDirectoryPolicyStatusProvider::GetStatus(
-    base::DictionaryValue* dict) {
-  UserActiveDirectoryPolicyStatusProvider::GetStatus(dict);
-  dict->SetStringKey("enterpriseDomainManager", enterprise_domain_manager_);
-}
-
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-#if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-UpdaterStatusProvider::UpdaterStatusProvider() {
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::BindOnce(&UpdaterStatusProvider::FetchActiveDirectoryDomain),
-      base::BindOnce(&UpdaterStatusProvider::OnDomainReceived,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void UpdaterStatusProvider::SetUpdaterStatus(
-    std::unique_ptr<GoogleUpdateState> status) {
-  updater_status_ = std::move(status);
-  NotifyStatusChange();
-}
-
-void UpdaterStatusProvider::GetStatus(base::DictionaryValue* dict) {
-  if (!domain_.empty())
-    dict->SetStringKey("domain", domain_);
-  if (!updater_status_)
-    return;
-  if (!updater_status_->version.empty())
-    dict->SetStringKey("version", base::WideToUTF8(updater_status_->version));
-  if (!updater_status_->last_checked_time.is_null()) {
-    dict->SetStringKey(
-        "timeSinceLastRefresh",
-        GetTimeSinceLastActionString(updater_status_->last_checked_time));
-  }
-}
-
-// static
-std::string UpdaterStatusProvider::FetchActiveDirectoryDomain() {
-  std::string domain;
-  ::DSROLE_PRIMARY_DOMAIN_INFO_BASIC* info = nullptr;
-  if (::DsRoleGetPrimaryDomainInformation(nullptr,
-                                          ::DsRolePrimaryDomainInfoBasic,
-                                          (PBYTE*)&info) != ERROR_SUCCESS) {
-    return domain;
-  }
-  if (info->DomainNameDns)
-    domain = base::WideToUTF8(info->DomainNameDns);
-  ::DsRoleFreeMemory(info);
-  return domain;
-}
-
-void UpdaterStatusProvider::OnDomainReceived(std::string domain) {
-  domain_ = std::move(domain);
-  NotifyStatusChange();
-}
-
-#endif  // BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-
 PolicyUIHandler::PolicyUIHandler() = default;
 
 PolicyUIHandler::~PolicyUIHandler() {
diff --git a/chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.cc b/chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.cc
new file mode 100644
index 0000000..dfcb75d
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.cc
@@ -0,0 +1,21 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.h"
+
+#include "base/values.h"
+#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h"
+
+DeviceActiveDirectoryPolicyStatusProvider::
+    DeviceActiveDirectoryPolicyStatusProvider(
+        policy::ActiveDirectoryPolicyManager* policy_manager,
+        const std::string& enterprise_domain_manager)
+    : UserActiveDirectoryPolicyStatusProvider(policy_manager, nullptr),
+      enterprise_domain_manager_(enterprise_domain_manager) {}
+
+void DeviceActiveDirectoryPolicyStatusProvider::GetStatus(
+    base::DictionaryValue* dict) {
+  UserActiveDirectoryPolicyStatusProvider::GetStatus(dict);
+  dict->SetStringKey("enterpriseDomainManager", enterprise_domain_manager_);
+}
diff --git a/chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.h b/chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.h
new file mode 100644
index 0000000..be6bfa4
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/device_active_directory_policy_status_provider.h
@@ -0,0 +1,42 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_ACTIVE_DIRECTORY_POLICY_STATUS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_ACTIVE_DIRECTORY_POLICY_STATUS_PROVIDER_H_
+
+#include <string>
+
+#include "chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.h"
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+namespace policy {
+class ActiveDirectoryPolicyManager;
+}  // namespace policy
+
+// Provides status for Device Active Directory policy.
+class DeviceActiveDirectoryPolicyStatusProvider
+    : public UserActiveDirectoryPolicyStatusProvider {
+ public:
+  DeviceActiveDirectoryPolicyStatusProvider(
+      policy::ActiveDirectoryPolicyManager* policy_manager,
+      const std::string& enterprise_domain_manager);
+
+  DeviceActiveDirectoryPolicyStatusProvider(
+      const DeviceActiveDirectoryPolicyStatusProvider&) = delete;
+  DeviceActiveDirectoryPolicyStatusProvider& operator=(
+      const DeviceActiveDirectoryPolicyStatusProvider&) = delete;
+
+  ~DeviceActiveDirectoryPolicyStatusProvider() override = default;
+
+  // PolicyStatusProvider implementation.
+  void GetStatus(base::DictionaryValue* dict) override;
+
+ private:
+  std::string enterprise_domain_manager_;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_ACTIVE_DIRECTORY_POLICY_STATUS_PROVIDER_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.cc b/chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.cc
new file mode 100644
index 0000000..f39e82ea
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.cc
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.h"
+
+#include "base/values.h"
+#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h"
+#include "components/policy/core/browser/cloud/message_util.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+
+DeviceLocalAccountPolicyStatusProvider::DeviceLocalAccountPolicyStatusProvider(
+    const std::string& user_id,
+    policy::DeviceLocalAccountPolicyService* service)
+    : user_id_(user_id), service_(service) {
+  service_->AddObserver(this);
+}
+
+DeviceLocalAccountPolicyStatusProvider::
+    ~DeviceLocalAccountPolicyStatusProvider() {
+  service_->RemoveObserver(this);
+}
+
+void DeviceLocalAccountPolicyStatusProvider::GetStatus(
+    base::DictionaryValue* dict) {
+  const policy::DeviceLocalAccountPolicyBroker* broker =
+      service_->GetBrokerForUser(user_id_);
+  if (broker) {
+    policy::PolicyStatusProvider::GetStatusFromCore(broker->core(), dict);
+  } else {
+    dict->SetBoolKey("error", true);
+    dict->SetStringKey("status",
+                       policy::FormatStoreStatus(
+                           policy::CloudPolicyStore::STATUS_BAD_STATE,
+                           policy::CloudPolicyValidatorBase::VALIDATION_OK));
+    dict->SetStringKey("username", std::string());
+  }
+  ExtractDomainFromUsername(dict);
+  dict->SetBoolKey("publicAccount", true);
+}
+
+void DeviceLocalAccountPolicyStatusProvider::OnPolicyUpdated(
+    const std::string& user_id) {
+  if (user_id == user_id_)
+    NotifyStatusChange();
+}
+
+void DeviceLocalAccountPolicyStatusProvider::OnDeviceLocalAccountsChanged() {
+  NotifyStatusChange();
+}
diff --git a/chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.h b/chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.h
new file mode 100644
index 0000000..a07f12ad
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/device_local_account_policy_status_provider.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_LOCAL_ACCOUNT_POLICY_STATUS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_LOCAL_ACCOUNT_POLICY_STATUS_PROVIDER_H_
+
+#include <string>
+
+#include "chrome/browser/ash/policy/core/device_local_account_policy_service.h"
+#include "components/policy/core/browser/webui/policy_status_provider.h"
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+// A cloud policy status provider that reads policy status from the policy core
+// associated with the device-local account specified by |user_id| at
+// construction time. The indirection via user ID and
+// DeviceLocalAccountPolicyService is necessary because the device-local account
+// may go away any time behind the scenes, at which point the status message
+// text will indicate CloudPolicyStore::STATUS_BAD_STATE.
+class DeviceLocalAccountPolicyStatusProvider
+    : public policy::PolicyStatusProvider,
+      public policy::DeviceLocalAccountPolicyService::Observer {
+ public:
+  DeviceLocalAccountPolicyStatusProvider(
+      const std::string& user_id,
+      policy::DeviceLocalAccountPolicyService* service);
+
+  DeviceLocalAccountPolicyStatusProvider(
+      const DeviceLocalAccountPolicyStatusProvider&) = delete;
+  DeviceLocalAccountPolicyStatusProvider& operator=(
+      const DeviceLocalAccountPolicyStatusProvider&) = delete;
+
+  ~DeviceLocalAccountPolicyStatusProvider() override;
+
+  // PolicyStatusProvider implementation.
+  void GetStatus(base::DictionaryValue* dict) override;
+
+  // policy::DeviceLocalAccountPolicyService::Observer implementation.
+  void OnPolicyUpdated(const std::string& user_id) override;
+  void OnDeviceLocalAccountsChanged() override;
+
+ private:
+  const std::string user_id_;
+  policy::DeviceLocalAccountPolicyService* service_;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_DEVICE_LOCAL_ACCOUNT_POLICY_STATUS_PROVIDER_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/updater_status_provider.cc b/chrome/browser/ui/webui/policy/status_provider/updater_status_provider.cc
new file mode 100644
index 0000000..b753316
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/updater_status_provider.cc
@@ -0,0 +1,67 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/policy/status_provider/updater_status_provider.h"
+
+#include <windows.h>
+
+#include <DSRole.h>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/values.h"
+#include "chrome/browser/google/google_update_policy_fetcher_win.h"
+#include "chrome/install_static/install_util.h"
+
+UpdaterStatusProvider::UpdaterStatusProvider() {
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+      base::BindOnce(&UpdaterStatusProvider::FetchActiveDirectoryDomain),
+      base::BindOnce(&UpdaterStatusProvider::OnDomainReceived,
+                     weak_factory_.GetWeakPtr()));
+}
+
+UpdaterStatusProvider::~UpdaterStatusProvider() {}
+
+void UpdaterStatusProvider::SetUpdaterStatus(
+    std::unique_ptr<GoogleUpdateState> status) {
+  updater_status_ = std::move(status);
+  NotifyStatusChange();
+}
+
+void UpdaterStatusProvider::GetStatus(base::DictionaryValue* dict) {
+  if (!domain_.empty())
+    dict->SetStringKey("domain", domain_);
+  if (!updater_status_)
+    return;
+  if (!updater_status_->version.empty())
+    dict->SetStringKey("version", base::WideToUTF8(updater_status_->version));
+  if (!updater_status_->last_checked_time.is_null()) {
+    dict->SetStringKey(
+        "timeSinceLastRefresh",
+        GetTimeSinceLastActionString(updater_status_->last_checked_time));
+  }
+}
+
+// static
+std::string UpdaterStatusProvider::FetchActiveDirectoryDomain() {
+  std::string domain;
+  ::DSROLE_PRIMARY_DOMAIN_INFO_BASIC* info = nullptr;
+  if (::DsRoleGetPrimaryDomainInformation(nullptr,
+                                          ::DsRolePrimaryDomainInfoBasic,
+                                          (PBYTE*)&info) != ERROR_SUCCESS) {
+    return domain;
+  }
+  if (info->DomainNameDns)
+    domain = base::WideToUTF8(info->DomainNameDns);
+  ::DsRoleFreeMemory(info);
+  return domain;
+}
+
+void UpdaterStatusProvider::OnDomainReceived(std::string domain) {
+  domain_ = std::move(domain);
+  NotifyStatusChange();
+}
diff --git a/chrome/browser/ui/webui/policy/status_provider/updater_status_provider.h b/chrome/browser/ui/webui/policy/status_provider/updater_status_provider.h
new file mode 100644
index 0000000..b632ae8
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/updater_status_provider.h
@@ -0,0 +1,34 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_UPDATER_STATUS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_UPDATER_STATUS_PROVIDER_H_
+
+#include <string>
+
+#include "components/policy/core/browser/webui/policy_status_provider.h"
+
+struct GoogleUpdateState;
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+class UpdaterStatusProvider : public policy::PolicyStatusProvider {
+ public:
+  UpdaterStatusProvider();
+  ~UpdaterStatusProvider() override;
+  void SetUpdaterStatus(std::unique_ptr<GoogleUpdateState> status);
+  void GetStatus(base::DictionaryValue* dict) override;
+
+ private:
+  static std::string FetchActiveDirectoryDomain();
+  void OnDomainReceived(std::string domain);
+
+  std::unique_ptr<GoogleUpdateState> updater_status_;
+  std::string domain_;
+  base::WeakPtrFactory<UpdaterStatusProvider> weak_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_UPDATER_STATUS_PROVIDER_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.cc b/chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.cc
new file mode 100644
index 0000000..2677d8a
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.cc
@@ -0,0 +1,82 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.h"
+
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/ash/policy/active_directory/active_directory_policy_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h"
+#include "components/policy/core/browser/cloud/message_util.h"
+#include "components/policy/core/common/cloud/cloud_policy_core.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+#include "ui/base/l10n/time_format.h"
+
+UserActiveDirectoryPolicyStatusProvider::
+    UserActiveDirectoryPolicyStatusProvider(
+        policy::ActiveDirectoryPolicyManager* policy_manager,
+        Profile* profile)
+    : policy_manager_(policy_manager) {
+  policy_manager_->store()->AddObserver(this);
+  profile_ = profile;
+}
+
+UserActiveDirectoryPolicyStatusProvider::
+    ~UserActiveDirectoryPolicyStatusProvider() {
+  policy_manager_->store()->RemoveObserver(this);
+}
+
+void UserActiveDirectoryPolicyStatusProvider::GetStatus(
+    base::DictionaryValue* dict) {
+  const enterprise_management::PolicyData* policy =
+      policy_manager_->store()->policy();
+  const std::string client_id = policy ? policy->device_id() : std::string();
+  const std::string username = policy ? policy->username() : std::string();
+  const std::u16string status =
+      policy::FormatStoreStatus(policy_manager_->store()->status(),
+                                policy_manager_->store()->validation_status());
+  dict->SetStringKey("status", status);
+  dict->SetStringKey("username", username);
+  dict->SetStringKey("clientId", client_id);
+
+  const base::TimeDelta refresh_interval =
+      policy_manager_->scheduler()->interval();
+  dict->SetStringKey(
+      "refreshInterval",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_SHORT, refresh_interval));
+
+  const base::Time last_refresh_time =
+      (policy && policy->has_timestamp())
+          ? base::Time::FromJavaTime(policy->timestamp())
+          : base::Time();
+  dict->SetStringKey("timeSinceLastRefresh",
+                     GetTimeSinceLastActionString(last_refresh_time));
+
+  const base::Time last_refresh_attempt_time =
+      policy_manager_->scheduler()->last_refresh_attempt();
+  dict->SetStringKey("timeSinceLastFetchAttempt",
+                     GetTimeSinceLastActionString(last_refresh_attempt_time));
+
+  // Check if profile is present. Note that profile is not present if object is
+  // an instance of DeviceActiveDirectoryPolicyStatusProvider that inherits from
+  // UserActiveDirectoryPolicyStatusProvider.
+  // TODO(b/182585903): Extend browser test to cover Active Directory case.
+  if (profile_) {
+    GetUserAffiliationStatus(dict, profile_);
+    GetUserManager(dict, profile_);
+  }
+}
+
+void UserActiveDirectoryPolicyStatusProvider::OnStoreLoaded(
+    policy::CloudPolicyStore* store) {
+  NotifyStatusChange();
+}
+
+void UserActiveDirectoryPolicyStatusProvider::OnStoreError(
+    policy::CloudPolicyStore* store) {
+  NotifyStatusChange();
+}
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.h b/chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.h
new file mode 100644
index 0000000..02cea50
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/user_active_directory_policy_status_provider.h
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_ACTIVE_DIRECTORY_POLICY_STATUS_PROVIDER_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_ACTIVE_DIRECTORY_POLICY_STATUS_PROVIDER_H_
+
+#include "components/policy/core/browser/webui/policy_status_provider.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+
+class Profile;
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+namespace policy {
+class ActiveDirectoryPolicyManager;
+}  // namespace policy
+
+// Provides status for Active Directory user policy.
+class UserActiveDirectoryPolicyStatusProvider
+    : public policy::PolicyStatusProvider,
+      public policy::CloudPolicyStore::Observer {
+ public:
+  explicit UserActiveDirectoryPolicyStatusProvider(
+      policy::ActiveDirectoryPolicyManager* policy_manager,
+      Profile* profile);
+
+  UserActiveDirectoryPolicyStatusProvider(
+      const UserActiveDirectoryPolicyStatusProvider&) = delete;
+  UserActiveDirectoryPolicyStatusProvider& operator=(
+      const UserActiveDirectoryPolicyStatusProvider&) = delete;
+
+  ~UserActiveDirectoryPolicyStatusProvider() override;
+
+  // PolicyStatusProvider implementation.
+  void GetStatus(base::DictionaryValue* dict) override;
+
+  // policy::CloudPolicyStore::Observer implementation.
+  void OnStoreLoaded(policy::CloudPolicyStore* store) override;
+  void OnStoreError(policy::CloudPolicyStore* store) override;
+
+ private:
+  policy::ActiveDirectoryPolicyManager* const policy_manager_;  // not owned.
+  Profile* profile_;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_ACTIVE_DIRECTORY_POLICY_STATUS_PROVIDER_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.cc b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.cc
new file mode 100644
index 0000000..223039121
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.cc
@@ -0,0 +1,30 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.h"
+
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h"
+#include "components/policy/core/common/cloud/cloud_policy_core.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+
+UserCloudPolicyStatusProviderChromeOS::UserCloudPolicyStatusProviderChromeOS(
+    policy::CloudPolicyCore* core,
+    Profile* profile)
+    : UserCloudPolicyStatusProvider(core, profile) {
+  profile_ = profile;
+}
+
+UserCloudPolicyStatusProviderChromeOS::
+    ~UserCloudPolicyStatusProviderChromeOS() = default;
+
+void UserCloudPolicyStatusProviderChromeOS::GetStatus(
+    base::DictionaryValue* dict) {
+  if (!core_->store()->is_managed())
+    return;
+  UserCloudPolicyStatusProvider::GetStatus(dict);
+  GetUserAffiliationStatus(dict, profile_);
+  GetUserManager(dict, profile_);
+}
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.h b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.h
new file mode 100644
index 0000000..a5dd983
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider_chromeos.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_CLOUD_POLICY_STATUS_PROVIDER_CHROMEOS_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_CLOUD_POLICY_STATUS_PROVIDER_CHROMEOS_H_
+
+#include "chrome/browser/ui/webui/policy/status_provider/user_cloud_policy_status_provider.h"
+
+class Profile;
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+namespace policy {
+class CloudPolicyCore;
+}  // namespace policy
+
+// A cloud policy status provider for user policy on Chrome OS.
+class UserCloudPolicyStatusProviderChromeOS
+    : public UserCloudPolicyStatusProvider {
+ public:
+  explicit UserCloudPolicyStatusProviderChromeOS(policy::CloudPolicyCore* core,
+                                                 Profile* profile);
+
+  UserCloudPolicyStatusProviderChromeOS(
+      const UserCloudPolicyStatusProviderChromeOS&) = delete;
+  UserCloudPolicyStatusProviderChromeOS& operator=(
+      const UserCloudPolicyStatusProviderChromeOS&) = delete;
+
+  ~UserCloudPolicyStatusProviderChromeOS() override;
+
+  // CloudPolicyCoreStatusProvider implementation.
+  void GetStatus(base::DictionaryValue* dict) override;
+
+ private:
+  Profile* profile_;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_CLOUD_POLICY_STATUS_PROVIDER_CHROMEOS_H_
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.cc b/chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.cc
new file mode 100644
index 0000000..13fad57
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.cc
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.h"
+
+#include "base/time/time.h"
+#include "base/values.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/policy/status_provider/status_provider_util.h"
+#include "components/policy/core/browser/cloud/message_util.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/core/common/policy_loader_lacros.h"
+#include "components/policy/proto/device_management_backend.pb.h"
+
+UserPolicyStatusProviderLacros::UserPolicyStatusProviderLacros(
+    policy::PolicyLoaderLacros* loader,
+    Profile* profile)
+    : profile_(profile), loader_(loader) {}
+
+UserPolicyStatusProviderLacros::~UserPolicyStatusProviderLacros() = default;
+
+void UserPolicyStatusProviderLacros::GetStatus(base::DictionaryValue* dict) {
+  enterprise_management::PolicyData* policy = loader_->GetPolicyData();
+  if (!policy)
+    return;
+  GetStatusFromPolicyData(policy, dict);
+  ExtractDomainFromUsername(dict);
+  GetUserAffiliationStatus(dict, profile_);
+
+  // Get last fetched time from policy, since we have no refresh scheduler here.
+  base::Time last_refresh_time =
+      policy && policy->has_timestamp()
+          ? base::Time::FromJavaTime(policy->timestamp())
+          : base::Time();
+  dict->SetStringKey("timeSinceLastRefresh",
+                     GetTimeSinceLastActionString(last_refresh_time));
+
+  const base::Time last_refresh_attempt_time = loader_->last_fetch_timestamp();
+  dict->SetStringKey("timeSinceLastFetchAttempt",
+                     GetTimeSinceLastActionString(last_refresh_attempt_time));
+
+  // TODO(https://crbug.com/1243869): Pass this information from Ash through
+  // Mojo. Assume no error for now.
+  dict->SetBoolKey("error", false);
+  dict->SetStringKey(
+      "status", FormatStoreStatus(
+                    policy::CloudPolicyStore::STATUS_OK,
+                    policy::CloudPolicyValidatorBase::Status::VALIDATION_OK));
+}
diff --git a/chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.h b/chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.h
new file mode 100644
index 0000000..8a25c035
--- /dev/null
+++ b/chrome/browser/ui/webui/policy/status_provider/user_policy_status_provider_lacros.h
@@ -0,0 +1,41 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_POLICY_STATUS_PROVIDER_LACROS_H_
+#define CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_POLICY_STATUS_PROVIDER_LACROS_H_
+
+#include "components/policy/core/browser/webui/policy_status_provider.h"
+
+class Profile;
+
+namespace base {
+class DictionaryValue;
+}  // namespace base
+
+namespace policy {
+class PolicyLoaderLacros;
+}  // namespace policy
+
+// A cloud policy status provider for device account.
+class UserPolicyStatusProviderLacros : public policy::PolicyStatusProvider {
+ public:
+  UserPolicyStatusProviderLacros(policy::PolicyLoaderLacros* loader,
+                                 Profile* profile);
+
+  UserPolicyStatusProviderLacros(const UserPolicyStatusProviderLacros&) =
+      delete;
+  UserPolicyStatusProviderLacros& operator=(
+      const UserPolicyStatusProviderLacros&) = delete;
+
+  ~UserPolicyStatusProviderLacros() override;
+
+  // CloudPolicyCoreStatusProvider implementation.
+  void GetStatus(base::DictionaryValue* dict) override;
+
+ private:
+  raw_ptr<Profile> profile_;
+  raw_ptr<policy::PolicyLoaderLacros> loader_;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_POLICY_STATUS_PROVIDER_USER_POLICY_STATUS_PROVIDER_LACROS_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
index 2e8b014..6c64f40 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
@@ -121,6 +121,7 @@
   kSecurityAndSignInV2 = 1101,
   kFingerprintV2 = 1102,
   kSmartPrivacy = 1103,
+  kPrivacyHub = 1104,
 
   // Languages and Input section.
   // 1200 was used for kLanguagesAndInputDetails. Do not reuse.
@@ -243,6 +244,7 @@
 const string kSecurityAndSignInSubpagePathV2 = "osPrivacy/lockScreen";
 const string kFingerprintSubpagePathV2 = "osPrivacy/lockScreen/fingerprint";
 const string kSmartPrivacySubpagePath = "osPrivacy/smartPrivacy";
+const string kPrivacyHubSubpagePath = "osPrivacy/privacyHub";
 
 // Languages and Input section.
 const string kLanguagesAndInputSectionPath = "osLanguages";
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
index 85ef16ca..a352a0ea 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
@@ -97,6 +97,7 @@
       chromeos::settings::mojom::kFingerprintSubpagePathV2,
       chromeos::settings::mojom::kManageOtherPeopleSubpagePathV2,
       chromeos::settings::mojom::kSmartPrivacySubpagePath,
+      chromeos::settings::mojom::kPrivacyHubSubpagePath,
 
       // Languages and Input section.
       chromeos::settings::mojom::kLanguagesAndInputSectionPath,
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
index 50d8914..0642fe0 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
@@ -205,6 +205,7 @@
   kPeripheralDataAccessProtection = 1113,
   kSnoopingProtection = 1114,
   kQuickDim = 1115,
+  kCameraOnOff = 1116,
 
   // Languages and Input section.
   kAddLanguage = 1200,
diff --git a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
index 119068a..8f958ce80 100644
--- a/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc
@@ -320,6 +320,8 @@
        IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_SUBTEXT},
       {"smartPrivacySnoopingNotifications",
        IDS_OS_SETTINGS_SMART_PRIVACY_SNOOPING_NOTIFICATIONS},
+      {"privacyHubTitle", IDS_OS_SETTINGS_PRIVACY_HUB_TITLE},
+      {"cameraToggleTitle", IDS_OS_SETTINGS_CAMERA_TOGGLE_TITLE},
   };
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
@@ -328,6 +330,9 @@
   html_source->AddBoolean("isQuickDimEnabled",
                           ash::features::IsQuickDimEnabled());
 
+  html_source->AddBoolean("showPrivacyHub", base::FeatureList::IsEnabled(
+                                                ::features::kCrosPrivacyHub));
+
   html_source->AddString(
       "smartPrivacyDesc",
       ui::SubstituteChromeOSDeviceType(IDS_OS_SETTINGS_SMART_PRIVACY_DESC));
@@ -453,6 +458,14 @@
       mojom::Subpage::kSmartPrivacy,
       {{mojom::Setting::kSnoopingProtection, mojom::Setting::kQuickDim}},
       generator);
+
+  // Privacy hub.
+  generator->RegisterTopLevelSubpage(
+      IDS_OS_SETTINGS_PRIVACY_HUB_TITLE, mojom::Subpage::kPrivacyHub,
+      mojom::SearchResultIcon::kShield, mojom::SearchResultDefaultRank::kMedium,
+      mojom::kPrivacyHubSubpagePath);
+  RegisterNestedSettingBulk(mojom::Subpage::kPrivacyHub,
+                            {{mojom::Setting::kCameraOnOff}}, generator);
 }
 
 bool PrivacySection::AreFingerprintSettingsAllowed() {
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index ccc7957..fb7235c 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -338,13 +338,7 @@
 
 void PeopleHandler::DisplayGaiaLoginInNewTabOrWindow(
     signin_metrics::AccessPoint access_point) {
-  Browser* browser =
-      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
-  if (!browser)
-    return;
-
-  auto* identity_manager =
-      IdentityManagerFactory::GetForProfile(browser->profile());
+  auto* identity_manager = IdentityManagerFactory::GetForProfile(profile_);
 
   syncer::SyncService* service = GetSyncService();
   if (service && service->HasUnrecoverableError() &&
@@ -361,13 +355,13 @@
   // same email address.
   if (identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
     SigninErrorController* error_controller =
-        SigninErrorControllerFactory::GetForProfile(browser->profile());
+        SigninErrorControllerFactory::GetForProfile(profile_);
     DCHECK(error_controller->HasError());
-    signin_ui_util::ShowReauthForPrimaryAccountWithAuthError(browser,
+    signin_ui_util::ShowReauthForPrimaryAccountWithAuthError(profile_,
                                                              access_point);
   } else {
-    signin_ui_util::EnableSyncFromSingleAccountPromo(browser, CoreAccountInfo(),
-                                                     access_point);
+    signin_ui_util::EnableSyncFromSingleAccountPromo(
+        profile_, CoreAccountInfo(), access_point);
   }
 }
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -482,16 +476,11 @@
          AccountConsistencyModeManager::IsMirrorEnabledForProfile(profile_));
   const base::Value& email = args[0];
   const base::Value& is_default_promo_account = args[1];
-
-  Browser* browser =
-      chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
-
   AccountInfo maybe_account =
       IdentityManagerFactory::GetForProfile(profile_)
           ->FindExtendedAccountInfoByEmailAddress(email.GetString());
-
   signin_ui_util::EnableSyncFromMultiAccountPromo(
-      browser, maybe_account,
+      profile_, maybe_account,
       signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS,
       is_default_promo_account.GetBool());
 #else
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 fed5b99..4424fb6 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2139,7 +2139,7 @@
     {"cookiePageBlockAllBulTwo", IDS_SETTINGS_COOKIES_BLOCK_ALL_BULLET_TWO},
     {"cookiePageBlockAllBulThree", IDS_SETTINGS_COOKIES_BLOCK_ALL_BULLET_THREE},
     {"cookiePageClearOnExit", IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT},
-#if !BUILDFLAG(IS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
     {"cookiePageClearOnExitDesc", IDS_SETTINGS_COOKIES_CLEAR_ON_EXIT_DESC},
 #endif
     {"cookiePageAllSitesLink", IDS_SETTINGS_COOKIES_ALL_SITES_LINK},
diff --git a/chrome/browser/web_applications/web_app_id_constants.cc b/chrome/browser/web_applications/web_app_id_constants.cc
index 8c86d8be..d3d3630 100644
--- a/chrome/browser/web_applications/web_app_id_constants.cc
+++ b/chrome/browser/web_applications/web_app_id_constants.cc
@@ -24,6 +24,9 @@
 //     "https://canvas.apps.chrome/"))
 const char kCanvasAppId[] = "ieailfmhaghpphfffooibmlghaeopach";
 
+// TODO(crbug.com/1334053)
+const char kCloudGamingPartnerPlatform[] = "egmafekfmcnknbdlbfbhafbllplmjlhn";
+
 // Generated as: web_app::GenerateAppId(/*manifest_id=*/absl::nullopt, GURL(
 //     "chrome-untrusted://crosh/"))
 const char kCroshAppId[] = "cgfnfgkafmcdkdgilmojlnaadileaach";
diff --git a/chrome/browser/web_applications/web_app_id_constants.h b/chrome/browser/web_applications/web_app_id_constants.h
index cf6452b..24c6f95 100644
--- a/chrome/browser/web_applications/web_app_id_constants.h
+++ b/chrome/browser/web_applications/web_app_id_constants.h
@@ -12,6 +12,7 @@
 extern const char kCalculatorAppId[];
 extern const char kCameraAppId[];
 extern const char kCanvasAppId[];
+extern const char kCloudGamingPartnerPlatform[];
 extern const char kCroshAppId[];
 extern const char kCursiveAppId[];
 extern const char kDiagnosticsAppId[];
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index dd13145..7689fe8 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1654624801-4beb99d41aaf96bbdc7070975c5f320f7318ff27.profdata
+chrome-linux-main-1654688617-d23b8156b805598a45b784d8578978f0c92fdea7.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 0529e359..7338b0c 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1654646316-bbd54d35f9d32541507d3a59be35ad2b5d6fc660.profdata
+chrome-mac-main-1654688617-f62d54d4f4ee54fed29469958cf988d755ba5592.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 3e69c5b..0585784 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1654646316-cc64f043d6eaf00ceb77f0b06fbef47e57b8c4a4.profdata
+chrome-win32-main-1654678108-b654f5c2b5a7a3768455503ebea5b09101c3fbf4.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index f371125b..3896525 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1654657147-f31aea24b202c703f44c852b045de09e50068779.profdata
+chrome-win64-main-1654667317-afa9375656b40db85fb6a08c81df9e90b652c011.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1e9114c..ce438ef 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -6290,6 +6290,7 @@
       "../browser/apps/intent_helper/intent_picker_auto_display_prefs_unittest.cc",
       "../browser/apps/intent_helper/intent_picker_internal_unittest.cc",
       "../browser/apps/intent_helper/page_transition_util_unittest.cc",
+      "../browser/autofill/autofill_context_menu_manager_unittest.cc",
       "../browser/autofill_assistant/password_change/apc_client_impl_unittest.cc",
       "../browser/autofill_assistant/password_change/apc_onboarding_coordinator_impl_unittest.cc",
       "../browser/browser_commands_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
index 8892817..b2a134f 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/signin/SigninTestUtil.java
@@ -49,7 +49,6 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
                     Profile.getLastUsedRegularProfile());
-            signinManager.onFirstRunCheckDone(); // Allow sign-in
             signinManager.signin(AccountUtils.createAccountFromName(coreAccountInfo.getEmail()),
                     new SigninManager.SignInCallback() {
                         @Override
@@ -88,7 +87,6 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             SigninManager signinManager = IdentityServicesProvider.get().getSigninManager(
                     Profile.getLastUsedRegularProfile());
-            signinManager.onFirstRunCheckDone(); // Allow sign-in
             signinManager.signinAndEnableSync(SigninAccessPoint.UNKNOWN,
                     AccountUtils.createAccountFromName(coreAccountInfo.getEmail()),
                     new SigninManager.SignInCallback() {
diff --git a/chrome/test/data/extensions/api_test/preference/onchange_lacros/manifest.json b/chrome/test/data/extensions/api_test/preference/onchange_lacros/manifest.json
new file mode 100644
index 0000000..f34208e
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/preference/onchange_lacros/manifest.json
@@ -0,0 +1,11 @@
+{
+  "name" : "Preferences API Test Extension (Lacros events)",
+  "version" : "0.1",
+  "manifest_version": 2,
+  "description" : "Preferences API Test Extension (Lacros events)",
+  "permissions": [ "accessibilityFeatures.read", "accessibilityFeatures.modify" ],
+  "background": {
+    "scripts": [ "test.js" ],
+    "persistent": false
+  }
+}
diff --git a/chrome/test/data/extensions/api_test/preference/onchange_lacros/test.js b/chrome/test/data/extensions/api_test/preference/onchange_lacros/test.js
new file mode 100644
index 0000000..22c128d
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/preference/onchange_lacros/test.js
@@ -0,0 +1,38 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Preferences API test for extension controlled prefs where the underlying
+// feature lives in ash. These tests make use of the crosapi to set the value
+// in ash. Thus, they run as lacros_chrome_browsertests. This test verifies the
+// hehavior of the onChange callback.
+// Run with lacros_chrome_browsertests_run_in_series \
+//     --gtest_filter=ExtensionPreferenceLacrosBrowserTest.Lacros
+
+// Listen until |event| has fired with the |expected| value.
+function listenUntil(event, expected) {
+  var done = chrome.test.listenForever(event, function(value) {
+    chrome.test.assertEq(expected, value);
+    done();
+  });
+}
+
+var af = chrome.accessibilityFeatures;
+chrome.test.runTests([
+  function changeDefault() {
+    listenUntil(af.autoclick.onChange, {
+      value: false,
+      levelOfControl: 'controlled_by_this_extension'
+    });
+    af.autoclick.set({
+      value:false
+    }, chrome.test.callbackPass());
+  },
+  function clearDefault() {
+    listenUntil(af.autoclick.onChange, {
+      value: false,
+      levelOfControl: 'controllable_by_this_extension'
+    });
+    af.autoclick.clear({}, chrome.test.callbackPass());
+  }
+]);
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
index 0a0e603..75a1b1f3 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/BUILD.gn
@@ -58,6 +58,7 @@
 js_library("file_attachment_test") {
   deps = [
     "../..:chai_assert",
+    "//ash/webui/common/resources:mojo_utils",
     "//ash/webui/os_feedback_ui/resources:file_attachment",
   ]
   externs_list = [ "$externs_path/mocha-2.5.js" ]
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
index b5cf1c26..b5d56f0 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/file_attachment_test.js
@@ -2,9 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {FileAttachmentElement} from 'chrome://os-feedback/file_attachment.js';
+import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
+import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
 
-import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {FileAttachmentElement} from 'chrome://os-feedback/file_attachment.js';
+import {mojoString16ToString} from 'chrome://resources/ash/common/mojo_utils.js';
+
+import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {eventToPromise, flushTasks, isVisible} from '../../test_util.js';
 
 export function fileAttachmentTestSuite() {
@@ -121,10 +125,77 @@
     const fakeFile = /** @type {!File} */ ({name: 'fake.zip'});
     page.setSelectedFileForTesting(fakeFile);
 
-    assertEquals('fake.zip', page.selectedFile.name);
+    // The selected file name is set properly.
+    assertEquals('fake.zip', getElementContent('#selectedFileName'));
+    // The select file checkbox is checked automatically when a file is
+    // selected.
+    assertTrue(getElement('#selectFileCheckbox').checked);
     // The addFileContainer should be invisible.
     assertFalse(isVisible(getElement('#addFileContainer')));
     // The replaceFileContainer should be visible.
     assertTrue(isVisible(getElement('#replaceFileContainer')));
   });
+
+  // Test that when there is not a file selected, getAttachedFile returns null.
+  test('hasNotSelectedAFile', async () => {
+    await initializePage();
+
+    // The selected file name is empty.
+    assertEquals('', getElementContent('#selectedFileName'));
+    // The select file checkbox is unchecked.
+    assertFalse(getElement('#selectFileCheckbox').checked);
+
+    const attachedFile = await page.getAttachedFile();
+    assertEquals(null, attachedFile);
+  });
+
+  // Test that when a file was selected but the checkbox is unchecked,
+  // getAttachedFile returns null.
+  test('selectedAFileButUnchecked', async () => {
+    await initializePage();
+
+    const selectFileCheckbox = getElement('#selectFileCheckbox');
+    // Set selected file manually.
+    /** @type {!File} */
+    const fakeFile = /** @type {!File} */ ({name: 'fake.zip'});
+    page.setSelectedFileForTesting(fakeFile);
+    selectFileCheckbox.checked = false;
+
+    assertFalse(selectFileCheckbox.checked);
+    assertEquals('fake.zip', getElementContent('#selectedFileName'));
+    const attachedFile = await page.getAttachedFile();
+    assertEquals(null, attachedFile);
+  });
+
+  // Test that when a file was selected but the checkbox is checked,
+  // getAttachedFile returns correct data.
+  test('selectedAFileAndchecked', async () => {
+    await initializePage();
+
+    const selectFileCheckbox = getElement('#selectFileCheckbox');
+
+    const fakeData = [12, 11, 99];
+
+    /** @type {!File} */
+    const fakeFile = /** @type {!File} */ ({
+      name: 'fake.zip',
+      arrayBuffer: async () => {
+        return new Uint8Array(fakeData).buffer;
+      },
+    });
+    // Set selected file manually.
+    page.setSelectedFileForTesting(fakeFile);
+    selectFileCheckbox.checked = true;
+
+    assertEquals('fake.zip', getElementContent('#selectedFileName'));
+    const attachedFile = await page.getAttachedFile();
+    // Verify the fileData field.
+    assertTrue(!!attachedFile);
+    assertTrue(!!attachedFile.fileData);
+    assertTrue(!!attachedFile.fileData.bytes);
+    assertArrayEquals(
+        fakeData, /** @type {!Array<Number>} */ (attachedFile.fileData.bytes));
+    // Verify the fileName field.
+    assertEquals('fake.zip', attachedFile.fileName.path.path);
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
index b65233c9..51d7891 100644
--- a/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
+++ b/chrome/test/data/webui/chromeos/os_feedback_ui/share_data_page_test.js
@@ -2,11 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js';
+import 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js';
+
 import {fakeEmptyFeedbackContext, fakeFeedbackContext} from 'chrome://os-feedback/fake_data.js';
 import {FeedbackFlowState} from 'chrome://os-feedback/feedback_flow.js';
 import {ShareDataPageElement} from 'chrome://os-feedback/share_data_page.js';
+import {mojoString16ToString, stringToMojoString16} from 'chrome://resources/ash/common/mojo_utils.js';
 
-import {assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
+import {assertArrayEquals, assertEquals, assertFalse, assertTrue} from '../../chai_assert.js';
 import {eventToPromise, flushTasks, isVisible} from '../../test_util.js';
 
 /** @type {string} */
@@ -319,4 +323,33 @@
     assertTrue(!!screenshotImage.src);
     assertEquals(imgUrl, screenshotImage.src);
   });
+
+  /**
+   * Test that when when the send button is clicked, the getAttachedFile has
+   * been called.
+   */
+  test('getAttachedFileCalled', async () => {
+    await initializePage();
+    page.feedbackContext = fakeFeedbackContext;
+
+    const fileAttachment = getElement('file-attachment');
+    const fakeFileData = [11, 22, 99];
+    fileAttachment.getAttachedFile = async () => {
+      return {
+        fileName: stringToMojoString16('fake.zip'),
+        fileData: {
+          bytes: fakeFileData,
+        }
+      };
+    };
+
+    const request = (await clickSendAndWait(page)).report;
+
+    const attachedFile = request.attachedFile;
+    assertTrue(!!attachedFile);
+    assertEquals('fake.zip', mojoString16ToString(attachedFile.fileName));
+    assertArrayEquals(
+        fakeFileData,
+        /** @type {!Array<Number>} */ (attachedFile.fileData.bytes));
+  });
 }
diff --git a/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js b/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js
index 4a8306f4..8f21bff5 100644
--- a/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js
+++ b/chrome/test/data/webui/chromeos/scanning/scanning_app_browsertest.js
@@ -44,7 +44,8 @@
   'SourceSelect'
 ];
 
-TEST_F('ScanningAppBrowserTest', 'All', function() {
+// Flaky. See crbug.com/1334465
+TEST_F('ScanningAppBrowserTest', 'DISABLED_All', function() {
   assertDeepEquals(
       debug_suites_list, test_suites_list,
       'List of registered tests suites and debug suites do not match.\n' +
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index cf606e3..47bdc28d 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -11,6 +11,7 @@
   "about_page_tests.ts",
   "appearance_page_test.ts",
   "clear_browsing_data_test.ts",
+  "cookies_page_test.ts",
   "downloads_page_test.ts",
   "password_check_test.ts",
   "password_edit_dialog_test.ts",
@@ -61,7 +62,6 @@
   "collapse_radio_button_tests.ts",
   "controlled_button_tests.ts",
   "controlled_radio_button_tests.ts",
-  "cookies_page_test.ts",
   "do_not_track_toggle_test.ts",
   "dropdown_menu_tests.ts",
   "extension_controlled_indicator_tests.ts",
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
index 6f5d57ad..db710fe04 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -386,6 +386,7 @@
    {enabled: ['ash::features::kPersonalizationHub']}
  ],
  ['PrintingPage', 'os_printing_page_tests.js'],
+ ['PrivacyHubSubpage', 'privacy_hub_subpage_tests.js'],
  ['PrivacyPage', 'os_privacy_page_test.js'],
  ['ResetPage', 'os_reset_page_test.js'],
  ['SettingsSchedulerSlider', 'settings_scheduler_slider_test.js'],
diff --git a/chrome/test/data/webui/settings/chromeos/privacy_hub_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/privacy_hub_subpage_tests.js
new file mode 100644
index 0000000..a6311cc
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/privacy_hub_subpage_tests.js
@@ -0,0 +1,49 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://os-settings/chromeos/lazy_load.js';
+
+import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
+import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {waitAfterNextRender} from 'chrome://test/test_util.js';
+
+import {assertEquals} from '../../chai_assert.js';
+
+suite('PrivacyHubSubpageTests', function() {
+  /** @type {SettingsPrivacyHubPage} */
+  let privacyHubSubpage = null;
+
+  setup(async () => {
+    loadTimeData.overrideValues({
+      showPrivacyHub: true,
+    });
+
+    PolymerTest.clearBody();
+    privacyHubSubpage = document.createElement('settings-privacy-hub-page');
+    document.body.appendChild(privacyHubSubpage);
+  });
+
+  teardown(function() {
+    privacyHubSubpage.remove();
+    Router.getInstance().resetRouteForTesting();
+  });
+
+  test('Deep link to camera toggle on privacy hub', async () => {
+    const params = new URLSearchParams();
+    params.append('settingId', '1116');
+    Router.getInstance().navigateTo(routes.PRIVACY_HUB, params);
+
+    flush();
+
+    const deepLinkElement =
+        privacyHubSubpage.shadowRoot.querySelector('#cameraToggle')
+            .shadowRoot.querySelector('cr-toggle');
+    await waitAfterNextRender(deepLinkElement);
+    assertEquals(
+        deepLinkElement, getDeepActiveElement(),
+        'Camera toggle should be focused for settingId=1116.');
+  });
+});
\ No newline at end of file
diff --git a/chrome/test/data/webui/settings/cookies_page_test.ts b/chrome/test/data/webui/settings/cookies_page_test.ts
index 82d1b746..f58612ed 100644
--- a/chrome/test/data/webui/settings/cookies_page_test.ts
+++ b/chrome/test/data/webui/settings/cookies_page_test.ts
@@ -25,6 +25,9 @@
   suiteSetup(function() {
     loadTimeData.overrideValues({
       consolidatedSiteStorageControlsEnabled: false,
+      // <if expr="chromeos_lacros">
+      isSecondaryUser: false,
+      // </if>
     });
     settingsPrefs = document.createElement('settings-prefs');
     return CrSettingsPrefs.initialized;
@@ -104,6 +107,20 @@
     assertEquals(PrivacyElementInteractions.COOKIES_SESSION, result);
   });
 
+  // Checks that the sub label for "Clear on Exit" is shown for Lacros primary
+  // profiles, and desktop platforms. It is not shown on Ash.
+  // Note: Secondary Lacros profiles are tested in the suite
+  // `CrSettingsCookiesPageTest_lacrosSecondaryProfile`.
+  test('CookieSessionSublabel', function() {
+    const clearOnExitRow =
+        page.shadowRoot!.querySelector<CrLinkRowElement>('#clearOnExit')!;
+    let expectedSubLabel = '';
+    // <if expr="not chromeos_ash">
+    expectedSubLabel = page.i18n('cookiePageClearOnExitDesc');
+    // </if>
+    assertEquals(clearOnExitRow.subLabel, expectedSubLabel);
+  });
+
   test('CookieSettingExceptions_Search', async function() {
     const exceptionPrefs = createSiteSettingsPrefs([], [
       createContentSettingTypeToValuePair(
@@ -353,3 +370,36 @@
         Router.getInstance().getCurrentRoute(), routes.SITE_SETTINGS_ALL);
   });
 });
+
+// <if expr="chromeos_lacros">
+suite('CrSettingsCookiesPageTest_lacrosSecondaryProfile', function() {
+  let page: SettingsCookiesPageElement;
+  let settingsPrefs: SettingsPrefsElement;
+
+  suiteSetup(function() {
+    loadTimeData.overrideValues({isSecondaryUser: true});
+    settingsPrefs = document.createElement('settings-prefs');
+    return CrSettingsPrefs.initialized;
+  });
+
+  setup(function() {
+    document.body.innerHTML = '';
+    page = document.createElement('settings-cookies-page');
+    page.prefs = settingsPrefs.prefs!;
+    document.body.appendChild(page);
+    flush();
+  });
+
+  teardown(function() {
+    page.remove();
+  });
+
+  // Checks that the sub label for "Clear on Exit" is not shown for secondary
+  // Lacros profiles.
+  test('CookieSessionSublabel', function() {
+    const clearOnExitRow =
+        page.shadowRoot!.querySelector<CrLinkRowElement>('#clearOnExit')!;
+    assertEquals(clearOnExitRow.subLabel, '');
+  });
+});
+// </if>
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 06442d2..d24a272ee 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -631,6 +631,12 @@
   }
 };
 
+GEN('#if BUILDFLAG(IS_CHROMEOS_LACROS)');
+TEST_F('CrSettingsCookiesPageTest', 'LacrosSecondaryProfile', function() {
+  runMochaSuite('CrSettingsCookiesPageTest_lacrosSecondaryProfile');
+});
+GEN('#endif');
+
 TEST_F('CrSettingsRouteTest', 'Basic', function() {
   runMochaSuite('route');
 });
diff --git a/chromeos/ash/components/network/onc/network_onc_utils.cc b/chromeos/ash/components/network/onc/network_onc_utils.cc
index 800ca67..557d49d 100644
--- a/chromeos/ash/components/network/onc/network_onc_utils.cc
+++ b/chromeos/ash/components/network/onc/network_onc_utils.cc
@@ -330,6 +330,24 @@
       ->GetGlobalConfigFromPolicy(username_hash);
 }
 
+// Replaces user-specific string placeholders in |network_configs|, which must
+// be a list of ONC NetworkConfigurations. Currently only user name placeholders
+// are implemented, which are replaced by attributes from |user|.
+void ExpandStringPlaceholdersInNetworksForUser(const user_manager::User* user,
+                                               base::Value* network_configs) {
+  DCHECK(network_configs->is_list());
+  if (!user) {
+    // In tests no user may be logged in. It's not harmful if we just don't
+    // expand the strings.
+    return;
+  }
+
+  // Note: It is OK for the placeholders to be replaced with empty strings if
+  // that is what the getters on |user| provide.
+  VariableExpander variable_expander(GetVariableExpansionsForUser(user));
+  chromeos::onc::ExpandStringsInNetworks(variable_expander, network_configs);
+}
+
 }  // namespace
 
 NetworkTypePattern NetworkTypePatternFromOncType(const std::string& type) {
@@ -467,23 +485,13 @@
   return proxy_settings;
 }
 
-void ExpandStringPlaceholdersInNetworksForUser(const user_manager::User* user,
-                                               base::Value* network_configs) {
-  DCHECK(network_configs->is_list());
-  if (!user) {
-    // In tests no user may be logged in. It's not harmful if we just don't
-    // expand the strings.
-    return;
-  }
-
-  // Note: It is OK for the placeholders to be replaced with empty strings if
-  // that is what the getters on |user| provide.
-  base::flat_map<std::string, std::string> substitutions;
-  substitutions[::onc::substitutes::kLoginID] = user->GetAccountName(false);
-  substitutions[::onc::substitutes::kLoginEmail] =
+base::flat_map<std::string, std::string> GetVariableExpansionsForUser(
+    const user_manager::User* user) {
+  base::flat_map<std::string, std::string> expansions;
+  expansions[::onc::substitutes::kLoginID] = user->GetAccountName(false);
+  expansions[::onc::substitutes::kLoginEmail] =
       user->GetAccountId().GetUserEmail();
-  VariableExpander variable_expander(std::move(substitutions));
-  chromeos::onc::ExpandStringsInNetworks(variable_expander, network_configs);
+  return expansions;
 }
 
 int ImportNetworksForUser(const user_manager::User* user,
@@ -511,8 +519,8 @@
     base::Value normalized_network = normalizer.NormalizeObject(
         &onc::kNetworkConfigurationSignature, network);
 
-    // TODO(pneubeck): Use ONC and ManagedNetworkConfigurationHandler instead.
-    // crbug.com/457936
+    // TODO(b/235297258): Use ONC and ManagedNetworkConfigurationHandler
+    // instead.
     base::Value shill_dict = onc::TranslateONCObjectToShill(
         &onc::kNetworkConfigurationSignature, normalized_network);
 
diff --git a/chromeos/ash/components/network/onc/network_onc_utils.h b/chromeos/ash/components/network/onc/network_onc_utils.h
index 17c325f..1c54ed0 100644
--- a/chromeos/ash/components/network/onc/network_onc_utils.h
+++ b/chromeos/ash/components/network/onc/network_onc_utils.h
@@ -11,6 +11,7 @@
 #include <vector>
 
 #include "base/component_export.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/ref_counted.h"
 #include "chromeos/components/onc/variable_expander.h"
 #include "chromeos/network/network_type_pattern.h"
@@ -51,12 +52,9 @@
 base::Value ConvertProxyConfigToOncProxySettings(
     const base::Value& proxy_config_value);
 
-// Replaces user-specific string placeholders in |network_configs|, which must
-// be a list of ONC NetworkConfigurations. Currently only user name placeholders
-// are implemented, which are replaced by attributes from |user|.
 COMPONENT_EXPORT(CHROMEOS_NETWORK)
-void ExpandStringPlaceholdersInNetworksForUser(const user_manager::User* user,
-                                               base::Value* network_configs);
+base::flat_map<std::string, std::string> GetVariableExpansionsForUser(
+    const user_manager::User* user);
 
 // Returns the number of networks successfully imported.
 COMPONENT_EXPORT(CHROMEOS_NETWORK)
diff --git a/chromeos/components/test/data/onc/repaired_toplevel_partially_invalid.onc b/chromeos/components/test/data/onc/repaired_toplevel_partially_invalid.onc
index 39d0a19..b99505ed 100644
--- a/chromeos/components/test/data/onc/repaired_toplevel_partially_invalid.onc
+++ b/chromeos/components/test/data/onc/repaired_toplevel_partially_invalid.onc
@@ -14,7 +14,7 @@
           "Type": "OpenVPN",
           "OpenVPN": {
               "Port": 1194,
-              "Username": "abc fake email def",
+              "Username": "abc ${LOGIN_ID} def",
               // "UNKNOWN_FIELD" is removed and "OTP" is added (issue 817617).
               "Recommended": [ "Username", "Password", "OTP" ],
               "ClientCertType": "Pattern",
diff --git a/chromeos/network/managed_network_configuration_handler.h b/chromeos/network/managed_network_configuration_handler.h
index 0eaacdb..438c25c 100644
--- a/chromeos/network/managed_network_configuration_handler.h
+++ b/chromeos/network/managed_network_configuration_handler.h
@@ -140,6 +140,16 @@
   // NetworkPolicyObservers are notified about applications finishing.
   virtual bool IsAnyPolicyApplicationRunning() const = 0;
 
+  // Sets ONC variable expansions for |userhash|.
+  // These expansions are profile-wide, i.e. they will apply to all networks
+  // that belong to |userhash|.
+  // This overwrites any previously-set profile-wide variable expansions.
+  // If this call changes the effective ONC policy (after variable expansion) of
+  // any network config, it triggers re-application of that network policy.
+  virtual void SetProfileWideVariableExpansions(
+      const std::string& userhash,
+      base::flat_map<std::string, std::string> expansions) = 0;
+
   // Returns the user policy for user |userhash| or device policy, which has
   // |guid|. If |userhash| is empty, only looks for a device policy. If such
   // doesn't exist, returns NULL. Sets |onc_source| accordingly.
diff --git a/chromeos/network/managed_network_configuration_handler_impl.cc b/chromeos/network/managed_network_configuration_handler_impl.cc
index 91e3eaa..16983be52 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.cc
+++ b/chromeos/network/managed_network_configuration_handler_impl.cc
@@ -491,15 +491,7 @@
 
   // |userhash| must be empty for device policies.
   DCHECK(onc_source != ::onc::ONC_SOURCE_DEVICE_POLICY || userhash.empty());
-  ProfilePolicies* policies = nullptr;
-  if (base::Contains(policies_by_user_, userhash)) {
-    policies = policies_by_user_[userhash].get();
-  } else {
-    auto policies_owned = std::make_unique<ProfilePolicies>();
-    policies = policies_owned.get();
-    policies_by_user_[userhash] = std::move(policies_owned);
-  }
-
+  ProfilePolicies* policies = GetOrCreatePoliciesForUser(userhash);
   policies->SetGlobalNetworkConfig(global_network_config);
 
   // Update prohibited technologies.
@@ -568,6 +560,15 @@
   return true;
 }
 
+void ManagedNetworkConfigurationHandlerImpl::SetProfileWideVariableExpansions(
+    const std::string& userhash,
+    base::flat_map<std::string, std::string> expansions) {
+  base::flat_set<std::string> modified_policy_guids =
+      GetOrCreatePoliciesForUser(userhash)->SetProfileWideExpansions(
+          std::move(expansions));
+  ApplyOrQueuePolicies(userhash, &modified_policy_guids);
+}
+
 void ManagedNetworkConfigurationHandlerImpl::set_ui_proxy_config_service(
     UIProxyConfigService* ui_proxy_config_service) {
   ui_proxy_config_service_ = ui_proxy_config_service;
@@ -910,13 +911,27 @@
   return blocked_hex_ssids;
 }
 
+ProfilePolicies*
+ManagedNetworkConfigurationHandlerImpl::GetOrCreatePoliciesForUser(
+    const std::string& userhash) {
+  auto it = policies_by_user_.find(userhash);
+
+  ProfilePolicies* policies = nullptr;
+  if (it != policies_by_user_.end()) {
+    policies = it->second.get();
+  } else {
+    auto policies_owned = std::make_unique<ProfilePolicies>();
+    policies = policies_owned.get();
+    policies_by_user_[userhash] = std::move(policies_owned);
+  }
+  return policies;
+}
+
 const ProfilePolicies*
 ManagedNetworkConfigurationHandlerImpl::GetPoliciesForUser(
     const std::string& userhash) const {
-  UserToPoliciesMap::const_iterator it = policies_by_user_.find(userhash);
-  if (it == policies_by_user_.end())
-    return nullptr;
-  return it->second.get();
+  auto it = policies_by_user_.find(userhash);
+  return it != policies_by_user_.end() ? it->second.get() : nullptr;
 }
 
 const ProfilePolicies*
diff --git a/chromeos/network/managed_network_configuration_handler_impl.h b/chromeos/network/managed_network_configuration_handler_impl.h
index c03fd117..00ccd0d1 100644
--- a/chromeos/network/managed_network_configuration_handler_impl.h
+++ b/chromeos/network/managed_network_configuration_handler_impl.h
@@ -90,6 +90,10 @@
 
   bool IsAnyPolicyApplicationRunning() const override;
 
+  void SetProfileWideVariableExpansions(
+      const std::string& userhash,
+      base::flat_map<std::string, std::string> expansions) override;
+
   const base::Value* FindPolicyByGUID(
       const std::string userhash,
       const std::string& guid,
@@ -187,6 +191,10 @@
             ProhibitedTechnologiesHandler* prohibitied_technologies_handler);
 
   // Returns the ProfilePolicies for the given |userhash|, or the device
+  // policies if |userhash| is empty. Creates the ProfilePolicies entry if it
+  // does not exist yet.
+  ProfilePolicies* GetOrCreatePoliciesForUser(const std::string& userhash);
+  // Returns the ProfilePolicies for the given |userhash|, or the device
   // policies if |userhash| is empty.
   const ProfilePolicies* GetPoliciesForUser(const std::string& userhash) const;
   // Returns the ProfilePolicies for the given network |profile|. These could be
diff --git a/chromeos/network/mock_managed_network_configuration_handler.h b/chromeos/network/mock_managed_network_configuration_handler.h
index 9dfa17b2..f774278 100644
--- a/chromeos/network/mock_managed_network_configuration_handler.h
+++ b/chromeos/network/mock_managed_network_configuration_handler.h
@@ -66,6 +66,9 @@
                     const base::Value& network_configs_onc,
                     const base::Value& global_network_config));
   MOCK_CONST_METHOD0(IsAnyPolicyApplicationRunning, bool());
+  MOCK_METHOD2(SetProfileWideVariableExpansions,
+               void(const std::string& userhash,
+                    base::flat_map<std::string, std::string> expansions));
   MOCK_CONST_METHOD3(FindPolicyByGUID,
                      const base::Value*(const std::string userhash,
                                         const std::string& guid,
diff --git a/components/autofill/android/OWNERS b/components/autofill/android/OWNERS
new file mode 100644
index 0000000..2329f15
--- /dev/null
+++ b/components/autofill/android/OWNERS
@@ -0,0 +1 @@
+lizapopova@google.com
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index e3d3e90..3aa2f2a0 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -321,6 +321,9 @@
   // is created on the first call.
   virtual content::WebContents* GetWebContentsForJsExecution() = 0;
 
+  // Get the library to be executed before every JS flow action.
+  virtual const std::string& GetJsFlowLibrary() const = 0;
+
   // Get the ElementStore.
   virtual ElementStore* GetElementStore() const = 0;
 
@@ -481,7 +484,8 @@
   // Executes the |external_action|.
   virtual void RequestExternalAction(
       const ExternalActionProto& external_action,
-      base::OnceCallback<void()> start_dom_checks_callback,
+      base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+          start_dom_checks_callback,
       base::OnceCallback<void(const external::Result& result)>
           end_action_callback) = 0;
 
diff --git a/components/autofill_assistant/browser/actions/external_action.cc b/components/autofill_assistant/browser/actions/external_action.cc
index 89ccc1b8..ec25401d 100644
--- a/components/autofill_assistant/browser/actions/external_action.cc
+++ b/components/autofill_assistant/browser/actions/external_action.cc
@@ -43,27 +43,154 @@
   // code after the above call.
 }
 
-void ExternalAction::StartDomChecks() {
+void ExternalAction::StartDomChecks(
+    ExternalActionDelegate::DomUpdateCallback dom_update_callback) {
   const auto& external_action = proto_.external_action();
-  if (external_action.allow_interrupt()) {
+  if (!external_action.conditions().empty() ||
+      external_action.allow_interrupt()) {
+    // We keep track of the fact that we have an active WaitForDom to make sure
+    // we end it gracefully.
+    has_pending_wait_for_dom_ = true;
+    dom_update_callback_ = std::move(dom_update_callback);
+    SetupConditions();
+    // TODO(b/201964908): fix time tracking.
     delegate_->WaitForDom(
         /* max_wait_time= */ base::TimeDelta::Max(),
         /* allow_observer_mode = */ false, external_action.allow_interrupt(),
-        /* observer= */ nullptr, /* check_elements= */ base::DoNothing(),
-        /* callback= */ base::DoNothing());
+        /* observer= */ nullptr,
+        base::BindRepeating(&ExternalAction::RegisterChecks,
+                            weak_ptr_factory_.GetWeakPtr()),
+        base::BindOnce(&ExternalAction::OnWaitForElementTimed,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       base::BindOnce(&ExternalAction::OnDoneWaitForDom,
+                                      weak_ptr_factory_.GetWeakPtr())));
   }
 }
 
+void ExternalAction::SetupConditions() {
+  for (const auto& condition_proto : proto_.external_action().conditions()) {
+    ConditionStatus condition_status;
+    condition_status.proto = condition_proto;
+    conditions_.emplace_back(condition_status);
+  }
+}
+
+void ExternalAction::RegisterChecks(
+    BatchElementChecker* checker,
+    base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback) {
+  if (!callback_) {
+    // Action is done; checks aren't necessary anymore.
+    std::move(wait_for_dom_callback).Run(OkClientStatus());
+    return;
+  }
+
+  for (size_t i = 0; i < conditions_.size(); i++) {
+    checker->AddElementConditionCheck(
+        conditions_[i].proto.element_condition(),
+        base::BindOnce(&ExternalAction::OnPreconditionResult,
+                       weak_ptr_factory_.GetWeakPtr(), i));
+  }
+
+  checker->AddAllDoneCallback(base::BindOnce(
+      &ExternalAction::OnElementChecksDone, weak_ptr_factory_.GetWeakPtr(),
+      std::move(wait_for_dom_callback)));
+}
+
+void ExternalAction::OnPreconditionResult(
+    size_t condition_index,
+    const ClientStatus& status,
+    const std::vector<std::string>& ignored_payloads,
+    const std::vector<std::string>& ignored_tags,
+    const base::flat_map<std::string, DomObjectFrameStack>& ignored_elements) {
+  DCHECK_LT(condition_index, conditions_.size());
+  bool precondition_is_met = status.ok();
+
+  // If this is the first time we perform the check, we consider the
+  // precondition as 'changed' since we always want to send the notification
+  // after the first check.
+  if (first_condition_notification_sent_ &&
+      conditions_[condition_index].result == precondition_is_met) {
+    return;
+  }
+
+  conditions_[condition_index].result = precondition_is_met;
+  conditions_[condition_index].changed = true;
+}
+
+void ExternalAction::OnElementChecksDone(
+    base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback) {
+  // If it was decided to end the action in the meantime, we send an OK status
+  // to WaitForDom so that it can end gracefully. Note that it is possible for
+  // WaitForDom to decide not to call OnDoneWaitForDom, if an interrupt triggers
+  // at the same time.
+  if (external_action_end_requested_) {
+    std::move(wait_for_dom_callback).Run(OkClientStatus());
+    return;
+  }
+
+  external::ElementConditionsUpdate update_proto;
+  for (auto& condition : conditions_) {
+    if (!condition.changed)
+      continue;
+
+    condition.changed = false;
+    external::ElementConditionsUpdate::ConditionResult result;
+    result.set_id(condition.proto.id());
+    result.set_satisfied(condition.result);
+    *update_proto.add_results() = result;
+  }
+
+  // We only send the notification if there were any changes since the last
+  // check.
+  if (!update_proto.results().empty()) {
+    first_condition_notification_sent_ = true;
+    dom_update_callback_.Run(update_proto);
+  }
+
+  // Whether we had satisfied element conditions or not, we run this callback
+  // with |ELEMENT_RESOLUTION_FAILED| to let the WaitForDom know that we want to
+  // keep running checks.
+  std::move(wait_for_dom_callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED));
+}
+
+void ExternalAction::OnDoneWaitForDom(const ClientStatus& status) {
+  if (!callback_) {
+    return;
+  }
+
+  // If |status| is OK we send the external response. Otherwise we ignore the
+  // external response and report the error, as we might not be in a state to
+  // continue with the script.
+  if (status.ok() && external_action_end_requested_) {
+    EndWithExternalResult();
+    return;
+  }
+  EndAction(status);
+}
+
 void ExternalAction::OnExternalActionFinished(const external::Result& result) {
   if (!callback_) {
     return;
   }
 
-  *processed_action_proto_->mutable_external_action_result()
-       ->mutable_result_info() = result.result_info();
+  external_action_result_ = result;
 
-  EndAction(result.success() ? ClientStatus(ACTION_APPLIED)
-                             : ClientStatus(UNKNOWN_ACTION_STATUS));
+  // If there is an ongoing WaitForDom, we end the action on the next WaitForDom
+  // notification to make sure we end gracefully.
+  if (has_pending_wait_for_dom_) {
+    external_action_end_requested_ = true;
+    return;
+  }
+
+  EndWithExternalResult();
+}
+
+void ExternalAction::EndWithExternalResult() {
+  *processed_action_proto_->mutable_external_action_result()
+       ->mutable_result_info() = external_action_result_.result_info();
+  EndAction(external_action_result_.success()
+                ? ClientStatus(ACTION_APPLIED)
+                : ClientStatus(UNKNOWN_ACTION_STATUS));
 }
 
 void ExternalAction::EndAction(const ClientStatus& status) {
diff --git a/components/autofill_assistant/browser/actions/external_action.h b/components/autofill_assistant/browser/actions/external_action.h
index 193e059c1..aaacd8d 100644
--- a/components/autofill_assistant/browser/actions/external_action.h
+++ b/components/autofill_assistant/browser/actions/external_action.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_EXTERNAL_ACTION_H_
 
 #include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/batch_element_checker.h"
 #include "components/autofill_assistant/browser/public/external_action_delegate.h"
 #include "components/autofill_assistant/browser/public/external_script_controller.h"
 #include "components/autofill_assistant/browser/wait_for_dom_observer.h"
@@ -22,15 +23,56 @@
   ~ExternalAction() override;
 
  private:
+  struct ConditionStatus {
+    ExternalActionProto::ExternalCondition proto;
+
+    // We always update the result and send the notification for each condition
+    // after the first check so the initial value of these does not matter.
+    bool result = false;
+    bool changed = false;
+  };
+
   // Overrides Action:
   void InternalProcessAction(ProcessActionCallback callback) override;
 
-  void StartDomChecks();
+  void StartDomChecks(
+      ExternalActionDelegate::DomUpdateCallback dom_update_callback);
+  void SetupConditions();
+  void OnPreconditionResult(
+      size_t condition_index,
+      const ClientStatus& status,
+      const std::vector<std::string>& ignored_payloads,
+      const std::vector<std::string>& ignored_tags,
+      const base::flat_map<std::string, DomObjectFrameStack>& ignored_elements);
+  void RegisterChecks(
+      BatchElementChecker* checker,
+      base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback);
+  void OnElementChecksDone(
+      base::OnceCallback<void(const ClientStatus&)> wait_for_dom_callback);
+  void OnDoneWaitForDom(const ClientStatus& status);
   void OnExternalActionFinished(const external::Result& success);
+  void EndWithExternalResult();
   void EndAction(const ClientStatus& status);
 
   ProcessActionCallback callback_;
 
+  // The list of conditions to be checked.
+  std::vector<ConditionStatus> conditions_;
+  // Keeps track of whether we have already sent the first notification about
+  // the conditions.
+  bool first_condition_notification_sent_ = false;
+  // Whether there is a currently running WaitForDom.
+  bool has_pending_wait_for_dom_ = false;
+  // The callback to notify element condition updates.
+  ExternalActionDelegate::DomUpdateCallback dom_update_callback_;
+
+  // Whether we received a notification from the external caller to end the
+  // action.
+  bool external_action_end_requested_ = false;
+  // The external result reported when the external caller requested to end the
+  // action.
+  external::Result external_action_result_;
+
   base::WeakPtrFactory<ExternalAction> weak_ptr_factory_{this};
 };
 
diff --git a/components/autofill_assistant/browser/actions/external_action_unittest.cc b/components/autofill_assistant/browser/actions/external_action_unittest.cc
index 41fbccc..ac8be3fd 100644
--- a/components/autofill_assistant/browser/actions/external_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/external_action_unittest.cc
@@ -6,9 +6,13 @@
 
 #include "base/test/gmock_callback_support.h"
 #include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_simple_task_runner.h"
 #include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/actions/wait_for_dom_test_base.h"
 #include "components/autofill_assistant/browser/external_action_extension_test.pb.h"
 #include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/web/mock_web_controller.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
@@ -16,11 +20,18 @@
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::ElementsAre;
 using ::testing::Eq;
+using ::testing::Pointee;
 using ::testing::Property;
 using ::testing::Return;
+using ::testing::UnorderedElementsAre;
+using ::testing::WithArgs;
 
-class ExternalActionTest : public ::testing::Test {
+class ExternalActionTest : public WaitForDomTestBase {
+ public:
+  ExternalActionTest() = default;
+
  protected:
   void Run() {
     ON_CALL(mock_action_delegate_, SupportsExternalActions)
@@ -28,13 +39,14 @@
 
     ActionProto action_proto;
     *action_proto.mutable_external_action() = proto_;
-    ExternalAction action(&mock_action_delegate_, action_proto);
-    action.ProcessAction(callback_.Get());
+    action_ =
+        std::make_unique<ExternalAction>(&mock_action_delegate_, action_proto);
+    action_->ProcessAction(callback_.Get());
   }
 
-  MockActionDelegate mock_action_delegate_;
   base::MockCallback<Action::ProcessActionCallback> callback_;
   ExternalActionProto proto_;
+  std::unique_ptr<ExternalAction> action_;
 };
 
 external::Result MakeResult(bool success) {
@@ -118,18 +130,21 @@
   proto_.set_allow_interrupt(true);
 
   EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
-      .WillOnce([](const ExternalActionProto& external_action,
-                   base::OnceCallback<void()> start_dom_checks_callback,
-                   base::OnceCallback<void(const external::Result& result)>
-                       end_action_callback) {
-        std::move(start_dom_checks_callback).Run();
-        std::move(end_action_callback).Run(MakeResult(/* success= */ true));
-      });
-  EXPECT_CALL(mock_action_delegate_, WaitForDom);
+      .WillOnce(
+          [](const ExternalActionProto& external_action,
+             base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+                 start_dom_checks_callback,
+             base::OnceCallback<void(const external::Result& result)>
+                 end_action_callback) {
+            std::move(start_dom_checks_callback).Run(base::DoNothing());
+            std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+          });
   EXPECT_CALL(
       callback_,
       Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
   Run();
+  // The action should end at the next WaitForDom notification.
+  task_env_.FastForwardBy(base::Seconds(1));
 }
 
 TEST_F(ExternalActionTest, ExternalActionWithoutInterrupts) {
@@ -137,13 +152,15 @@
   proto_.set_allow_interrupt(false);
 
   EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
-      .WillOnce([](const ExternalActionProto& external_action,
-                   base::OnceCallback<void()> start_dom_checks_callback,
-                   base::OnceCallback<void(const external::Result& result)>
-                       end_action_callback) {
-        std::move(start_dom_checks_callback).Run();
-        std::move(end_action_callback).Run(MakeResult(/* success= */ true));
-      });
+      .WillOnce(
+          [](const ExternalActionProto& external_action,
+             base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+                 start_dom_checks_callback,
+             base::OnceCallback<void(const external::Result& result)>
+                 end_action_callback) {
+            std::move(start_dom_checks_callback).Run(base::DoNothing());
+            std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+          });
   EXPECT_CALL(mock_action_delegate_, WaitForDom).Times(0);
   EXPECT_CALL(
       callback_,
@@ -156,14 +173,16 @@
   proto_.set_allow_interrupt(true);
 
   EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
-      .WillOnce([](const ExternalActionProto& external_action,
-                   base::OnceCallback<void()> start_dom_checks_callback,
-                   base::OnceCallback<void(const external::Result& result)>
-                       end_action_callback) {
-        // We call the |end_action_callback| without calling
-        // |start_dom_checks_callback|.
-        std::move(end_action_callback).Run(MakeResult(/* success= */ true));
-      });
+      .WillOnce(
+          [](const ExternalActionProto& external_action,
+             base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+                 start_dom_checks_callback,
+             base::OnceCallback<void(const external::Result& result)>
+                 end_action_callback) {
+            // We call the |end_action_callback| without calling
+            // |start_dom_checks_callback|.
+            std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+          });
   EXPECT_CALL(mock_action_delegate_, WaitForDom).Times(0);
   EXPECT_CALL(
       callback_,
@@ -171,5 +190,170 @@
   Run();
 }
 
+TEST_F(ExternalActionTest, ExternalActionWithDomChecks) {
+  proto_.mutable_info();
+  ExternalActionProto::ExternalCondition condition;
+  condition.set_id(55);
+  *condition.mutable_element_condition()->mutable_match() =
+      ToSelectorProto("element");
+  *proto_.add_conditions() = condition;
+
+  base::MockCallback<ExternalActionDelegate::DomUpdateCallback>
+      dom_update_callback;
+
+  EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+      .WillOnce([&dom_update_callback](
+                    const ExternalActionProto& external_action,
+                    base::OnceCallback<void(
+                        ExternalActionDelegate::DomUpdateCallback)>
+                        start_dom_checks_callback,
+                    base::OnceCallback<void(const external::Result& result)>
+                        end_action_callback) {
+        std::move(start_dom_checks_callback).Run(dom_update_callback.Get());
+        std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+      });
+
+  EXPECT_CALL(
+      dom_update_callback,
+      Run(Property(
+          &external::ElementConditionsUpdate::results,
+          ElementsAre(AllOf(
+              Property(&external::ElementConditionsUpdate::ConditionResult::id,
+                       55),
+              Property(&external::ElementConditionsUpdate::ConditionResult::
+                           satisfied,
+                       false))))));
+  Run();
+
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, ACTION_APPLIED))));
+  // The action should end at the next WaitForDom notification.
+  task_env_.FastForwardBy(base::Seconds(1));
+}
+
+TEST_F(ExternalActionTest, DomChecksOnlyUpdateOnChange) {
+  proto_.mutable_info();
+  ExternalActionProto::ExternalCondition changing_condition;
+  changing_condition.set_id(55);
+  *changing_condition.mutable_element_condition()->mutable_match() =
+      ToSelectorProto("changing_condition");
+  ExternalActionProto::ExternalCondition unchanging_condition;
+  unchanging_condition.set_id(9);
+  *unchanging_condition.mutable_element_condition()->mutable_match() =
+      ToSelectorProto("unchanging_condition");
+  *proto_.add_conditions() = changing_condition;
+  *proto_.add_conditions() = unchanging_condition;
+
+  base::MockCallback<ExternalActionDelegate::DomUpdateCallback>
+      dom_update_callback;
+
+  EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+      .WillOnce([&dom_update_callback](
+                    const ExternalActionProto& external_action,
+                    base::OnceCallback<void(
+                        ExternalActionDelegate::DomUpdateCallback)>
+                        start_dom_checks_callback,
+                    base::OnceCallback<void(const external::Result& result)>
+                        end_action_callback) {
+        std::move(start_dom_checks_callback).Run(dom_update_callback.Get());
+      });
+
+  // For the first rounds of checks, all elements should be in the notification.
+  // Note that the |mock_web_controller_| reports an element as missing by
+  // default in the fixture.
+  EXPECT_CALL(
+      dom_update_callback,
+      Run(Property(
+          &external::ElementConditionsUpdate::results,
+          UnorderedElementsAre(
+              AllOf(Property(
+                        &external::ElementConditionsUpdate::ConditionResult::id,
+                        55),
+                    Property(&external::ElementConditionsUpdate::
+                                 ConditionResult::satisfied,
+                             false)),
+              AllOf(Property(
+                        &external::ElementConditionsUpdate::ConditionResult::id,
+                        9),
+                    Property(&external::ElementConditionsUpdate::
+                                 ConditionResult::satisfied,
+                             false))))));
+
+  Run();
+
+  // For the second rounds of checks, we simulate the |changing_condition|
+  // changing to being satisfied and |unchanging_condition| remaining
+  // unsatisfied.
+  EXPECT_CALL(mock_web_controller_,
+              FindElement(Selector({"changing_condition"}), _, _))
+      .WillOnce(WithArgs<2>([](auto&& callback) {
+        std::move(callback).Run(OkClientStatus(),
+                                std::make_unique<ElementFinderResult>());
+      }));
+  EXPECT_CALL(mock_web_controller_,
+              FindElement(Selector({"unchanging_condition"}), _, _))
+      .WillOnce(WithArgs<2>([](auto&& callback) {
+        std::move(callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED),
+                                std::make_unique<ElementFinderResult>());
+      }));
+
+  // The notification should now only contain an entry for |changed_condition|.
+  EXPECT_CALL(
+      dom_update_callback,
+      Run(Property(
+          &external::ElementConditionsUpdate::results,
+          UnorderedElementsAre(AllOf(
+              Property(&external::ElementConditionsUpdate::ConditionResult::id,
+                       55),
+              Property(&external::ElementConditionsUpdate::ConditionResult::
+                           satisfied,
+                       true))))));
+  task_env_.FastForwardBy(base::Seconds(1));
+
+  // We keep the same state as the last roundtrip.
+  EXPECT_CALL(mock_web_controller_,
+              FindElement(Selector({"changing_condition"}), _, _))
+      .WillOnce(WithArgs<2>([](auto&& callback) {
+        std::move(callback).Run(OkClientStatus(),
+                                std::make_unique<ElementFinderResult>());
+      }));
+  EXPECT_CALL(mock_web_controller_,
+              FindElement(Selector({"unchanging_condition"}), _, _))
+      .WillOnce(WithArgs<2>([](auto&& callback) {
+        std::move(callback).Run(ClientStatus(ELEMENT_RESOLUTION_FAILED),
+                                std::make_unique<ElementFinderResult>());
+      }));
+  // Since there were no changes, no notification is sent.
+  EXPECT_CALL(dom_update_callback, Run(_)).Times(0);
+  task_env_.FastForwardBy(base::Seconds(1));
+}
+
+TEST_F(ExternalActionTest, WaitForDomFailure) {
+  proto_.mutable_info();
+  proto_.set_allow_interrupt(true);
+
+  EXPECT_CALL(mock_action_delegate_, RequestExternalAction)
+      .WillOnce(
+          [](const ExternalActionProto& external_action,
+             base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+                 start_dom_checks_callback,
+             base::OnceCallback<void(const external::Result& result)>
+                 end_action_callback) {
+            std::move(start_dom_checks_callback).Run(base::DoNothing());
+            std::move(end_action_callback).Run(MakeResult(/* success= */ true));
+          });
+
+  // Even if the external action ended in a success, if the WaitForDom ends in
+  // an error we expect the error to be reported.
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, INTERRUPT_FAILED))));
+  Run();
+  wait_for_dom_status_ = ClientStatus(INTERRUPT_FAILED);
+  // The action should end at the next WaitForDom notification.
+  task_env_.FastForwardBy(base::Seconds(1));
+}
+
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/js_flow_action.cc b/components/autofill_assistant/browser/actions/js_flow_action.cc
index 1892570..9dd7e7c 100644
--- a/components/autofill_assistant/browser/actions/js_flow_action.cc
+++ b/components/autofill_assistant/browser/actions/js_flow_action.cc
@@ -27,8 +27,9 @@
 JsFlowAction::JsFlowAction(ActionDelegate* delegate, const ActionProto& proto)
     : Action(delegate, proto),
       js_flow_executor_(std::make_unique<JsFlowExecutorImpl>(
+          /* delegate= */ this,
           delegate->GetWebContentsForJsExecution(),
-          this)) {
+          delegate->GetJsFlowLibrary())) {
   DCHECK(proto_.has_js_flow());
 }
 
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index d7eed52f..a4a0735 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -136,6 +136,7 @@
                      password_manager::PasswordChangeSuccessTracker*());
   MOCK_CONST_METHOD0(GetWebContents, content::WebContents*());
   MOCK_METHOD0(GetWebContentsForJsExecution, content::WebContents*());
+  MOCK_METHOD(const std::string&, GetJsFlowLibrary, (), (const override));
   MOCK_CONST_METHOD0(GetWebController, WebController*());
   MOCK_CONST_METHOD0(GetEmailAddressForAccessTokenAccount, std::string());
   MOCK_CONST_METHOD0(GetUkmRecorder, ukm::UkmRecorder*());
@@ -216,11 +217,13 @@
            base::OnceCallback<void(bool, const GetUserDataResponseProto&)>
                callback));
   MOCK_METHOD0(SupportsExternalActions, bool());
-  MOCK_METHOD3(RequestExternalAction,
-               void(const ExternalActionProto& external_action,
-                    base::OnceCallback<void()> start_dom_checks_callback,
-                    base::OnceCallback<void(const external::Result& result)>
-                        end_action_callback));
+  MOCK_METHOD3(
+      RequestExternalAction,
+      void(const ExternalActionProto& external_action,
+           base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+               start_dom_checks_callback,
+           base::OnceCallback<void(const external::Result& result)>
+               end_action_callback));
   MOCK_CONST_METHOD0(MustUseBackendData, bool());
   MOCK_METHOD1(MaybeSetPreviousAction,
                void(const ProcessedActionProto& processed_action));
diff --git a/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc b/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc
index 6aedc042..d8342481 100644
--- a/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc
+++ b/components/autofill_assistant/browser/actions/wait_for_dom_test_base.cc
@@ -73,6 +73,14 @@
   if (!fake_wait_for_dom_done_)
     return;
 
+  // If we manually set |wait_for_dom_status_| to an error status we simulate
+  // the WaitForDom ending in an error.
+  if (!wait_for_dom_status_.ok()) {
+    std::move(fake_wait_for_dom_done_)
+        .Run(wait_for_dom_status_, base::Milliseconds(0));
+    return;
+  }
+
   if (check_elements_result_.ok()) {
     std::move(fake_wait_for_dom_done_)
         .Run(check_elements_result_, base::Milliseconds(fake_wait_time_));
diff --git a/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h b/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h
index 70d28e9..0e1d7e3d 100644
--- a/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h
+++ b/components/autofill_assistant/browser/actions/wait_for_dom_test_base.h
@@ -74,6 +74,7 @@
   std::unique_ptr<base::OneShotTimer> wait_for_dom_timer_;
   int fake_wait_time_ = 0;
   int fake_check_time_ = 0;
+  ClientStatus wait_for_dom_status_ = OkClientStatus();
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/client_context.cc b/components/autofill_assistant/browser/client_context.cc
index aa36888f..35589ce 100644
--- a/components/autofill_assistant/browser/client_context.cc
+++ b/components/autofill_assistant/browser/client_context.cc
@@ -90,6 +90,11 @@
   proto_.mutable_annotate_dom_model_context()->set_model_version(model_version);
 }
 
+void ClientContextImpl::UpdateJsFlowLibraryLoaded(
+    const bool js_flow_library_loaded) {
+  proto_.set_js_flow_library_loaded(js_flow_library_loaded);
+}
+
 ClientContextProto ClientContextImpl::AsProto() const {
   return proto_;
 }
diff --git a/components/autofill_assistant/browser/client_context.h b/components/autofill_assistant/browser/client_context.h
index 03664c7..64665a0 100644
--- a/components/autofill_assistant/browser/client_context.h
+++ b/components/autofill_assistant/browser/client_context.h
@@ -20,6 +20,8 @@
   virtual void Update(const TriggerContext& trigger_context) = 0;
   // Updates the annotate DOM model context.
   virtual void UpdateAnnotateDomModelContext(int64_t model_version) {}
+  // Updates whether the JS flow library is loaded.
+  virtual void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded){};
   // Returns the proto representation of this client context.
   virtual ClientContextProto AsProto() const = 0;
 };
@@ -28,10 +30,11 @@
 class ClientContextImpl : public ClientContext {
  public:
   // |client| must outlive this instance.
-  ClientContextImpl(const Client* client);
+  explicit ClientContextImpl(const Client* client);
   ~ClientContextImpl() override = default;
   void Update(const TriggerContext& trigger_context) override;
   void UpdateAnnotateDomModelContext(int64_t model_version) override;
+  void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded) override;
   ClientContextProto AsProto() const override;
 
  private:
diff --git a/components/autofill_assistant/browser/client_context_unittest.cc b/components/autofill_assistant/browser/client_context_unittest.cc
index 613bc95c..413f905 100644
--- a/components/autofill_assistant/browser/client_context_unittest.cc
+++ b/components/autofill_assistant/browser/client_context_unittest.cc
@@ -67,6 +67,7 @@
   EXPECT_THAT(actual_client_context.window_size().height_pixels(), Eq(1920));
   EXPECT_THAT(actual_client_context.screen_orientation(),
               ClientContextProto::PORTRAIT);
+  EXPECT_EQ(actual_client_context.js_flow_library_loaded(), false);
 #if BUILDFLAG(IS_ANDROID)
   EXPECT_THAT(actual_client_context.platform_type(),
               ClientContextProto::PLATFORM_TYPE_ANDROID);
@@ -208,5 +209,14 @@
               Eq(ClientContextProto::SIGNED_IN));
 }
 
+TEST_F(ClientContextTest, UpdateJsFlowLibraryLoaded) {
+  ClientContextImpl client_context(&mock_client_);
+  EXPECT_EQ(client_context.AsProto().js_flow_library_loaded(), false);
+  client_context.UpdateJsFlowLibraryLoaded(true);
+  EXPECT_EQ(client_context.AsProto().js_flow_library_loaded(), true);
+  client_context.UpdateJsFlowLibraryLoaded(false);
+  EXPECT_EQ(client_context.AsProto().js_flow_library_loaded(), false);
+}
+
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index c3a48082..35a08b1c 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -164,6 +164,15 @@
   return web_contents_for_js_execution_.get();
 }
 
+void Controller::SetJsFlowLibrary(const std::string& js_flow_library) {
+  js_flow_library_ = js_flow_library;
+  GetService()->UpdateJsFlowLibraryLoaded(!js_flow_library_.empty());
+}
+
+const std::string& Controller::GetJsFlowLibrary() const {
+  return js_flow_library_;
+}
+
 std::string Controller::GetEmailAddressForAccessTokenAccount() {
   return client_->GetEmailAddressForAccessTokenAccount();
 }
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index 83244f5..2f5c4ed 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -125,6 +125,8 @@
   GetPasswordChangeSuccessTracker() override;
   content::WebContents* GetWebContents() override;
   content::WebContents* GetWebContentsForJsExecution() override;
+  void SetJsFlowLibrary(const std::string& js_flow_library) override;
+  const std::string& GetJsFlowLibrary() const override;
   std::string GetEmailAddressForAccessTokenAccount() override;
   ukm::UkmRecorder* GetUkmRecorder() override;
   void SetTouchableElementArea(const ElementAreaProto& area) override;
@@ -443,6 +445,7 @@
 
   // Lazily instantiated in GetWebContentsForJsExecution()
   std::unique_ptr<content::WebContents> web_contents_for_js_execution_;
+  std::string js_flow_library_;
 
   base::WeakPtrFactory<Controller> weak_ptr_factory_{this};
 };
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 69df4d8..12c61ab 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -2396,4 +2396,23 @@
                          TriggerContext::Options()));
 }
 
+TEST_F(ControllerTest, SettingJsFlowLibraryWorks) {
+  const std::string js_flow_library = "const st = 2;";
+  EXPECT_EQ(controller_->GetJsFlowLibrary(), "");
+  controller_->SetJsFlowLibrary(js_flow_library);
+  EXPECT_EQ(controller_->GetJsFlowLibrary(), js_flow_library);
+}
+
+TEST_F(ControllerTest, UpdatesJsFlowLibraryLoaded) {
+  EXPECT_CALL(*mock_service_, UpdateJsFlowLibraryLoaded(true));
+
+  controller_->SetJsFlowLibrary("const st = 2;");
+}
+
+TEST_F(ControllerTest, JsFlowLibraryNotLoadedForEmpty) {
+  EXPECT_CALL(*mock_service_, UpdateJsFlowLibraryLoaded(false));
+
+  controller_->SetJsFlowLibrary("");
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index 99ccb057..711d1757 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -66,6 +66,15 @@
   return web_contents_;
 }
 
+void FakeScriptExecutorDelegate::SetJsFlowLibrary(
+    const std::string& js_flow_library) {
+  js_flow_library_ = js_flow_library;
+}
+
+const std::string& FakeScriptExecutorDelegate::GetJsFlowLibrary() const {
+  return js_flow_library_;
+}
+
 std::string FakeScriptExecutorDelegate::GetEmailAddressForAccessTokenAccount() {
   return std::string();
 }
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index 33b770d2..3cdb79c4 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -46,6 +46,8 @@
   GetPasswordChangeSuccessTracker() override;
   content::WebContents* GetWebContents() override;
   content::WebContents* GetWebContentsForJsExecution() override;
+  void SetJsFlowLibrary(const std::string& js_flow_library) override;
+  const std::string& GetJsFlowLibrary() const override;
   std::string GetEmailAddressForAccessTokenAccount() override;
   ukm::UkmRecorder* GetUkmRecorder() override;
   bool EnterState(AutofillAssistantState state) override;
@@ -125,6 +127,7 @@
   raw_ptr<Service> service_ = nullptr;
   raw_ptr<WebController> web_controller_ = nullptr;
   raw_ptr<content::WebContents> web_contents_ = nullptr;
+  std::string js_flow_library_;
   std::unique_ptr<TriggerContext> trigger_context_;
   std::vector<AutofillAssistantState> state_history_;
   std::vector<ElementAreaProto> touchable_element_area_history_;
diff --git a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
index 554e106..c354be7 100644
--- a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.cc
@@ -170,7 +170,8 @@
 
 void FakeScriptExecutorUiDelegate::ExecuteExternalAction(
     const external::Action& external_action,
-    base::OnceCallback<void()> start_dom_checks_callback,
+    base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+        start_dom_checks_callback,
     base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
   external::Result result;
diff --git a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
index 66b0a2a..d6a64b5b 100644
--- a/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_ui_delegate.h
@@ -84,7 +84,8 @@
   bool SupportsExternalActions() override;
   void ExecuteExternalAction(
       const external::Action& external_action,
-      base::OnceCallback<void()> start_dom_checks_callback,
+      base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+          start_dom_checks_callback,
       base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
   void OnInterruptStarted() override;
diff --git a/components/autofill_assistant/browser/headless/headless_ui_controller.cc b/components/autofill_assistant/browser/headless/headless_ui_controller.cc
index 043d44b..5def234b 100644
--- a/components/autofill_assistant/browser/headless/headless_ui_controller.cc
+++ b/components/autofill_assistant/browser/headless/headless_ui_controller.cc
@@ -16,7 +16,8 @@
 
 void HeadlessUiController::ExecuteExternalAction(
     const external::Action& external_action,
-    base::OnceCallback<void()> start_dom_checks_callback,
+    base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+        start_dom_checks_callback,
     base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
   DCHECK(action_extension_delegate_);
diff --git a/components/autofill_assistant/browser/headless/headless_ui_controller.h b/components/autofill_assistant/browser/headless/headless_ui_controller.h
index ed9c09a..eae4973e 100644
--- a/components/autofill_assistant/browser/headless/headless_ui_controller.h
+++ b/components/autofill_assistant/browser/headless/headless_ui_controller.h
@@ -80,7 +80,8 @@
   bool SupportsExternalActions() override;
   void ExecuteExternalAction(
       const external::Action& external_action,
-      base::OnceCallback<void()> start_dom_checks_callback,
+      base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+          start_dom_checks_callback,
       base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
   void OnInterruptStarted() override;
diff --git a/components/autofill_assistant/browser/js_flow_executor_impl.cc b/components/autofill_assistant/browser/js_flow_executor_impl.cc
index 43a05433..5b38ab45 100644
--- a/components/autofill_assistant/browser/js_flow_executor_impl.cc
+++ b/components/autofill_assistant/browser/js_flow_executor_impl.cc
@@ -119,15 +119,17 @@
 }  // namespace
 
 JsFlowExecutorImpl::JsFlowExecutorImpl(
+    Delegate* delegate,
     content::WebContents* web_contents_for_js_execution,
-    Delegate* delegate)
+    const std::string& js_flow_library)
     : delegate_(delegate),
       devtools_client_(std::make_unique<DevtoolsClient>(
           content::DevToolsAgentHost::GetOrCreateFor(
               web_contents_for_js_execution),
           base::FeatureList::IsEnabled(
               autofill_assistant::features::
-                  kAutofillAssistantFullJsFlowStackTraces))) {}
+                  kAutofillAssistantFullJsFlowStackTraces))),
+      js_flow_library_(js_flow_library) {}
 
 JsFlowExecutorImpl::~JsFlowExecutorImpl() = default;
 
@@ -168,11 +170,11 @@
           .SetFrameId(result->GetFrameTree()->GetFrame()->GetId())
           .Build(),
       kMainFrame,
-      base::BindOnce(&JsFlowExecutorImpl::IsolatedWorldCreated,
+      base::BindOnce(&JsFlowExecutorImpl::OnIsolatedWorldCreated,
                      weak_ptr_factory_.GetWeakPtr()));
 }
 
-void JsFlowExecutorImpl::IsolatedWorldCreated(
+void JsFlowExecutorImpl::OnIsolatedWorldCreated(
     const DevtoolsClient::ReplyStatus& reply_status,
     std::unique_ptr<page::CreateIsolatedWorldResult> result) {
   if (!result) {
@@ -184,6 +186,7 @@
   }
 
   isolated_world_context_id_ = result->GetExecutionContextId();
+
   InternalStart();
 }
 
@@ -197,9 +200,9 @@
 
   // Wrap the main js_flow in an async function containing a method to
   // request native actions. This is essentially providing |js_flow| with a
-  // JS API to call native functionality.
-  js_flow_ = std::make_unique<std::string>(
-      base::StrCat({kLeadingWrapper, *js_flow_, kTrailingWrapper}));
+  // JS API to call native functionality. Also adds the js_flow_library.
+  js_flow_ = std::make_unique<std::string>(base::StrCat(
+      {kLeadingWrapper, js_flow_library_, *js_flow_, kTrailingWrapper}));
 
   // Run the wrapped js_flow in the sandbox and serve potential native action
   // requests as they arrive.
diff --git a/components/autofill_assistant/browser/js_flow_executor_impl.h b/components/autofill_assistant/browser/js_flow_executor_impl.h
index c75b2a5..d7d96a0 100644
--- a/components/autofill_assistant/browser/js_flow_executor_impl.h
+++ b/components/autofill_assistant/browser/js_flow_executor_impl.h
@@ -22,8 +22,9 @@
 class JsFlowExecutorImpl : public JsFlowExecutor {
  public:
   // |delegate| must outlive the JsFlowExecutorImpl.
-  JsFlowExecutorImpl(content::WebContents* web_contents_for_js_execution,
-                     Delegate* delegate);
+  JsFlowExecutorImpl(Delegate* delegate,
+                     content::WebContents* web_contents_for_js_execution,
+                     const std::string& js_flow_library);
   ~JsFlowExecutorImpl() override;
   JsFlowExecutorImpl(const JsFlowExecutorImpl&) = delete;
   JsFlowExecutorImpl& operator=(const JsFlowExecutorImpl&) = delete;
@@ -77,11 +78,13 @@
 
  private:
   void InternalStart();
+
   void OnGetFrameTree(const DevtoolsClient::ReplyStatus& reply_status,
                       std::unique_ptr<page::GetFrameTreeResult> result);
-  void IsolatedWorldCreated(
+  void OnIsolatedWorldCreated(
       const DevtoolsClient::ReplyStatus& reply_status,
       std::unique_ptr<page::CreateIsolatedWorldResult> result);
+
   void RefreshNativeActionPromise();
   void OnNativeActionRequested(const DevtoolsClient::ReplyStatus& reply_status,
                                std::unique_ptr<runtime::EvaluateResult> result);
@@ -122,6 +125,8 @@
 
   const raw_ptr<Delegate> delegate_;
   std::unique_ptr<DevtoolsClient> devtools_client_;
+  const std::string& js_flow_library_;
+
   int isolated_world_context_id_ = -1;
 
   // Only set during a flow.
diff --git a/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc b/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
index 3aceb29..73f1a8e 100644
--- a/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
+++ b/components/autofill_assistant/browser/js_flow_executor_impl_browsertest.cc
@@ -77,7 +77,7 @@
     BaseBrowserTest::SetUpOnMainThread();
 
     flow_executor_ = std::make_unique<JsFlowExecutorImpl>(
-        shell()->web_contents(), &mock_delegate_);
+        &mock_delegate_, shell()->web_contents(), "");
   }
 
   // Overload, ignore result value, just return the client status.
@@ -416,6 +416,5 @@
             ACTION_APPLIED);
   EXPECT_EQ(*result, base::Value(5));
 }
-
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/js_flow_util.cc b/components/autofill_assistant/browser/js_flow_util.cc
index 4c1a895..ed8f5a6 100644
--- a/components/autofill_assistant/browser/js_flow_util.cc
+++ b/components/autofill_assistant/browser/js_flow_util.cc
@@ -4,6 +4,7 @@
 
 #include "components/autofill_assistant/browser/js_flow_util.h"
 #include "base/base64.h"
+#include "base/logging.h"
 #include "base/strings/strcat.h"
 #include "components/autofill_assistant/browser/model.pb.h"
 #include "components/autofill_assistant/browser/service.pb.h"
@@ -150,6 +151,7 @@
   }
 
   if (!value.is_dict()) {
+    VLOG(1) << "JS flow did not return a dictionary.";
     return ClientStatusWithSourceLocation(INVALID_ACTION, __FILE__, __LINE__);
   }
 
@@ -157,6 +159,7 @@
   absl::optional<int> flow_status = dict->FindInt(kStatusKey);
   const base::Value* flow_return_value = dict->Find(kResultKey);
   if (!flow_status || !ProcessedActionStatusProto_IsValid(*flow_status)) {
+    VLOG(1) << "JS flow did not return a valid ActionStatus in " << kStatusKey;
     return ClientStatusWithSourceLocation(INVALID_ACTION, __FILE__, __LINE__);
   }
 
diff --git a/components/autofill_assistant/browser/mock_client_context.h b/components/autofill_assistant/browser/mock_client_context.h
index 2827a9a..43debe39 100644
--- a/components/autofill_assistant/browser/mock_client_context.h
+++ b/components/autofill_assistant/browser/mock_client_context.h
@@ -18,9 +18,19 @@
   MockClientContext();
   ~MockClientContext() override;
 
-  MOCK_METHOD1(Update, void(const TriggerContext& trigger_context));
-  MOCK_METHOD1(UpdateAnnotateDomModelContext, void(int64_t model_version));
-  MOCK_CONST_METHOD0(AsProto, ClientContextProto());
+  MOCK_METHOD(void,
+              Update,
+              (const TriggerContext& trigger_context),
+              (override));
+  MOCK_METHOD(void,
+              UpdateAnnotateDomModelContext,
+              (int64_t model_version),
+              (override));
+  MOCK_METHOD(void,
+              UpdateJsFlowLibraryLoaded,
+              (bool js_flow_library_loaded),
+              (override));
+  MOCK_METHOD(ClientContextProto, AsProto, (), (const override));
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/mock_script_executor_delegate.h b/components/autofill_assistant/browser/mock_script_executor_delegate.h
index 7b872467..df6de1b 100644
--- a/components/autofill_assistant/browser/mock_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/mock_script_executor_delegate.h
@@ -53,6 +53,12 @@
               GetWebContentsForJsExecution,
               (),
               (override));
+  MOCK_METHOD(void,
+              SetJsFlowLibrary,
+              (const std::string& js_flow_library),
+              (override));
+  MOCK_METHOD(const std::string&, GetJsFlowLibrary, (), (const override));
+
   MOCK_METHOD(std::string,
               GetEmailAddressForAccessTokenAccount,
               (),
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index b3c147e6..30fc7f0 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -208,6 +208,7 @@
   ScriptActionRequestProto request_proto;
   request_proto.set_global_payload(global_payload);
   request_proto.set_script_payload(script_payload);
+
   NextScriptActionsRequestProto* next_request =
       request_proto.mutable_next_request();
   for (const auto& processed_action : processed_actions) {
@@ -768,7 +769,8 @@
                                  std::string* return_script_payload,
                                  std::vector<std::unique_ptr<Action>>* actions,
                                  std::vector<std::unique_ptr<Script>>* scripts,
-                                 bool* should_update_scripts) {
+                                 bool* should_update_scripts,
+                                 std::string* js_flow_library) {
   DCHECK(actions);
   DCHECK(scripts);
 
@@ -787,6 +789,9 @@
   if (return_script_payload) {
     *return_script_payload = response_proto.script_payload();
   }
+  if (js_flow_library) {
+    *js_flow_library = std::move(*response_proto.mutable_js_flow_library());
+  }
 
   for (const auto& action : response_proto.actions()) {
     std::unique_ptr<Action> client_action = CreateAction(delegate, action);
diff --git a/components/autofill_assistant/browser/protocol_utils.h b/components/autofill_assistant/browser/protocol_utils.h
index ab906e3..52e872e 100644
--- a/components/autofill_assistant/browser/protocol_utils.h
+++ b/components/autofill_assistant/browser/protocol_utils.h
@@ -116,7 +116,8 @@
                            std::string* return_script_payload,
                            std::vector<std::unique_ptr<Action>>* actions,
                            std::vector<std::unique_ptr<Script>>* scripts,
-                           bool* should_update_scripts);
+                           bool* should_update_scripts,
+                           std::string* js_flow_library);
 
   // Parses a single serialized ActionProto. Returns nullptr in the case of
   // parsing errors.
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 4438fd4..a4a30ff6 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -230,11 +230,12 @@
   bool unused;
   std::vector<std::unique_ptr<Action>> unused_actions;
   std::vector<std::unique_ptr<Script>> unused_scripts;
+  std::string unused_js_flow_library;
   EXPECT_FALSE(ProtocolUtils::ParseActions(
       /* delegate= */ nullptr, /* response= */ "invalid", /* run_id= */ nullptr,
-      /* global_payload= */ nullptr,
-      /* script_payload= */ nullptr, &unused_actions, &unused_scripts,
-      /* should_update_scripts= */ &unused));
+      /* return_global_payload= */ nullptr,
+      /* return_script_payload= */ nullptr, &unused_actions, &unused_scripts,
+      /* should_update_scripts= */ &unused, &unused_js_flow_library));
 }
 
 TEST_F(ProtocolUtilsTest, ParseActionParseError) {
@@ -258,10 +259,11 @@
   bool should_update_scripts = true;
   std::vector<std::unique_ptr<Action>> actions;
   std::vector<std::unique_ptr<Script>> scripts;
+  std::string unused_js_flow_library;
 
   EXPECT_TRUE(ProtocolUtils::ParseActions(
       nullptr, proto_str, &run_id, &global_payload, &script_payload, &actions,
-      &scripts, &should_update_scripts));
+      &scripts, &should_update_scripts, &unused_js_flow_library));
   EXPECT_EQ(1u, run_id);
   EXPECT_EQ("global_payload", global_payload);
   EXPECT_EQ("script_payload", script_payload);
@@ -288,11 +290,12 @@
   bool should_update_scripts = false;
   std::vector<std::unique_ptr<Script>> scripts;
   std::vector<std::unique_ptr<Action>> unused_actions;
+  std::string unused_js_flow_library;
 
   EXPECT_TRUE(ProtocolUtils::ParseActions(
       nullptr, proto_str, /* run_id= */ nullptr, /* global_payload= */ nullptr,
       /* script_payload */ nullptr, &unused_actions, &scripts,
-      &should_update_scripts));
+      &should_update_scripts, &unused_js_flow_library));
   EXPECT_TRUE(should_update_scripts);
   EXPECT_TRUE(scripts.empty());
 }
@@ -313,11 +316,13 @@
   bool should_update_scripts = false;
   std::vector<std::unique_ptr<Script>> scripts;
   std::vector<std::unique_ptr<Action>> unused_actions;
+  std::string unused_js_flow_library;
 
   EXPECT_TRUE(ProtocolUtils::ParseActions(
-      nullptr, proto_str, /* run_id= */ nullptr, /* global_payload= */ nullptr,
-      /* script_payload= */ nullptr, &unused_actions, &scripts,
-      &should_update_scripts));
+      nullptr, proto_str, /* run_id= */ nullptr,
+      /* return_global_payload= */ nullptr,
+      /* return_script_payload= */ nullptr, &unused_actions, &scripts,
+      &should_update_scripts, &unused_js_flow_library));
   EXPECT_TRUE(should_update_scripts);
   EXPECT_THAT(scripts, SizeIs(1));
   EXPECT_THAT("a", Eq(scripts[0]->handle.path));
diff --git a/components/autofill_assistant/browser/public/external_action.proto b/components/autofill_assistant/browser/public/external_action.proto
index e75afaf..595f218 100644
--- a/components/autofill_assistant/browser/public/external_action.proto
+++ b/components/autofill_assistant/browser/public/external_action.proto
@@ -35,3 +35,18 @@
 message ResultInfo {
   extensions 100 to max;
 }
+
+// An update on the status of the DOM conditions for the current action.
+message ElementConditionsUpdate {
+  // The list of conditions whose status changed since the last update. The
+  // first update for each action will contain all the conditions which are
+  // specified in the equivalent action in the backend.
+  message ConditionResult {
+    // The identifier of this condition, matches the one specified in the
+    // equivalent action in the backend.
+    optional int32 id = 1;
+    // Whether the condition was satisfied.
+    optional bool satisfied = 2;
+  }
+  repeated ConditionResult results = 1;
+}
diff --git a/components/autofill_assistant/browser/public/external_action_delegate.h b/components/autofill_assistant/browser/public/external_action_delegate.h
index 32161ab0..427ef0f 100644
--- a/components/autofill_assistant/browser/public/external_action_delegate.h
+++ b/components/autofill_assistant/browser/public/external_action_delegate.h
@@ -14,6 +14,10 @@
 // script.
 class ExternalActionDelegate {
  public:
+  // Called to notify a change in the DOM.
+  using DomUpdateCallback =
+      base::RepeatingCallback<void(const external::ElementConditionsUpdate&)>;
+
   virtual ~ExternalActionDelegate() = default;
   // Called when the script reaches an external action.
   // The |start_dom_checks_callback| can optionally be called to start the DOM
@@ -22,15 +26,15 @@
   // resume the execution of the rest of the script.
   virtual void OnActionRequested(
       const external::Action& action_info,
-      base::OnceCallback<void()> start_dom_checks_callback,
-      base::OnceCallback<void(const external::Result& result)>
+      base::OnceCallback<void(DomUpdateCallback)> start_dom_checks_callback,
+      base::OnceCallback<void(const external::Result&)>
           end_action_callback) = 0;
 
   // Called before starting the execution of an interrupt.
   virtual void OnInterruptStarted() = 0;
 
   // Called after finishing to execute an interrupt, before resuming the
-  // execution of the main script
+  // execution of the main script.
   virtual void OnInterruptFinished() = 0;
 };
 
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 0b68013b..19ceffd 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -633,6 +633,10 @@
   return delegate_->GetWebContentsForJsExecution();
 }
 
+const std::string& ScriptExecutor::GetJsFlowLibrary() const {
+  return delegate_->GetJsFlowLibrary();
+}
+
 ElementStore* ScriptExecutor::GetElementStore() const {
   return element_store_.get();
 }
@@ -833,6 +837,7 @@
       roundtrip_duration.InMilliseconds());
   bool success = http_status == net::HTTP_OK &&
                  ProcessNextActionResponse(response, response_info);
+
   if (should_stop_script_) {
     // The last action forced the script to stop. Sending the result of the
     // action is considered best effort in this situation. Report a successful
@@ -874,14 +879,19 @@
   actions_.clear();
 
   bool should_update_scripts = false;
+  std::string js_flow_library;
   std::vector<std::unique_ptr<Script>> scripts;
   bool parse_result = ProtocolUtils::ParseActions(
       this, response, &run_id_, &last_global_payload_, &last_script_payload_,
-      &actions_, &scripts, &should_update_scripts);
+      &actions_, &scripts, &should_update_scripts, &js_flow_library);
   if (!parse_result) {
     return false;
   }
 
+  if (!js_flow_library.empty()) {
+    delegate_->SetJsFlowLibrary(js_flow_library);
+  }
+
   roundtrip_network_stats_ =
       ProtocolUtils::ComputeNetworkStats(response, response_info, actions_);
   ReportPayloadsToListener();
@@ -1131,7 +1141,8 @@
 
 void ScriptExecutor::RequestExternalAction(
     const ExternalActionProto& external_action,
-    base::OnceCallback<void()> start_dom_checks_callback,
+    base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+        start_dom_checks_callback,
     base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
   bool prompt = external_action.allow_interrupt() ||
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index bf54420e..a0996f2ba 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -211,6 +211,7 @@
   GetPasswordChangeSuccessTracker() const override;
   content::WebContents* GetWebContents() const override;
   content::WebContents* GetWebContentsForJsExecution() override;
+  const std::string& GetJsFlowLibrary() const override;
   ElementStore* GetElementStore() const override;
   WebController* GetWebController() const override;
   std::string GetEmailAddressForAccessTokenAccount() const override;
@@ -270,7 +271,8 @@
   bool SupportsExternalActions() override;
   void RequestExternalAction(
       const ExternalActionProto& external_action,
-      base::OnceCallback<void()> start_dom_checks_callback,
+      base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+          start_dom_checks_callback,
       base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
   bool MustUseBackendData() const override;
diff --git a/components/autofill_assistant/browser/script_executor_browsertest.cc b/components/autofill_assistant/browser/script_executor_browsertest.cc
index c9ed1cf..c4c7b64 100644
--- a/components/autofill_assistant/browser/script_executor_browsertest.cc
+++ b/components/autofill_assistant/browser/script_executor_browsertest.cc
@@ -31,6 +31,8 @@
 
 using ::base::test::RunOnceCallback;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::DoAll;
 using ::testing::ElementsAre;
 using ::testing::Eq;
 using ::testing::Field;
@@ -43,6 +45,7 @@
 using ::testing::SizeIs;
 using ::testing::StrictMock;
 using ::testing::WithArg;
+using ::testing::WithArgs;
 
 class ScriptExecutorBrowserTest : public BaseBrowserTest {
  public:
@@ -66,11 +69,10 @@
                                      actions_response.SerializeAsString(),
                                      ServiceRequestSender::ResponseInfo{}));
 
-    EXPECT_CALL(mock_service_,
-                GetNextActions(_, _, _, processed_actions_matcher_, _, _, _))
-        .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
-                                     ActionsResponseProto().SerializeAsString(),
-                                     ServiceRequestSender::ResponseInfo{}));
+    ON_CALL(mock_service_, GetNextActions)
+        .WillByDefault(RunOnceCallback<6>(
+            net::HTTP_OK, ActionsResponseProto().SerializeAsString(),
+            ServiceRequestSender::ResponseInfo{}));
 
     base::RunLoop run_loop;
 
@@ -89,11 +91,21 @@
 
   void RunJsFlow(const std::string& js_flow,
                  const ProcessedActionStatusProto& result) {
-    processed_actions_matcher_ =
-        ElementsAre(Property(&ProcessedActionProto::status, result));
     EXPECT_CALL(executor_callback_,
                 Run(Field(&ScriptExecutor::Result::success, true)));
 
+    EXPECT_CALL(mock_service_, GetNextActions)
+        .WillOnce(DoAll(
+            WithArgs<3>([&result](const std::vector<ProcessedActionProto>&
+                                      processed_actions) {
+              EXPECT_THAT(
+                  processed_actions,
+                  ElementsAre(Property(&ProcessedActionProto::status, result)));
+            }),
+            RunOnceCallback<6>(net::HTTP_OK,
+                               ActionsResponseProto().SerializeAsString(),
+                               ServiceRequestSender::ResponseInfo{})));
+
     ActionsResponseProto actions_response;
     actions_response.add_actions()->mutable_js_flow()->set_js_flow(js_flow);
     Run(actions_response);
@@ -112,8 +124,6 @@
 
   StrictMock<base::MockCallback<ScriptExecutor::RunScriptCallback>>
       executor_callback_;
-  Matcher<const std::vector<ProcessedActionProto>&> processed_actions_matcher_ =
-      _;
 };
 
 std::string CreateRunNativeActionCall(
@@ -253,6 +263,160 @@
   Run(actions_response);
 }
 
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsLibraryIsUsed) {
+  ActionsResponseProto actions_response;
+  actions_response.set_js_flow_library("const status = 2;");
+  actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+      "return {status};");
+
+  EXPECT_CALL(mock_service_,
+              GetNextActions(_, _, _,
+                             ElementsAre(Property(&ProcessedActionProto::status,
+                                                  ACTION_APPLIED)),
+                             _, _, _))
+      .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+                                   ActionsResponseProto().SerializeAsString(),
+                                   ServiceRequestSender::ResponseInfo{}));
+
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
+
+  Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsLibraryIsReused) {
+  ActionsResponseProto actions_response;
+  actions_response.set_js_flow_library("const status = 2;");
+  actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+      "return {status};");
+  actions_response.add_actions()->mutable_js_flow()->set_js_flow(
+      "return {status};");
+
+  EXPECT_CALL(
+      mock_service_,
+      GetNextActions(
+          _, _, _,
+          ElementsAre(Property(&ProcessedActionProto::status, ACTION_APPLIED),
+                      Property(&ProcessedActionProto::status, ACTION_APPLIED)),
+          _, _, _))
+      .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+                                   ActionsResponseProto().SerializeAsString(),
+                                   ServiceRequestSender::ResponseInfo{}));
+
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
+
+  Run(actions_response);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsLibraryIsUsedAccrossCalls) {
+  ActionsResponseProto actions_response_1;
+  actions_response_1.set_js_flow_library("const st = 2;");
+  actions_response_1.add_actions()->mutable_js_flow()->set_js_flow(
+      "return {status: st};");
+
+  ActionsResponseProto actions_response_2;
+  actions_response_2.add_actions()->mutable_js_flow()->set_js_flow(
+      "return {status: st};");
+
+  EXPECT_CALL(mock_service_,
+              GetNextActions(_, _, _,
+                             ElementsAre(Property(&ProcessedActionProto::status,
+                                                  ACTION_APPLIED)),
+                             _, _, _))
+      .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+                                   actions_response_2.SerializeAsString(),
+                                   ServiceRequestSender::ResponseInfo{}))
+      .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+                                   ActionsResponseProto().SerializeAsString(),
+                                   ServiceRequestSender::ResponseInfo{}));
+
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
+
+  Run(actions_response_1);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsFlowErrorInJsLibraryFlow) {
+  ActionsResponseProto actions_response_1;
+  actions_response_1.set_js_flow_library("throw new Error();");
+  actions_response_1.add_actions()->mutable_js_flow()->set_js_flow(
+      "return {status: 2};");
+
+  EXPECT_CALL(mock_service_, GetNextActions)
+      .WillOnce(DoAll(
+          WithArgs<3>(
+              [](const std::vector<ProcessedActionProto>& processed_actions) {
+                ASSERT_THAT(processed_actions, SizeIs(1));
+
+                const auto& processed_action = processed_actions[0];
+                EXPECT_THAT(processed_action,
+                            Property(&ProcessedActionProto::status,
+                                     UNEXPECTED_JS_ERROR));
+
+                const auto& unexpected_error_info =
+                    processed_action.status_details().unexpected_error_info();
+                EXPECT_THAT(
+                    unexpected_error_info,
+                    Property(
+                        &UnexpectedErrorInfoProto::js_exception_line_numbers,
+                        ElementsAre(0)));
+                EXPECT_THAT(
+                    unexpected_error_info,
+                    Property(
+                        &UnexpectedErrorInfoProto::js_exception_column_numbers,
+                        ElementsAre(6)));
+              }),
+          RunOnceCallback<6>(net::HTTP_OK,
+                             ActionsResponseProto().SerializeAsString(),
+                             ServiceRequestSender::ResponseInfo{})));
+
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
+
+  Run(actions_response_1);
+}
+
+IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, JsFlowErrorInJsFlow) {
+  ActionsResponseProto actions_response_1;
+  actions_response_1.set_js_flow_library("const st = 2;\n");
+  actions_response_1.add_actions()->mutable_js_flow()->set_js_flow(
+      "throw new Error();");
+
+  EXPECT_CALL(mock_service_, GetNextActions)
+      .WillOnce(DoAll(
+          WithArgs<3>(
+              [](const std::vector<ProcessedActionProto>& processed_actions) {
+                ASSERT_THAT(processed_actions, SizeIs(1));
+
+                const auto& processed_action = processed_actions[0];
+                EXPECT_THAT(processed_action,
+                            Property(&ProcessedActionProto::status,
+                                     UNEXPECTED_JS_ERROR));
+
+                const auto& unexpected_error_info =
+                    processed_action.status_details().unexpected_error_info();
+                EXPECT_THAT(
+                    unexpected_error_info,
+                    Property(
+                        &UnexpectedErrorInfoProto::js_exception_line_numbers,
+                        ElementsAre(1)));
+                EXPECT_THAT(
+                    unexpected_error_info,
+                    Property(
+                        &UnexpectedErrorInfoProto::js_exception_column_numbers,
+                        ElementsAre(6)));
+              }),
+          RunOnceCallback<6>(net::HTTP_OK,
+                             ActionsResponseProto().SerializeAsString(),
+                             ServiceRequestSender::ResponseInfo{})));
+
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
+
+  Run(actions_response_1);
+}
+
 IN_PROC_BROWSER_TEST_F(ScriptExecutorBrowserTest, WaitForDomSucceeds) {
   WaitForDomProto wait_for_dom;
   wait_for_dom.mutable_wait_condition()
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index 42d5e5a..c2b2911 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -63,10 +63,13 @@
   virtual password_manager::PasswordChangeSuccessTracker*
   GetPasswordChangeSuccessTracker() = 0;
   virtual content::WebContents* GetWebContents() = 0;
-
   // Get dummy web contents that can be used for JS execution. The web contents
   // is created on the first call.
   virtual content::WebContents* GetWebContentsForJsExecution() = 0;
+
+  virtual void SetJsFlowLibrary(const std::string& js_flow_library) = 0;
+  virtual const std::string& GetJsFlowLibrary() const = 0;
+
   virtual std::string GetEmailAddressForAccessTokenAccount() = 0;
   virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
 
@@ -156,7 +159,7 @@
   virtual bool MustUseBackendData() const = 0;
 
  protected:
-  virtual ~ScriptExecutorDelegate() {}
+  virtual ~ScriptExecutorDelegate() = default;
 };
 }  // namespace autofill_assistant
 
diff --git a/components/autofill_assistant/browser/script_executor_ui_delegate.h b/components/autofill_assistant/browser/script_executor_ui_delegate.h
index ea5cbc4..cd927d8 100644
--- a/components/autofill_assistant/browser/script_executor_ui_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_ui_delegate.h
@@ -98,7 +98,8 @@
   // Executes the external action.
   virtual void ExecuteExternalAction(
       const external::Action& external_action,
-      base::OnceCallback<void()> start_dom_checks_callback,
+      base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+          start_dom_checks_callback,
       base::OnceCallback<void(const external::Result& result)>
           end_action_callback) = 0;
 
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 469b2dc..8b4a25b 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -2392,5 +2392,26 @@
                           AutofillAssistantState::RUNNING));
 }
 
+TEST_F(ScriptExecutorTest, JsFlowLibraryUpdated) {
+  ActionsResponseProto actions_response;
+  actions_response.set_js_flow_library("const status = 2;");
+  actions_response.add_actions()->mutable_tell()->set_message("message");
+
+  EXPECT_CALL(mock_service_, GetActions)
+      .WillOnce(RunOnceCallback<5>(net::HTTP_OK,
+                                   actions_response.SerializeAsString(),
+                                   ServiceRequestSender::ResponseInfo{}));
+  EXPECT_CALL(mock_service_, GetNextActions)
+      .WillOnce(RunOnceCallback<6>(net::HTTP_OK,
+                                   ActionsResponseProto().SerializeAsString(),
+                                   ServiceRequestSender::ResponseInfo{}));
+
+  EXPECT_CALL(executor_callback_,
+              Run(Field(&ScriptExecutor::Result::success, true)));
+
+  executor_->Run(&user_data_, executor_callback_.Get());
+  EXPECT_EQ(delegate_.GetJsFlowLibrary(), actions_response.js_flow_library());
+}
+
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 8b90962a..413309db 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -28,7 +28,7 @@
 }
 
 // Context contains client environment details.
-// Next ID: 22
+// Next ID: 24
 message ClientContextProto {
   message Chrome {
     optional string chrome_version = 1;
@@ -143,6 +143,9 @@
   }
   optional AnnotateDomModelContextProto annotate_dom_model_context = 22;
 
+  // Set if the client has the JS flow library needed for JS flow execution.
+  optional bool js_flow_library_loaded = 23;
+
   reserved 19, 21;
 }
 
@@ -748,6 +751,11 @@
   message UpdateScriptListProto { repeated SupportedScriptProto scripts = 1; }
   optional UpdateScriptListProto update_script_list = 5;
 
+  // Needs to be evaluated before each JS flow action. Contains function
+  // definitions that js flows might call.
+  // Only set if ClientContextProto::js_flow_library_loaded is false.
+  optional string js_flow_library = 13;
+
   // Id of the current run.
   optional uint64 run_id = 12;
 
@@ -3428,7 +3436,9 @@
     // field will be empty and the action will return INVALID_ACTION.
     optional string result_json = 1;
   }
-  // The JS flow to execute in a sandbox.
+
+  // The JS flow to execute in a sandbox. If present
+  // ActionsResponseProto::js_flow_library needs to be evaluated first.
   optional string js_flow = 1;
 }
 
@@ -3447,6 +3457,17 @@
   // state.
   optional bool allow_interrupt = 3;
 
+  // A condition on the DOM. Whenever its status changes a notification is sent
+  // to the external caller.
+  message ExternalCondition {
+    // The identifier to be used in the external notification.
+    optional int32 id = 1;
+
+    // The condition to be checked internally.
+    optional ElementConditionProto element_condition = 2;
+  }
+  repeated ExternalCondition conditions = 4;
+
   message Result {
     optional external.ResultInfo result_info = 1;
   }
diff --git a/components/autofill_assistant/browser/service/mock_service.h b/components/autofill_assistant/browser/service/mock_service.h
index abc4cd2..02d06509 100644
--- a/components/autofill_assistant/browser/service/mock_service.h
+++ b/components/autofill_assistant/browser/service/mock_service.h
@@ -63,6 +63,10 @@
               UpdateAnnotateDomModelContext,
               (int64_t model_version),
               (override));
+  MOCK_METHOD(void,
+              UpdateJsFlowLibraryLoaded,
+              (bool js_flow_library_loaded),
+              (override));
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service.h b/components/autofill_assistant/browser/service/service.h
index c0154c0d..c879077e0 100644
--- a/components/autofill_assistant/browser/service/service.h
+++ b/components/autofill_assistant/browser/service/service.h
@@ -62,6 +62,8 @@
 
   virtual void UpdateAnnotateDomModelContext(int64_t model_version) {}
 
+  virtual void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded){};
+
  protected:
   Service() = default;
 };
diff --git a/components/autofill_assistant/browser/service/service_impl.cc b/components/autofill_assistant/browser/service/service_impl.cc
index d713e03..00f11d6f 100644
--- a/components/autofill_assistant/browser/service/service_impl.cc
+++ b/components/autofill_assistant/browser/service/service_impl.cc
@@ -214,4 +214,8 @@
   client_context_->UpdateAnnotateDomModelContext(model_version);
 }
 
+void ServiceImpl::UpdateJsFlowLibraryLoaded(const bool js_flow_library_loaded) {
+  client_context_->UpdateJsFlowLibraryLoaded(js_flow_library_loaded);
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service/service_impl.h b/components/autofill_assistant/browser/service/service_impl.h
index e16ee262..aaadc0b6 100644
--- a/components/autofill_assistant/browser/service/service_impl.h
+++ b/components/autofill_assistant/browser/service/service_impl.h
@@ -96,6 +96,8 @@
 
   void UpdateAnnotateDomModelContext(int64_t model_version) override;
 
+  void UpdateJsFlowLibraryLoaded(bool js_flow_library_loaded) override;
+
  private:
   void SendUserDataRequest(
       uint64_t run_id,
diff --git a/components/autofill_assistant/browser/service/service_impl_unittest.cc b/components/autofill_assistant/browser/service/service_impl_unittest.cc
index f2504a6e..860db4a 100644
--- a/components/autofill_assistant/browser/service/service_impl_unittest.cc
+++ b/components/autofill_assistant/browser/service/service_impl_unittest.cc
@@ -259,5 +259,10 @@
   service_->UpdateAnnotateDomModelContext(123456);
 }
 
+TEST_F(ServiceImplTest, UpdateJsFlowLibraryLoaded) {
+  EXPECT_CALL(*mock_client_context_, UpdateJsFlowLibraryLoaded(true));
+  service_->UpdateJsFlowLibraryLoaded(true);
+}
+
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/ui_controller.cc b/components/autofill_assistant/browser/ui_controller.cc
index 3aab78c..4e56560 100644
--- a/components/autofill_assistant/browser/ui_controller.cc
+++ b/components/autofill_assistant/browser/ui_controller.cc
@@ -1180,7 +1180,8 @@
 
 void UiController::ExecuteExternalAction(
     const external::Action& external_action,
-    base::OnceCallback<void()> start_dom_checks_callback,
+    base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+        start_dom_checks_callback,
     base::OnceCallback<void(const external::Result& result)>
         end_action_callback) {
   NOTREACHED() << "Flows using default UI don't support external actions.";
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index a8c8aa4..abb003c5 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -198,7 +198,8 @@
   bool SupportsExternalActions() override;
   void ExecuteExternalAction(
       const external::Action& external_action,
-      base::OnceCallback<void()> start_dom_checks_callback,
+      base::OnceCallback<void(ExternalActionDelegate::DomUpdateCallback)>
+          start_dom_checks_callback,
       base::OnceCallback<void(const external::Result& result)>
           end_action_callback) override;
 
diff --git a/components/dom_distiller/ios/BUILD.gn b/components/dom_distiller/ios/BUILD.gn
index 29c300e..3cff524 100644
--- a/components/dom_distiller/ios/BUILD.gn
+++ b/components/dom_distiller/ios/BUILD.gn
@@ -17,6 +17,7 @@
     "//components/dom_distiller/core/proto",
     "//components/favicon/ios",
     "//ios/web/public",
+    "//ios/web/public/js_messaging",
     "//url",
   ]
 }
diff --git a/components/dom_distiller/ios/distiller_page_ios.mm b/components/dom_distiller/ios/distiller_page_ios.mm
index cb6701b9..e51048b 100644
--- a/components/dom_distiller/ios/distiller_page_ios.mm
+++ b/components/dom_distiller/ios/distiller_page_ios.mm
@@ -16,6 +16,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "ios/web/public/browser_state.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 #import "ios/web/public/navigation/web_state_policy_decider.h"
 #import "ios/web/public/web_state.h"
@@ -210,9 +212,16 @@
     HandleJavaScriptResult(nil);
     return;
   }
+
+  web::WebFrame* main_frame = web::GetMainFrame(web_state_.get());
+  if (!main_frame) {
+    HandleJavaScriptResult(nil);
+    return;
+  }
+
   // Inject the script.
   base::WeakPtr<DistillerPageIOS> weak_this = weak_ptr_factory_.GetWeakPtr();
-  web_state_->ExecuteJavaScript(
+  main_frame->ExecuteJavaScript(
       base::UTF8ToUTF16(script_),
       base::BindOnce(&DistillerPageIOS::HandleJavaScriptResult, weak_this));
 }
diff --git a/components/page_info/core/about_this_site_validation.cc b/components/page_info/core/about_this_site_validation.cc
index b94172ad..bba00d4d 100644
--- a/components/page_info/core/about_this_site_validation.cc
+++ b/components/page_info/core/about_this_site_validation.cc
@@ -4,6 +4,8 @@
 
 #include "components/page_info/core/about_this_site_validation.h"
 
+#include "base/feature_list.h"
+#include "components/page_info/core/features.h"
 #include "components/page_info/core/proto/about_this_site_metadata.pb.h"
 #include "url/gurl.h"
 
@@ -69,15 +71,23 @@
   AboutThisSiteStatus status = AboutThisSiteStatus::kValid;
   if (!site_info.has_description())
     return AboutThisSiteStatus::kMissingDescription;
+
   status = ValidateDescription(site_info.description());
   if (status != AboutThisSiteStatus::kValid)
     return status;
 
   if (site_info.has_first_seen())
     status = ValidateFirstSeen(site_info.first_seen());
+
   if (status != AboutThisSiteStatus::kValid)
     return status;
 
+  // The kPageInfoAboutThisSiteMoreInfo requires a 'MoreAbout' URL.
+  if (base::FeatureList::IsEnabled(kPageInfoAboutThisSiteMoreInfo) &&
+      !site_info.has_more_about()) {
+    return AboutThisSiteStatus::kMissingMoreAbout;
+  }
+
   if (site_info.has_more_about())
     status = ValidateMoreAbout(site_info.more_about());
 
diff --git a/components/page_info/core/about_this_site_validation.h b/components/page_info/core/about_this_site_validation.h
index 643cfc0f..339b113 100644
--- a/components/page_info/core/about_this_site_validation.h
+++ b/components/page_info/core/about_this_site_validation.h
@@ -32,8 +32,9 @@
   kMissingDescriptionSource = 14,
   kMissingBannerInfo = 15,
   kInvalidMoreAbout = 16,
+  kMissingMoreAbout = 17,
 
-  kMaxValue = kInvalidMoreAbout,
+  kMaxValue = kMissingMoreAbout,
 };
 
 AboutThisSiteStatus ValidateMetadata(
diff --git a/components/page_info/core/about_this_site_validation_unittest.cc b/components/page_info/core/about_this_site_validation_unittest.cc
index f5112626..1b3802b 100644
--- a/components/page_info/core/about_this_site_validation_unittest.cc
+++ b/components/page_info/core/about_this_site_validation_unittest.cc
@@ -4,11 +4,12 @@
 
 #include "components/page_info/core/about_this_site_validation.h"
 
+#include "base/test/scoped_feature_list.h"
+#include "components/page_info/core/features.h"
 #include "components/page_info/core/proto/about_this_site_metadata.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace page_info {
-namespace about_this_site_validation {
+namespace page_info::about_this_site_validation {
 
 proto::Hyperlink GetSampleSource() {
   proto::Hyperlink link;
@@ -62,7 +63,7 @@
   auto metadata = GetSampleMetaData();
   EXPECT_EQ(ValidateMetadata(metadata), AboutThisSiteStatus::kValid);
 
-  // The proto should still be valid without a timestamp or without description.
+  // The proto should still be valid without a timestamp.
   metadata.mutable_site_info()->clear_first_seen();
   EXPECT_EQ(ValidateMetadata(metadata), AboutThisSiteStatus::kValid);
 }
@@ -151,6 +152,24 @@
             AboutThisSiteStatus::kInvalidMoreAbout);
 }
 
-}  // namespace about_this_site_validation
+TEST(AboutThisSiteValidation, MissingMoreAbout_FeatureEDisabled) {
+  proto::AboutThisSiteMetadata meta_data = GetSampleMetaData();
+  EXPECT_EQ(ValidateMetadata(meta_data), AboutThisSiteStatus::kValid);
 
-}  // namespace page_info
+  meta_data.mutable_site_info()->clear_more_about();
+  EXPECT_EQ(ValidateMetadata(meta_data), AboutThisSiteStatus::kValid);
+}
+
+TEST(AboutThisSiteValidation, MissingMoreAbout_FeatureEnabled) {
+  base::test::ScopedFeatureList features;
+  features.InitAndEnableFeature(kPageInfoAboutThisSiteMoreInfo);
+
+  proto::AboutThisSiteMetadata meta_data = GetSampleMetaData();
+  EXPECT_EQ(ValidateMetadata(meta_data), AboutThisSiteStatus::kValid);
+
+  meta_data.mutable_site_info()->clear_more_about();
+  EXPECT_EQ(ValidateMetadata(meta_data),
+            AboutThisSiteStatus::kMissingMoreAbout);
+}
+
+}  // namespace page_info::about_this_site_validation
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index ba348e7..7274c28 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -692,9 +692,15 @@
   <message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_HEADER" desc="The header label of the 'About this site' subpage in Page Info bubble.">
     From the web
   </message>
+    <message name="IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE" desc="The title of the 'About this page' row in the Page Info bubble.">
+    About this page
+  </message>
   <message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_TOOLTIP" desc="The tooltip of the button that opens 'About this site' subpage in Page Info bubble.">
     Show information from the web
   </message>
+  <message name="IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP" desc="The tooltip of the button that opens 'About this page' details in Page Info bubble.">
+    Learn about this page's source &amp; topic
+  </message>
   <message name="IDS_PAGE_INFO_ABOUT_THIS_SITE_SUBPAGE_FROM_LABEL" desc="The label containing the source of the description in the 'About this site' subpage in Page Info bubble.">
     From <ph name="SOURCE_NAME">$1<ex>Wikipedia</ex></ph>
   </message>
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1
new file mode 100644
index 0000000..660d392
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TITLE.png.sha1
@@ -0,0 +1 @@
+23952a4790bc0a6e4c9f97a712746539771af8ef
\ No newline at end of file
diff --git a/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP.png.sha1 b/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP.png.sha1
new file mode 100644
index 0000000..660d392
--- /dev/null
+++ b/components/page_info_strings_grdp/IDS_PAGE_INFO_ABOUT_THIS_PAGE_TOOLTIP.png.sha1
@@ -0,0 +1 @@
+23952a4790bc0a6e4c9f97a712746539771af8ef
\ No newline at end of file
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper.cc b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
index 818311df..fddcfea 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_helper.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper.cc
@@ -12,6 +12,7 @@
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_scripts_fetcher.h"
 #include "components/password_manager/core/browser/password_store_interface.h"
+#include "components/password_manager/core/browser/psl_matching_helper.h"
 
 namespace password_manager {
 
@@ -90,12 +91,27 @@
     }
   }
 
-  IsSaved is_saved(
-      base::ranges::any_of(partial_results_, [this](const auto& form) {
-        return form->url == url_ && form->username_value == username_ &&
+  // Returns true if the urls are identical or one is a PSL match of the other.
+  auto are_urls_equivalent = [&](const GURL& url1, const GURL& url2) -> bool {
+    return url1 == url2 || IsPublicSuffixDomainMatch(url1.spec(), url2.spec());
+  };
+
+  IsSaved is_saved(base::ranges::any_of(
+      partial_results_, [this, are_urls_equivalent](const auto& form) {
+        return are_urls_equivalent(form->url, url_) &&
+               form->username_value == username_ &&
                form->password_value == password_;
       }));
-  IsReused is_reused(partial_results_.size() > (is_saved ? 1 : 0));
+
+  // Check if the password is reused on a different origin, or on the same
+  // origin with a different username.
+  IsReused is_reused(base::ranges::any_of(
+      partial_results_, [this, are_urls_equivalent](const auto& form) {
+        return form->password_value == password_ &&
+               (!are_urls_equivalent(form->url, url_) ||
+                form->username_value != username_);
+      }));
+
   HasChangeScript has_change_script(script_is_available_);
 
   std::move(callback_).Run(is_saved, is_reused, has_change_script,
diff --git a/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc b/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
index f56c171c..53f276a9 100644
--- a/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
+++ b/components/password_manager/core/browser/leak_detection_delegate_helper_unittest.cc
@@ -31,6 +31,7 @@
 
 namespace {
 constexpr char16_t kLeakedPassword[] = u"leaked_password";
+constexpr char16_t kOtherPassword[] = u"other_password";
 constexpr char16_t kLeakedUsername[] = u"leaked_username";
 constexpr char16_t kLeakedUsernameNonCanonicalized[] =
     u"Leaked_Username@gmail.com";
@@ -104,7 +105,7 @@
   }
 
   // Sets the |PasswordForm|s which are retrieve from the |PasswordStore|.
-  void SetGetLoginByPasswordConsumerInvocation(
+  void SetGetAutofillableLoginsConsumerInvocation(
       std::vector<PasswordForm> password_forms) {
     EXPECT_CALL(*store_, GetAutofillableLogins)
         .WillOnce(testing::WithArg<0>(
@@ -123,9 +124,10 @@
 
 // Credentials are neither saved nor is the password reused.
 TEST_F(LeakDetectionDelegateHelperTest, NeitherSaveNotReused) {
-  std::vector<PasswordForm> password_forms;
+  std::vector<PasswordForm> password_forms = {
+      CreateForm(kOtherOrigin, kOtherUsername, kOtherPassword)};
 
-  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
   SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(false),
                                                 HasChangeScript(false));
   InitiateGetCredentialLeakType();
@@ -136,7 +138,7 @@
   std::vector<PasswordForm> password_forms = {
       CreateForm(kLeakedOrigin, kLeakedUsername)};
 
-  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
   SetOnShowLeakDetectionNotificationExpectation(IsSaved(true), IsReused(false),
                                                 HasChangeScript(false),
                                                 {GURL(kLeakedOrigin)});
@@ -151,7 +153,7 @@
       CreateForm(kLeakedOrigin, kLeakedUsername),
       CreateForm(kOtherOrigin, kLeakedUsername)};
 
-  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
   SetOnShowLeakDetectionNotificationExpectation(
       IsSaved(true), IsReused(true), HasChangeScript(false),
       {GURL(kLeakedOrigin), GURL(kOtherOrigin)});
@@ -167,7 +169,7 @@
       CreateForm(kLeakedOrigin, kLeakedUsername),
       CreateForm(kLeakedOrigin, kOtherUsername)};
 
-  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
   SetOnShowLeakDetectionNotificationExpectation(IsSaved(true), IsReused(true),
                                                 HasChangeScript(false),
                                                 {GURL(kLeakedOrigin)});
@@ -180,7 +182,7 @@
   std::vector<PasswordForm> password_forms = {
       CreateForm(kLeakedOrigin, kOtherUsername)};
 
-  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
   // Don't expect anything in |all_urls_with_leaked_credentials| since it should
   // only contain url:username pairs for which both the username and password
   // match.
@@ -194,7 +196,7 @@
   std::vector<PasswordForm> password_forms = {
       CreateForm(kOtherOrigin, kLeakedUsername)};
 
-  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
   SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(true),
                                                 HasChangeScript(false),
                                                 {GURL(kOtherOrigin)});
@@ -208,7 +210,7 @@
   std::vector<PasswordForm> password_forms = {
       CreateForm(kOtherOrigin, kOtherUsername)};
 
-  SetGetLoginByPasswordConsumerInvocation(std::move(password_forms));
+  SetGetAutofillableLoginsConsumerInvocation(std::move(password_forms));
   SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(true),
                                                 HasChangeScript(false));
   InitiateGetCredentialLeakType();
@@ -222,9 +224,9 @@
       CreateForm(kOtherOrigin, kLeakedUsername, kLeakedPassword);
   PasswordForm leaked_origin_other_username =
       CreateForm(kLeakedOrigin, kOtherUsername, kLeakedPassword);
-  SetGetLoginByPasswordConsumerInvocation({leaked_origin,
-                                           other_origin_same_credential,
-                                           leaked_origin_other_username});
+  SetGetAutofillableLoginsConsumerInvocation({leaked_origin,
+                                              other_origin_same_credential,
+                                              leaked_origin_other_username});
 
   SetOnShowLeakDetectionNotificationExpectation(
       IsSaved(true), IsReused(true), HasChangeScript(false),
@@ -245,7 +247,7 @@
 TEST_F(LeakDetectionDelegateHelperTest, SaveLeakedCredentialsCanonicalized) {
   PasswordForm non_canonicalized_username = CreateForm(
       kOtherOrigin, kLeakedUsernameNonCanonicalized, kLeakedPassword);
-  SetGetLoginByPasswordConsumerInvocation({non_canonicalized_username});
+  SetGetAutofillableLoginsConsumerInvocation({non_canonicalized_username});
   SetOnShowLeakDetectionNotificationExpectation(IsSaved(false), IsReused(true),
                                                 HasChangeScript(false),
                                                 {GURL(kOtherOrigin)});
@@ -258,29 +260,6 @@
   InitiateGetCredentialLeakType();
 }
 
-// Credentials are saved and the password is reused on the same origin &
-// username but with a different password.
-TEST_F(LeakDetectionDelegateHelperTest,
-       SavedCredentialsAndReusedPasswordWithOtherPassword) {
-  std::vector<PasswordForm> password_forms = {
-      CreateForm(kLeakedOrigin, kLeakedUsername),
-      CreateForm(kLeakedOrigin, kLeakedUsername)};
-  password_forms.back().password_value = u"another_password";
-
-  SetGetLoginByPasswordConsumerInvocation(password_forms);
-  // There's at least one set of leaked username:password on kLeakedOrigin, so
-  // it should be in |all_urls_with_leaked_credentials| even though another set
-  // of credentials on that origin has a different password.
-  SetOnShowLeakDetectionNotificationExpectation(IsSaved(true), IsReused(true),
-                                                HasChangeScript(false),
-                                                {GURL(kLeakedOrigin)});
-  password_forms.at(0).password_issues.insert_or_assign(
-      InsecureType::kLeaked,
-      InsecurityMetadata(base::Time::Now(), IsMuted(false)));
-  EXPECT_CALL(*store_, UpdateLogin(password_forms[0]));
-  InitiateGetCredentialLeakType();
-}
-
 namespace {
 class LeakDetectionDelegateHelperWithTwoStoreTest
     : public testing::Test,
diff --git a/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h b/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h
index cb90ac6..3615033 100644
--- a/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h
+++ b/components/policy/core/common/cloud/encrypted_reporting_job_configuration.h
@@ -19,7 +19,7 @@
 
 namespace policy {
 
-// {{{Note}}} ERP Payload Overview
+// {{{Note}}} ERP Request Payload Overview
 //
 // EncryptedReportingJobConfiguration configures a payload for the Encrypted
 // server endpoint. A JSON version of the payload looks like this:
diff --git a/components/services/quarantine/quarantine_mac_unittest.mm b/components/services/quarantine/quarantine_mac_unittest.mm
index c7358a3..36572f5 100644
--- a/components/services/quarantine/quarantine_mac_unittest.mm
+++ b/components/services/quarantine/quarantine_mac_unittest.mm
@@ -68,7 +68,8 @@
   base::scoped_nsobject<NSURL> file_url_;
 };
 
-TEST_F(QuarantineMacTest, CheckMetadataSetCorrectly) {
+// TODO(crbug.com/1334495): Enable this test.
+TEST_F(QuarantineMacTest, DISABLED_CheckMetadataSetCorrectly) {
   QuarantineFile(
       test_file_, source_url_, referrer_url_, "",
       base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
@@ -76,7 +77,8 @@
   EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_));
 }
 
-TEST_F(QuarantineMacTest, SetMetadataMultipleTimes) {
+// TODO(crbug.com/1334495): Enable this test.
+TEST_F(QuarantineMacTest, DISABLED_SetMetadataMultipleTimes) {
   GURL dummy_url("http://www.dummy.example.com");
   QuarantineFile(
       test_file_, source_url_, referrer_url_, "",
@@ -97,7 +99,8 @@
   EXPECT_FALSE(IsFileQuarantined(test_file_, GURL(), GURL()));
 }
 
-TEST_F(QuarantineMacTest, IsFileQuarantined_SourceUrlOnly) {
+// TODO(crbug.com/1334495): Enable this test.
+TEST_F(QuarantineMacTest, DISABLED_IsFileQuarantined_SourceUrlOnly) {
   QuarantineFile(
       test_file_, source_url_, GURL(), std::string(),
       base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
@@ -108,7 +111,8 @@
   EXPECT_FALSE(IsFileQuarantined(test_file_, referrer_url_, GURL()));
 }
 
-TEST_F(QuarantineMacTest, IsFileQuarantined_FullMetadata) {
+// TODO(crbug.com/1334495): Enable this test.
+TEST_F(QuarantineMacTest, DISABLED_IsFileQuarantined_FullMetadata) {
   QuarantineFile(
       test_file_, source_url_, referrer_url_, std::string(),
       base::BindOnce(&CheckQuarantineResult, QuarantineFileResult::OK));
@@ -121,7 +125,8 @@
   EXPECT_FALSE(IsFileQuarantined(test_file_, referrer_url_, referrer_url_));
 }
 
-TEST_F(QuarantineMacTest, IsFileQuarantined_Sanitize) {
+// TODO(crbug.com/1334495): Enable this test.
+TEST_F(QuarantineMacTest, DISABLED_IsFileQuarantined_Sanitize) {
   GURL host_url{"https://user:pass@example.com/foo/bar?x#y"};
   GURL host_url_clean{"https://example.com/foo/bar?x#y"};
   GURL referrer_url{"https://user:pass@example.com/foo/index?x#y"};
diff --git a/components/signin/public/base/signin_metrics.cc b/components/signin/public/base/signin_metrics.cc
index d0330455..8aa43dd3 100644
--- a/components/signin/public/base/signin_metrics.cc
+++ b/components/signin/public/base/signin_metrics.cc
@@ -887,137 +887,6 @@
   }
 }
 
-#if !BUILDFLAG(IS_IOS)
-void RecordSigninImpressionWithAccountUserActionForAccessPoint(
-    AccessPoint access_point,
-    bool with_account) {
-  switch (access_point) {
-    case AccessPoint::ACCESS_POINT_SETTINGS:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromSettings"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromSettings"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromExtensionInstallBubble"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromExtensionInstallBubble"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromBookmarkBubble"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromBookmarkBubble"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromBookmarkManager"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromBookmarkManager"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromAvatarBubbleSignin"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromAvatarBubbleSignin"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_RECENT_TABS:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromRecentTabs"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromRecentTabs"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromPasswordBubble"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromPasswordBubble"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_TAB_SWITCHER:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromTabSwitcher"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromTabSwitcher"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromNTPContentSuggestions"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromNTPContentSuggestions"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_NTP_FEED_TOP_PROMO:
-      if (with_account) {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithAccount_FromNTPFeedTopPromo"));
-      } else {
-        base::RecordAction(base::UserMetricsAction(
-            "Signin_ImpressionWithNoAccount_FromNTPFeedTopPromo"));
-      }
-      break;
-    case AccessPoint::ACCESS_POINT_ENTERPRISE_SIGNOUT_COORDINATOR:
-    case AccessPoint::ACCESS_POINT_START_PAGE:
-    case AccessPoint::ACCESS_POINT_NTP_LINK:
-    case AccessPoint::ACCESS_POINT_MENU:
-    case AccessPoint::ACCESS_POINT_SUPERVISED_USER:
-    case AccessPoint::ACCESS_POINT_EXTENSIONS:
-    case AccessPoint::ACCESS_POINT_USER_MANAGER:
-    case AccessPoint::ACCESS_POINT_DEVICES_PAGE:
-    case AccessPoint::ACCESS_POINT_CLOUD_PRINT:
-    case AccessPoint::ACCESS_POINT_CONTENT_AREA:
-    case AccessPoint::ACCESS_POINT_SIGNIN_PROMO:
-    case AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN:
-    case AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR:
-    case AccessPoint::ACCESS_POINT_UNKNOWN:
-    case AccessPoint::ACCESS_POINT_MACHINE_LOGON:
-    case AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS:
-    case AccessPoint::ACCESS_POINT_SYNC_ERROR_CARD:
-    case AccessPoint::ACCESS_POINT_FORCED_SIGNIN:
-    case AccessPoint::ACCESS_POINT_ACCOUNT_RENAMED:
-    case AccessPoint::ACCESS_POINT_WEB_SIGNIN:
-    case AccessPoint::ACCESS_POINT_SAFETY_CHECK:
-    case AccessPoint::ACCESS_POINT_KALEIDOSCOPE:
-    case AccessPoint::ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
-    case AccessPoint::ACCESS_POINT_SEND_TAB_TO_SELF_PROMO:
-      NOTREACHED() << "Signin_Impression{With|WithNo}Account_From* user actions"
-                   << " are not recorded for access point "
-                   << static_cast<int>(access_point)
-                   << " as it does not support a personalized sign-in promo.";
-      break;
-    case AccessPoint::ACCESS_POINT_MAX:
-      NOTREACHED();
-      break;
-  }
-}
-#endif  // !BUILDFLAG(IS_IOS)
-
 #if BUILDFLAG(IS_IOS)
 void RecordConsistencyPromoUserAction(AccountConsistencyPromoAction action) {
   UMA_HISTOGRAM_ENUMERATION(
diff --git a/components/signin/public/base/signin_metrics.h b/components/signin/public/base/signin_metrics.h
index b85ad48..c26a830 100644
--- a/components/signin/public/base/signin_metrics.h
+++ b/components/signin/public/base/signin_metrics.h
@@ -554,16 +554,9 @@
 void RecordSigninUserActionForAccessPoint(AccessPoint access_point,
                                           PromoAction promo_action);
 
-// Records |Signin_ImpressionWithAccount_From*| user action.
+// Records |Signin_Impression_From*| user action.
 void RecordSigninImpressionUserActionForAccessPoint(AccessPoint access_point);
 
-#if !BUILDFLAG(IS_IOS)
-// Records |Signin_Impression{With|No}Account_From*| user action.
-void RecordSigninImpressionWithAccountUserActionForAccessPoint(
-    AccessPoint access_point,
-    bool with_account);
-#endif  // !BUILDFLAG(IS_IOS)
-
 #if BUILDFLAG(IS_IOS)
 // Records |Signin.AccountConsistencyPromoAction| histogram.
 void RecordConsistencyPromoUserAction(AccountConsistencyPromoAction action);
diff --git a/components/signin/public/base/signin_metrics_unittest.cc b/components/signin/public/base/signin_metrics_unittest.cc
index 93c90a8f..f15794a 100644
--- a/components/signin/public/base/signin_metrics_unittest.cc
+++ b/components/signin/public/base/signin_metrics_unittest.cc
@@ -238,27 +238,5 @@
   }
 }
 
-#if !BUILDFLAG(IS_IOS)
-TEST_F(SigninMetricsTest, RecordSigninImpressionWithAccountUserAction) {
-  for (const AccessPoint& ap : kAccessPointsThatSupportPersonalizedPromos) {
-    base::UserActionTester user_action_tester;
-    RecordSigninImpressionWithAccountUserActionForAccessPoint(ap, true);
-    EXPECT_EQ(1, user_action_tester.GetActionCount(
-                     "Signin_ImpressionWithAccount_From" +
-                     GetAccessPointDescription(ap)));
-  }
-}
-
-TEST_F(SigninMetricsTest, RecordSigninImpressionWithNoAccountUserAction) {
-  for (const AccessPoint& ap : kAccessPointsThatSupportPersonalizedPromos) {
-    base::UserActionTester user_action_tester;
-    RecordSigninImpressionWithAccountUserActionForAccessPoint(ap, false);
-    EXPECT_EQ(1, user_action_tester.GetActionCount(
-                     "Signin_ImpressionWithNoAccount_From" +
-                     GetAccessPointDescription(ap)));
-  }
-}
-#endif  // !BUILDFLAG(IS_IOS)
-
 }  // namespace
 }  // namespace signin_metrics
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 7401578..8f637970 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -220,6 +220,7 @@
       "trusted_vault/trusted_vault_connection_impl_unittest.cc",
       "trusted_vault/trusted_vault_crypto_unittest.cc",
       "trusted_vault/trusted_vault_request_unittest.cc",
+      "trusted_vault/trusted_vault_server_constants_unittest.cc",
     ]
   }
 
diff --git a/components/sync/trusted_vault/trusted_vault_request.cc b/components/sync/trusted_vault/trusted_vault_request.cc
index 255edf0..34da26bc 100644
--- a/components/sync/trusted_vault/trusted_vault_request.cc
+++ b/components/sync/trusted_vault/trusted_vault_request.cc
@@ -11,6 +11,7 @@
 #include "components/signin/public/identity_manager/access_token_info.h"
 #include "components/sync/driver/trusted_vault_histograms.h"
 #include "components/sync/trusted_vault/trusted_vault_access_token_fetcher.h"
+#include "components/sync/trusted_vault/trusted_vault_server_constants.h"
 #include "net/base/url_util.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
@@ -26,8 +27,6 @@
 
 const char kAuthorizationHeader[] = "Authorization";
 const char kProtobufContentType[] = "application/x-protobuf";
-const char kQueryParameterAlternateOutputKey[] = "alt";
-const char kQueryParameterAlternateOutputProto[] = "proto";
 
 net::NetworkTrafficAnnotationTag CreateTrafficAnnotationTag() {
   return net::DefineNetworkTrafficAnnotation("trusted_vault_request",
diff --git a/components/sync/trusted_vault/trusted_vault_server_constants.cc b/components/sync/trusted_vault/trusted_vault_server_constants.cc
index d00c915..6fc7e7e 100644
--- a/components/sync/trusted_vault/trusted_vault_server_constants.cc
+++ b/components/sync/trusted_vault/trusted_vault_server_constants.cc
@@ -9,13 +9,6 @@
 
 namespace syncer {
 
-namespace {
-
-const char kQueryParameterAlternateOutputKey[] = "alt";
-const char kQueryParameterAlternateOutputProto[] = "proto";
-
-}  // namespace
-
 std::vector<uint8_t> GetConstantTrustedVaultKey() {
   return std::vector<uint8_t>(16, 0);
 }
diff --git a/components/sync/trusted_vault/trusted_vault_server_constants.h b/components/sync/trusted_vault/trusted_vault_server_constants.h
index c7f06897..8198be40 100644
--- a/components/sync/trusted_vault/trusted_vault_server_constants.h
+++ b/components/sync/trusted_vault/trusted_vault_server_constants.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_SYNC_TRUSTED_VAULT_TRUSTED_VAULT_SERVER_CONSTANTS_H_
 #define COMPONENTS_SYNC_TRUSTED_VAULT_TRUSTED_VAULT_SERVER_CONSTANTS_H_
 
+#include <cstdint>
 #include <string>
 #include <vector>
 
@@ -26,6 +27,9 @@
 inline constexpr char kGetSecurityDomainURLPathAndQuery[] =
     "users/me/securitydomains/chromesync?view=2";
 
+inline constexpr char kQueryParameterAlternateOutputKey[] = "alt";
+inline constexpr char kQueryParameterAlternateOutputProto[] = "proto";
+
 std::vector<uint8_t> GetConstantTrustedVaultKey();
 std::string GetGetSecurityDomainMemberURLPathAndQuery(
     base::span<const uint8_t> public_key);
diff --git a/components/sync/trusted_vault/trusted_vault_server_constants_unittest.cc b/components/sync/trusted_vault/trusted_vault_server_constants_unittest.cc
new file mode 100644
index 0000000..9f776cac
--- /dev/null
+++ b/components/sync/trusted_vault/trusted_vault_server_constants_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/trusted_vault/trusted_vault_server_constants.h"
+
+#include <cstdint>
+#include <vector>
+
+#include "components/sync/trusted_vault/securebox.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+namespace {
+
+using testing::Eq;
+
+TEST(TrustedVaultServerConstantsTest,
+     ShouldGetGetSecurityDomainMemberURLPathAndQuery) {
+  // Arbitrary key, with an appropriate length.
+  const std::vector<uint8_t> kPublicKey{
+      0x4,  0xF2, 0x4C, 0x45, 0xBA, 0xF4, 0xF8, 0x6C, 0xF9, 0x73, 0xCE,
+      0x75, 0xC,  0xC9, 0xD4, 0xF,  0x4A, 0x53, 0xB7, 0x85, 0x46, 0x41,
+      0xFB, 0x31, 0x17, 0xF,  0xEB, 0xB,  0x45, 0xE4, 0x29, 0x69, 0x9B,
+      0xB2, 0x7,  0x12, 0xC1, 0x9,  0x3D, 0xEF, 0xBB, 0x57, 0xDC, 0x56,
+      0x12, 0x29, 0xF2, 0x73, 0xE1, 0xC5, 0x99, 0x1C, 0x49, 0x3A, 0xA2,
+      0x30, 0xF9, 0xBA, 0x3B, 0xB1, 0x83, 0xCF, 0x1B, 0x5D, 0xE8};
+
+  // Guard against future code changes, in case the key length changes.
+  ASSERT_THAT(kPublicKey.size(), Eq(syncer::SecureBoxKeyPair::GenerateRandom()
+                                        ->public_key()
+                                        .ExportToBytes()
+                                        .size()));
+
+  // Note that production code (TrustedVaultRequest::CreateURLLoader) will
+  // append &alt=proto to the URL.
+  EXPECT_THAT(
+      GetGetSecurityDomainMemberURLPathAndQuery(kPublicKey),
+      Eq("users/me/members/"
+         "BPJMRbr0-Gz5c851DMnUD0pTt4VGQfsxFw_"
+         "rC0XkKWmbsgcSwQk977tX3FYSKfJz4cWZHEk6ojD5ujuxg88bXeg?view=2"));
+}
+
+}  // namespace
+
+}  // namespace syncer
diff --git a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
index 1762302ac..6f326772 100644
--- a/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
+++ b/content/browser/accessibility/ax_platform_node_textrangeprovider_win_browsertest.cc
@@ -2976,6 +2976,99 @@
 }
 
 IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest,
+                       ExpandToLineCrossingBoundary) {
+  LoadInitialAccessibilityTreeFromHtml(
+      R"HTML(<!DOCTYPE html>
+      <html>
+        <body>
+          plain text <b>on <i>line</i></b><i><b> one<br>
+          <span>next</span> <span>text</span> </b> on </i>line two<br>
+          line three,
+        </body>
+      </html>)HTML");
+
+  BrowserAccessibility* start_of_second_line =
+      FindNode(ax::mojom::Role::kStaticText, "next");
+  ASSERT_NE(nullptr, start_of_second_line);
+
+  ComPtr<ITextRangeProvider> text_range_provider;
+  GetTextRangeProviderFromTextNode(*start_of_second_line, &text_range_provider);
+  ASSERT_NE(nullptr, text_range_provider.Get());
+
+  // Ensure ExpandToEnclosingUnit by Line both moves the start and end endpoints
+  // appropriately (doesn't move to previous line for start and spans multiple
+  // elements).
+  text_range_provider->ExpandToEnclosingUnit(TextUnit_Line);
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"next text on line two");
+
+  BrowserAccessibility* text_on_second_line =
+      FindNode(ax::mojom::Role::kStaticText, "next");
+  ASSERT_NE(nullptr, text_on_second_line);
+
+  GetTextRangeProviderFromTextNode(*text_on_second_line, &text_range_provider);
+  ASSERT_NE(nullptr, text_range_provider.Get());
+
+  // Ensure ExpandToEnclosingUnit by Line moves the start past the anchor
+  // boundary (but not past the start of the line).
+  text_range_provider->ExpandToEnclosingUnit(TextUnit_Line);
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"next text on line two");
+}
+
+IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest,
+                       ExpandToParagraphCrossingBoundary) {
+  LoadInitialAccessibilityTreeFromHtml(
+      R"HTML(<!DOCTYPE html>
+      <html>
+        <body>
+          plain text <b>on <i>line</i></b><i><b> one<br>
+          <span>next</span> <span>text</span> </b> on </i>line two<br>
+          line three,
+        </body>
+      </html>)HTML");
+
+  BrowserAccessibility* first_bold_text =
+      FindNode(ax::mojom::Role::kStaticText, "line two");
+  ASSERT_NE(nullptr, first_bold_text);
+
+  ComPtr<ITextRangeProvider> text_range_provider;
+  GetTextRangeProviderFromTextNode(*first_bold_text, &text_range_provider);
+  ASSERT_NE(nullptr, text_range_provider.Get());
+
+  // Ensure ExpandToEnclosingUnit by Line both moves the start and end endpoints
+  // appropriately.
+  text_range_provider->ExpandToEnclosingUnit(TextUnit_Paragraph);
+  EXPECT_UIA_TEXTRANGE_EQ(text_range_provider, L"next text on line two\n");
+}
+
+IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest,
+                       ExpandToPageCrossingBoundary) {
+  LoadInitialAccessibilityTreeFromHtml(
+      R"HTML(<!DOCTYPE html>
+      <html>
+        <body>
+          plain text <b>on <i>line</i></b><i><b> one<br>
+          <span>next</span> <span>text</span> </b> on </i>line two<br>
+          line three,
+        </body>
+      </html>)HTML");
+
+  BrowserAccessibility* first_bold_text =
+      FindNode(ax::mojom::Role::kStaticText, "next");
+  ASSERT_NE(nullptr, first_bold_text);
+
+  ComPtr<ITextRangeProvider> text_range_provider;
+  GetTextRangeProviderFromTextNode(*first_bold_text, &text_range_provider);
+  ASSERT_NE(nullptr, text_range_provider.Get());
+
+  // Ensure ExpandToEnclosingUnit by Line both moves the start and end endpoints
+  // appropriately.
+  text_range_provider->ExpandToEnclosingUnit(TextUnit_Page);
+  EXPECT_UIA_TEXTRANGE_EQ(
+      text_range_provider,
+      L"plain text on line one\nnext text on line two\nline three,");
+}
+
+IN_PROC_BROWSER_TEST_F(AXPlatformNodeTextRangeProviderWinBrowserTest,
                        MoveByCharacterWithEmbeddedObject) {
   const std::string html_markup = R"HTML(<!DOCTYPE html>
   <html>
diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
index 21277cd..8e81a5cb 100644
--- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc
@@ -579,7 +579,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   if (!crop_id_passed_validation) {
-    std::move(callback).Run(media::mojom::CropRequestResult::kErrorGeneric);
+    std::move(callback).Run(
+        media::mojom::CropRequestResult::kInvalidCropTarget);
     return;
   }
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 233bfa4..d44c037 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -6017,6 +6017,7 @@
     }
     // At this point we can't create anything.
     NOTREACHED();
+    CHECK(false);
     return nullptr;
   }
   return std::make_unique<FrameURLLoaderFactory>(weak_factory_.GetWeakPtr());
diff --git a/content/test/attribution_simulator_input_parser.cc b/content/test/attribution_simulator_input_parser.cc
index 6cb971a..7f18577 100644
--- a/content/test/attribution_simulator_input_parser.cc
+++ b/content/test/attribution_simulator_input_parser.cc
@@ -373,7 +373,8 @@
           event_triggers.emplace_back(trigger_data, priority, dedup_key,
                                       std::move(filters),
                                       std::move(not_filters));
-        }));
+        }),
+        /*max_size=*/blink::kMaxAttributionEventTriggerData);
 
     return event_triggers;
   }
diff --git a/content/test/attribution_simulator_input_parser_unittest.cc b/content/test/attribution_simulator_input_parser_unittest.cc
index 9ebe3631..6938d95 100644
--- a/content/test/attribution_simulator_input_parser_unittest.cc
+++ b/content/test/attribution_simulator_input_parser_unittest.cc
@@ -575,6 +575,48 @@
   }
 }
 
+TEST(AttributionSimulatorInputParserTest, InvalidEventTriggerDataSize) {
+  const struct {
+    size_t size;
+    bool valid;
+  } kTestCases[]{
+      {blink::kMaxAttributionEventTriggerData, true},
+      {blink::kMaxAttributionEventTriggerData + 1, false},
+  };
+
+  static constexpr char kError[] =
+      R"(["triggers"][0]["Attribution-Reporting-Register-Trigger"]["event_trigger_data"]: too many elements)";
+
+  for (const auto test_case : kTestCases) {
+    base::Value::List list;
+    for (size_t i = 0; i < test_case.size; ++i) {
+      list.Append("");
+    }
+    base::Value::Dict trigger;
+    trigger.Set("event_trigger_data", std::move(list));
+
+    base::Value::Dict dict;
+    dict.Set("Attribution-Reporting-Register-Trigger", std::move(trigger));
+
+    base::Value::List triggers;
+    triggers.Append(std::move(dict));
+
+    base::Value::Dict input;
+    input.Set("triggers", std::move(triggers));
+
+    std::ostringstream error_stream;
+    EXPECT_EQ(ParseAttributionSimulationInput(base::Value(std::move(input)),
+                                              kOffsetTime, error_stream),
+              absl::nullopt);
+
+    if (test_case.valid) {
+      EXPECT_THAT(error_stream.str(), Not(HasSubstr(kError)));
+    } else {
+      EXPECT_THAT(error_stream.str(), HasSubstr(kError));
+    }
+  }
+}
+
 struct ParseErrorTestCase {
   const char* expected_failure_substr;
   const char* json;
diff --git a/ios/chrome/browser/passwords/password_tab_helper.mm b/ios/chrome/browser/passwords/password_tab_helper.mm
index ac5d9642..e569c546 100644
--- a/ios/chrome/browser/passwords/password_tab_helper.mm
+++ b/ios/chrome/browser/passwords/password_tab_helper.mm
@@ -9,7 +9,6 @@
 #include "base/metrics/histogram_macros.h"
 #include "components/password_manager/core/browser/manage_passwords_referrer.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #import "ios/chrome/browser/passwords/password_controller.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
@@ -91,10 +90,7 @@
   if (request_info.target_frame_is_main &&
       ui::PageTransitionCoreTypeIs(request_info.transition_type,
                                    ui::PAGE_TRANSITION_LINK) &&
-      request_url == GURL(password_manager::kManageMyPasswordsURL) &&
-      base::FeatureList::IsEnabled(
-          password_manager::features::
-              kIOSEnablePasswordManagerBrandingUpdate)) {
+      request_url == GURL(password_manager::kManageMyPasswordsURL)) {
     id<ApplicationSettingsCommands> settings_command_handler =
         HandlerForProtocol(controller_.dispatcher, ApplicationSettingsCommands);
 
diff --git a/ios/chrome/browser/passwords/password_tab_helper_unittest.mm b/ios/chrome/browser/passwords/password_tab_helper_unittest.mm
index d623237..17bcf68 100644
--- a/ios/chrome/browser/passwords/password_tab_helper_unittest.mm
+++ b/ios/chrome/browser/passwords/password_tab_helper_unittest.mm
@@ -9,11 +9,9 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/autofill/ios/form_util/unique_id_data_tab_helper.h"
 #include "components/password_manager/core/browser/manage_passwords_referrer.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
-#include "components/password_manager/core/common/password_manager_features.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #import "ios/chrome/browser/ui/commands/application_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
@@ -52,9 +50,6 @@
 
     UniqueIDDataTabHelper::CreateForWebState(web_state_.get());
     PasswordTabHelper::CreateForWebState(web_state_.get());
-
-    feature_list_.InitAndEnableFeature(
-        password_manager::features::kIOSEnablePasswordManagerBrandingUpdate);
   }
 
   void SetUp() override {
@@ -74,7 +69,6 @@
   }
 
  protected:
-  base::test::ScopedFeatureList feature_list_;
   web::ScopedTestingWebClient web_client_;
   web::WebTaskEnvironment task_environment_;
   std::unique_ptr<TestChromeBrowserState> browser_state_;
diff --git a/ios/chrome/browser/sync/sync_setup_service.cc b/ios/chrome/browser/sync/sync_setup_service.cc
index 5297a7e..0428a9c 100644
--- a/ios/chrome/browser/sync/sync_setup_service.cc
+++ b/ios/chrome/browser/sync/sync_setup_service.cc
@@ -30,6 +30,7 @@
 
 SyncSetupService::~SyncSetupService() {}
 
+// static
 syncer::ModelType SyncSetupService::GetModelType(SyncableDatatype datatype) {
   DCHECK(datatype < std::size(kDataTypes));
   return kDataTypes[datatype];
@@ -66,11 +67,7 @@
       selected_types.Put(type);
     }
   }
-  if (enabled && !CanSyncFeatureStart())
-    SetSyncEnabledWithoutChangingDatatypes(true);
   user_settings->SetSelectedTypes(IsSyncingAllDataTypes(), selected_types);
-  if (GetPreferredDataTypes().Empty())
-    SetSyncEnabled(false);
 }
 
 bool SyncSetupService::UserActionIsRequiredToHaveTabSyncWork() {
@@ -104,8 +101,6 @@
 void SyncSetupService::SetSyncingAllDataTypes(bool sync_all) {
   if (!sync_blocker_)
     sync_blocker_ = sync_service_->GetSetupInProgressHandle();
-  if (sync_all && !CanSyncFeatureStart())
-    SetSyncEnabled(true);
   sync_service_->GetUserSettings()->SetSelectedTypes(
       sync_all, sync_service_->GetUserSettings()->GetSelectedTypes());
 }
@@ -119,7 +114,14 @@
 }
 
 void SyncSetupService::SetSyncEnabled(bool sync_enabled) {
-  SetSyncEnabledWithoutChangingDatatypes(sync_enabled);
+  if (!sync_blocker_)
+    sync_blocker_ = sync_service_->GetSetupInProgressHandle();
+  if (!sync_enabled) {
+    UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::CHROME_SYNC_SETTINGS,
+                              syncer::STOP_SOURCE_LIMIT);
+  }
+  sync_service_->GetUserSettings()->SetSyncRequested(sync_enabled);
+
   if (sync_enabled && GetPreferredDataTypes().Empty())
     SetSyncingAllDataTypes(true);
 }
@@ -217,14 +219,3 @@
 bool SyncSetupService::HasUncommittedChanges() {
   return sync_service_->IsSetupInProgress();
 }
-
-void SyncSetupService::SetSyncEnabledWithoutChangingDatatypes(
-    bool sync_enabled) {
-  if (!sync_blocker_)
-    sync_blocker_ = sync_service_->GetSetupInProgressHandle();
-  if (!sync_enabled) {
-    UMA_HISTOGRAM_ENUMERATION("Sync.StopSource", syncer::CHROME_SYNC_SETTINGS,
-                              syncer::STOP_SOURCE_LIMIT);
-  }
-  sync_service_->GetUserSettings()->SetSyncRequested(sync_enabled);
-}
diff --git a/ios/chrome/browser/sync/sync_setup_service.h b/ios/chrome/browser/sync/sync_setup_service.h
index 9f6f56d5..2ef4f60 100644
--- a/ios/chrome/browser/sync/sync_setup_service.h
+++ b/ios/chrome/browser/sync/sync_setup_service.h
@@ -34,6 +34,7 @@
   };
 
   // The set of user-selectable datatypes handled by Chrome for iOS.
+  // TODO(crbug.com/1067280): Use syncer::UserSelectableType instead.
   using SyncableDatatype = enum {
     kSyncBookmarks,
     kSyncOmniboxHistory,
@@ -54,14 +55,18 @@
 
   // Returns the |syncer::ModelType| associated to the given
   // |SyncableDatatypes|.
-  syncer::ModelType GetModelType(SyncableDatatype datatype);
+  static syncer::ModelType GetModelType(SyncableDatatype datatype);
 
   // Returns whether the user wants Sync to run.
+  // TODO(crbug.com/1291946): Callers should typically use CanSyncFeatureStart()
+  // or IsSyncFeatureEnabled() instead.
   virtual bool IsSyncRequested() const;
   // Returns whether Sync-the-transport can start the Sync feature.
   virtual bool CanSyncFeatureStart() const;
   // Enables or disables sync. Changes won't take effect in the sync backend
   // before the next call to |CommitChanges|.
+  // TODO(crbug.com/1291946): This is only used in sync_test_util.mm; inline it
+  // there.
   virtual void SetSyncEnabled(bool sync_enabled);
 
   // Returns all currently enabled datatypes.
@@ -114,18 +119,12 @@
   bool IsFirstSetupComplete() const;
 
   // Commits all the pending configuration changes to Sync.
-  // This method should only be used with UnifiedConsent flag.
   void CommitSyncChanges();
 
-  // Returns true if there are uncommitted sync changes;
+  // Returns true if there are uncommitted sync changes.
   bool HasUncommittedChanges();
 
  private:
-  // Enables or disables sync. Changes won't take effect in the sync backend
-  // before the next call to |CommitChanges|. No changes are made to the
-  // currently selected datatypes.
-  void SetSyncEnabledWithoutChangingDatatypes(bool sync_enabled);
-
   syncer::SyncService* const sync_service_;
 
   // Prevents Sync from running until configuration is complete.
diff --git a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h
index 187037d..6da1ea0 100644
--- a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity.h
@@ -18,9 +18,9 @@
 // Activity that adds the page to bookmarks.
 @interface BookmarkActivity : UIActivity
 
-// Initializes the bookmark activity with a page's |URL| and |title|. The
-// |bookmarkModel| to verify if the page has already been bookmarked or not. The
-// |handler| is used to add the page to the bookmarks. The |prefService| is used
+// Initializes the bookmark activity with a page's `URL` and `title`. The
+// `bookmarkModel` to verify if the page has already been bookmarked or not. The
+// `handler` is used to add the page to the bookmarks. The `prefService` is used
 // to verify if the user can edit their bookmarks or not.
 - (instancetype)initWithURL:(const GURL&)URL
                       title:(NSString*)title
diff --git a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm
index ede98e2c..193ec09 100644
--- a/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/bookmark_activity_unittest.mm
@@ -47,13 +47,13 @@
         bookmarks::prefs::kEditBookmarksEnabled, true);
   }
 
-  // Sets the edit bookmarks pref to |canEdit|.
+  // Sets the edit bookmarks pref to `canEdit`.
   void SetCanEditBookmarkPref(bool canEdit) {
     testing_pref_service_.SetBoolean(bookmarks::prefs::kEditBookmarksEnabled,
                                      canEdit);
   }
 
-  // Creates a BookmarkActivity instance with the given |URL|.
+  // Creates a BookmarkActivity instance with the given `URL`.
   BookmarkActivity* CreateActivity(const GURL& URL) {
     return [[BookmarkActivity alloc] initWithURL:URL
                                            title:kTestTitle
diff --git a/ios/chrome/browser/ui/activity_services/activities/copy_activity.h b/ios/chrome/browser/ui/activity_services/activities/copy_activity.h
index 37fb2c11..6bfca2c 100644
--- a/ios/chrome/browser/ui/activity_services/activities/copy_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/copy_activity.h
@@ -12,11 +12,11 @@
 // Activity that copies the URL to the pasteboard.
 @interface CopyActivity : UIActivity
 
-// Initializes the copy activity with the objects in |dataItems| holding URLs
-// and potentially, additional text to be copied. |dataItems| must be non-null
+// Initializes the copy activity with the objects in `dataItems` holding URLs
+// and potentially, additional text to be copied. `dataItems` must be non-null
 // and not empty.
-// |ShareToData.additionalText| will only be shared to the pasteboard if a
-// single item is passed in |dataItems|. (When multiple items are passed, the
+// `ShareToData.additionalText` will only be shared to the pasteboard if a
+// single item is passed in `dataItems`. (When multiple items are passed, the
 // URLs are made available in the pasteboard both as NSURLs and strings.)
 - (instancetype)initWithDataItems:(NSArray<ShareToData*>*)dataItems;
 
diff --git a/ios/chrome/browser/ui/activity_services/activities/copy_activity_unittest.mm b/ios/chrome/browser/ui/activity_services/activities/copy_activity_unittest.mm
index 78e13835..6b85ef3f 100644
--- a/ios/chrome/browser/ui/activity_services/activities/copy_activity_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/copy_activity_unittest.mm
@@ -43,7 +43,7 @@
     ClearPasteboard();
   }
 
-  // Creates a ShareToData instance with the given |additional_text|.
+  // Creates a ShareToData instance with the given `additional_text`.
   ShareToData* CreateData(NSString* additional_text) {
     return CreateData(kTestShareURL, kTestVisibleURL, additional_text);
   }
diff --git a/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity.h b/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity.h
index 1e647e4..391465c 100644
--- a/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity.h
@@ -13,8 +13,8 @@
 // Activity to trigger the find in page feature.
 @interface FindInPageActivity : UIActivity
 
-// Initializes the find in page activity with the given |data| and the
-// |handler|.
+// Initializes the find in page activity with the given `data` and the
+// `handler`.
 - (instancetype)initWithData:(ShareToData*)data
                      handler:(id<FindInPageCommands>)handler
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity_unittest.mm b/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity_unittest.mm
index 1a65bca..4b048e9 100644
--- a/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/find_in_page_activity_unittest.mm
@@ -26,7 +26,7 @@
     mocked_handler_ = OCMStrictProtocolMock(@protocol(FindInPageCommands));
   }
 
-  // Creates a ShareToData instance with |is_page_searchable| set.
+  // Creates a ShareToData instance with `is_page_searchable` set.
   ShareToData* CreateData(bool is_page_searchable) {
     return [[ShareToData alloc] initWithShareURL:GURL("https://www.google.com/")
                                       visibleURL:GURL("https://google.com/")
diff --git a/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h b/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h
index e741ed8..d09ffa1 100644
--- a/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h
@@ -13,8 +13,8 @@
 // Activity that ends up showing a QR code for the given URL.
 @interface GenerateQrCodeActivity : UIActivity
 
-// Initializes the GenerateQrCodeActivity with the |activityURL| used to
-// generate the QR code, the |title| of the page at that URL, and a |dispatcher|
+// Initializes the GenerateQrCodeActivity with the `activityURL` used to
+// generate the QR code, the `title` of the page at that URL, and a `dispatcher`
 // to handle the command.
 - (instancetype)initWithURL:(const GURL&)activityURL
                       title:(NSString*)title
diff --git a/ios/chrome/browser/ui/activity_services/activities/print_activity.h b/ios/chrome/browser/ui/activity_services/activities/print_activity.h
index f5421eefe2..3f94e83 100644
--- a/ios/chrome/browser/ui/activity_services/activities/print_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/print_activity.h
@@ -14,15 +14,15 @@
 // Activity that triggers the printing service.
 @interface PrintActivity : UIActivity
 
-// Initializes the print activity with the given tab |data| and the |handler|.
-// Print preview will be presented on top of |baseViewController|.
+// Initializes the print activity with the given tab `data` and the `handler`.
+// Print preview will be presented on top of `baseViewController`.
 // TODO(crbug.com/906662): Use id<BrowserCoordinatorCommands> for handler.
 - (instancetype)initWithData:(ShareToData*)data
                      handler:(id<BrowserCommands>)handler
           baseViewController:(UIViewController*)baseViewController
     NS_DESIGNATED_INITIALIZER;
-// Initializes the print activity with the given |imageData| and the |handler|.
-// Print preview will be presented on top of |baseViewController|.
+// Initializes the print activity with the given `imageData` and the `handler`.
+// Print preview will be presented on top of `baseViewController`.
 // TODO(crbug.com/906662): Use id<BrowserCoordinatorCommands> for handler.
 - (instancetype)initWithImageData:(ShareImageData*)imageData
                           handler:(id<BrowserCommands>)handler
diff --git a/ios/chrome/browser/ui/activity_services/activities/print_activity_unittest.mm b/ios/chrome/browser/ui/activity_services/activities/print_activity_unittest.mm
index e6f9cef6..44c6f5f 100644
--- a/ios/chrome/browser/ui/activity_services/activities/print_activity_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/print_activity_unittest.mm
@@ -28,7 +28,7 @@
     mocked_handler_ = OCMStrictProtocolMock(@protocol(BrowserCommands));
   }
 
-  // Creates a ShareToData instance with |is_page_printable| set.
+  // Creates a ShareToData instance with `is_page_printable` set.
   ShareToData* CreateData(bool is_page_printable) {
     return [[ShareToData alloc] initWithShareURL:GURL("https://www.google.com/")
                                       visibleURL:GURL("https://google.com/")
diff --git a/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity.h b/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity.h
index d02e5b0..a4af68ab 100644
--- a/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity.h
@@ -16,7 +16,7 @@
 @interface RequestDesktopOrMobileSiteActivity : UIActivity
 
 // Initializes an activity to change between Mobile versus Desktop user agent,
-// with the current |userAgent| and |handler| to execute the action.
+// with the current `userAgent` and `handler` to execute the action.
 - (instancetype)initWithUserAgent:(web::UserAgentType)userAgent
                           handler:(id<BrowserCommands>)handler
                   navigationAgent:(WebNavigationBrowserAgent*)agent
diff --git a/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity_unittest.mm b/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity_unittest.mm
index 852a416..cae2e5e 100644
--- a/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/request_desktop_or_mobile_site_activity_unittest.mm
@@ -62,7 +62,7 @@
   std::unique_ptr<TestChromeBrowserState> browser_state_;
   std::unique_ptr<TestBrowser> browser_;
   WebNavigationBrowserAgent* agent_;
-  // Navigation manager for the web state at index 0 in |browser_|'s web state
+  // Navigation manager for the web state at index 0 in `browser_`'s web state
   // list.
   web::FakeNavigationManager* navigation_manager_;
 };
diff --git a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h
index 6de0a2e..8c14bef 100644
--- a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h
+++ b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity.h
@@ -13,8 +13,8 @@
 // Activity that sends the tab to another of the user's devices.
 @interface SendTabToSelfActivity : UIActivity
 
-// Initializes the send tab to self activity with the given |data| and the
-// |handler| that is used to add the tab to the other device.
+// Initializes the send tab to self activity with the given `data` and the
+// `handler` that is used to add the tab to the other device.
 - (instancetype)initWithData:(ShareToData*)data
                      handler:(id<BrowserCommands>)handler
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity_unittest.mm b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity_unittest.mm
index 0df8462..fc600b2b 100644
--- a/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/activities/send_tab_to_self_activity_unittest.mm
@@ -26,7 +26,7 @@
     mocked_handler_ = OCMStrictProtocolMock(@protocol(BrowserCommands));
   }
 
-  // Creates a ShareToData instance with |can_send_tab_to_self| set.
+  // Creates a ShareToData instance with `can_send_tab_to_self` set.
   ShareToData* CreateData(bool can_send_tab_to_self) {
     return [[ShareToData alloc] initWithShareURL:GURL("https://www.google.com/")
                                       visibleURL:GURL("https://google.com/")
diff --git a/ios/chrome/browser/ui/activity_services/activity_params.h b/ios/chrome/browser/ui/activity_services/activity_params.h
index 365ac3a..41ee379 100644
--- a/ios/chrome/browser/ui/activity_services/activity_params.h
+++ b/ios/chrome/browser/ui/activity_services/activity_params.h
@@ -16,29 +16,29 @@
 @interface ActivityParams : NSObject
 
 // Initializes an instance configured to share the current tab's URL for the
-// metrics |scenario|.
+// metrics `scenario`.
 - (instancetype)initWithScenario:(ActivityScenario)scenario
     NS_DESIGNATED_INITIALIZER;
 
-// Initializes an instance configured to share an |image|, along
-// with its |title|, for the metrics |scenario|.
+// Initializes an instance configured to share an `image`, along
+// with its `title`, for the metrics `scenario`.
 - (instancetype)initWithImage:(UIImage*)image
                         title:(NSString*)title
                      scenario:(ActivityScenario)scenario;
 
-// Initializes an instance configured to share |URL|, along with its |title| for
-// the metrics |scenario|.
+// Initializes an instance configured to share `URL`, along with its `title` for
+// the metrics `scenario`.
 - (instancetype)initWithURL:(const GURL&)URL
                       title:(NSString*)title
                    scenario:(ActivityScenario)scenario;
 
 // Initializes an instance configured to share one or more URLs represented by
-// |URLWithTitle|s, for the metrics |scenario|.
+// `URLWithTitle`s, for the metrics `scenario`.
 - (instancetype)initWithURLs:(NSArray<URLWithTitle*>*)URLs
                     scenario:(ActivityScenario)scenario;
 
-// Initializes an instance configured to share an |URL|, along
-// with its |title| and |additionalText|, for the metrics |scenario|.
+// Initializes an instance configured to share an `URL`, along
+// with its `title` and `additionalText`, for the metrics `scenario`.
 - (instancetype)initWithURL:(const GURL&)URL
                       title:(NSString*)title
              additionalText:(NSString*)additionalText
@@ -49,7 +49,7 @@
 // Image to be shared.
 @property(nonatomic, readonly, strong) UIImage* image;
 
-// Title of the content that will be shared. Must be set if |image| is set.
+// Title of the content that will be shared. Must be set if `image` is set.
 @property(nonatomic, readonly, copy) NSString* imageTitle;
 
 // URLs, and associated titles, of the page(s) to be shared.
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h
index a18d2a5..5f9ab6b91 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h
+++ b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.h
@@ -20,7 +20,7 @@
 @interface ActivityServiceCoordinator : ChromeCoordinator
 
 // Initializes a coordinator instance configured to share the current tab's URL
-// based on |baseViewController| and |browser|, and where |params| contains all
+// based on `baseViewController` and `browser`, and where `params` contains all
 // necessary values to drive the scenario.
 - (instancetype)initWithBaseViewController:(UIViewController*)baseViewController
                                    browser:(Browser*)browser
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
index 79a7af2a..b59247c 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_coordinator.mm
@@ -96,7 +96,7 @@
   }
 
   if (self.params.URLs.count > 0) {
-    // If at least one valid URL is found, share the URLs in |_params|.
+    // If at least one valid URL is found, share the URLs in `_params`.
     for (URLWithTitle* urlWithTitle in self.params.URLs) {
       if (!urlWithTitle.URL.is_empty()) {
         [self shareURLs];
@@ -120,7 +120,7 @@
 
 #pragma mark - Private Methods
 
-// Sets up the activity ViewController with the given |items| and |activities|.
+// Sets up the activity ViewController with the given `items` and `activities`.
 - (void)shareItems:(NSArray<id<ChromeActivityItemSource>>*)items
         activities:(NSArray*)activities {
   self.viewController =
@@ -193,7 +193,7 @@
   });
 }
 
-// Shares the current page using its |canonicalURL|.
+// Shares the current page using its `canonicalURL`.
 - (void)sharePageWithCanonicalURL:(const GURL&)canonicalURL {
   ShareToData* data = activity_services::ShareToDataForWebState(
       self.browser->GetWebStateList()->GetActiveWebState(), canonicalURL);
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_histograms.h b/ios/chrome/browser/ui/activity_services/activity_service_histograms.h
index fbcf79e..c7f7530 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_histograms.h
+++ b/ios/chrome/browser/ui/activity_services/activity_service_histograms.h
@@ -11,11 +11,11 @@
 // Records a histogram metric for the current scenario.
 void RecordScenarioInitiated(ActivityScenario scenario);
 
-// Records the given activity |type| for a |scenario|.
+// Records the given activity `type` for a `scenario`.
 void RecordActivityForScenario(activity_type_util::ActivityType type,
                                ActivityScenario scenario);
 
-// Records the given activity |type| for a |scenario|.
+// Records the given activity `type` for a `scenario`.
 void RecordCancelledScenario(ActivityScenario scenario);
 
 #endif  // IOS_CHROME_BROWSER_UI_ACTIVITY_SERVICES_ACTIVITY_SERVICE_HISTOGRAMS_H_
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_histograms.mm b/ios/chrome/browser/ui/activity_services/activity_service_histograms.mm
index 02088ce3b..1bcbc62 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_histograms.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_histograms.mm
@@ -37,7 +37,7 @@
 const char kShareTabGridSelectionModeActionsHistogram[] =
     "Mobile.Share.TabGridSelectionMode.Actions";
 
-// Enum representing an aggregation of the |ActivityType| enum values in a way
+// Enum representing an aggregation of the `ActivityType` enum values in a way
 // that is relevant for metric collection. Current values should not
 // be renumbered. Please keep in sync with "IOSShareAction" in
 // src/tools/metrics/histograms/enums.xml.
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_mediator.h b/ios/chrome/browser/ui/activity_services/activity_service_mediator.h
index e0c64932b..6698f70 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_mediator.h
+++ b/ios/chrome/browser/ui/activity_services/activity_service_mediator.h
@@ -30,12 +30,12 @@
 // Mediator used to generate activities.
 @interface ActivityServiceMediator : NSObject
 
-// Initializes a mediator instance with a |handler| used to execute action, a
-// |bookmarksHandler| to execute Bookmarks actions, a
-// |qrGenerationHandler| to execute QR generation actions, a |prefService| to
-// read settings and policies, and a |bookmarkModel| to retrieve bookmark
+// Initializes a mediator instance with a `handler` used to execute action, a
+// `bookmarksHandler` to execute Bookmarks actions, a
+// `qrGenerationHandler` to execute QR generation actions, a `prefService` to
+// read settings and policies, and a `bookmarkModel` to retrieve bookmark
 // states.
-// |baseViewController| can be passed to activities which need to present VCs.
+// `baseViewController` can be passed to activities which need to present VCs.
 - (instancetype)initWithHandler:(id<BrowserCommands, FindInPageCommands>)handler
                bookmarksHandler:(id<BookmarksCommands>)bookmarksHandler
             qrGenerationHandler:(id<QRGenerationCommands>)qrGenerationHandler
@@ -50,33 +50,33 @@
 @property(nonatomic, weak) DefaultBrowserPromoNonModalScheduler* promoScheduler;
 
 // Generates an array of activity items to be shared via an activity view for
-// the given objects in |dataItems|.
+// the given objects in `dataItems`.
 - (NSArray<id<ChromeActivityItemSource>>*)activityItemsForDataItems:
     (NSArray<ShareToData*>*)dataItems;
 
 // Generates an array of activities to be added to the activity view for the
-// given objects in |dataItems|. The items returned will be those supported
-// by all objects in |dataItems|.
+// given objects in `dataItems`. The items returned will be those supported
+// by all objects in `dataItems`.
 - (NSArray*)applicationActivitiesForDataItems:(NSArray<ShareToData*>*)dataItems;
 
 // Generates an array of activity items to be shared via an activity view for
-// the given |data|.
+// the given `data`.
 - (NSArray<ChromeActivityImageSource*>*)activityItemsForImageData:
     (ShareImageData*)data;
 
 // Generates an array of activities to be added to the activity view for the
-// given |data|.
+// given `data`.
 - (NSArray*)applicationActivitiesForImageData:(ShareImageData*)data;
 
-// Returns the union of excluded activity types given |items| to share.
+// Returns the union of excluded activity types given `items` to share.
 - (NSSet*)excludedActivityTypesForItems:
     (NSArray<id<ChromeActivityItemSource>>*)items;
 
-// Handles metric reporting when a sharing |scenario| is initiated.
+// Handles metric reporting when a sharing `scenario` is initiated.
 - (void)shareStartedWithScenario:(ActivityScenario)scenario;
 
-// Handles completion of a share |scenario| with a given action's
-// |activityType|. The value of |completed| represents whether the activity
+// Handles completion of a share `scenario` with a given action's
+// `activityType`. The value of `completed` represents whether the activity
 // was completed successfully or not.
 - (void)shareFinishedWithScenario:(ActivityScenario)scenario
                      activityType:(NSString*)activityType
diff --git a/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm b/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm
index f6b314e..9c7a9b5 100644
--- a/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_service_mediator.mm
@@ -87,8 +87,8 @@
     (NSArray<ShareToData*>*)dataItems {
   NSMutableArray* items = [[NSMutableArray alloc] init];
 
-  // The |additionalText| is not added when sharing multiple URLs since items
-  // are not associated with each other and the |additionalText| is not likely
+  // The `additionalText` is not added when sharing multiple URLs since items
+  // are not associated with each other and the `additionalText` is not likely
   // be meaningful without the context of the page it came from.
   if (dataItems.count == 1 && dataItems.firstObject.additionalText) {
     [items addObject:[[ChromeActivityTextSource alloc]
diff --git a/ios/chrome/browser/ui/activity_services/activity_type_util.h b/ios/chrome/browser/ui/activity_services/activity_type_util.h
index 4a82ac2b..3d678ef6 100644
--- a/ios/chrome/browser/ui/activity_services/activity_type_util.h
+++ b/ios/chrome/browser/ui/activity_services/activity_type_util.h
@@ -49,12 +49,12 @@
   UNKNOWN,
 };
 
-// Returns the ActivityType enum associated with |activityString|, which is the
-// bundle ID of a iOS App Extension. Returns UNKNOWN if |activityString| does
-// match any known App Extensions. |activityString| must not be nil.
+// Returns the ActivityType enum associated with `activityString`, which is the
+// bundle ID of a iOS App Extension. Returns UNKNOWN if `activityString` does
+// match any known App Extensions. `activityString` must not be nil.
 ActivityType TypeFromString(NSString* activityString);
 
-// Records the UMA for activity |type|.
+// Records the UMA for activity `type`.
 void RecordMetricForActivity(ActivityType type);
 
 }  // namespace activity_type_util
diff --git a/ios/chrome/browser/ui/activity_services/canonical_url_retriever.h b/ios/chrome/browser/ui/activity_services/canonical_url_retriever.h
index be1b8a8..6a6ff80 100644
--- a/ios/chrome/browser/ui/activity_services/canonical_url_retriever.h
+++ b/ios/chrome/browser/ui/activity_services/canonical_url_retriever.h
@@ -15,21 +15,21 @@
 
 namespace activity_services {
 
-// Retrieves the canonical URL in the web page represented by |web_state|.
+// Retrieves the canonical URL in the web page represented by `web_state`.
 // This method is asynchronous and the URL is returned by calling the
-// |completion| block.
+// `completion` block.
 // There are a few special cases:
 // 1. If there is more than one canonical URL defined, the first one
-// (found through a depth-first search of the DOM) is given to |completion|.
+// (found through a depth-first search of the DOM) is given to `completion`.
 // 2. If either no canonical URL is found, or the canonical URL is invalid, an
-// empty GURL is given to |completion|.
+// empty GURL is given to `completion`.
 // 3. If the canonical URL is a relative path, it is ignored and an empty GURL
-// is given to |completion|.
+// is given to `completion`.
 // 4. If the the visible URL is not HTTPS, then an empty
-// GURL is given to |completion|. This prevents untrusted sites from specifying
+// GURL is given to `completion`. This prevents untrusted sites from specifying
 // the canonical URL.
 // 5. If the canonical URL is not HTTPS, then an empty GURL is given to
-// |completion|. This prevents the canonical URL from being downgraded to HTTP
+// `completion`. This prevents the canonical URL from being downgraded to HTTP
 // from the HTTPS visible URL.
 void RetrieveCanonicalUrl(web::WebState* web_state,
                           ProceduralBlockWithURL completion);
diff --git a/ios/chrome/browser/ui/activity_services/canonical_url_retriever.mm b/ios/chrome/browser/ui/activity_services/canonical_url_retriever.mm
index 9fdba3a2..2d67b61 100644
--- a/ios/chrome/browser/ui/activity_services/canonical_url_retriever.mm
+++ b/ios/chrome/browser/ui/activity_services/canonical_url_retriever.mm
@@ -25,13 +25,13 @@
     u"  return linkNode ? linkNode.getAttribute(\"href\") : \"\";"
     u"})()";
 
-// Logs |result| in the Mobile.CanonicalURLResult histogram.
+// Logs `result` in the Mobile.CanonicalURLResult histogram.
 void LogCanonicalUrlResultHistogram(ui_metrics::CanonicalURLResult result) {
   UMA_HISTOGRAM_ENUMERATION(ui_metrics::kCanonicalURLResultHistogram, result,
                             ui_metrics::CANONICAL_URL_RESULT_COUNT);
 }
 
-// Converts a |value| to a GURL. Returns an empty GURL if |value| is not a valid
+// Converts a `value` to a GURL. Returns an empty GURL if `value` is not a valid
 // HTTPS URL, indicating that retrieval failed. This function also handles
 // logging retrieval failures if applicable.
 GURL UrlFromValue(const base::Value* value) {
@@ -43,7 +43,7 @@
 
     // This variable is required for metrics collection in order to distinguish
     // between the no canonical URL found and the invalid canonical URL found
-    // cases. The |canonical_url| GURL cannot be relied upon to distinguish
+    // cases. The `canonical_url` GURL cannot be relied upon to distinguish
     // between these cases because GURLs created with invalid URLs can be
     // constructed as empty GURLs.
     canonical_url_found = true;
diff --git a/ios/chrome/browser/ui/activity_services/canonical_url_retriever_unittest.mm b/ios/chrome/browser/ui/activity_services/canonical_url_retriever_unittest.mm
index 4abb7339..2011de84 100644
--- a/ios/chrome/browser/ui/activity_services/canonical_url_retriever_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/canonical_url_retriever_unittest.mm
@@ -44,7 +44,7 @@
   void SetUp() override { PlatformTest::SetUp(); }
 
  protected:
-  // Retrieves the canonical URL and returns it through the |url| out parameter.
+  // Retrieves the canonical URL and returns it through the `url` out parameter.
   // Returns whether the operation was successful.
   bool RetrieveCanonicalUrl(GURL* url) {
     __block GURL result;
diff --git a/ios/chrome/browser/ui/activity_services/data/chrome_activity_image_source.h b/ios/chrome/browser/ui/activity_services/data/chrome_activity_image_source.h
index 67f874e..4faa562 100644
--- a/ios/chrome/browser/ui/activity_services/data/chrome_activity_image_source.h
+++ b/ios/chrome/browser/ui/activity_services/data/chrome_activity_image_source.h
@@ -12,7 +12,7 @@
 // Returns an image to the UIActivities that can take advantage of it.
 @interface ChromeActivityImageSource : NSObject <ChromeActivityItemSource>
 
-// Default initializer. |image| and |title| must not be nil.
+// Default initializer. `image` and `title` must not be nil.
 - (instancetype)initWithImage:(UIImage*)image title:(NSString*)title;
 
 @end
diff --git a/ios/chrome/browser/ui/activity_services/data/chrome_activity_item_thumbnail_generator.h b/ios/chrome/browser/ui/activity_services/data/chrome_activity_item_thumbnail_generator.h
index ddf3fda..284b8cf 100644
--- a/ios/chrome/browser/ui/activity_services/data/chrome_activity_item_thumbnail_generator.h
+++ b/ios/chrome/browser/ui/activity_services/data/chrome_activity_item_thumbnail_generator.h
@@ -16,7 +16,7 @@
 // thumbnail given WebState.
 @interface ChromeActivityItemThumbnailGenerator : NSObject
 
-// Default initializer. |webState| must not be nullptr.
+// Default initializer. `webState` must not be nullptr.
 - (instancetype)initWithWebState:(web::WebState*)webState
     NS_DESIGNATED_INITIALIZER;
 
diff --git a/ios/chrome/browser/ui/activity_services/data/chrome_activity_text_source.h b/ios/chrome/browser/ui/activity_services/data/chrome_activity_text_source.h
index 5afccc43..28c1426a 100644
--- a/ios/chrome/browser/ui/activity_services/data/chrome_activity_text_source.h
+++ b/ios/chrome/browser/ui/activity_services/data/chrome_activity_text_source.h
@@ -12,7 +12,7 @@
 // This UIActivityItemSource-conforming object conforms to UTType public.text.
 @interface ChromeActivityTextSource : NSObject <ChromeActivityItemSource>
 
-// Default initializer. |text| must not be nil.
+// Default initializer. `text` must not be nil.
 - (instancetype)initWithText:(NSString*)text;
 
 @end
diff --git a/ios/chrome/browser/ui/activity_services/data/chrome_activity_url_source.h b/ios/chrome/browser/ui/activity_services/data/chrome_activity_url_source.h
index 711696e..f71812b 100644
--- a/ios/chrome/browser/ui/activity_services/data/chrome_activity_url_source.h
+++ b/ios/chrome/browser/ui/activity_services/data/chrome_activity_url_source.h
@@ -12,12 +12,12 @@
 @class ChromeActivityItemThumbnailGenerator;
 
 // This UIActivityItemSource-conforming object conforms to UTType public.url so
-// it can be used with other Social Sharing Extensions as well. The |shareURL|
-// is the URL shared with Social Sharing Extensions. The |subject| is used by
+// it can be used with other Social Sharing Extensions as well. The `shareURL`
+// is the URL shared with Social Sharing Extensions. The `subject` is used by
 // Mail applications to pre-fill in the subject line.
 @interface ChromeActivityURLSource : NSObject <ChromeActivityItemSource>
 
-// Default initializer. |shareURL| and |subject| must not be nil.
+// Default initializer. `shareURL` and `subject` must not be nil.
 - (instancetype)initWithShareURL:(NSURL*)shareURL subject:(NSString*)subject;
 
 // Thumbnail generator used to provide thumbnails to extensions that request
diff --git a/ios/chrome/browser/ui/activity_services/data/share_to_data_builder.h b/ios/chrome/browser/ui/activity_services/data/share_to_data_builder.h
index 5c18355..4a3ed7c 100644
--- a/ios/chrome/browser/ui/activity_services/data/share_to_data_builder.h
+++ b/ios/chrome/browser/ui/activity_services/data/share_to_data_builder.h
@@ -18,22 +18,22 @@
 
 namespace activity_services {
 
-// Returns a ShareToData object using data from |web_state|. |share_url| is the
-// URL to be shared with share extensions. If |share_url| is empty, the visible
-// URL associated with |web_state| will be used instead. |web_state| must not be
+// Returns a ShareToData object using data from `web_state`. `share_url` is the
+// URL to be shared with share extensions. If `share_url` is empty, the visible
+// URL associated with `web_state` will be used instead. `web_state` must not be
 // nil. Function may return nil.
 ShareToData* ShareToDataForWebState(web::WebState* web_state,
                                     const GURL& share_url);
 
-// Returns a ShareToData object for a single |URL|, and its page's |title|,
-// which is not associated to a WebState. Will also add |additionalText|, if
+// Returns a ShareToData object for a single `URL`, and its page's `title`,
+// which is not associated to a WebState. Will also add `additionalText`, if
 // present.
 ShareToData* ShareToDataForURL(const GURL& URL,
                                NSString* title,
                                NSString* additionalText);
 
-// Returns a ShareToData object for a single |URLWithTitle|, which is not
-// associated to a WebState. Will also add |additionalText|, if present.
+// Returns a ShareToData object for a single `URLWithTitle`, which is not
+// associated to a WebState. Will also add `additionalText`, if present.
 ShareToData* ShareToDataForURLWithTitle(URLWithTitle* URLWithTitle);
 
 }  // namespace activity_services
diff --git a/ios/chrome/browser/ui/activity_services/data/share_to_data_builder_unittest.mm b/ios/chrome/browser/ui/activity_services/data/share_to_data_builder_unittest.mm
index e8ff49f..fd30428 100644
--- a/ios/chrome/browser/ui/activity_services/data/share_to_data_builder_unittest.mm
+++ b/ios/chrome/browser/ui/activity_services/data/share_to_data_builder_unittest.mm
@@ -133,7 +133,7 @@
   }
 }
 
-// Verifies that |ShareToDataForWebState()| returns nil if the WebState passed
+// Verifies that `ShareToDataForWebState()` returns nil if the WebState passed
 // is nullptr.
 TEST_F(ShareToDataBuilderTest, TestReturnsNilWhenClosing) {
   EXPECT_EQ(nil, activity_services::ShareToDataForWebState(nullptr, GURL()));
diff --git a/ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h b/ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h
index 667d95e..81e9461 100644
--- a/ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h
+++ b/ios/chrome/browser/ui/activity_services/requirements/activity_service_positioner.h
@@ -22,8 +22,8 @@
 @optional
 
 // Returns the bar button item where the UIActivityViewController should be
-// presented from. If a non null value is returned, |sourceView| and
-// |sourceRect| are not used.
+// presented from. If a non null value is returned, `sourceView` and
+// `sourceRect` are not used.
 - (UIBarButtonItem*)barButtonItem;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
index 9452c79..7dde5c11 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
@@ -288,7 +288,7 @@
   testAddressIconIsNotVisibleWhenAddressStoreEmpty
 #endif
 - (void)MAYBE_testAddressIconIsNotVisibleWhenAddressStoreEmpty {
-  // Delete the profile that is added on |-setUp|.
+  // Delete the profile that is added on `-setUp`.
   [AutofillAppInterface clearProfilesStore];
 
   // Bring up the keyboard.
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.h b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.h
index 763acbe0..33fb889 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.h
@@ -35,8 +35,8 @@
 // in the forms.
 @property(nonatomic, strong) ManualFillInjectionHandler* injectionHandler;
 
-// Creates a coordinator that uses a |viewController|, |browser| and an
-// |injectionHandler|.
+// Creates a coordinator that uses a `viewController`, `browser` and an
+// `injectionHandler`.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
                           injectionHandler:
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm
index d15bf42..f3d7555 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm
@@ -60,7 +60,7 @@
 - (void)presentFromButton:(UIButton*)button {
   self.viewController.modalPresentationStyle = UIModalPresentationPopover;
 
-  // |topFrontWindow| is used in order to present above the keyboard. This way
+  // `topFrontWindow` is used in order to present above the keyboard. This way
   // the popover will be dismissed on keyboard interaction and it won't be
   // covered when the keyboard is near the top of the screen.
   UIWindow* topFrontWindow =
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/fallback_view_controller.mm
index 0a7bcf6..82cd2d7 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_view_controller.mm
@@ -24,13 +24,13 @@
 
 namespace {
 
-// This is the width used for |self.preferredContentSize|.
+// This is the width used for `self.preferredContentSize`.
 constexpr CGFloat PopoverPreferredWidth = 320;
 
-// This is the maximum height used for |self.preferredContentSize|.
+// This is the maximum height used for `self.preferredContentSize`.
 constexpr CGFloat PopoverMaxHeight = 360;
 
-// This is the height used for |self.preferredContentSize| when showing the
+// This is the height used for `self.preferredContentSize` when showing the
 // loading indicator on iPad.
 constexpr CGFloat PopoverLoadingHeight = 185.5;
 
@@ -71,7 +71,7 @@
 }
 
 - (void)viewDidLoad {
-  // Super's |viewDidLoad| uses |styler.tableViewBackgroundColor| so it needs to
+  // Super's `viewDidLoad` uses `styler.tableViewBackgroundColor` so it needs to
   // be set before.
   self.styler.tableViewBackgroundColor = [UIColor colorNamed:kBackgroundColor];
 
@@ -192,7 +192,7 @@
   }
 }
 
-// Presents |items| in the respective section. Handles creating or deleting the
+// Presents `items` in the respective section. Handles creating or deleting the
 // section accordingly.
 - (void)presentFallbackItems:(NSArray<TableViewItem*>*)items
                    inSection:(SectionIdentifier)sectionIdentifier {
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.h b/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.h
index 60e4f928..b318713 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.h
@@ -20,7 +20,7 @@
 // Returns a fresh object observing the active web state for the passed list.
 - (instancetype)initWithWebStateList:(WebStateList*)webStateList;
 
-// Not available. Use |initWithWebStateList:|.
+// Not available. Use `initWithWebStateList:`.
 - (instancetype)init NS_UNAVAILABLE;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.mm b/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.mm
index 8dc8451a..b12cf20b 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/form_observer_helper.mm
@@ -26,7 +26,7 @@
   // Bridge to observe the web state list from Objective-C.
   std::unique_ptr<WebStateListObserverBridge> _webStateListObserver;
 
-  // Bridge to observe form activity in |_webState|.
+  // Bridge to observe form activity in `_webState`.
   std::unique_ptr<autofill::FormActivityObserverBridge>
       _formActivityObserverBridge;
 }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
index 790c2dca..bb81683 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.mm
@@ -132,7 +132,7 @@
 
 #pragma mark - Private
 
-// Helper to create a system button with the passed data and |self| as the
+// Helper to create a system button with the passed data and `self` as the
 // target. Such button has been configured to have some preset properties
 - (UIButton*)manualFillButtonWithAction:(SEL)selector
                              ImageNamed:(NSString*)imageName
@@ -257,7 +257,7 @@
 - (void)setKeyboardButtonHidden:(BOOL)hidden animated:(BOOL)animated {
   [UIView animateWithDuration:animated ? MFAnimationDuration : 0
                    animations:^{
-                     // Workaround setting more than once the |hidden| property
+                     // Workaround setting more than once the `hidden` property
                      // in stacked views.
                      if (self.keyboardButton.hidden != hidden) {
                        self.keyboardButton.hidden = hidden;
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile.h
index 6520c58..dc59d693 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile.h
@@ -13,15 +13,15 @@
 class AutofillProfile;
 }
 
-// Extends |ManualFillAddress| with a convenience initializer from c++
-// |autofill::AutofillProfile|.
+// Extends `ManualFillAddress` with a convenience initializer from c++
+// `autofill::AutofillProfile`.
 @interface ManualFillAddress (AutofillProfile)
 
 // Convenience initializer from an autofill::AutofillProfile.
 - (instancetype)initWithProfile:(const autofill::AutofillProfile&)profile;
 
-// Converts a list of |autofill::AutofillProfile| into a list of
-// |ManualFillAddress|.
+// Converts a list of `autofill::AutofillProfile` into a list of
+// `ManualFillAddress`.
 + (NSArray<ManualFillAddress*>*)manualFillAddressesFromProfiles:
     (std::vector<autofill::AutofillProfile*>)profiles;
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address.h
index 1783eab..94fa93f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address.h
@@ -61,8 +61,8 @@
                      emailAddress:(NSString*)emailAddress
     NS_DESIGNATED_INITIALIZER;
 
-// Unavailable. Please use |initWithFirstName:middleNameOrInitial:lastName:
-// line1:line2:zip:city:state:country:|.
+// Unavailable. Please use `initWithFirstName:middleNameOrInitial:lastName:
+// line1:line2:zip:city:state:country:`.
 - (instancetype)init NS_UNAVAILABLE;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.h
index 73093a3..c6a6b32 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.h
@@ -17,7 +17,7 @@
 // Wrapper to show address cells in a ChromeTableViewController.
 @interface ManualFillAddressItem : TableViewItem
 
-// Inits an address with a |profile| and the |delegate| for user selection.
+// Inits an address with a `profile` and the `delegate` for user selection.
 - (instancetype)initWithAddress:(ManualFillAddress*)address
                 contentInjector:(id<ManualFillContentInjector>)contentInjector
     NS_DESIGNATED_INITIALIZER;
@@ -30,7 +30,7 @@
 // and sendable the data to the delegate.
 @interface ManualFillAddressCell : TableViewCell
 
-// Updates the cell with address and the |delegate| to be notified.
+// Updates the cell with address and the `delegate` to be notified.
 - (void)setUpWithAddress:(ManualFillAddress*)profile
          contentInjector:(id<ManualFillContentInjector>)contentInjector;
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
index 114cf9a5..aebe7bd 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_cell.mm
@@ -326,10 +326,10 @@
 
 #pragma mark - Private
 
-// Dynamically lay givens |views| on |guide|, adding first view of every
-// generated line to |addFirstLineViewTo|. If |largeTypes| is true, fields are
+// Dynamically lay givens `views` on `guide`, adding first view of every
+// generated line to `addFirstLineViewTo`. If `largeTypes` is true, fields are
 // laid out vertically one per line, otherwise horizontally on one line.
-// Constraints are added to |self.dynamicConstraints| property.
+// Constraints are added to `self.dynamicConstraints` property.
 - (void)layMultipleViews:(NSArray<UIView*>*)views
           withLargeTypes:(BOOL)largeTypes
                  onGuide:(UIView*)guide
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_mediator.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_mediator.h
index b82c3b9..18aed4fb 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_mediator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address_mediator.h
@@ -37,10 +37,10 @@
 - (instancetype)initWithProfiles:
     (std::vector<autofill::AutofillProfile*>)profiles NS_DESIGNATED_INITIALIZER;
 
-// Unavailable. Use |initWithProfiles:|.
+// Unavailable. Use `initWithProfiles:`.
 - (instancetype)init NS_UNAVAILABLE;
 
-// Updates the |profiles| being presented.
+// Updates the `profiles` being presented.
 - (void)reloadWithProfiles:(std::vector<autofill::AutofillProfile*>)profiles;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.h
index 6d14cb33..b67c2d7f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_cell.h
@@ -30,7 +30,7 @@
 // and send the data to the delegate.
 @interface ManualFillCardCell : TableViewCell
 
-// Updates the cell with credit card and the |delegate| to be notified.
+// Updates the cell with credit card and the `delegate` to be notified.
 - (void)setUpWithCreditCard:(ManualFillCreditCard*)card
             contentInjector:(id<ManualFillContentInjector>)contentInjector
          navigationDelegate:(id<CardListDelegate>)navigationDelegate;
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_mediator.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_mediator.h
index e50077a7..5f1973f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_mediator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_card_mediator.h
@@ -39,17 +39,17 @@
 // The delegate in charge of navigation.
 @property(nonatomic, weak) id<CardListDelegate> navigationDelegate;
 
-// The designated initializer. |cards| must not be nil.
+// The designated initializer. `cards` must not be nil.
 - (instancetype)initWithCards:(std::vector<autofill::CreditCard*>)cards
     NS_DESIGNATED_INITIALIZER;
 
-// Unavailable. Use |initWithCards:|.
+// Unavailable. Use `initWithCards:`.
 - (instancetype)init NS_UNAVAILABLE;
 
-// Finds the original autofill::CreditCard from given |GUID|.
+// Finds the original autofill::CreditCard from given `GUID`.
 - (const autofill::CreditCard*)findCreditCardfromGUID:(NSString*)GUID;
 
-// Updates the |cards| being presented.
+// Updates the `cards` being presented.
 - (void)reloadWithCards:(std::vector<autofill::CreditCard*>)cards;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h
index 5466a76f..82821e1 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.h
@@ -21,7 +21,7 @@
 // The multiplier for the base system spacing at the bottom margin.
 static const CGFloat BottomSystemSpacingMultiplier = 2.26;
 
-// Options for |AppendHorizontalConstraintsForViews|.
+// Options for `AppendHorizontalConstraintsForViews`.
 typedef NS_OPTIONS(NSUInteger, AppendConstraints) {
   AppendConstraintsNone = 0,
   // Add an equal constraint to the baselines.
@@ -32,11 +32,11 @@
 
 }  // namespace
 
-// Creates a blank button in chip style, for the given |action| and |target|.
+// Creates a blank button in chip style, for the given `action` and `target`.
 UIButton* CreateChipWithSelectorAndTarget(SEL action, id target);
 
-// Adds vertical constraints to given list, laying |views| vertically (based on
-// firstBaselineAnchor for the buttons or labels) inside |container|, starting
+// Adds vertical constraints to given list, laying `views` vertically (based on
+// firstBaselineAnchor for the buttons or labels) inside `container`, starting
 // at its topAnchor. Constrainst are not activated.  Default multipliers are
 // applied.
 void AppendVerticalConstraintsSpacingForViews(
@@ -44,7 +44,7 @@
     NSArray<UIView*>* views,
     UIView* container);
 
-// Adds vertical constraints like |AppendVerticalConstraintsSpacingForViews|
+// Adds vertical constraints like `AppendVerticalConstraintsSpacingForViews`
 // above but using given mutipliers at top, bottom and in-between rows.
 void AppendVerticalConstraintsSpacingForViews(
     NSMutableArray<NSLayoutConstraint*>* constraints,
@@ -53,24 +53,24 @@
     CGFloat topSystemSpacingMultiplier,
     CGFloat BottomSystemSpacingMultiplier);
 
-// Adds constraints to the given list, for the given |views|, so as to lay them
-// out horizontally, parallel to the |guide| view. Constraints are not
+// Adds constraints to the given list, for the given `views`, so as to lay them
+// out horizontally, parallel to the `guide` view. Constraints are not
 // activated.
 void AppendHorizontalConstraintsForViews(
     NSMutableArray<NSLayoutConstraint*>* constraints,
     NSArray<UIView*>* views,
     UIView* guide);
 
-// Adds constraints like |AppendHorizontalConstraintsForViews| above but also
-// applies the given constant |margin| at both ends of the whole row.
+// Adds constraints like `AppendHorizontalConstraintsForViews` above but also
+// applies the given constant `margin` at both ends of the whole row.
 void AppendHorizontalConstraintsForViews(
     NSMutableArray<NSLayoutConstraint*>* constraints,
     NSArray<UIView*>* views,
     UIView* guide,
     CGFloat margin);
 
-// Adds constraints like |AppendHorizontalConstraintsForViews| above
-// but with given |options|.
+// Adds constraints like `AppendHorizontalConstraintsForViews` above
+// but with given `options`.
 void AppendHorizontalConstraintsForViews(
     NSMutableArray<NSLayoutConstraint*>* constraints,
     NSArray<UIView*>* views,
@@ -78,7 +78,7 @@
     CGFloat margin,
     AppendConstraints options);
 
-// Adds all baseline anchor constraints for the given |views| to match the first
+// Adds all baseline anchor constraints for the given `views` to match the first
 // one. Constrainst are not activated.
 void AppendEqualBaselinesConstraints(
     NSMutableArray<NSLayoutConstraint*>* constraints,
@@ -88,7 +88,7 @@
 UILabel* CreateLabel();
 
 // Creates a gray horizontal line separator, with the same margin as the other
-// components here. The gray line is added to the given |container| and proper
+// components here. The gray line is added to the given `container` and proper
 // constraints are enabled to keep the line at the bottom of the container and
 // within the horizontal safe area.
 UIView* CreateGraySeparatorForContainer(UIView* container);
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm
index 928e887..3b399ed 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_cell_utils.mm
@@ -17,7 +17,7 @@
 #endif
 
 namespace {
-// Horizontal spacing between views in |AppendHorizontalConstraintsForViews|.
+// Horizontal spacing between views in `AppendHorizontalConstraintsForViews`.
 constexpr CGFloat kHorizontalSpacing = 16;
 }  // namespace
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_injector.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_injector.h
index 29d38cfb..66d8fc10 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_injector.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_content_injector.h
@@ -11,7 +11,7 @@
 // state.
 @protocol ManualFillContentInjector <NSObject>
 
-// Must be called before |userDidPickContent| to validate if a value type can be
+// Must be called before `userDidPickContent` to validate if a value type can be
 // injected, if either flag is true. If not, an alert is given to the user and
 // NO is returned.
 // @param passwordField YES if the user selected content that requires a
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credential.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credential.h
index 8e2ecdf..2a571eb 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credential.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credential.h
@@ -36,7 +36,7 @@
                             host:(NSString*)host
                              URL:(const GURL&)URL NS_DESIGNATED_INITIALIZER;
 
-// Unavailable. Please use |initWithUsername:password:siteName:host:|.
+// Unavailable. Please use `initWithUsername:password:siteName:host:`.
 - (instancetype)init NS_UNAVAILABLE;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credit_card.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credit_card.h
index 3b98482..7c97d619 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credit_card.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_credit_card.h
@@ -38,8 +38,8 @@
 // The credit card icon id.
 @property(nonatomic, readonly) int issuerNetworkIconID;
 
-// Default init. |GUID| and |number| are the only fields considered for
-// equality, so we can differentiate between an obfuscated and a comlete one.
+// Default init. `GUID` and `number` are the only fields considered for
+// equality, so we can differentiate between an obfuscated and a complete one.
 - (instancetype)initWithGUID:(NSString*)GUID
                      network:(NSString*)network
          issuerNetworkIconID:(int)issuerNetworkIconID
@@ -51,8 +51,8 @@
              expirationMonth:(NSString*)expirationMonth
     NS_DESIGNATED_INITIALIZER;
 
-// Unavailable. Please use |initWithGuid:network:bankName:cardholder:number:
-// obfuscatedNumber:expirationYear:expirationMonth:|.
+// Unavailable. Please use `initWithGuid:network:bankName:cardholder:number:
+// obfuscatedNumber:expirationYear:expirationMonth:`.
 - (instancetype)init NS_UNAVAILABLE;
 
 @end
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_full_card_requester.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_full_card_requester.h
index cd746d6..1c8e9b6 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_full_card_requester.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_full_card_requester.h
@@ -22,7 +22,7 @@
 // let user 'unlock' server side credit card by input correct CVC.
 @interface ManualFillFullCardRequester : NSObject
 
-// Inits the requests with required parameters and the |delegate| to receive the
+// Inits the requests with required parameters and the `delegate` to receive the
 // success/failure state of the request.
 - (instancetype)initWithBrowserState:(ChromeBrowserState*)browserState
                         webStateList:(WebStateList*)webStateList
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h
index 0aa8396..7b803bec 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.h
@@ -17,8 +17,8 @@
 // TODO(crbug.com/1116980): Convert ManualFillInjectionHandler to browser agent.
 @interface ManualFillInjectionHandler : NSObject <ManualFillContentInjector>
 
-// Returns a handler using the |WebStateList| to inject JS to the active web
-// state and |securityAlertPresenter| to present alerts.
+// Returns a handler using the `WebStateList` to inject JS to the active web
+// state and `securityAlertPresenter` to present alerts.
 - (instancetype)
       initWithWebStateList:(WebStateList*)webStateList
       securityAlertHandler:(id<SecurityAlertCommands>)securityAlertHandler
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
index 8156fd7..a537ed9 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
@@ -45,7 +45,7 @@
 // The object in charge of listening to form events and reporting back.
 @property(nonatomic, strong) FormObserverHelper* formHelper;
 
-// Interface for |reauthenticationModule|, handling mostly the case when no
+// Interface for `reauthenticationModule`, handling mostly the case when no
 // hardware for authentication is available.
 @property(nonatomic, strong) ReauthenticationModule* reauthenticationModule;
 
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.h
index 4cb96dd..abde828 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_cell.h
@@ -48,8 +48,8 @@
 // Identifier to match a URLItem with its URLCell.
 @property(nonatomic, readonly) NSString* uniqueIdentifier;
 
-// Updates the cell with the |credential|. If the user iteracts with it, the
-// |delegate| will be notified.
+// Updates the cell with the `credential`. If the user iteracts with it, the
+// `delegate` will be notified.
 - (void)setUpWithCredential:(ManualFillCredential*)credential
     isConnectedToPreviousCell:(BOOL)isConnectedToPreviousCell
         isConnectedToNextCell:(BOOL)isConnectedToNextCell
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h
index 001e0e6..2da1b7f 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_coordinator.h
@@ -32,8 +32,8 @@
 // FallbackCoordinatorDelegate, and replaces the superclass delegate.
 @property(nonatomic, weak) id<PasswordCoordinatorDelegate> delegate;
 
-// Creates a coordinator that uses a |viewController|, |browser|,
-// |URL| and an |injectionHandler|.
+// Creates a coordinator that uses a `viewController`, `browser`,
+// `URL` and an `injectionHandler`.
 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
                                    browser:(Browser*)browser
                                        URL:(const GURL&)URL
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.h b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.h
index 31460d9..0d151bf 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.h
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.h
@@ -62,7 +62,7 @@
 @property(nonatomic, assign, getter=isActionSectionEnabled)
     BOOL actionSectionEnabled;
 
-// The designated initializer. |passwordStore| must not be nil.
+// The designated initializer. `passwordStore` must not be nil.
 - (instancetype)initWithPasswordStore:
                     (scoped_refptr<password_manager::PasswordStoreInterface>)
                         passwordStore
@@ -73,7 +73,7 @@
                invokedOnPasswordField:(BOOL)invokedOnPasswordField
     NS_DESIGNATED_INITIALIZER;
 
-// Unavailable. Use |initWithPasswordStore:faviconLoader:|.
+// Unavailable. Use `initWithPasswordStore:faviconLoader:`.
 - (instancetype)init NS_UNAVAILABLE;
 
 // Fetches passwords using the URL provided at initialisation as the filter.
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
index 9a9a20b..888716c 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_password_mediator.mm
@@ -99,7 +99,7 @@
   // Bridge to observe the web state from Objective-C.
   std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
 
-  // Bridge to observe form activity in |_webState|.
+  // Bridge to observe form activity in `_webState`.
   std::unique_ptr<autofill::FormActivityObserverBridge>
       _formActivityObserverBridge;
 
@@ -198,7 +198,7 @@
   }
 }
 
-// Posts the credentials to the consumer. If filtered is |YES| it only post the
+// Posts the credentials to the consumer. If filtered is `YES` it only post the
 // ones associated with the active web state.
 - (void)postCredentialsToConsumer {
   if (!self.consumer) {
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
index 53c92b6..6b4b19a00 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/password_view_controller.mm
@@ -177,7 +177,7 @@
 
 #pragma mark - Private
 
-// Retrieves favicon from FaviconLoader and sets image in |cell|.
+// Retrieves favicon from FaviconLoader and sets image in `cell`.
 - (void)loadFaviconForCell:(UITableViewCell*)cell
                  indexPath:(NSIndexPath*)indexPath {
   TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
diff --git a/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h b/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h
index 798fc3e..bbc521b1 100644
--- a/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h
+++ b/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer.h
@@ -44,7 +44,7 @@
 
 // Observer method for objects that care about whether the main content area is
 // being dragged.  Note that if a drag ends with residual velocity, it's
-// possible for |dragging| to be NO while |scrolling| is still YES.
+// possible for `dragging` to be NO while `scrolling` is still YES.
 - (void)broadcastScrollViewIsDragging:(BOOL)dragging;
 
 #pragma mark - Toolbar UI
diff --git a/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge.h b/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge.h
index 5374cf8..ddfd8622 100644
--- a/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge.h
+++ b/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge.h
@@ -12,34 +12,34 @@
  public:
   virtual ~ChromeBroadcastObserverInterface();
 
-  // Invoked by |-broadcastScrollViewSize:|.
+  // Invoked by `-broadcastScrollViewSize:`.
   virtual void OnScrollViewSizeBroadcasted(CGSize scroll_view_size) {}
 
-  // Invoked by |-broadcastScrollViewContentSize:|.
+  // Invoked by `-broadcastScrollViewContentSize:`.
   virtual void OnScrollViewContentSizeBroadcasted(CGSize content_size) {}
 
-  // Invoked by |-broadcastScrollViewContentInset:|.
+  // Invoked by `-broadcastScrollViewContentInset:`.
   virtual void OnScrollViewContentInsetBroadcasted(UIEdgeInsets conent_inset) {}
 
-  // Invoked by |-broadcastContentScrollOffset:|.
+  // Invoked by `-broadcastContentScrollOffset:`.
   virtual void OnContentScrollOffsetBroadcasted(CGFloat offset) {}
 
-  // Invoked by |-broadcastScrollViewIsScrolling:|.
+  // Invoked by `-broadcastScrollViewIsScrolling:`.
   virtual void OnScrollViewIsScrollingBroadcasted(bool scrolling) {}
 
-  // Invoked by |-broadcastScrollViewIsZooming:|.
+  // Invoked by `-broadcastScrollViewIsZooming:`.
   virtual void OnScrollViewIsZoomingBroadcasted(bool zooming) {}
 
-  // Invoked by |-broadcastScrollViewIsDragging:|.
+  // Invoked by `-broadcastScrollViewIsDragging:`.
   virtual void OnScrollViewIsDraggingBroadcasted(bool dragging) {}
 
-  // Invoked by |-broadcastCollapsedToolbarHeight:|.
+  // Invoked by `-broadcastCollapsedToolbarHeight:`.
   virtual void OnCollapsedToolbarHeightBroadcasted(CGFloat height) {}
 
-  // Invoked by |-broadcastExpandedToolbarHeight:|.
+  // Invoked by `-broadcastExpandedToolbarHeight:`.
   virtual void OnExpandedToolbarHeightBroadcasted(CGFloat height) {}
 
-  // Invoked by |-broadcastBottomToolbarHeight:|.
+  // Invoked by `-broadcastBottomToolbarHeight:`.
   virtual void OnBottomToolbarHeightBroadcasted(CGFloat height) {}
 };
 
@@ -51,7 +51,7 @@
 @property(nonatomic, readonly, nonnull)
     ChromeBroadcastObserverInterface* observer;
 
-// Initializer for a bridge that updates |observer|.
+// Initializer for a bridge that updates `observer`.
 - (nullable instancetype)initWithObserver:
     (nonnull ChromeBroadcastObserverInterface*)observer
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge_unittest.mm b/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge_unittest.mm
index cdf6b5d..7e59299 100644
--- a/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge_unittest.mm
+++ b/ios/chrome/browser/ui/broadcaster/chrome_broadcast_observer_bridge_unittest.mm
@@ -62,7 +62,7 @@
   __strong ChromeBroadcastOberverBridge* bridge_ = nil;
 };
 
-// Tests that |-broadcastContentScrollOffset:| is correctly forwarded to the
+// Tests that `-broadcastContentScrollOffset:` is correctly forwarded to the
 // observer.
 TEST_F(ChromeBroadcastObserverBridgeTest, ContentOffset) {
   ASSERT_EQ(observer().scroll_offset(), 0.0);
@@ -71,7 +71,7 @@
   EXPECT_EQ(observer().scroll_offset(), kOffset);
 }
 
-// Tests that |-broadcastScrollViewIsScrolling:| is correctly forwarded to the
+// Tests that `-broadcastScrollViewIsScrolling:` is correctly forwarded to the
 // observer.
 TEST_F(ChromeBroadcastObserverBridgeTest, ScrollViewIsScrolling) {
   ASSERT_FALSE(observer().scroll_view_scrolling());
@@ -79,7 +79,7 @@
   EXPECT_TRUE(observer().scroll_view_scrolling());
 }
 
-// Tests that |-broadcastScrollViewIsDragging:| is correctly forwarded to the
+// Tests that `-broadcastScrollViewIsDragging:` is correctly forwarded to the
 // observer.
 TEST_F(ChromeBroadcastObserverBridgeTest, ScrollViewIsDragging) {
   ASSERT_FALSE(observer().scroll_view_dragging());
@@ -87,7 +87,7 @@
   EXPECT_TRUE(observer().scroll_view_dragging());
 }
 
-// Tests that |-broadcastToolbarHeight:| is correctly forwarded to the
+// Tests that `-broadcastToolbarHeight:` is correctly forwarded to the
 // observer.
 TEST_F(ChromeBroadcastObserverBridgeTest, ToolbarHeight) {
   ASSERT_EQ(observer().collapsed_height(), 0.0);
diff --git a/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.h b/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.h
index 8c530c6..f4fd3c44ec 100644
--- a/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.h
+++ b/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.h
@@ -24,33 +24,33 @@
 // simplicity.)
 @interface ChromeBroadcaster : NSObject
 
-// Makes the value (property) of |object| identified by |valueKey| observable
-// via |selector|. It is an error if |selector| is not defined in the
+// Makes the value (property) of `object` identified by `valueKey` observable
+// via `selector`. It is an error if `selector` is not defined in the
 // BroadcastObserver protocol, or if a value is already being broadcast
-// for |selector|.
-// If there are already observers for |selector|, they will have their observer
+// for `selector`.
+// If there are already observers for `selector`, they will have their observer
 // methods called immediately with the current broadcast value, before this
 // method returns.
 - (void)broadcastValue:(NSString*)valueKey
               ofObject:(NSObject*)object
               selector:(SEL)selector;
 
-// Stop broadcasting for |selector|. This doesn't remove or change any
-// observers for that selector. If |selector| is not being broadcast, this
+// Stop broadcasting for `selector`. This doesn't remove or change any
+// observers for that selector. If `selector` is not being broadcast, this
 // method does nothing.
 - (void)stopBroadcastingForSelector:(SEL)selector;
 
-// Adds |observer| as an observer for |selector|. If |selector| is already being
-// broadcast, |selector| will be called on |observer| with the current value of
+// Adds `observer` as an observer for `selector`. If `selector` is already being
+// broadcast, `selector` will be called on `observer` with the current value of
 // the broadcast property before this method returns.
-// It is an error if |selector| is not one of the methods in the
-// BroadcastObserver protocol, or if |observer| does not respond to |selector|.
+// It is an error if `selector` is not one of the methods in the
+// BroadcastObserver protocol, or if `observer` does not respond to `selector`.
 - (void)addObserver:(id<ChromeBroadcastObserver>)observer
         forSelector:(SEL)selector;
 
-// Removes |observer| from the observers for |selector|. If |observer| is also
+// Removes `observer` from the observers for `selector`. If `observer` is also
 // an observer for another selector, this method will not change that.
-// It is an error if |selector| is not one of the methods in the
+// It is an error if `selector` is not one of the methods in the
 // BroadcastObserver protocol.
 - (void)removeObserver:(id<ChromeBroadcastObserver>)observer
            forSelector:(SEL)selector;
diff --git a/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.mm b/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.mm
index 9701a39e..daf9a3f 100644
--- a/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.mm
+++ b/ios/chrome/browser/ui/broadcaster/chrome_broadcaster.mm
@@ -19,7 +19,7 @@
 namespace {
 
 // Constructs an NSInvocation that will be used for repeated execution of
-// |selector|. |selector| must return void and take exactly one argument; it is
+// `selector`. `selector` must return void and take exactly one argument; it is
 // an error otherwise.
 NSInvocation* InvocationForBroadcasterSelector(SEL selector) {
   struct objc_method_description methodDesc = protocol_getMethodDescription(
@@ -66,7 +66,7 @@
 @property(nonatomic, readonly, copy) NSString* key;
 // The name associated with this observation.
 @property(nonatomic, readonly, copy) NSString* name;
-// The current value of |key| on |object|.
+// The current value of `key` on `object`.
 @property(nonatomic, readonly) NSValue* currentValue;
 
 // Designated initializer.
@@ -76,9 +76,9 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
-// Add |observer| as a KVO of the object and key represented by the receiver.
+// Add `observer` as a KVO of the object and key represented by the receiver.
 - (void)addObserver:(NSObject*)observer;
-// Remove |observer| from the KVO represented by the receiver.
+// Remove `observer` from the KVO represented by the receiver.
 - (void)removeObserver:(NSObject*)observer;
 @end
 
@@ -188,13 +188,13 @@
               ofObject:(NSObject*)object
               selector:(SEL)selector {
   NSString* name = NSStringFromSelector(selector);
-  // Sanity check: |selector| must be one of the selectors that are mapped.
+  // Sanity check: `selector` must be one of the selectors that are mapped.
   DCHECK(self.observerInvocations[name]);
-  // Sanity check: |selector| must not already be broadcast.
+  // Sanity check: `selector` must not already be broadcast.
   DCHECK(!self.items[name]);
 
   // TODO(crbug.com/719911) -- Another sanity check is needed here -- verify
-  // that the value to be observed is of the type that |selector| expects.
+  // that the value to be observed is of the type that `selector` expects.
 
   self.items[name] =
       [[BroadcastItem alloc] initWithObject:object key:valueKey name:name];
@@ -215,9 +215,9 @@
 - (void)addObserver:(id<ChromeBroadcastObserver>)observer
         forSelector:(SEL)selector {
   NSString* name = NSStringFromSelector(selector);
-  // Sanity check: |selector| must be one of the keys that are mapped.
+  // Sanity check: `selector` must be one of the keys that are mapped.
   DCHECK(self.observerInvocations[name]);
-  // Sanity check: |observer| must implement the selector for |selector|.
+  // Sanity check: `observer` must implement the selector for `selector`.
   DCHECK([observer respondsToSelector:selector]);
 
   if (!self.observers[name])
@@ -236,7 +236,7 @@
 - (void)removeObserver:(id<ChromeBroadcastObserver>)observer
            forSelector:(SEL)selector {
   NSString* name = NSStringFromSelector(selector);
-  // Sanity check: |selector| must be one of the selectors that are mapped.
+  // Sanity check: `selector` must be one of the selectors that are mapped.
   DCHECK(self.observerInvocations[name]);
 
   [self.observers[name] removeObserver:observer];
@@ -252,10 +252,10 @@
                        context:(void*)context {
   // Bridge cast the context back to a selector name.
   NSString* name = (__bridge NSString*)context;
-  // Sanity check: |name| must be one of the selectors that are mapped.
+  // Sanity check: `name` must be one of the selectors that are mapped.
   DCHECK(self.observerInvocations[name]);
-  // Sanity check: |object| should be the object currently being observed for
-  // |name|.
+  // Sanity check: `object` should be the object currently being observed for
+  // `name`.
   DCHECK(self.items[name].object == object);
 
   BroadcastObservers* observers = self.observers[name];
@@ -290,14 +290,14 @@
 
 #pragma mark - internal
 
-// Returns the invocation for the selector named |name|, populated with
-// |value| as the argument.
-// This method mutates the invocations stored in |self.observerInvocations|, so
+// Returns the invocation for the selector named `name`, populated with
+// `value` as the argument.
+// This method mutates the invocations stored in `self.observerInvocations`, so
 // any code that gets an invocation from that dictionary to be invoked should
 // do so through this method.
 - (NSInvocation*)invocationForName:(NSString*)name value:(NSValue*)value {
   NSInvocation* invocation = self.observerInvocations[name];
-  // Attempt to cast |value| into an NSNumber; ObjCCast will instead return
+  // Attempt to cast `value` into an NSNumber; ObjCCast will instead return
   // nil if this isn't possible.
   NSNumber* valueAsNumber = base::mac::ObjCCast<NSNumber>(value);
   std::string type([invocation.methodSignature getArgumentTypeAtIndex:2]);
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_consumer.h b/ios/chrome/browser/ui/browser_container/browser_container_consumer.h
index 058b8920..23341da4 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_consumer.h
+++ b/ios/chrome/browser/ui/browser_container/browser_container_consumer.h
@@ -11,7 +11,7 @@
 
 // Whether the content view should be blocked.  When set to YES, the content
 // area is blocked.  Overlay UI shown in OverlayModality::kWebContentArea remain
-// visible when |contentBlocked| is YES.
+// visible when `contentBlocked` is YES.
 - (void)setContentBlocked:(BOOL)contentBlocked;
 
 @end
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_mediator.h b/ios/chrome/browser/ui/browser_container/browser_container_mediator.h
index 12e1b8e..4615895 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_mediator.h
+++ b/ios/chrome/browser/ui/browser_container/browser_container_mediator.h
@@ -14,10 +14,10 @@
 // Mediator that updates a BrowserContainerConsumer
 @interface BrowserContainerMediator : NSObject
 
-// Initializer for a mediator.  |webStateList| is the WebStateList for the
+// Initializer for a mediator.  `webStateList` is the WebStateList for the
 // Browser whose content is shown within the BrowserContainerConsumer.
-// |overlayPresenter| is the OverlayModality::kWebContentArea OverlayPresenter.
-// Both |webSateList| and |overlaPresenter| must be non-null.
+// `overlayPresenter` is the OverlayModality::kWebContentArea OverlayPresenter.
+// Both `webSateList` and `overlaPresenter` must be non-null.
 - (instancetype)initWithWebStateList:(WebStateList*)webStateList
       webContentAreaOverlayPresenter:(OverlayPresenter*)overlayPresenter
     NS_DESIGNATED_INITIALIZER;
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_mediator.mm b/ios/chrome/browser/ui/browser_container/browser_container_mediator.mm
index 5c75120..703b8067 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_mediator.mm
+++ b/ios/chrome/browser/ui/browser_container/browser_container_mediator.mm
@@ -24,7 +24,7 @@
 
 namespace {
 // Checks whether an HTTP authentication dialog is being shown by
-// |overlay_presenter| for a page whose host does not match |web_state_list|'s
+// `overlay_presenter` for a page whose host does not match `web_state_list`'s
 // active WebState's last committed URL.
 bool IsActiveOverlayRequestForNonCommittedHttpAuthentication(
     WebStateList* web_state_list) {
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_view_controller.h b/ios/chrome/browser/ui/browser_container/browser_container_view_controller.h
index 0b2419bf..0aee0fb1 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_view_controller.h
+++ b/ios/chrome/browser/ui/browser_container/browser_container_view_controller.h
@@ -26,14 +26,14 @@
 // The delegate to handle link to text button selection.
 @property(nonatomic, weak) id<LinkToTextDelegate> linkToTextDelegate;
 
-// Adds the given |contentView| as a subview and removes the previously added
-// |contentView| or |contentViewController|, if any. If |contentView| is nil
+// Adds the given `contentView` as a subview and removes the previously added
+// `contentView` or `contentViewController`, if any. If `contentView` is nil
 // then only old content view or view controller is removed.
 - (void)setContentView:(UIView*)contentView;
 
-// Adds the given |contentViewController| as a child view controller and removes
-// the previously added |contentViewController| if any.  Setting
-// |contentViewController| does not clear |contentView|.
+// Adds the given `contentViewController` as a child view controller and removes
+// the previously added `contentViewController` if any.  Setting
+// `contentViewController` does not clear `contentView`.
 - (void)setContentViewController:(UIViewController*)contentViewController;
 
 @end
diff --git a/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm b/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm
index 7ed04d0..6fcbd14 100644
--- a/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_container/browser_container_view_controller.mm
@@ -114,7 +114,7 @@
     return;
   if (_contentBlocked) {
     // If the content was previously blocked, remove the blocking view before
-    // resetting to |contentBlocked|.
+    // resetting to `contentBlocked`.
     [self.contentBlockingView removeFromSuperview];
     self.contentBlockingView = nil;
   }
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
index 99200ed6..f3fd1f6 100644
--- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -1318,7 +1318,7 @@
   }
 }
 
-// Install delegates for |webState|.
+// Install delegates for `webState`.
 - (void)installDelegatesForWebState:(web::WebState*)webState {
   if (AutofillTabHelper::FromWebState(webState)) {
     AutofillTabHelper::FromWebState(webState)->SetBaseViewController(
@@ -1345,7 +1345,7 @@
   }
 }
 
-// Uninstalls delegates for |webState|.
+// Uninstalls delegates for `webState`.
 - (void)uninstallDelegatesForWebState:(web::WebState*)webState {
   if (AutofillTabHelper::FromWebState(webState)) {
     AutofillTabHelper::FromWebState(webState)->SetBaseViewController(nil);
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller+private.h b/ios/chrome/browser/ui/browser_view/browser_view_controller+private.h
index ca00cd3..911a101 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller+private.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller+private.h
@@ -19,13 +19,13 @@
 // an inactive BVC should not be visible.
 @property(nonatomic, assign, getter=isActive) BOOL active;
 
-// Dismisses all presented views, excluding the omnibox if |dismissOmnibox| is
-// NO, then calls |completion|.
+// Dismisses all presented views, excluding the omnibox if `dismissOmnibox` is
+// NO, then calls `completion`.
 - (void)clearPresentedStateWithCompletion:(ProceduralBlock)completion
                            dismissOmnibox:(BOOL)dismissOmnibox;
 
-// Does an animation from |originPoint| when opening a background tab, then
-// calls |completion|.
+// Does an animation from `originPoint` when opening a background tab, then
+// calls `completion`.
 - (void)animateOpenBackgroundTabFromOriginPoint:(CGPoint)originPoint
                                      completion:(void (^)())completion;
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.h b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
index c52d954..72d5b30 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
@@ -53,13 +53,13 @@
                         WebStateContainerViewProvider>
 
 // Initializes a new BVC.
-// |browser| is the browser whose tabs this BVC will display.
-// |factory| is the dependency factory created for this BVC instance.
-// |browserContainerViewController| is the container object this BVC will exist
+// `browser` is the browser whose tabs this BVC will display.
+// `factory` is the dependency factory created for this BVC instance.
+// `browserContainerViewController` is the container object this BVC will exist
 // inside.
-// |dispatcher| is the dispatcher instance this BVC will use.
+// `dispatcher` is the dispatcher instance this BVC will use.
 // TODO(crbug.com/992582): Remove references to model objects -- including
-//   |browser| and |dispatcher| -- from this class.
+//   `browser` and `dispatcher` -- from this class.
 // TODO(crbug.com/1328039): Remove all use of the prerender service from BVC
 // TODO(crbug.com/1331229): Remove all use of the download manager coordinator
 // from BVC
@@ -128,12 +128,12 @@
 // Called when the user explicitly opens the tab switcher.
 - (void)userEnteredTabSwitcher;
 
-// Opens a new tab as if originating from |originPoint| and |focusOmnibox|.
+// Opens a new tab as if originating from `originPoint` and `focusOmnibox`.
 - (void)openNewTabFromOriginPoint:(CGPoint)originPoint
                      focusOmnibox:(BOOL)focusOmnibox
                     inheritOpener:(BOOL)inheritOpener;
 
-// Adds |tabAddedCompletion| to the completion block (if any) that will be run
+// Adds `tabAddedCompletion` to the completion block (if any) that will be run
 // the next time a tab is added to the Browser this object was initialized
 // with.
 - (void)appendTabAddedCompletion:(ProceduralBlock)tabAddedCompletion;
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index 89c0c57..a52a449 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -279,8 +279,8 @@
   // Identifier for each animation of an NTP opening.
   NSInteger _NTPAnimationIdentifier;
 
-  // Facade objects used by |_toolbarCoordinator|.
-  // Must outlive |_toolbarCoordinator|.
+  // Facade objects used by `_toolbarCoordinator`.
+  // Must outlive `_toolbarCoordinator`.
   std::unique_ptr<LocationBarModelDelegateIOS> _locationBarModelDelegate;
   std::unique_ptr<LocationBarModel> _locationBarModel;
 
@@ -320,10 +320,10 @@
   // authentication from the user before it can be accessed.
   BOOL _itemsRequireAuthentication;
 
-  // The last point within |contentArea| that's received a touch.
+  // The last point within `contentArea` that's received a touch.
   CGPoint _lastTapPoint;
 
-  // The time at which |_lastTapPoint| was most recently set.
+  // The time at which `_lastTapPoint` was most recently set.
   CFTimeInterval _lastTapTime;
 
   // The controller that shows the bookmarking UI after the user taps the star
@@ -408,17 +408,17 @@
 @property(nonatomic, strong) IncognitoReauthView* blockingView;
 // Whether the controller is currently dismissing a presented view controller.
 @property(nonatomic, assign, getter=isDismissingModal) BOOL dismissingModal;
-// Whether web usage is enabled for the WebStates in |self.browser|.
+// Whether web usage is enabled for the WebStates in `self.browser`.
 @property(nonatomic, assign, getter=isWebUsageEnabled) BOOL webUsageEnabled;
 // Whether a new tab animation is occurring.
 @property(nonatomic, assign, getter=isInNewTabAnimation) BOOL inNewTabAnimation;
 // Whether BVC prefers to hide the status bar. This value is used to determine
-// the response from the |prefersStatusBarHidden| method.
+// the response from the `prefersStatusBarHidden` method.
 @property(nonatomic, assign) BOOL hideStatusBar;
 // Whether the BVC is positioned at the bottom of the window, for example after
 // switching from thumb strip to tab grid.
 @property(nonatomic, assign) BOOL bottomPosition;
-// A block to be run when the |tabWasAdded:| method completes the animation
+// A block to be run when the `tabWasAdded:` method completes the animation
 // for the presentation of a new tab. Can be used to record performance metrics.
 @property(nonatomic, strong, nullable)
     ProceduralBlock foregroundTabWasAddedCompletionBlock;
@@ -542,7 +542,7 @@
   self = [super initWithNibName:nil bundle:base::mac::FrameworkBundle()];
   if (self) {
     DCHECK(factory);
-    // TODO(crbug.com/1272524): DCHECK that |browser| is non-null.
+    // TODO(crbug.com/1272524): DCHECK that `browser` is non-null.
 
     _commandDispatcher = dispatcher;
     _browserContainerViewController = browserContainerViewController;
@@ -589,7 +589,7 @@
 
 // TODO(crbug.com/1323764): This uses PopupMenuCommands via inclusion in
 // BrowserCommands. This is also not a public property. Instead of using
-// |self.dispatcher| internally, this should use the currect pattern for handler
+// `self.dispatcher` internally, this should use the currect pattern for handler
 // injection into a view controller with a dedicated id<PopupMenuCommands>
 // public property set externally.
 // TODO(crbug.com/1323778): This uses SnackbarCommands via inclusion in
@@ -862,7 +862,7 @@
 }
 
 // TODO(crbug.com/1265565): Remove once kSingleNtp feature is launched and
-// directly reference |self.ntpCoordinator|.
+// directly reference `self.ntpCoordinator`.
 - (NewTabPageCoordinator*)ntpCoordinatorForWebState:(web::WebState*)webState {
   if (IsSingleNtpEnabled()) {
     return _ntpCoordinator;
@@ -933,7 +933,7 @@
 
   // In most cases, we want to take a snapshot of the current tab before opening
   // a new tab. However, if the current tab is not fully visible (did not finish
-  // |-viewDidAppear:|, then we must not take an empty snapshot, replacing an
+  // `-viewDidAppear:`, then we must not take an empty snapshot, replacing an
   // existing snapshot for the tab. This can happen when a new regular tab is
   // opened from an incognito tab. A different BVC is displayed, which may not
   // have enough time to finish appearing before a snapshot is requested.
@@ -1091,8 +1091,8 @@
     // Note that currently, some controllers like the bookmark ones were already
     // dismissed (in this example in -dismissBookmarkModalControllerAnimated:),
     // but are still reported as the presentedViewController.  Calling
-    // |dismissViewControllerAnimated:completion:| again would dismiss the BVC
-    // itself, so instead check the value of |self.dismissingModal| and only
+    // `dismissViewControllerAnimated:completion:` again would dismiss the BVC
+    // itself, so instead check the value of `self.dismissingModal` and only
     // call dismiss if one of the above calls has not already triggered a
     // dismissal.
     //
@@ -1247,8 +1247,8 @@
 #pragma mark - UIResponder helpers
 
 // Whether the BVC should declare keyboard commands.
-// Since |-keyCommands| can be called by UIKit at any time, no assumptions
-// about the state of |self| can be made; accordingly, if there's anything
+// Since `-keyCommands` can be called by UIKit at any time, no assumptions
+// about the state of `self` can be made; accordingly, if there's anything
 // not initialized (or being torn down), this method should return NO.
 - (BOOL)shouldRegisterKeyboardCommands {
   if (_isShutdown)
@@ -1348,7 +1348,7 @@
 
 - (void)viewDidLayoutSubviews {
   [super viewDidLayoutSubviews];
-  // Update the toolbar height to account for |topLayoutGuide| changes.
+  // Update the toolbar height to account for `topLayoutGuide` changes.
   self.primaryToolbarHeightConstraint.constant =
       [self primaryToolbarHeightWithInset];
 
@@ -1365,9 +1365,9 @@
   [self updateBroadcastState];
   [self updateToolbarState];
 
-  // |viewDidAppear| can be called after |browserState| is destroyed. Since
-  // |presentBubblesIfEligible| requires that |self.browserState| is not NULL,
-  // check for |self.browserState| before calling the presenting the bubbles.
+  // `viewDidAppear` can be called after `browserState` is destroyed. Since
+  // `presentBubblesIfEligible` requires that `self.browserState` is not NULL,
+  // check for `self.browserState` before calling the presenting the bubbles.
   // TODO(crbug.com/1329091): determine if this check is still needed?
   if (self.browserState) {
     [self.helpHandler showHelpBubbleIfEligible];
@@ -1445,7 +1445,7 @@
 - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
   [super traitCollectionDidChange:previousTraitCollection];
 
-  // After |-shutdown| is called, |self.browserState| is invalid and will cause
+  // After `-shutdown` is called, `self.browserState` is invalid and will cause
   // a crash.
   if (!self.browserState || _isShutdown)
     return;
@@ -1519,7 +1519,7 @@
            (id<UIViewControllerTransitionCoordinator>)coordinator {
   [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
 
-  // After |-shutdown| is called, |self.browser| is invalid and will cause
+  // After `-shutdown` is called, `self.browser` is invalid and will cause
   // a crash.
   if (_isShutdown)
     return;
@@ -1582,12 +1582,12 @@
     return;
   }
 
-  // Some calling code invokes |dismissViewControllerAnimated:completion:|
+  // Some calling code invokes `dismissViewControllerAnimated:completion:`
   // multiple times. Because the BVC is presented, subsequent calls end up
   // dismissing the BVC itself. This is never what should happen, so check for
   // this case and return early.  It is not enough to check
-  // |self.dismissingModal| because some dismissals do not go through
-  // -[BrowserViewController dismissViewControllerAnimated:completion:|.
+  // `self.dismissingModal` because some dismissals do not go through
+  // -[BrowserViewController dismissViewControllerAnimated:completion:`.
   // TODO(crbug.com/782338): Fix callers and remove this early return.
   if (self.dismissingModal || self.presentedViewController.isBeingDismissed) {
     return;
@@ -1616,7 +1616,7 @@
   // when the controller for the FRE has been presented. This fix should be
   // removed when the FRE startup code is rewritten.
   const bool firstRunLaunch = ShouldPresentFirstRunExperience();
-  // These if statements check that |presentViewController| is being called for
+  // These if statements check that `presentViewController` is being called for
   // the FRE case.
   if (firstRunLaunch &&
       [viewControllerToPresent isKindOfClass:[UINavigationController class]]) {
@@ -1636,8 +1636,8 @@
                                              options:nil];
       UIViewController* launchScreenController =
           base::mac::ObjCCastStrict<UIViewController>([topObjects lastObject]);
-      // |launchScreenView| is loaded as an autoreleased object, and is retained
-      // by the |completion| block below.
+      // `launchScreenView` is loaded as an autoreleased object, and is retained
+      // by the `completion` block below.
       UIView* launchScreenView = launchScreenController.view;
       launchScreenView.userInteractionEnabled = NO;
       // TODO(crbug.com/1011155): Displaying the launch screen is a hack to hide
@@ -1650,7 +1650,7 @@
       [launchScreenView layoutIfNeeded];
 
       // Replace the completion handler sent to the superclass with one which
-      // removes |launchScreenView| and resets the status bar. If |completion|
+      // removes `launchScreenView` and resets the status bar. If `completion`
       // exists, it is called from within the new completion handler.
       __weak BrowserViewController* weakSelf = self;
       finalCompletionHandler = ^{
@@ -1723,7 +1723,7 @@
 // In most cases, they will not, to improve startup performance.
 // In order to handle this, initialization of various aspects of BVC have been
 // broken out into the following functions, which have expectations (enforced
-// with DCHECKs) regarding |self.browserState|, |self.browser|, and [self
+// with DCHECKs) regarding `self.browserState`, `self.browser`, and [self
 // isViewLoaded].
 
 // Updates non-view-related functionality with the given browser and tab
@@ -1820,7 +1820,7 @@
   _locationBarModel = std::make_unique<LocationBarModelImpl>(
       _locationBarModelDelegate.get(), kMaxURLDisplayChars);
 
-  // TODO(crbug.com/1329102): Remove |self.helper|.
+  // TODO(crbug.com/1329102): Remove `self.helper`.
   self.helper = [_dependencyFactory newBrowserViewControllerHelper];
 
   // TODO(crbug.com/1329094): Move this coordinator to BrowserCoordinator
@@ -1901,7 +1901,7 @@
 // Called by NSNotificationCenter when the view's window becomes key to account
 // for topLayoutGuide length updates.
 - (void)updateToolbarHeightForKeyWindow {
-  // Update the toolbar height to account for |topLayoutGuide| changes.
+  // Update the toolbar height to account for `topLayoutGuide` changes.
   self.primaryToolbarHeightConstraint.constant =
       [self primaryToolbarHeightWithInset];
   // Stop listening for the key window notification.
@@ -1930,7 +1930,7 @@
   // the unsafe area.
   CGFloat unsafeHeight = self.rootSafeAreaInsets.top;
 
-  // The topmost header is laid out |headerOffset| from the top of |view|, so
+  // The topmost header is laid out `headerOffset` from the top of `view`, so
   // subtract that from the unsafe height.
   unsafeHeight -= self.headerOffset;
   return intrinsicHeight + unsafeHeight;
@@ -1977,7 +1977,7 @@
   }
 
   // Only add leading and trailing constraints once as they are never updated.
-  // This uses the existence of |primaryToolbarOffsetConstraint| as a proxy for
+  // This uses the existence of `primaryToolbarOffsetConstraint` as a proxy for
   // whether we've already added the leading and trailing constraints.
   if (!self.primaryToolbarOffsetConstraint) {
     [NSLayoutConstraint activateConstraints:@[
@@ -2061,8 +2061,8 @@
 }
 
 // Updates view-related functionality with the given browser and browser
-// state. The view must have been loaded.  Uses |self.browserState| and
-// |self.browser|.
+// state. The view must have been loaded.  Uses `self.browserState` and
+// `self.browser`.
 - (void)addUIFunctionalityForBrowserAndBrowserState {
   DCHECK(self.browserState);
   DCHECK(_locationBarModel);
@@ -2109,7 +2109,7 @@
 }
 
 // Sets the correct frame and hierarchy for subviews and helper views.  Only
-// insert views on |initialLayout|.
+// insert views on `initialLayout`.
 - (void)setUpViewLayout:(BOOL)initialLayout {
   DCHECK([self isViewLoaded]);
 
@@ -2193,8 +2193,8 @@
     NamedGuide* contentAreaGuide = [NamedGuide guideWithName:kContentAreaGuide
                                                         view:self.view];
 
-    // TODO(crbug.com/1136765): Sometimes, |contentAreaGuide| and
-    // |primaryToolbarView| aren't in the same view hierarchy; this seems to be
+    // TODO(crbug.com/1136765): Sometimes, `contentAreaGuide` and
+    // `primaryToolbarView` aren't in the same view hierarchy; this seems to be
     // impossible,  but it does still happen. This will cause an exception in
     // when activiating these constraints. To gather more information about this
     // state, explciitly check the view hierarchy roots. Local variables are
@@ -2254,7 +2254,7 @@
 }
 
 // TODO(crbug.com/1329088): Have a mediator inject the view to be displayed, not
-// a webstate. Makes |webState| the currently visible WebState, displaying its
+// a webstate. Makes `webState` the currently visible WebState, displaying its
 // view.
 - (void)displayWebState:(web::WebState*)webState {
   DCHECK(webState);
@@ -2268,7 +2268,7 @@
 
   if (!self.inNewTabAnimation) {
     // TODO(crbug.com/1329087): -updateToolbar will move out of the BVC; make
-    // sure this comment remains accurate. Hide findbar.  |updateToolbar| will
+    // sure this comment remains accurate. Hide findbar.  `updateToolbar` will
     // restore the findbar later.
     [self.dispatcher hideFindUI];
     [self.textZoomHandler hideTextZoomUI];
@@ -2358,9 +2358,9 @@
 - (void)bringOverlayContainerToFront:
     (UIViewController*)containerViewController {
   [self.view bringSubviewToFront:containerViewController.view];
-  // If |containerViewController| is presenting a view over its current context,
+  // If `containerViewController` is presenting a view over its current context,
   // its presentation container view is added as a sibling to
-  // |containerViewController|'s view. This presented view should be brought in
+  // `containerViewController`'s view. This presented view should be brought in
   // front of the container view.
   UIView* presentedContainerView =
       containerViewController.presentedViewController.presentationController
@@ -2501,7 +2501,7 @@
          WebStateList::kInvalidIndex);
   TabUsageRecorderBrowserAgent* tabUsageRecoder =
       TabUsageRecorderBrowserAgent::FromBrowser(_browser);
-  // TODO(crbug.com/904588): Move |RecordPageLoadStart| to TabUsageRecorder.
+  // TODO(crbug.com/904588): Move `RecordPageLoadStart` to TabUsageRecorder.
   if (webState->IsEvicted() && tabUsageRecoder) {
     tabUsageRecoder->RecordPageLoadStart(webState);
   }
@@ -2514,7 +2514,7 @@
 
 #pragma mark - Private Methods: Tap handling
 
-// Record the last tap point based on the |originPoint| (if any) passed in
+// Record the last tap point based on the `originPoint` (if any) passed in
 // command.
 - (void)setLastTapPointFromCommand:(CGPoint)originPoint {
   if (CGPointEqualToPoint(originPoint, CGPointZero)) {
@@ -2526,7 +2526,7 @@
   _lastTapTime = CACurrentMediaTime();
 }
 
-// Returns the last stored |_lastTapPoint| if it's been set within the past
+// Returns the last stored `_lastTapPoint` if it's been set within the past
 // second.
 - (CGPoint)lastTapPoint {
   if (CACurrentMediaTime() - _lastTapTime < 1) {
@@ -2535,7 +2535,7 @@
   return CGPointZero;
 }
 
-// Store the tap CGPoint in |_lastTapPoint| and the current timestamp.
+// Store the tap CGPoint in `_lastTapPoint` and the current timestamp.
 - (void)saveContentAreaTapLocation:(UIGestureRecognizer*)gestureRecognizer {
   if (_isShutdown) {
     return;
@@ -2559,7 +2559,7 @@
 #pragma mark - Private Methods: Tab creation and selection
 
 // DEPRECATED -- Do not add further logic to this method.
-// Add all delegates to the provided |webState|.
+// Add all delegates to the provided `webState`.
 // Unregistration happens when the WebState is removed from the WebStateList.
 // TODO(crbug.com/1290819): Remove this method.
 - (void)installDelegatesForWebState:(web::WebState*)webState {
@@ -2580,7 +2580,7 @@
 }
 
 // DEPRECATED -- Do not add further logic to this method.
-// Remove delegates from the provided |webState|.
+// Remove delegates from the provided `webState`.
 // TODO(crbug.com/1290819): Remove this method.
 - (void)uninstallDelegatesForWebState:(web::WebState*)webState {
   // If the WebState is unrealized, then the delegate had not been installed
@@ -2602,9 +2602,9 @@
   NewTabPageTabHelper::FromWebState(webState)->SetDelegate(nil);
 }
 
-// Called when a |webState| is selected in the WebStateList. Make any required
-// view changes. The notification will not be sent when the |webState| is
-// already the selected WebState. |notifyToolbar| indicates whether the toolbar
+// Called when a `webState` is selected in the WebStateList. Make any required
+// view changes. The notification will not be sent when the `webState` is
+// already the selected WebState. `notifyToolbar` indicates whether the toolbar
 // is notified that the webState has changed.
 - (void)webStateSelected:(web::WebState*)webState
            notifyToolbar:(BOOL)notifyToolbar {
@@ -2638,7 +2638,7 @@
 
 #pragma mark - Private Methods: Voice Search
 
-// Lazily instantiates |_voiceSearchController|.
+// Lazily instantiates `_voiceSearchController`.
 - (void)ensureVoiceSearchControllerCreated {
   if (_voiceSearchController)
     return;
@@ -2702,7 +2702,7 @@
 #pragma mark - Private SingleNTP feature helper methods
 
 // Checks if there are any WebStates showing an NTP at this time. If not, then
-// deconstructs |ntpCoordinator|.
+// deconstructs `ntpCoordinator`.
 - (void)stopNTPIfNeeded {
   DCHECK(IsSingleNtpEnabled());
   BOOL activeNTP = NO;
@@ -2989,7 +2989,7 @@
     [self installFakeStatusBar];
     [self setupStatusBarLayout];
 
-    // See the comments in |-willAnimateViewReveal:| for the explanation of why
+    // See the comments in `-willAnimateViewReveal:` for the explanation of why
     // this is necessary.
     if (fullscreen::features::ShouldUseSmoothScrolling()) {
       self.viewTranslatedForSmoothScrolling = NO;
@@ -3134,14 +3134,14 @@
   }
 
   // The overlay container view controller is presenting something if it has
-  // a |presentedViewController| AND that view controller's
-  // |presentingViewController| is the overlay container. Otherwise, some other
+  // a `presentedViewController` AND that view controller's
+  // `presentingViewController` is the overlay container. Otherwise, some other
   // view controller higher up in the hierarchy is doing the presenting. E.g.
   // for the overflow menu, the BVC (and eventually the tab grid view
   // controller) are presenting the overflow menu, but because those view
-  // controllers are also above tthe |overlayContainerViewController| in the
+  // controllers are also above tthe `overlayContainerViewController` in the
   // view hierarchy, the overflow menu view controller is also the
-  // |overlayContainerViewController|'s presentedViewController.
+  // `overlayContainerViewController`'s presentedViewController.
   UIViewController* overlayContainerViewController =
       self.browserContainerViewController
           .webContentsOverlayContainerViewController;
@@ -3276,7 +3276,7 @@
 - (void)newTabWillLoadURL:(GURL)URL isUserInitiated:(BOOL)isUserInitiated {
   if (isUserInitiated) {
     // Send either the "New Tab Opened" or "New Incognito Tab" opened to the
-    // feature_engagement::Tracker based on |inIncognito|.
+    // feature_engagement::Tracker based on `inIncognito`.
     feature_engagement::NotifyNewTabEvent(self.browserState,
                                           self.isOffTheRecord);
   }
@@ -3561,7 +3561,7 @@
   return std::max(0.0, fullyExpandedHeight - fullyCollapsedHeight);
 }
 
-// Translates the header views up and down according to |progress|, where a
+// Translates the header views up and down according to `progress`, where a
 // progress of 1.0 fully shows the headers and a progress of 0.0 fully hides
 // them.
 - (void)updateHeadersForFullscreenProgress:(CGFloat)progress {
@@ -3570,7 +3570,7 @@
   [self setFramesForHeaders:[self headerViews] atOffset:offset];
 }
 
-// Translates the footer view up and down according to |progress|, where a
+// Translates the footer view up and down according to `progress`, where a
 // progress of 1.0 fully shows the footer and a progress of 0.0 fully hides it.
 - (void)updateFootersForFullscreenProgress:(CGFloat)progress {
 
@@ -3598,7 +3598,7 @@
   if (!self.currentWebState)
     return;
 
-  // Calculate the heights of the toolbars for |progress|.  |-toolbarHeight|
+  // Calculate the heights of the toolbars for `progress`.  `-toolbarHeight`
   // returns the height of the toolbar extending below this view controller's
   // safe area, so the unsafe top height must be added.
   CGFloat top = AlignValueToPixel(
@@ -3609,7 +3609,7 @@
   [self updateContentPaddingForTopToolbarHeight:top bottomToolbarHeight:bottom];
 }
 
-// Updates the frame of the web view so that it's |offset| from the bottom of
+// Updates the frame of the web view so that it's `offset` from the bottom of
 // the container view.
 - (void)updateWebViewFrameForBottomOffset:(CGFloat)offset {
   if (!self.currentWebState)
@@ -3643,7 +3643,7 @@
 
 // Updates the padding of the web view proxy. This either resets the frame of
 // the WKWebView or the contentInsets of the WKWebView's UIScrollView, depending
-// on the the proxy's |shouldUseViewContentInset| property.
+// on the the proxy's `shouldUseViewContentInset` property.
 - (void)updateContentPaddingForTopToolbarHeight:(CGFloat)topToolbarHeight
                             bottomToolbarHeight:(CGFloat)bottomToolbarHeight {
   if (!self.currentWebState)
@@ -3661,7 +3661,7 @@
   if (!headers.count)
     return 0.0;
 
-  // Prerender tab does not have a toolbar, return |headerHeight| as promised by
+  // Prerender tab does not have a toolbar, return `headerHeight` as promised by
   // API documentation.
   // TODO(crbug.com/1328039): Remove all use of the prerender service from BVC
   if (_prerenderService && _prerenderService->IsLoadingPrerender())
@@ -3671,7 +3671,7 @@
   return -(topHeader.frame.origin.y - self.headerOffset);
 }
 
-// Returns the insets into |view| that result in the visible viewport.
+// Returns the insets into `view` that result in the visible viewport.
 - (UIEdgeInsets)viewportInsetsForView:(UIView*)view {
   DCHECK(view);
   UIEdgeInsets viewportInsets =
@@ -3735,7 +3735,7 @@
     return;
 
   // If the active index isn't the last index, activate the next index.
-  // (the last index is always |count() - 1|).
+  // (the last index is always `count() - 1`).
   // Otherwise activate the first index.
   if (activeIndex < (webStateList->count() - 1)) {
     webStateList->ActivateWebStateAt(activeIndex + 1);
@@ -3756,7 +3756,7 @@
     return;
 
   // If the active index isn't the first index, activate the prior index.
-  // Otherwise index the last index (|count() - 1|).
+  // Otherwise index the last index (`count() - 1`).
   if (activeIndex > 0) {
     webStateList->ActivateWebStateAt(activeIndex - 1);
   } else {
@@ -3833,7 +3833,7 @@
         // This can happen if one quickly resigns the omnibox and then taps
         // on the omnibox again during this animation. If the animation is
         // interrupted and the toolbar controller is first responder, it's safe
-        // to assume |self.typingShield| shouldn't be hidden here.
+        // to assume `self.typingShield` shouldn't be hidden here.
         if (!finished &&
             [self.primaryToolbarCoordinator isOmniboxFirstResponder])
           return;
@@ -3901,7 +3901,7 @@
   [_voiceSearchController prepareToAppear];
 }
 
-// TODO(crbug.com/1272511): Move |showTranslate| out of the BVC.
+// TODO(crbug.com/1272511): Move `showTranslate` out of the BVC.
 - (void)showTranslate {
   feature_engagement::Tracker* engagement_tracker =
       feature_engagement::TrackerFactory::GetForBrowserState(self.browserState);
@@ -3919,7 +3919,7 @@
   }
 }
 
-// TODO(crbug.com/1329106): Move |showHelpPage| out of the BVC.
+// TODO(crbug.com/1329106): Move `showHelpPage` out of the BVC.
 - (void)showHelpPage {
   GURL helpUrl(l10n_util::GetStringUTF16(IDS_IOS_TOOLS_MENU_HELP_URL));
   UrlLoadParams params = UrlLoadParams::InNewTab(helpUrl);
@@ -3929,7 +3929,7 @@
   UrlLoadingBrowserAgent::FromBrowser(self.browser)->Load(params);
 }
 
-// TODO(crbug.com/1329107): Move |showBookmarksManager| out of the BVC.
+// TODO(crbug.com/1329107): Move `showBookmarksManager` out of the BVC.
 - (void)showBookmarksManager {
   [self initializeBookmarkInteractionController];
   [_bookmarkInteractionController presentBookmarks];
@@ -4001,7 +4001,7 @@
   // Dismiss any presenting modal. Ex. Follow management page.
   [self
       clearPresentedStateWithCompletion:^{
-        // Configure next NTP to be scrolled into |feedType|.
+        // Configure next NTP to be scrolled into `feedType`.
         NewTabPageTabHelper* NTPHelper =
             NewTabPageTabHelper::FromWebState(self.currentWebState);
         if (NTPHelper) {
@@ -4096,7 +4096,7 @@
   }
 }
 
-// Observer method, WebState replaced in |webStateList|.
+// Observer method, WebState replaced in `webStateList`.
 - (void)webStateList:(WebStateList*)webStateList
     didReplaceWebState:(web::WebState*)oldWebState
           withWebState:(web::WebState*)newWebState
@@ -4104,12 +4104,12 @@
   [self uninstallDelegatesForWebState:oldWebState];
   [self installDelegatesForWebState:newWebState];
 
-  // Add |newTab|'s view to the hierarchy if it's the current Tab.
+  // Add `newTab`'s view to the hierarchy if it's the current Tab.
   if (self.active && self.currentWebState == newWebState)
     [self displayWebState:newWebState];
 }
 
-// Observer method, |webState| inserted in |webStateList|.
+// Observer method, `webState` inserted in `webStateList`.
 - (void)webStateList:(WebStateList*)webStateList
     didInsertWebState:(web::WebState*)webState
               atIndex:(int)index
@@ -4206,8 +4206,8 @@
   if (tabURL == kChromeUINewTabURL && !_isOffTheRecord &&
       ![self canShowTabStrip]) {
     if (IsSingleNtpEnabled()) {
-      // Update NTPCoordinator's WebState here since |self.currentWebState| has
-      // not been update to |webState| yet.
+      // Update NTPCoordinator's WebState here since `self.currentWebState` has
+      // not been update to `webState` yet.
       self.ntpCoordinator.webState = webState;
     }
     // Add a snapshot of the primary toolbar to the background as the
@@ -4242,7 +4242,7 @@
     newPage.userInteractionEnabled = YES;
     if (currentAnimationIdentifier != self->_NTPAnimationIdentifier) {
       // Prevent the completion block from being executed if a new animation has
-      // started in between. |self.foregroundTabWasAddedCompletionBlock| isn't
+      // started in between. `self.foregroundTabWasAddedCompletionBlock` isn't
       // called because it is overridden when a new animation is started.
       // Calling it here would call the block from the lastest animation that
       // haved started.
@@ -4252,7 +4252,7 @@
     self.inNewTabAnimation = NO;
     // Use the model's currentWebState here because it is possible that it can
     // be reset to a new value before the new Tab animation finished (e.g.
-    // if another Tab shows a dialog via |dialogPresenter|). However, that
+    // if another Tab shows a dialog via `dialogPresenter`). However, that
     // webState's view hasn't been displayed yet because it was in a new tab
     // animation.
     web::WebState* currentWebState = self.currentWebState;
@@ -4358,11 +4358,11 @@
   return YES;
 }
 
-// Tap gestures should only be recognized within |contentArea|.
+// Tap gestures should only be recognized within `contentArea`.
 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gesture {
   CGPoint location = [gesture locationInView:self.view];
 
-  // Only allow touches on descendant views of |contentArea|.
+  // Only allow touches on descendant views of `contentArea`.
   UIView* hitView = [self.view hitTest:location withEvent:nil];
   return [hitView isDescendantOfView:self.contentArea];
 }
@@ -4425,7 +4425,7 @@
 
 - (CGFloat)headerHeightForSideSwipe {
   // If the toolbar is hidden, only inset the side swipe navigation view by
-  // |safeAreaInsets.top|.  Otherwise insetting by |self.headerHeight| would
+  // `safeAreaInsets.top`.  Otherwise insetting by `self.headerHeight` would
   // show a grey strip where the toolbar would normally be.
   if (self.primaryToolbarCoordinator.viewController.view.hidden)
     return self.rootSafeAreaInsets.top;
@@ -4606,7 +4606,7 @@
   } else {
     if (NTPHelper->IsActive()) {
       DCHECK(![self ntpCoordinatorForWebState:webState]);
-      // Checks for leaks in |_ntpCoordinatorsForWebStates|.
+      // Checks for leaks in `_ntpCoordinatorsForWebStates`.
       DCHECK_LE(static_cast<int>(_ntpCoordinatorsForWebStates.size()),
                 self.browser->GetWebStateList()->count() - 1);
       // TODO(crbug.com/1300911): Have BrowserCoordinator manage the NTP.
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h b/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h
index 7a3f48d0..13cba239 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_dependency_factory.h
@@ -16,7 +16,7 @@
 // Creates helper objects needed by BrowserViewController.
 @interface BrowserViewControllerDependencyFactory : NSObject
 
-// Creates a new factory backed by |browser|. This must be the same browser
+// Creates a new factory backed by `browser`. This must be the same browser
 // provided to BrowserViewController (and like BVC, this is a weak reference).
 - (instancetype)initWithBrowser:(Browser*)browser;
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_helper.h b/ios/chrome/browser/ui/browser_view/browser_view_controller_helper.h
index ea5b953a..b28f085d 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller_helper.h
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_helper.h
@@ -15,15 +15,15 @@
 // functions in the correct domains. Helper for the BrowserViewController.
 @interface BrowserViewControllerHelper : NSObject
 
-// Returns true if the current tab of |webState| is currently loading.
+// Returns true if the current tab of `webState` is currently loading.
 - (BOOL)isToolbarLoading:(web::WebState*)webState;
 
 // TODO(crbug.com/1329102): Remove this method; it's unused.
-// Returns true if |webState|'s current tab's URL is bookmarked, either by the
+// Returns true if `webState`'s current tab's URL is bookmarked, either by the
 // user or by a managed bookmarks.
 - (BOOL)isWebStateBookmarked:(web::WebState*)webState;
 
-// Returns true if |webState|'s current tab's URL is bookmarked by the user;
+// Returns true if `webState`'s current tab's URL is bookmarked by the user;
 // returns false if the URL is bookmarked only in managed bookmarks.
 - (BOOL)isWebStateBookmarkedByUser:(web::WebState*)webState;
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_helper_unittest.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller_helper_unittest.mm
index 5a14590..cfe5b452 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller_helper_unittest.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_helper_unittest.mm
@@ -83,7 +83,7 @@
 }
 
 TEST_F(BrowserViewControllerHelperTest, TestisWebStateBookmarked) {
-  // Set the curent tab to |kWebUrl| and create a bookmark for |kWebUrl|, then
+  // Set the curent tab to `kWebUrl` and create a bookmark for `kWebUrl`, then
   // verify that the location bar model indicates that the URL is bookmarked.
   web_state_->SetCurrentURL(GURL(kWebUrl));
   bookmarks::BookmarkModel* bookmark_model =
diff --git a/ios/chrome/browser/ui/browser_view/key_commands_provider.mm b/ios/chrome/browser/ui/browser_view/key_commands_provider.mm
index 45a555d..d587f54 100644
--- a/ios/chrome/browser/ui/browser_view/key_commands_provider.mm
+++ b/ios/chrome/browser/ui/browser_view/key_commands_provider.mm
@@ -36,7 +36,7 @@
       weakDispatcher = dispatcher;
   __weak id<OmniboxCommands> weakOmniboxHandler = omniboxHandler;
 
-  // Block to have the tab model open the tab at |index|, if there is one.
+  // Block to have the tab model open the tab at `index`, if there is one.
   void (^focusTab)(NSUInteger) = ^(NSUInteger index) {
     [weakConsumer focusTabAtIndex:index];
   };
diff --git a/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.h b/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.h
index fb9cd01c..50dde838 100644
--- a/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.h
+++ b/ios/chrome/browser/ui/browser_view/tab_lifecycle_mediator.h
@@ -33,7 +33,7 @@
 @interface TabLifecycleMediator : NSObject
 
 // Creates an instance of the mediator. Delegates will be installed into all
-// existing web states in |webStateList|. While the mediator is alive,
+// existing web states in `webStateList`. While the mediator is alive,
 // delegates will be added and removed from web states when they are inserted
 // into or removed from the web state list.
 - (instancetype)initWithWebStateList:(WebStateList*)webStateList
@@ -41,7 +41,7 @@
                         dependencies:(TabLifecycleDependencies)dependencies;
 
 // Disconnects all delegates set by the mediator on any web states in its
-// web state list. After |disconnect| is called, the mediator will not add
+// web state list. After `disconnect` is called, the mediator will not add
 // delegates to further webstates.
 - (void)disconnect;
 
diff --git a/ios/chrome/browser/ui/bubble/bubble_presenter.h b/ios/chrome/browser/ui/bubble/bubble_presenter.h
index 043d99e8..5217d8c 100644
--- a/ios/chrome/browser/ui/bubble/bubble_presenter.h
+++ b/ios/chrome/browser/ui/bubble/bubble_presenter.h
@@ -18,7 +18,7 @@
 @interface BubblePresenter : NSObject <HelpCommands>
 
 // Initializes a BubblePresenter whose bubbles are presented on the
-// |rootViewController|.
+// `rootViewController`.
 - (instancetype)initWithBrowserState:(ChromeBrowserState*)browserState
     NS_DESIGNATED_INITIALIZER;
 
diff --git a/ios/chrome/browser/ui/bubble/bubble_presenter.mm b/ios/chrome/browser/ui/bubble/bubble_presenter.mm
index e98c8d4..557c131 100644
--- a/ios/chrome/browser/ui/bubble/bubble_presenter.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_presenter.mm
@@ -41,19 +41,19 @@
 @interface BubblePresenter ()
 
 // Used to display the bottom toolbar tip in-product help promotion bubble.
-// |nil| if the tip bubble has not yet been presented. Once the bubble is
-// dismissed, it remains allocated so that |userEngaged| remains accessible.
+// `nil` if the tip bubble has not yet been presented. Once the bubble is
+// dismissed, it remains allocated so that `userEngaged` remains accessible.
 @property(nonatomic, strong)
     BubbleViewControllerPresenter* bottomToolbarTipBubblePresenter;
 // Used to display the long press on toolbar buttons tip in-product help
-// promotion bubble. |nil| if the tip bubble has not yet been presented. Once
-// the bubble is dismissed, it remains allocated so that |userEngaged| remains
+// promotion bubble. `nil` if the tip bubble has not yet been presented. Once
+// the bubble is dismissed, it remains allocated so that `userEngaged` remains
 // accessible.
 @property(nonatomic, strong)
     BubbleViewControllerPresenter* longPressToolbarTipBubblePresenter;
-// Used to display the new tab tip in-product help promotion bubble. |nil| if
+// Used to display the new tab tip in-product help promotion bubble. `nil` if
 // the new tab tip bubble has not yet been presented. Once the bubble is
-// dismissed, it remains allocated so that |userEngaged| remains accessible.
+// dismissed, it remains allocated so that `userEngaged` remains accessible.
 @property(nonatomic, strong)
     BubbleViewControllerPresenter* tabTipBubblePresenter;
 @property(nonatomic, strong, readwrite)
@@ -90,7 +90,7 @@
 - (void)showHelpBubbleIfEligible {
   DCHECK(self.browserState);
   // Waits to present the bubbles until the feature engagement tracker database
-  // is fully initialized. This method requires that |self.browserState| is not
+  // is fully initialized. This method requires that `self.browserState` is not
   // NULL.
   __weak BubblePresenter* weakSelf = self;
   void (^onInitializedBlock)(bool) = ^(bool successfullyLoaded) {
@@ -115,7 +115,7 @@
 - (void)showLongPressHelpBubbleIfEligible {
   DCHECK(self.browserState);
   // Waits to present the bubble until the feature engagement tracker database
-  // is fully initialized. This method requires that |self.browserState| is not
+  // is fully initialized. This method requires that `self.browserState` is not
   // NULL.
   __weak BubblePresenter* weakSelf = self;
   void (^onInitializedBlock)(bool) = ^(bool successfullyLoaded) {
@@ -177,7 +177,7 @@
 
   // If the feature engagement tracker does not consider it valid to display
   // the tip, then end early to prevent the potential reassignment of the
-  // existing |discoverFeedHeaderMenuTipBubblePresenter| to nil.
+  // existing `discoverFeedHeaderMenuTipBubblePresenter` to nil.
   BubbleViewControllerPresenter* presenter = [self
       presentBubbleForFeature:feature_engagement::kIPHDiscoverFeedHeaderFeature
                     direction:arrowDirection
@@ -204,7 +204,7 @@
 
   // If the feature engagement tracker does not consider it valid to display
   // the tip, then end early to prevent the potential reassignment of the
-  // existing |readingListTipBubblePresenter| to nil.
+  // existing `readingListTipBubblePresenter` to nil.
   BubbleViewControllerPresenter* presenter = [self
       presentBubbleForFeature:feature_engagement::kIPHReadingListMessagesFeature
                     direction:arrowDirection
@@ -232,7 +232,7 @@
 
   // If the feature engagement tracker does not consider it valid to display
   // the tip, then end early to prevent the potential reassignment of the
-  // existing |followWhileBrowsingBubbleTipPresenter| to nil.
+  // existing `followWhileBrowsingBubbleTipPresenter` to nil.
   BubbleViewControllerPresenter* presenter = [self
       presentBubbleForFeature:feature_engagement::kIPHFollowWhileBrowsingFeature
                     direction:arrowDirection
@@ -282,9 +282,9 @@
 
 - (void)presentBubbles {
   // If the tip bubble has already been presented and the user is still
-  // considered engaged, it can't be overwritten or set to |nil| or else it will
-  // reset the |userEngaged| property. Once the user is not engaged, the bubble
-  // can be safely overwritten or set to |nil|.
+  // considered engaged, it can't be overwritten or set to `nil` or else it will
+  // reset the `userEngaged` property. Once the user is not engaged, the bubble
+  // can be safely overwritten or set to `nil`.
   if (!self.tabTipBubblePresenter.userEngaged)
     [self presentNewTabTipBubble];
   if (!self.incognitoTabTipBubblePresenter.userEngaged)
@@ -312,7 +312,7 @@
 
   // If the feature engagement tracker does not consider it valid to display
   // the tip, then end early to prevent the potential reassignment of the
-  // existing |longPressToolbarTipBubblePresenter| to nil.
+  // existing `longPressToolbarTipBubblePresenter` to nil.
   BubbleViewControllerPresenter* presenter = [self
       presentBubbleForFeature:feature_engagement::kIPHLongPressToolbarTipFeature
                     direction:arrowDirection
@@ -328,8 +328,8 @@
   self.longPressToolbarTipBubblePresenter = presenter;
 }
 
-// Presents and returns a bubble view controller for the |feature| with an arrow
-// |direction|, an arrow |alignment| and a |text| on an |anchorPoint|.
+// Presents and returns a bubble view controller for the `feature` with an arrow
+// `direction`, an arrow `alignment` and a `text` on an `anchorPoint`.
 - (BubbleViewControllerPresenter*)
 presentBubbleForFeature:(const base::Feature&)feature
               direction:(BubbleArrowDirection)direction
@@ -358,7 +358,7 @@
 }
 
 // Presents a bubble associated with the bottom toolbar tip in-product help
-// promotion. This method requires that |self.browserState| is not NULL.
+// promotion. This method requires that `self.browserState` is not NULL.
 - (void)presentBottomToolbarTipBubble {
   if (!IsSplitToolbarMode(self.rootViewController))
     return;
@@ -374,7 +374,7 @@
 
   // If the feature engagement tracker does not consider it valid to display
   // the tip, then end early to prevent the potential reassignment of the
-  // existing |bottomToolbarTipBubblePresenter| to nil.
+  // existing `bottomToolbarTipBubblePresenter` to nil.
   BubbleViewControllerPresenter* presenter = [self
       presentBubbleForFeature:feature_engagement::kIPHBottomToolbarTipFeature
                     direction:arrowDirection
@@ -394,10 +394,10 @@
 
 // Optionally presents a bubble associated with the new tab tip in-product help
 // promotion. If the feature engagement tracker determines it is valid to show
-// the new tab tip, then it initializes |tabTipBubblePresenter| and presents
+// the new tab tip, then it initializes `tabTipBubblePresenter` and presents
 // the bubble. If it is not valid to show the new tab tip,
-// |tabTipBubblePresenter| is set to |nil| and no bubble is shown. This method
-// requires that |self.browserState| is not NULL.
+// `tabTipBubblePresenter` is set to `nil` and no bubble is shown. This method
+// requires that `self.browserState` is not NULL.
 - (void)presentNewTabTipBubble {
   if (![self canPresentBubble])
     return;
@@ -418,7 +418,7 @@
 
   // If the feature engagement tracker does not consider it valid to display
   // the new tab tip, then end early to prevent the potential reassignment
-  // of the existing |tabTipBubblePresenter| to nil.
+  // of the existing `tabTipBubblePresenter` to nil.
   BubbleViewControllerPresenter* presenter =
       [self presentBubbleForFeature:feature_engagement::kIPHNewTabTipFeature
                           direction:arrowDirection
@@ -433,7 +433,7 @@
 }
 
 // Presents a bubble associated with the new incognito tab tip in-product help
-// promotion. This method requires that |self.browserState| is not NULL.
+// promotion. This method requires that `self.browserState` is not NULL.
 - (void)presentNewIncognitoTabTipBubble {
   if (![self canPresentBubble])
     return;
@@ -449,7 +449,7 @@
 
   // If the feature engagement tracker does not consider it valid to display
   // the incognito tab tip, then end early to prevent the potential reassignment
-  // of the existing |incognitoTabTipBubblePresenter| to nil.
+  // of the existing `incognitoTabTipBubblePresenter` to nil.
   BubbleViewControllerPresenter* presenter = [self
       presentBubbleForFeature:feature_engagement::kIPHNewIncognitoTabTipFeature
                     direction:arrowDirection
@@ -467,8 +467,8 @@
 
 #pragma mark - Private Utils
 
-// Returns the anchor point for a bubble with an |arrowDirection| pointing to a
-// |guideName|. The point is in the window coordinates.
+// Returns the anchor point for a bubble with an `arrowDirection` pointing to a
+// `guideName`. The point is in the window coordinates.
 - (CGPoint)anchorPointToGuide:(GuideName*)guideName
                     direction:(BubbleArrowDirection)arrowDirection {
   UILayoutGuide* guide =
@@ -502,11 +502,11 @@
 }
 
 // Returns a bubble associated with an in-product help promotion if
-// it is valid to show the promotion and |nil| otherwise. |feature| is the
-// base::Feature object associated with the given promotion. |direction| is the
-// direction the bubble's arrow is pointing. |alignment| is the alignment of the
-// arrow on the button. |text| is the text displayed by the bubble. This method
-// requires that |self.browserState| is not NULL.
+// it is valid to show the promotion and `nil` otherwise. `feature` is the
+// base::Feature object associated with the given promotion. `direction` is the
+// direction the bubble's arrow is pointing. `alignment` is the alignment of the
+// arrow on the button. `text` is the text displayed by the bubble. This method
+// requires that `self.browserState` is not NULL.
 - (BubbleViewControllerPresenter*)
 bubblePresenterForFeature:(const base::Feature&)feature
                 direction:(BubbleArrowDirection)direction
@@ -517,8 +517,8 @@
            ->WouldTriggerHelpUI(feature)) {
     return nil;
   }
-  // Capture |weakSelf| instead of the feature engagement tracker object
-  // because |weakSelf| will safely become |nil| if it is deallocated, whereas
+  // Capture `weakSelf` instead of the feature engagement tracker object
+  // because `weakSelf` will safely become `nil` if it is deallocated, whereas
   // the feature engagement tracker will remain pointing to invalid memory if
   // its owner (the ChromeBrowserState) is deallocated.
   __weak BubblePresenter* weakSelf = self;
diff --git a/ios/chrome/browser/ui/bubble/bubble_util.h b/ios/chrome/browser/ui/bubble/bubble_util.h
index ada70b1..2f332d85 100644
--- a/ios/chrome/browser/ui/bubble/bubble_util.h
+++ b/ios/chrome/browser/ui/bubble/bubble_util.h
@@ -15,18 +15,18 @@
 CGFloat BubbleDefaultAlignmentOffset();
 
 // Calculate the coordinates of the point of the bubble's arrow based on the
-// |targetFrame| of the target UI element and the bubble's |arrowDirection|. The
-// returned point is in the same coordinate system as |targetFrame|.
+// `targetFrame` of the target UI element and the bubble's `arrowDirection`. The
+// returned point is in the same coordinate system as `targetFrame`.
 CGPoint AnchorPoint(CGRect targetFrame, BubbleArrowDirection arrowDirection);
 
 // Calculate the maximum size of the bubble such that it stays within its
 // superview's bounding coordinate space and does not overlap the other side of
-// the anchor point. |anchorPoint| is the point on the target UI element the
+// the anchor point. `anchorPoint` is the point on the target UI element the
 // bubble is anchored at in the bubble's superview's coordinate system.
-// |bubbleAlignmentOffset| is the distance from the leading edge of the bubble
+// `bubbleAlignmentOffset` is the distance from the leading edge of the bubble
 // to the anchor point if leading aligned, and from the trailing edge of the
-// bubble to the anchor point if trailing aligned. |direction| is the bubble's
-// direction. |alignment| is the bubble's alignment. |boundingSize| is the size
+// bubble to the anchor point if trailing aligned. `direction` is the bubble's
+// direction. `alignment` is the bubble's alignment. `boundingSize` is the size
 // of the superview. Uses the ICU default locale of the device to determine
 // whether the language is RTL.
 CGSize BubbleMaxSize(CGPoint anchorPoint,
@@ -35,13 +35,13 @@
                      BubbleAlignment alignment,
                      CGSize boundingSize);
 
-// Calculate the bubble's frame. |anchorPoint| is the point on the UI element
-// the bubble is pointing to. |bubbleAlignmentOffset| is the distance from the
+// Calculate the bubble's frame. `anchorPoint` is the point on the UI element
+// the bubble is pointing to. `bubbleAlignmentOffset` is the distance from the
 // leading edge of the bubble to the anchor point if leading aligned, and from
 // the trailing edge of the bubble to the anchor point if trailing aligned.
-// |size| is the size of the bubble. |direction| is the direction the bubble's
-// arrow is pointing. |alignment| is the alignment of the anchor (either
-// leading, centered, or trailing). |boundingWidth| is the width of the bubble's
+// `size` is the size of the bubble. `direction` is the direction the bubble's
+// arrow is pointing. `alignment` is the alignment of the anchor (either
+// leading, centered, or trailing). `boundingWidth` is the width of the bubble's
 // superview.
 CGRect BubbleFrame(CGPoint anchorPoint,
                    CGFloat bubbleAlignmentOffset,
@@ -50,9 +50,9 @@
                    BubbleAlignment alignment,
                    CGFloat boundingWidth);
 
-// Returns alignment offset for a full width bubble. |boundingWidth| is the
-// width of the bubble's superview. |anchorPoint| is the point on the UI element
-// the bubble is pointing to. |alignment| is the alignment of the anchor (either
+// Returns alignment offset for a full width bubble. `boundingWidth` is the
+// width of the bubble's superview. `anchorPoint` is the point on the UI element
+// the bubble is pointing to. `alignment` is the alignment of the anchor (either
 // leading, centered, or trailing).
 CGFloat FullWidthBubbleAlignmentOffset(CGFloat boundingWidth,
                                        CGPoint anchorPoint,
diff --git a/ios/chrome/browser/ui/bubble/bubble_util.mm b/ios/chrome/browser/ui/bubble/bubble_util.mm
index ba9d1b8..52f42d5 100644
--- a/ios/chrome/browser/ui/bubble/bubble_util.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_util.mm
@@ -25,9 +25,9 @@
 // Calculate the distance from the bubble's leading edge to the leading edge of
 // its bounding coordinate system. In LTR contexts, the returned float is the
 // x-coordinate of the bubble's origin. This calculation is based on
-// |anchorPoint|, which is the point of the target UI element the bubble is
+// `anchorPoint`, which is the point of the target UI element the bubble is
 // anchored at, and the bubble's alignment offset, alignment, direction, and
-// size. The returned float is in the same coordinate system as |anchorPoint|,
+// size. The returned float is in the same coordinate system as `anchorPoint`,
 // which should be the coordinate system in which the bubble is drawn.
 CGFloat LeadingDistance(CGPoint anchorPoint,
                         CGFloat bubbleAlignmentOffset,
@@ -35,7 +35,7 @@
                         CGFloat bubbleWidth,
                         CGFloat boundingWidth,
                         bool isRTL) {
-  // Find |leadingOffset|, the distance from the bubble's leading edge to the
+  // Find `leadingOffset`, the distance from the bubble's leading edge to the
   // anchor point. This depends on alignment and bubble width.
   CGFloat leadingOffset;
   switch (alignment) {
@@ -62,10 +62,10 @@
   return floor(leadingDistance);
 }
 
-// Calculate the y-coordinate of the bubble's origin based on |anchorPoint|, the
+// Calculate the y-coordinate of the bubble's origin based on `anchorPoint`, the
 // point of the UI element the bubble is anchored at, and the bubble's arrow
 // direction and size. The returned float is in the same coordinate system as
-// |anchorPoint|, which should be the coordinate system in which the bubble is
+// `anchorPoint`, which should be the coordinate system in which the bubble is
 // drawn.
 CGFloat OriginY(CGPoint anchorPoint,
                 BubbleArrowDirection arrowDirection,
@@ -82,14 +82,14 @@
 }
 
 // Calculate the maximum width of the bubble such that it stays within its
-// bounding coordinate space. |anchorPointX| is the x-coordinate of the point on
+// bounding coordinate space. `anchorPointX` is the x-coordinate of the point on
 // the target UI element the bubble is anchored at. It is in the coordinate
-// system in which the bubble is drawn. |bubbleAlignmentOffset| is the distance
+// system in which the bubble is drawn. `bubbleAlignmentOffset` is the distance
 // from the leading edge of the bubble to the anchor point if leading aligned,
 // and from the trailing edge of the bubble to the anchor point if trailing
-// aligned. |alignment| is the bubble's alignment, |boundingWidth| is the width
-// of the coordinate space in which the bubble is drawn, and |isRTL| is true if
-// the language is RTL and |false| otherwise.
+// aligned. `alignment` is the bubble's alignment, `boundingWidth` is the width
+// of the coordinate space in which the bubble is drawn, and `isRTL` is true if
+// the language is RTL and `false` otherwise.
 CGFloat BubbleMaxWidth(CGFloat anchorPointX,
                        CGFloat bubbleAlignmentOffset,
                        BubbleAlignment alignment,
@@ -100,11 +100,11 @@
     case BubbleAlignmentLeading:
       if (isRTL) {
         // The bubble is aligned right, and can use space to the left of the
-        // anchor point and within |BubbleAlignmentOffset()| from the right.
+        // anchor point and within `BubbleAlignmentOffset()` from the right.
         maxWidth = anchorPointX + bubbleAlignmentOffset;
       } else {
         // The bubble is aligned left, and can use space to the right of the
-        // anchor point and within |BubbleAlignmentOffset()| from the left.
+        // anchor point and within `BubbleAlignmentOffset()` from the left.
         maxWidth = boundingWidth - anchorPointX + bubbleAlignmentOffset;
       }
       break;
@@ -116,11 +116,11 @@
     case BubbleAlignmentTrailing:
       if (isRTL) {
         // The bubble is aligned left, and can use space to the right of the
-        // anchor point and within |BubbleAlignmentOffset()| from the left.
+        // anchor point and within `BubbleAlignmentOffset()` from the left.
         maxWidth = boundingWidth - anchorPointX + bubbleAlignmentOffset;
       } else {
         // The bubble is aligned right, and can use space to the left of the
-        // anchor point and within |BubbleAlignmentOffset()| from the right.
+        // anchor point and within `BubbleAlignmentOffset()` from the right.
         maxWidth = anchorPointX + bubbleAlignmentOffset;
       }
       break;
@@ -133,10 +133,10 @@
 }
 
 // Calculate the maximum height of the bubble such that it stays within its
-// bounding coordinate space. |anchorPointY| is the y-coordinate of the point on
+// bounding coordinate space. `anchorPointY` is the y-coordinate of the point on
 // the target UI element the bubble is anchored at. It is in the coordinate
-// system in which the bubble is drawn. |direction| is the direction the arrow
-// is pointing. |boundingHeight| is the height of the coordinate space in which
+// system in which the bubble is drawn. `direction` is the direction the arrow
+// is pointing. `boundingHeight` is the height of the coordinate space in which
 // the bubble is drawn.
 CGFloat BubbleMaxHeight(CGFloat anchorPointY,
                         BubbleArrowDirection direction,
@@ -180,14 +180,14 @@
 
 // Calculate the maximum size of the bubble such that it stays within its
 // superview's bounding coordinate space and does not overlap the other side of
-// the anchor point. |anchorPoint| is the point on the target UI element the
+// the anchor point. `anchorPoint` is the point on the target UI element the
 // bubble is anchored at in the bubble's superview's coordinate system.
-// |bubbleAlignmentOffset| is the distance from the leading edge of the bubble
+// `bubbleAlignmentOffset` is the distance from the leading edge of the bubble
 // to the anchor point if leading aligned, and from the trailing edge of the
-// bubble to the anchor point if trailing aligned. |direction| is the bubble's
-// direction. |alignment| is the bubble's alignment. |boundingSize| is the size
-// of the superview. |isRTL| is |true| if the coordinates are in right-to-left
-// language coordinates and |false| otherwise. This method is unit tested so it
+// bubble to the anchor point if trailing aligned. `direction` is the bubble's
+// direction. `alignment` is the bubble's alignment. `boundingSize` is the size
+// of the superview. `isRTL` is `true` if the coordinates are in right-to-left
+// language coordinates and `false` otherwise. This method is unit tested so it
 // cannot be in the above anonymous namespace.
 CGSize BubbleMaxSize(CGPoint anchorPoint,
                      CGFloat bubbleAlignmentOffset,
@@ -212,15 +212,15 @@
                        boundingSize, isRTL);
 }
 
-// Calculate the bubble's frame. |anchorPoint| is the point on the UI element
-// the bubble is pointing to. |bubbleAlignmentOffset| is the distance from the
+// Calculate the bubble's frame. `anchorPoint` is the point on the UI element
+// the bubble is pointing to. `bubbleAlignmentOffset` is the distance from the
 // leading edge of the bubble to the anchor point if leading aligned, and from
 // the trailing edge of the bubble to the anchor point if trailing aligned.
-// |size| is the size of the bubble. |direction| is the direction the bubble's
-// arrow is pointing. |alignment| is the alignment of the anchor (either
-// leading, centered, or trailing). |boundingWidth| is the width of the bubble's
-// superview. |isRTL| is |true| if the coordinates are in right-to-left language
-// coordinates and |false| otherwise. This method is unit tested so it cannot be
+// `size` is the size of the bubble. `direction` is the direction the bubble's
+// arrow is pointing. `alignment` is the alignment of the anchor (either
+// leading, centered, or trailing). `boundingWidth` is the width of the bubble's
+// superview. `isRTL` is `true` if the coordinates are in right-to-left language
+// coordinates and `false` otherwise. This method is unit tested so it cannot be
 // in the above anonymous namespace.
 CGRect BubbleFrame(CGPoint anchorPoint,
                    CGFloat bubbleAlignmentOffset,
@@ -233,7 +233,7 @@
       LeadingDistance(anchorPoint, bubbleAlignmentOffset, alignment, size.width,
                       boundingWidth, isRTL);
   CGFloat originY = OriginY(anchorPoint, direction, size.height);
-  // Use a |LayoutRect| to ensure that the bubble is mirrored in RTL contexts.
+  // Use a `LayoutRect` to ensure that the bubble is mirrored in RTL contexts.
   base::i18n::TextDirection textDirection =
       isRTL ? base::i18n::RIGHT_TO_LEFT : base::i18n::LEFT_TO_RIGHT;
   CGRect bubbleFrame = LayoutRectGetRectUsingDirection(
@@ -269,7 +269,7 @@
       alignmentOffset = isRTL ? anchorPoint.x : boundingWidth - anchorPoint.x;
       break;
   }
-  // Alignment offset must be greater than |BubbleDefaultAlignmentOffset| to
+  // Alignment offset must be greater than `BubbleDefaultAlignmentOffset` to
   // make sure the arrow is in the frame of the background of the bubble. The
   // maximum is set to the middle of the bubble so the arrow stays close to the
   // leading edge when using a leading alignment.
diff --git a/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm b/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm
index 84c7ee3..3953865 100644
--- a/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_util_unittest.mm
@@ -57,12 +57,12 @@
   const CGSize bubbleSize_;
   // Bounding size of the bubble's coordinate system.
   const CGSize containerSize_;
-  // Distance from the anchor point to the |BubbleAlignment| edge of the
+  // Distance from the anchor point to the `BubbleAlignment` edge of the
   // bubble's frame.
   const CGFloat bubbleAlignmentOffset_;
 };
 
-// Test the |AnchorPoint| method when the arrow is pointing upwards, meaning the
+// Test the `AnchorPoint` method when the arrow is pointing upwards, meaning the
 // bubble is below the UI element.
 TEST_F(BubbleUtilTest, AnchorPointUp) {
   CGPoint anchorPoint = bubble_util::AnchorPoint(
@@ -70,7 +70,7 @@
   EXPECT_TRUE(CGPointEqualToPoint({300.0f, 300.0f}, anchorPoint));
 }
 
-// Test the |AnchorPoint| method when the arrow is pointing downwards, meaning
+// Test the `AnchorPoint` method when the arrow is pointing downwards, meaning
 // the bubble is above the UI element.
 TEST_F(BubbleUtilTest, AnchorPointDown) {
   CGPoint anchorPoint = bubble_util::AnchorPoint(
@@ -78,7 +78,7 @@
   EXPECT_TRUE(CGPointEqualToPoint({300.0f, 200.0f}, anchorPoint));
 }
 
-// Test the |BubbleMaxSize| method when the bubble is leading aligned, the
+// Test the `BubbleMaxSize` method when the bubble is leading aligned, the
 // target is on the left side of the container, the bubble is pointing up, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeLeadingWithTargetOnLeftUp) {
@@ -90,7 +90,7 @@
   EXPECT_FLOAT_EQ(350.0f, leftAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is leading aligned, the
+// Test the `BubbleMaxSize` method when the bubble is leading aligned, the
 // target is on the center of the container, the bubble is pointing down, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeLeadingWithTargetOnCenterDown) {
@@ -103,7 +103,7 @@
   EXPECT_FLOAT_EQ(250.0f, centerAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is leading aligned, the
+// Test the `BubbleMaxSize` method when the bubble is leading aligned, the
 // target is on the right side of the container, the bubble is pointing up, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeLeadingWithTargetOnRightUp) {
@@ -115,7 +115,7 @@
   EXPECT_FLOAT_EQ(350.0f, rightAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is center aligned, the target
+// Test the `BubbleMaxSize` method when the bubble is center aligned, the target
 // is on the left side of the container, the bubble is pointing down, and the
 // language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeCenterWithTargetOnLeftDown) {
@@ -127,7 +127,7 @@
   EXPECT_FLOAT_EQ(250.0f, leftAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is center aligned, the target
+// Test the `BubbleMaxSize` method when the bubble is center aligned, the target
 // is on the center of the container, the bubble is pointing up, and the
 // language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeCenterWithTargetOnCenterUp) {
@@ -139,7 +139,7 @@
   EXPECT_FLOAT_EQ(350.0f, centerAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is center aligned, the target
+// Test the `BubbleMaxSize` method when the bubble is center aligned, the target
 // is on the right side of the container, the bubble is pointing down, and the
 // language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeCenterWithTargetOnRightDown) {
@@ -152,7 +152,7 @@
   EXPECT_FLOAT_EQ(250.0f, rightAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is trailing aligned, the
+// Test the `BubbleMaxSize` method when the bubble is trailing aligned, the
 // target is on the left side of the container, the bubble is pointing up, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeTrailingWithTargetOnLeftUp) {
@@ -164,7 +164,7 @@
   EXPECT_FLOAT_EQ(350.0f, leftAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is trailing aligned, the
+// Test the `BubbleMaxSize` method when the bubble is trailing aligned, the
 // target is on the center of the container, the bubble is pointing down, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeTrailingWithTargetOnCenterDown) {
@@ -177,7 +177,7 @@
   EXPECT_FLOAT_EQ(250.0f, centerAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is trailing aligned, the
+// Test the `BubbleMaxSize` method when the bubble is trailing aligned, the
 // target is on the right side of the container, the bubble is pointing up, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleMaxSizeTrailingWithTargetOnRightUp) {
@@ -189,7 +189,7 @@
   EXPECT_FLOAT_EQ(350.0f, rightAlignedSize.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is leading aligned, the
+// Test the `BubbleMaxSize` method when the bubble is leading aligned, the
 // target is on the left side of the container, the bubble is pointing down, and
 // the language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeLeadingWithTargetOnLeftDownRTL) {
@@ -201,7 +201,7 @@
   EXPECT_FLOAT_EQ(250.0f, leftAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is leading aligned, the
+// Test the `BubbleMaxSize` method when the bubble is leading aligned, the
 // target is on the center of the container, the bubble is pointing up, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeLeadingWithTargetOnCenterUpRTL) {
@@ -213,7 +213,7 @@
   EXPECT_FLOAT_EQ(350.0f, centerAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is leading aligned, the
+// Test the `BubbleMaxSize` method when the bubble is leading aligned, the
 // target is on the right side of the container, the bubble is pointing down,
 // and the language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeLeadingWithTargetOnRightDownRTL) {
@@ -226,7 +226,7 @@
   EXPECT_FLOAT_EQ(250.0f, rightAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is center aligned, the target
+// Test the `BubbleMaxSize` method when the bubble is center aligned, the target
 // is on the left side of the container, the bubble is pointing up, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeCenterWithTargetOnLeftUpRTL) {
@@ -238,7 +238,7 @@
   EXPECT_FLOAT_EQ(350.0f, leftAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is center aligned, the target
+// Test the `BubbleMaxSize` method when the bubble is center aligned, the target
 // is on the center of the container, the bubble is pointing down, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeCenterWithTargetOnCenterDownRTL) {
@@ -251,7 +251,7 @@
   EXPECT_FLOAT_EQ(250.0f, centerAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is center aligned, the target
+// Test the `BubbleMaxSize` method when the bubble is center aligned, the target
 // is on the right side of the container, the bubble is pointing up, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeCenterWithTargetOnRightUpRTL) {
@@ -263,7 +263,7 @@
   EXPECT_FLOAT_EQ(350.0f, rightAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is trailing aligned, the
+// Test the `BubbleMaxSize` method when the bubble is trailing aligned, the
 // target is on the left side of the container, the bubble is pointing down, and
 // the language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeTrailingWithTargetOnLeftDownRTL) {
@@ -275,7 +275,7 @@
   EXPECT_FLOAT_EQ(250.0f, leftAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is trailing aligned, the
+// Test the `BubbleMaxSize` method when the bubble is trailing aligned, the
 // target is on the center of the container, the bubble is pointing up, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeTrailingWithTargetOnCenterUpRTL) {
@@ -287,7 +287,7 @@
   EXPECT_FLOAT_EQ(350.0f, centerAlignedSizeRTL.height);
 }
 
-// Test the |BubbleMaxSize| method when the bubble is trailing aligned, the
+// Test the `BubbleMaxSize` method when the bubble is trailing aligned, the
 // target is on the right side of the container, the bubble is pointing down,
 // and the language is RTL.
 TEST_F(BubbleUtilTest, BubbleMaxSizeTrailingWithTargetOnRightDownRTL) {
@@ -300,8 +300,8 @@
   EXPECT_FLOAT_EQ(250.0f, rightAlignedSizeRTL.height);
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionUp|, the alignment is |BubbleAlignmentLeading|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionUp`, the alignment is `BubbleAlignmentLeading`, and the
 // language is LTR.
 TEST_F(BubbleUtilTest, BubbleFrameUpLeadingLTR) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -314,8 +314,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionUp|, the alignment is |BubbleAlignmentLeading|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionUp`, the alignment is `BubbleAlignmentLeading`, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleFrameUpLeadingRTL) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -328,8 +328,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionUp|, the alignment is |BubbleAlignmentCenter|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionUp`, the alignment is `BubbleAlignmentCenter`, and the
 // language is LTR.
 TEST_F(BubbleUtilTest, BubbleFrameUpCenteredLTR) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -342,8 +342,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionUp|, the alignment is |BubbleAlignmentCenter|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionUp`, the alignment is `BubbleAlignmentCenter`, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleFrameUpCenteredRTL) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -356,8 +356,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionUp|, the alignment is |BubbleAlignmentTrailing|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionUp`, the alignment is `BubbleAlignmentTrailing`, and the
 // language is LTR.
 TEST_F(BubbleUtilTest, BubbleFrameUpTrailingLTR) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -370,8 +370,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionUp|, the alignment is |BubbleAlignmentTrailing|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionUp`, the alignment is `BubbleAlignmentTrailing`, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleFrameUpTrailingRTL) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -384,8 +384,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionDown|, the alignment is |BubbleAlignmentLeading|, and
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionDown`, the alignment is `BubbleAlignmentLeading`, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleFrameDownLeadingLTR) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -398,8 +398,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionDown|, the alignment is |BubbleAlignmentLeading|, and
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionDown`, the alignment is `BubbleAlignmentLeading`, and
 // the language is RTL.
 TEST_F(BubbleUtilTest, BubbleFrameDownLeadingRTL) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -412,8 +412,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionDown|, the alignment is |BubbleAlignmentCenter|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionDown`, the alignment is `BubbleAlignmentCenter`, and the
 // language is LTR.
 TEST_F(BubbleUtilTest, BubbleFrameDownCenteredLTR) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -426,8 +426,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionDown|, the alignment is |BubbleAlignmentCenter|, and the
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionDown`, the alignment is `BubbleAlignmentCenter`, and the
 // language is RTL.
 TEST_F(BubbleUtilTest, BubbleFrameDownCenteredRTL) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -440,8 +440,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionDown|, the alignment is |BubbleAlignmentTrailing|, and
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionDown`, the alignment is `BubbleAlignmentTrailing`, and
 // the language is LTR.
 TEST_F(BubbleUtilTest, BubbleFrameDownTrailingLTR) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -454,8 +454,8 @@
   EXPECT_FLOAT_EQ(bubbleSize_.height, CGRectGetHeight(bubbleFrame));
 }
 
-// Test |BubbleFrame| when the bubble's direction is
-// |BubbleArrowDirectionDown|, the alignment is |BubbleAlignmentTrailing|, and
+// Test `BubbleFrame` when the bubble's direction is
+// `BubbleArrowDirectionDown`, the alignment is `BubbleAlignmentTrailing`, and
 // the language is RTL.
 TEST_F(BubbleUtilTest, BubbleFrameDownTrailingRTL) {
   CGRect bubbleFrame = bubble_util::BubbleFrame(
@@ -481,8 +481,8 @@
 TEST_F(BubbleUtilTest, BubbleFullWidthAlignmentOffsetCenter) {
   CGFloat alignmentOffset = bubble_util::FullWidthBubbleAlignmentOffset(
       containerSize_.width, centerAlignedAnchorPoint_, BubbleAlignmentCenter);
-  // Bubble is center aligned, the |alignmentOffset| is ignored, it's set to the
-  // minimum of |BubbleDefaultAlignmentOffset|.
+  // Bubble is center aligned, the `alignmentOffset` is ignored, it's set to the
+  // minimum of `BubbleDefaultAlignmentOffset`.
   EXPECT_FLOAT_EQ(29.0f, alignmentOffset);
 }
 
diff --git a/ios/chrome/browser/ui/bubble/bubble_view.h b/ios/chrome/browser/ui/bubble/bubble_view.h
index c980dc5..04235313 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view.h
+++ b/ios/chrome/browser/ui/bubble/bubble_view.h
@@ -93,8 +93,8 @@
 
 // Distance between the arrow's centerX and the (leading or trailing) edge of
 // the bubble, depending on the BubbleAlignment. If BubbleAlignment is center,
-// then |alignmentOffset| is ignored. |alignmentOffset| changes the minimum size
-// of the bubble, thus might change the value of |sizeThatFits|.
+// then `alignmentOffset` is ignored. `alignmentOffset` changes the minimum size
+// of the bubble, thus might change the value of `sizeThatFits`.
 @property(nonatomic) CGFloat alignmentOffset;
 
 @end
diff --git a/ios/chrome/browser/ui/bubble/bubble_view.mm b/ios/chrome/browser/ui/bubble/bubble_view.mm
index 48d659ab..30d2ef9bb 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view.mm
@@ -477,7 +477,7 @@
   }
   NSMutableArray<NSLayoutConstraint*>* constraints =
       [NSMutableArray arrayWithArray:@[
-        // Ensure the background view is smaller than |self.view|.
+        // Ensure the background view is smaller than `self.view`.
         [background.leadingAnchor
             constraintGreaterThanOrEqualToAnchor:self.leadingAnchor],
         [self.trailingAnchor
@@ -496,7 +496,7 @@
         [background.trailingAnchor
             constraintGreaterThanOrEqualToAnchor:label.trailingAnchor
                                         constant:kBubbleHorizontalPadding],
-        // Enforce the arrow's size, scaling by |kArrowScaleFactor| to prevent
+        // Enforce the arrow's size, scaling by `kArrowScaleFactor` to prevent
         // gaps between the arrow and the background view.
         [arrow.widthAnchor constraintEqualToConstant:kArrowSize.width],
         [arrow.heightAnchor constraintEqualToConstant:kArrowSize.height]
@@ -597,14 +597,14 @@
     (CGFloat)alignmentOffset {
   // The anchor of the bubble which is aligned with the arrow's center anchor.
   NSLayoutAnchor* anchor;
-  // The constant by which |anchor| is offset from the arrow's center anchor.
+  // The constant by which `anchor` is offset from the arrow's center anchor.
   CGFloat offset;
   switch (self.alignment) {
     case BubbleAlignmentLeading:
-      // The anchor point is at a distance of |alignmentOffset|
+      // The anchor point is at a distance of `alignmentOffset`
       // from the bubble's leading edge. Center align the arrow with the anchor
       // point by aligning the center of the arrow with the leading edge of the
-      // bubble view and adding an offset of |alignmentOffset|.
+      // bubble view and adding an offset of `alignmentOffset`.
       anchor = self.leadingAnchor;
       offset = alignmentOffset;
       break;
@@ -615,10 +615,10 @@
       offset = 0.0f;
       break;
     case BubbleAlignmentTrailing:
-      // The anchor point is at a distance of |alignmentOffset|
+      // The anchor point is at a distance of `alignmentOffset`
       // from the bubble's trailing edge. Center align the arrow with the anchor
       // point by aligning the center of the arrow with the trailing edge of the
-      // bubble view and adding an offset of |-alignmentOffset|.
+      // bubble view and adding an offset of `-alignmentOffset`.
       anchor = self.trailingAnchor;
       offset = -alignmentOffset;
       break;
@@ -668,14 +668,14 @@
 
 #pragma mark - UIView overrides
 
-// Override |willMoveToSuperview| to add view properties to the view hierarchy.
+// Override `willMoveToSuperview` to add view properties to the view hierarchy.
 - (void)willMoveToSuperview:(UIView*)newSuperview {
   // If constraints have not been added to the view, add them.
   if (self.needsAddConstraints) {
     [self activateConstraints];
     // Add drop shadow.
     [self addShadow];
-    // Set |needsAddConstraints| to NO to ensure that the constraints are only
+    // Set `needsAddConstraints` to NO to ensure that the constraints are only
     // added to the view hierarchy once.
     self.needsAddConstraints = NO;
   }
@@ -709,7 +709,7 @@
   return textSize;
 }
 
-// Override |sizeThatFits| to return the bubble's optimal size. Calculate
+// Override `sizeThatFits` to return the bubble's optimal size. Calculate
 // optimal size by finding the labels' optimal size, and adding inset distances
 // to the labels' dimensions. This method also enforces minimum bubble width to
 // prevent strange, undesired behaviors, and maximum labels width to preserve
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller.h b/ios/chrome/browser/ui/bubble/bubble_view_controller.h
index e3628ce..928d1c7 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller.h
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller.h
@@ -36,7 +36,7 @@
 // view hierarchy.
 - (void)animateContentIn;
 
-// Dismisses the bubble. If |animated| is true, the bubble fades out.
+// Dismisses the bubble. If `animated` is true, the bubble fades out.
 //
 // The bubble view controller is automatically removed from the view hierarchy.
 - (void)dismissAnimated:(BOOL)animated;
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller.mm b/ios/chrome/browser/ui/bubble/bubble_view_controller.mm
index 6565131..433534a 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller.mm
@@ -118,7 +118,7 @@
   [self.view setFrame:frame];
   [self.view setHidden:NO];
 
-  // Set the y-coordinate of |frame.origin| to its final value.
+  // Set the y-coordinate of `frame.origin` to its final value.
   frame.origin.y = frame.origin.y + kVerticalOffset;
   [UIView animateWithDuration:kAnimationDuration
                         delay:0.0
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h
index 88d12718..cb4c15e 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.h
@@ -25,33 +25,33 @@
 @interface BubbleViewControllerPresenter : NSObject
 
 // Determines whether the user is still engaged with the bubble view. Defaults
-// to |YES|. After a certain duration of time, is set to |NO| automatically.
+// to `YES`. After a certain duration of time, is set to `NO` automatically.
 // Used to determine whether the bubble has influenced the user's
 // action. For example, if a bubble appears pointing at the tab switcher button,
-// and the user taps the tab switcher button shortly after, |userEngaged| would
-// be |YES| because the user has recently seen the bubble and can reasonably be
+// and the user taps the tab switcher button shortly after, `userEngaged` would
+// be `YES` because the user has recently seen the bubble and can reasonably be
 // assumed to be influenced by it. However, if a bubble appears in the same
 // scenario, but the user ignores it and doesn't click the tab switcher button
-// until significantly later, |userEngaged| will be |NO| because it is unlikely
+// until significantly later, `userEngaged` will be `NO` because it is unlikely
 // the bubble had an effect on the user's action.
 @property(nonatomic, assign, readonly, getter=isUserEngaged) BOOL userEngaged;
 
 // Determines whether a follow-up action, such as highlighting a UI element,
-// should be triggered. This depends on |userEngaged|, since a follow-up action
-// should only occur if the user is engaged with the bubble. Defaults to |YES|,
-// and is set to |NO| once |userEngaged| is set to |NO| or after the user has
+// should be triggered. This depends on `userEngaged`, since a follow-up action
+// should only occur if the user is engaged with the bubble. Defaults to `YES`,
+// and is set to `NO` once `userEngaged` is set to `NO` or after the user has
 // triggered the follow-up action.
 @property(nonatomic, assign) BOOL triggerFollowUpAction;
 
 // Text to be announced with Voice Over when the bubble is presented.
 @property(nonatomic, copy) NSString* voiceOverAnnouncement;
 
-// Initializes the presenter. |text| is the text displayed by the bubble.
-// |titleString| is the title displayed by the bubble. |image| is the image
-// displayed by the bubble. |arrowDirection| is the direction the bubble's arrow
-// is pointing. |alignment| is the position of the arrow on the bubble. |type|
-// is the type of bubble content. |dismissalCallback| is a block invoked when
-// the bubble is dismissed (manual and automatic dismissal). |dismissalCallback|
+// Initializes the presenter. `text` is the text displayed by the bubble.
+// `titleString` is the title displayed by the bubble. `image` is the image
+// displayed by the bubble. `arrowDirection` is the direction the bubble's arrow
+// is pointing. `alignment` is the position of the arrow on the bubble. `type`
+// is the type of bubble content. `dismissalCallback` is a block invoked when
+// the bubble is dismissed (manual and automatic dismissal). `dismissalCallback`
 // is optional.
 - (instancetype)initWithText:(NSString*)text
                        title:(NSString*)titleString
@@ -62,11 +62,11 @@
            dismissalCallback:(ProceduralBlockWithSnoozeAction)dismissalCallback
     NS_DESIGNATED_INITIALIZER;
 
-// Initializes the presenter with a Default BubbleViewType. |text| is the text
-// displayed by the bubble. |arrowDirection| is the direction the bubble's arrow
-// is pointing. |alignment| is the position of the arrow on the bubble.
-// |dismissalCallback| is a block invoked when the bubble is dismissed (manual
-// and automatic dismissal). |dismissalCallback| is optional.
+// Initializes the presenter with a Default BubbleViewType. `text` is the text
+// displayed by the bubble. `arrowDirection` is the direction the bubble's arrow
+// is pointing. `alignment` is the position of the arrow on the bubble.
+// `dismissalCallback` is a block invoked when the bubble is dismissed (manual
+// and automatic dismissal). `dismissalCallback` is optional.
 - (instancetype)initWithText:(NSString*)text
               arrowDirection:(BubbleArrowDirection)arrowDirection
                    alignment:(BubbleAlignment)alignment
@@ -74,12 +74,12 @@
 
 - (instancetype)init NS_UNAVAILABLE;
 
-// Check if the bubble has enough space to be presented in |parentView| with an
-// anchor point at |anchorPoint|.
+// Check if the bubble has enough space to be presented in `parentView` with an
+// anchor point at `anchorPoint`.
 - (BOOL)canPresentInView:(UIView*)parentView anchorPoint:(CGPoint)anchorPoint;
 
-// Presents the bubble in |parentView|. The underlying BubbleViewController is
-// added as a child view controller of |parentViewController|. |anchorPoint|
+// Presents the bubble in `parentView`. The underlying BubbleViewController is
+// added as a child view controller of `parentViewController`. `anchorPoint`
 // determines where the bubble is anchored in window coordinates.
 - (void)presentInViewController:(UIViewController*)parentViewController
                            view:(UIView*)parentView
@@ -88,8 +88,8 @@
 // Removes the bubble from the screen and removes the BubbleViewController from
 // its parent. If the bubble is not visible, has no effect. Can be animated or
 // not. Invokes the dismissal callback. The callback is invoked immediately
-// regardless of the value of |animated|. It does not wait for the animation
-// to complete if |animated| is |YES|.
+// regardless of the value of `animated`. It does not wait for the animation
+// to complete if `animated` is `YES`.
 - (void)dismissAnimated:(BOOL)animated;
 
 @end
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm
index 4d6fc11..0826f43 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter.mm
@@ -35,7 +35,7 @@
 // Redeclared as readwrite so the value can be changed internally.
 @property(nonatomic, assign, readwrite, getter=isUserEngaged) BOOL userEngaged;
 // The underlying BubbleViewController managed by this object.
-// |bubbleViewController| manages the BubbleView instance.
+// `bubbleViewController` manages the BubbleView instance.
 @property(nonatomic, strong) BubbleViewController* bubbleViewController;
 // The tap gesture recognizer intercepting tap gestures occurring inside the
 // bubble view. Taps inside must be differentiated from taps outside to track
@@ -167,8 +167,8 @@
   self.bubbleViewController.view.frame =
       [self frameForBubbleInRect:parentView.bounds
                    atAnchorPoint:anchorPointInParent];
-  // The bubble's frame must be set. Call |canPresentInView| to make sure that
-  // the frame can be set before calling |presentInViewController|.
+  // The bubble's frame must be set. Call `canPresentInView` to make sure that
+  // the frame can be set before calling `presentInViewController`.
   DCHECK(!CGRectIsEmpty(self.bubbleViewController.view.frame));
 
   self.presenting = YES;
@@ -220,8 +220,8 @@
 
 - (void)dismissAnimated:(BOOL)animated
            snoozeAction:(feature_engagement::Tracker::SnoozeAction)action {
-  // Because this object must stay in memory to handle the |userEngaged|
-  // property correctly, it is possible for |dismissAnimated| to be called
+  // Because this object must stay in memory to handle the `userEngaged`
+  // property correctly, it is possible for `dismissAnimated` to be called
   // multiple times. However, only the first call should have any effect.
   if (!self.presenting) {
     return;
@@ -318,21 +318,21 @@
   [self dismissAnimated:YES];
 }
 
-// Automatically dismisses the bubble view when |bubbleDismissalTimer| fires.
+// Automatically dismisses the bubble view when `bubbleDismissalTimer` fires.
 - (void)bubbleDismissalTimerFired:(id)sender {
   [self dismissAnimated:YES];
 }
 
-// Marks the user as not engaged when |engagementTimer| fires.
+// Marks the user as not engaged when `engagementTimer` fires.
 - (void)engagementTimerFired:(id)sender {
   self.userEngaged = NO;
   self.triggerFollowUpAction = NO;
   self.engagementTimer = nil;
 }
 
-// Calculates the frame of the BubbleView. |rect| is the frame of the bubble's
-// superview. |anchorPoint| is the anchor point of the bubble. |anchorPoint|
-// and |rect| must be in the same coordinates.
+// Calculates the frame of the BubbleView. `rect` is the frame of the bubble's
+// superview. `anchorPoint` is the anchor point of the bubble. `anchorPoint`
+// and `rect` must be in the same coordinates.
 - (CGRect)frameForBubbleInRect:(CGRect)rect atAnchorPoint:(CGPoint)anchorPoint {
   const BOOL bubbleIsFullWidth = self.bubbleType != BubbleViewTypeDefault;
   CGFloat bubbleAlignmentOffset = bubble_util::BubbleDefaultAlignmentOffset();
@@ -340,7 +340,7 @@
     bubbleAlignmentOffset = bubble_util::FullWidthBubbleAlignmentOffset(
         rect.size.width, anchorPoint, self.alignment);
   }
-  // Set bubble alignment offset, must be set before the call to |sizeThatFits|.
+  // Set bubble alignment offset, must be set before the call to `sizeThatFits`.
   [self.bubbleViewController setBubbleAlignmentOffset:bubbleAlignmentOffset];
   CGSize maxBubbleSize = bubble_util::BubbleMaxSize(
       anchorPoint, bubbleAlignmentOffset, self.arrowDirection, self.alignment,
@@ -350,9 +350,9 @@
   if (bubbleIsFullWidth) {
     bubbleSize.width = maxBubbleSize.width;
   }
-  // If |bubbleSize| does not fit in |maxBubbleSize|, the bubble will be
+  // If `bubbleSize` does not fit in `maxBubbleSize`, the bubble will be
   // partially off screen and not look good. This is most likely a result of
-  // an incorrect value for |alignment| (such as a trailing aligned bubble
+  // an incorrect value for `alignment` (such as a trailing aligned bubble
   // anchored to an element on the leading edge of the screen).
   if (bubbleSize.width > maxBubbleSize.width ||
       bubbleSize.height > maxBubbleSize.height) {
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter_unittest.mm b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter_unittest.mm
index b8c1113..c1eb433 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter_unittest.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_presenter_unittest.mm
@@ -59,17 +59,17 @@
  protected:
   // The presenter object under test.
   BubbleViewControllerPresenter* bubbleViewControllerPresenter_;
-  // The window the |parentViewController_|'s view is in.
-  // -presentInViewController: expects the |anchorPoint| parameter to be in
-  // window coordinates, which requires the |view| property to be in a window.
+  // The window the `parentViewController_`'s view is in.
+  // -presentInViewController: expects the `anchorPoint` parameter to be in
+  // window coordinates, which requires the `view` property to be in a window.
   UIWindow* window_;
   // The view controller the BubbleViewController is added as a child of.
   UIViewController* parentViewController_;
   // The point at which the bubble is anchored.
   CGPoint anchorPoint_;
-  // How many times |bubbleViewControllerPresenter_|'s internal
-  // |dismissalCallback| has been invoked. Defaults to 0. Every time the
-  // callback is invoked, |dismissalCallbackCount_| increments.
+  // How many times `bubbleViewControllerPresenter_`'s internal
+  // `dismissalCallback` has been invoked. Defaults to 0. Every time the
+  // callback is invoked, `dismissalCallbackCount_` increments.
   int dismissalCallbackCount_;
   absl::optional<feature_engagement::Tracker::SnoozeAction>
       dismissalCallbackAction_;
@@ -143,13 +143,13 @@
   EXPECT_EQ(0, dismissalCallbackCount_);
 }
 
-// Tests that the timers are |nil| before the bubble is presented on screen.
+// Tests that the timers are `nil` before the bubble is presented on screen.
 TEST_F(BubbleViewControllerPresenterTest, TimersInitiallyNil) {
   EXPECT_EQ(nil, bubbleViewControllerPresenter_.bubbleDismissalTimer);
   EXPECT_EQ(nil, bubbleViewControllerPresenter_.engagementTimer);
 }
 
-// Tests that the timers are not |nil| once the bubble is presented on screen.
+// Tests that the timers are not `nil` once the bubble is presented on screen.
 TEST_F(BubbleViewControllerPresenterTest, TimersInstantiatedOnPresent) {
   [bubbleViewControllerPresenter_
       presentInViewController:parentViewController_
@@ -159,7 +159,7 @@
   EXPECT_NE(nil, bubbleViewControllerPresenter_.engagementTimer);
 }
 
-// Tests that the bubble timer is |nil| but the engagement timer is not |nil|
+// Tests that the bubble timer is `nil` but the engagement timer is not `nil`
 // when the bubble is presented and dismissed.
 TEST_F(BubbleViewControllerPresenterTest, BubbleTimerNilOnDismissal) {
   [bubbleViewControllerPresenter_
@@ -171,12 +171,12 @@
   EXPECT_NE(nil, bubbleViewControllerPresenter_.engagementTimer);
 }
 
-// Tests that the |userEngaged| property is initially |NO|.
+// Tests that the `userEngaged` property is initially `NO`.
 TEST_F(BubbleViewControllerPresenterTest, UserEngagedInitiallyNo) {
   EXPECT_FALSE(bubbleViewControllerPresenter_.isUserEngaged);
 }
 
-// Tests that the |userEngaged| property is |YES| once the bubble is presented
+// Tests that the `userEngaged` property is `YES` once the bubble is presented
 // on screen.
 TEST_F(BubbleViewControllerPresenterTest, UserEngagedYesOnPresent) {
   [bubbleViewControllerPresenter_
@@ -186,7 +186,7 @@
   EXPECT_TRUE(bubbleViewControllerPresenter_.isUserEngaged);
 }
 
-// Tests that the |userEngaged| property remains |YES| once the bubble is
+// Tests that the `userEngaged` property remains `YES` once the bubble is
 // presented and dismissed.
 TEST_F(BubbleViewControllerPresenterTest, UserEngagedYesOnDismissal) {
   [bubbleViewControllerPresenter_
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_controller_unittest.mm b/ios/chrome/browser/ui/bubble/bubble_view_controller_unittest.mm
index e67b16ad..9ad45fb 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view_controller_unittest.mm
@@ -36,7 +36,7 @@
   // The alignment of the bubble's arrow relative to the rest of the bubble.
   const BubbleAlignment alignment_;
 
-  // Tests that |bubbleViewController|'s bubbleView contains the expected
+  // Tests that `bubbleViewController`'s bubbleView contains the expected
   // subviews.
   void ExpectBubbleViewContent(BubbleViewController* bubbleViewController,
                                BOOL expectCloseButton,
diff --git a/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm b/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm
index 4f2f2421..dea0b6f 100644
--- a/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm
+++ b/ios/chrome/browser/ui/bubble/bubble_view_unittest.mm
@@ -65,7 +65,7 @@
   const BubbleAlignment alignment_;
   // Distance between the arrow's centerX and the (leading or trailing) edge of
   // the bubble, depending on the BubbleAlignment. If BubbleAlignment is center,
-  // then |alignmentOffset| is ignored.
+  // then `alignmentOffset` is ignored.
   const CGFloat alignmentOffset_;
   // Text that is shorter than the minimum line width.
   NSString* shortText_;
@@ -76,7 +76,7 @@
   NSTextAlignment textAlignment_;
 };
 
-// Test |sizeThatFits| given short text.
+// Test `sizeThatFits` given short text.
 TEST_F(BubbleViewTest, BubbleSizeShortText) {
   BubbleView* bubble = [[BubbleView alloc] initWithText:shortText_
                                          arrowDirection:arrowDirection_
@@ -263,7 +263,7 @@
   [bubble layoutIfNeeded];
   UIView* arrowView = GetArrowViewFromBubbleView(bubble);
   ASSERT_TRUE(arrowView);
-  // The center of the arrow must be at a distance of |alignmentOffset_| to the
+  // The center of the arrow must be at a distance of `alignmentOffset_` to the
   // leading edge of the bubble.
   EXPECT_EQ(CGRectGetMidX(arrowView.frame), alignmentOffset_);
 }
@@ -279,7 +279,7 @@
   [bubble layoutIfNeeded];
   UIView* arrowView = GetArrowViewFromBubbleView(bubble);
   ASSERT_TRUE(arrowView);
-  // |alignmentOffset| should be ignored with BubbleAlignmentCenter.
+  // `alignmentOffset` should be ignored with BubbleAlignmentCenter.
   EXPECT_EQ(CGRectGetMidX(arrowView.frame), CGRectGetMidX(bubble.frame));
 }
 
@@ -295,7 +295,7 @@
   [bubble layoutIfNeeded];
   UIView* arrowView = GetArrowViewFromBubbleView(bubble);
   ASSERT_TRUE(arrowView);
-  // The center of the arrow must be at a distance of |alignmentOffset_| to the
+  // The center of the arrow must be at a distance of `alignmentOffset_` to the
   // trailing edge of the bubble.
   EXPECT_EQ(CGRectGetMidX(arrowView.frame),
             bubble.frame.size.width - alignmentOffset_);
diff --git a/ios/chrome/browser/ui/settings/privacy/BUILD.gn b/ios/chrome/browser/ui/settings/privacy/BUILD.gn
index c359dad..813bb42 100644
--- a/ios/chrome/browser/ui/settings/privacy/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/privacy/BUILD.gn
@@ -11,6 +11,7 @@
     "privacy_safe_browsing_consumer.h",
     "privacy_safe_browsing_coordinator.h",
     "privacy_safe_browsing_coordinator.mm",
+    "privacy_safe_browsing_mediator+internal.h",
     "privacy_safe_browsing_mediator.h",
     "privacy_safe_browsing_mediator.mm",
     "privacy_safe_browsing_navigation_commands.h",
@@ -111,10 +112,15 @@
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
-  sources = [ "privacy_table_view_controller_unittest.mm" ]
+  sources = [
+    "privacy_safe_browsing_mediator_unittest.mm",
+    "privacy_table_view_controller_unittest.mm",
+  ]
   deps = [
+    "//base/test:test_support",
     "//components/handoff",
     "//components/prefs",
+    "//components/prefs:test_support",
     "//components/prefs/ios",
     "//components/safe_browsing/core/common",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
@@ -129,11 +135,15 @@
     "//ios/chrome/browser/main:test_support",
     "//ios/chrome/browser/prefs:browser_prefs",
     "//ios/chrome/browser/sync",
+    "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/settings/privacy:privacy_ui",
+    "//ios/chrome/browser/ui/settings/utils",
     "//ios/chrome/browser/ui/table_view:test_support",
+    "//ios/chrome/browser/ui/table_view/cells",
     "//ios/chrome/test:test_support",
     "//ios/web/public/test",
     "//testing/gtest",
+    "//third_party/ocmock:ocmock",
     "//ui/base",
   ]
 }
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator+internal.h b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator+internal.h
new file mode 100644
index 0000000..11b9e07
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator+internal.h
@@ -0,0 +1,23 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_SAFE_BROWSING_MEDIATOR_INTERNAL_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_SAFE_BROWSING_MEDIATOR_INTERNAL_H_
+
+#import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator.h"
+#import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
+
+// Used for testing.
+@interface PrivacySafeBrowsingMediator (Test)
+- (void)updatePrivacySafeBrowsingSectionAndNotifyConsumer:(BOOL)notifyConsumer;
+- (BOOL)shouldItemTypeHaveCheckmark:(NSInteger)itemType;
+@property(nonatomic, strong, readonly)
+    PrefBackedBoolean* safeBrowsingEnhancedProtectionPreference;
+@property(nonatomic, strong, readonly)
+    PrefBackedBoolean* safeBrowsingStandardProtectionPreference;
+@property(nonatomic, strong, readwrite)
+    NSArray<TableViewItem*>* safeBrowsingItems;
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_PRIVACY_PRIVACY_SAFE_BROWSING_MEDIATOR_INTERNAL_H_
diff --git a/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator_unittest.mm b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator_unittest.mm
new file mode 100644
index 0000000..fc9cc60
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator_unittest.mm
@@ -0,0 +1,148 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator.h"
+
+#import "base/test/scoped_feature_list.h"
+#import "components/prefs/testing_pref_service.h"
+#import "components/safe_browsing/core/common/features.h"
+#import "components/safe_browsing/core/common/safe_browsing_prefs.h"
+#import "components/sync_preferences/testing_pref_service_syncable.h"
+#import "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
+#import "ios/chrome/browser/main/test_browser.h"
+#include "ios/chrome/browser/prefs/browser_prefs.h"
+#import "ios/chrome/browser/ui/list_model/list_model.h"
+#import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_mediator+internal.h"
+#import "ios/chrome/browser/ui/settings/privacy/privacy_safe_browsing_navigation_commands.h"
+#import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_info_button_item.h"
+#import "ios/web/public/test/web_task_environment.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// List of item types.
+typedef NS_ENUM(NSInteger, ItemType) {
+  ItemTypeSafeBrowsingStandardProtection = kItemTypeEnumZero,
+  ItemTypeSafeBrowsingEnhancedProtection,
+  ItemTypeSafeBrowsingNoProtection,
+};
+
+// Registers account preference that will be used for Safe Browsing. Default
+// state of Safe Browsing should be Standard Protection.
+std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() {
+  auto prefs = std::make_unique<sync_preferences::TestingPrefServiceSyncable>();
+  user_prefs::PrefRegistrySyncable* registry = prefs->registry();
+  RegisterBrowserStatePrefs(registry);
+  return prefs;
+}
+
+};  // anonymous namespace
+
+class PrivacySafeBrowsingMediatorTest : public PlatformTest {
+ public:
+  PrivacySafeBrowsingMediatorTest() {
+    TestChromeBrowserState::Builder test_cbs_builder;
+    test_cbs_builder.SetPrefService(CreatePrefService());
+    browser_state_ = test_cbs_builder.Build();
+
+    mediator_ = [[PrivacySafeBrowsingMediator alloc]
+        initWithUserPrefService:browser_state_->GetPrefs()];
+    [mediator_ safeBrowsingItems];
+  }
+
+  void TearDown() override {
+    [[mediator_ safeBrowsingEnhancedProtectionPreference] stop];
+    [[mediator_ safeBrowsingStandardProtectionPreference] stop];
+    PlatformTest::TearDown();
+  }
+
+  TableViewItem* itemWithItemType(ItemType type) {
+    for (TableViewItem* item in mediator_.safeBrowsingItems) {
+      ItemType itemType = static_cast<ItemType>(item.type);
+      if (itemType == type)
+        return item;
+    }
+    return nil;
+  }
+
+ protected:
+  web::WebTaskEnvironment environment_;
+  std::unique_ptr<TestChromeBrowserState> browser_state_;
+  PrivacySafeBrowsingMediator* mediator_;
+};
+
+TEST_F(PrivacySafeBrowsingMediatorTest, TurnOnEnhancedProtection) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(safe_browsing::kEnhancedProtection);
+  TableViewItem* enhanced_safe_browsing_item =
+      itemWithItemType(ItemTypeSafeBrowsingEnhancedProtection);
+  [mediator_ didSelectItem:enhanced_safe_browsing_item];
+  [mediator_ selectSettingItem:enhanced_safe_browsing_item];
+  EXPECT_TRUE([mediator_
+      shouldItemTypeHaveCheckmark:ItemTypeSafeBrowsingEnhancedProtection]);
+  EXPECT_TRUE(
+      safe_browsing::GetSafeBrowsingState(*browser_state_->GetPrefs()) ==
+      safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
+}
+
+TEST_F(PrivacySafeBrowsingMediatorTest, TurnOnStandardProtection) {
+  TableViewItem* standard_safe_browsing_item =
+      itemWithItemType(ItemTypeSafeBrowsingStandardProtection);
+  [mediator_ didSelectItem:standard_safe_browsing_item];
+  [mediator_ selectSettingItem:standard_safe_browsing_item];
+  EXPECT_TRUE([mediator_
+      shouldItemTypeHaveCheckmark:ItemTypeSafeBrowsingStandardProtection]);
+  EXPECT_TRUE(
+      safe_browsing::GetSafeBrowsingState(*browser_state_->GetPrefs()) ==
+      safe_browsing::SafeBrowsingState::STANDARD_PROTECTION);
+}
+
+TEST_F(PrivacySafeBrowsingMediatorTest, TurnOffSafeBrowsing) {
+  id mock_handler =
+      OCMProtocolMock(@protocol(PrivacySafeBrowsingNavigationCommands));
+  OCMExpect([mock_handler showSafeBrowsingNoProtectionPopUp:[OCMArg any]]);
+  mediator_.handler = mock_handler;
+
+  // Simulate pressing the "No Protection" option in the Safe Browsing settings
+  // page and turning off Safe Browsing.
+  TableViewItem* no_safe_browsing_item =
+      itemWithItemType(ItemTypeSafeBrowsingNoProtection);
+  [mediator_ didSelectItem:no_safe_browsing_item];
+  [mediator_ selectSettingItem:no_safe_browsing_item];
+  EXPECT_TRUE(
+      [mediator_ shouldItemTypeHaveCheckmark:ItemTypeSafeBrowsingNoProtection]);
+  EXPECT_TRUE(
+      safe_browsing::GetSafeBrowsingState(*browser_state_->GetPrefs()) ==
+      safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING);
+
+  EXPECT_OCMOCK_VERIFY(mock_handler);
+}
+
+TEST_F(PrivacySafeBrowsingMediatorTest, CancelTurnOffSafeBrowsing) {
+  id mock_handler =
+      OCMProtocolMock(@protocol(PrivacySafeBrowsingNavigationCommands));
+  OCMExpect([mock_handler showSafeBrowsingNoProtectionPopUp:[OCMArg any]]);
+  mediator_.handler = mock_handler;
+
+  // Simulate pressing the "No Protection" option in the Safe Browsing settings
+  // page and pressing the cancel button in the No Protection pop up.
+  TableViewItem* no_safe_browsing_item =
+      itemWithItemType(ItemTypeSafeBrowsingNoProtection);
+  [mediator_ didSelectItem:no_safe_browsing_item];
+  EXPECT_FALSE(
+      [mediator_ shouldItemTypeHaveCheckmark:ItemTypeSafeBrowsingNoProtection]);
+  EXPECT_FALSE(
+      safe_browsing::GetSafeBrowsingState(*browser_state_->GetPrefs()) ==
+      safe_browsing::SafeBrowsingState::NO_SAFE_BROWSING);
+
+  EXPECT_OCMOCK_VERIFY(mock_handler);
+}
diff --git a/media/capture/mojom/video_capture_types.mojom b/media/capture/mojom/video_capture_types.mojom
index 74bde96..d9be2ad4 100644
--- a/media/capture/mojom/video_capture_types.mojom
+++ b/media/capture/mojom/video_capture_types.mojom
@@ -384,7 +384,7 @@
   kSuccess,
   kErrorGeneric,
   kUnsupportedCaptureDevice,
-  kErrorUnknownDeviceId,
   kNotImplemented,
   kNonIncreasingCropVersion,
+  kInvalidCropTarget,
 };
diff --git a/media/capture/video/win/video_capture_device_mf_win_unittest.cc b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
index bf4eb40..5429137 100644
--- a/media/capture/video/win/video_capture_device_mf_win_unittest.cc
+++ b/media/capture/video/win/video_capture_device_mf_win_unittest.cc
@@ -1463,6 +1463,49 @@
                            MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED);
 }
 
+// Send random event before MF_CAPTURE_ENGINE_STOPPED
+TEST_F(VideoCaptureDeviceMFWinTest,
+       SendArbitraryMFVideoCallbackBeforeOnStoppedEvent) {
+  if (ShouldSkipTest())
+    return;
+
+  VideoCaptureDeviceDescriptor descriptor = VideoCaptureDeviceDescriptor();
+  Microsoft::WRL::ComPtr<MockMFMediaSource> media_source =
+      new MockMFMediaSource();
+  Microsoft::WRL::ComPtr<MockMFCaptureEngine> engine =
+      new MockMFCaptureEngine();
+  std::unique_ptr<VideoCaptureDeviceMFWin> device =
+      std::make_unique<VideoCaptureDeviceMFWin>(
+          descriptor, media_source,
+          /*mf_dxgi_device_manager=*/nullptr, engine);
+
+  EXPECT_CALL(*(engine.Get()), OnInitEventGuid).WillOnce([]() {
+    return MF_CAPTURE_ENGINE_INITIALIZED;
+  });
+
+  EXPECT_CALL(*(engine.Get()), OnCorrectInitializeQueued());
+
+  EXPECT_TRUE(device->Init());
+
+  PrepareMFDeviceWithOneVideoStream(MFVideoFormat_I420);
+
+  EXPECT_CALL(*(engine_.Get()), OnStartPreview());
+  EXPECT_CALL(*client_, OnStarted());
+
+  VideoCaptureFormat format(gfx::Size(640, 480), 30, media::PIXEL_FORMAT_NV12);
+  VideoCaptureParams video_capture_params;
+  video_capture_params.requested_format = format;
+  device_->AllocateAndStart(video_capture_params, std::move(client_));
+
+  // Send an arbitrary event before stopping the preview.
+  EXPECT_CALL(*(engine_.Get()), OnStopPreview()).WillRepeatedly([&]() {
+    engine->FireCaptureEvent(MF_CAPTURE_ENGINE_CAMERA_STREAM_BLOCKED, S_OK);
+  });
+
+  capture_preview_sink_->sample_callback->OnSample(nullptr);
+  device_->StopAndDeAllocate();
+}
+
 // Allocates device with flaky methods failing with MF_E_INVALIDREQUEST and
 // expects the device to retry and start correctly
 TEST_F(VideoCaptureDeviceMFWinTest, AllocateAndStartWithFlakyInvalidRequest) {
diff --git a/services/device/generic_sensor/platform_sensor_fusion.cc b/services/device/generic_sensor/platform_sensor_fusion.cc
index a695412..e6d6335 100644
--- a/services/device/generic_sensor/platform_sensor_fusion.cc
+++ b/services/device/generic_sensor/platform_sensor_fusion.cc
@@ -201,8 +201,7 @@
   if (!fusion_algorithm_->GetFusedData(type, &reading))
     return;
 
-  reading_ = reading;
-  UpdateSharedBufferAndNotifyClients(reading_);
+  UpdateSharedBufferAndNotifyClients(reading);
 }
 
 void PlatformSensorFusion::OnSensorError() {
diff --git a/services/device/generic_sensor/platform_sensor_fusion.h b/services/device/generic_sensor/platform_sensor_fusion.h
index 43ec64e..84a3b20 100644
--- a/services/device/generic_sensor/platform_sensor_fusion.h
+++ b/services/device/generic_sensor/platform_sensor_fusion.h
@@ -85,7 +85,6 @@
                            FusionIsSignificantlyDifferent);
 
  private:
-  SensorReading reading_;
   std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm_;
   SourcesMap source_sensors_;
   mojom::ReportingMode reporting_mode_;
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index 75ee0d7..c0ca0f5d 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -733,9 +733,8 @@
       PreflightController::WithTrustedHeaderClient(
           options_ & mojom::kURLLoadOptionUseHeaderClient),
       non_wildcard_request_headers_support_,
-      PreflightController::EnforcePrivateNetworkAccessHeader(
-          !ShouldIgnorePrivateNetworkAccessErrors()),
-      tainted_, net::NetworkTrafficAnnotationTag(traffic_annotation_),
+      GetPrivateNetworkAccessPreflightBehavior(), tainted_,
+      net::NetworkTrafficAnnotationTag(traffic_annotation_),
       network_loader_factory_, isolation_info_, CloneClientSecurityState(),
       std::move(devtools_observer), net_log_);
 }
@@ -774,7 +773,7 @@
     status->target_address_space = request_.target_ip_address_space;
   }
 
-  if (should_ignore_preflight_errors_) {
+  if (sending_pna_only_warning_preflight_) {
     // Even if we ignore the error, record the warning in metrics and DevTools.
     base::UmaHistogramEnumeration(kPreflightWarningHistogramName,
                                   histogram_error);
@@ -888,7 +887,7 @@
       // preflight. Otherwise, if we had sent a preflight before we noticed the
       // private network access, then we rely on `PreflightController` to ignore
       // PNA-specific preflight errors during this second preflight request.
-      should_ignore_preflight_errors_ =
+      sending_pna_only_warning_preflight_ =
           ShouldIgnorePrivateNetworkAccessErrors() &&
           !(NeedsPreflight(request_).has_value() && fetch_cors_flag_);
 
@@ -1028,6 +1027,17 @@
                       mojom::PrivateNetworkRequestPolicy::kPreflightWarn;
 }
 
+PrivateNetworkAccessPreflightBehavior
+CorsURLLoader::GetPrivateNetworkAccessPreflightBehavior() const {
+  if (!ShouldIgnorePrivateNetworkAccessErrors()) {
+    return PrivateNetworkAccessPreflightBehavior::kEnforce;
+  }
+  if (sending_pna_only_warning_preflight_) {
+    return PrivateNetworkAccessPreflightBehavior::kWarnWithTimeout;
+  }
+  return PrivateNetworkAccessPreflightBehavior::kWarn;
+}
+
 // static
 absl::optional<std::string> CorsURLLoader::GetHeaderString(
     const mojom::URLResponseHead& response,
diff --git a/services/network/cors/cors_url_loader.h b/services/network/cors/cors_url_loader.h
index 9c52e2d..3347f50 100644
--- a/services/network/cors/cors_url_loader.h
+++ b/services/network/cors/cors_url_loader.h
@@ -165,9 +165,20 @@
   // This is used to soft-launch Private Network Access preflights: we send
   // preflights but do not require them to succeed.
   //
-  // TODO(https://crbug.com/1268378): Remove this once it never returns true.
+  // TODO(https://crbug.com/1268378): Remove this once preflight enforcement
+  // is enabled.
   bool ShouldIgnorePrivateNetworkAccessErrors() const;
 
+  // Returns the PNA-specific behavior to apply to the next preflight request.
+  //
+  // This is used to soft-launch Private Network Access preflights: we send
+  // preflights but do not require them to succeed.
+  //
+  // TODO(https://crbug.com/1268378): Remove this once preflight enforcement
+  // is enabled.
+  PrivateNetworkAccessPreflightBehavior
+  GetPrivateNetworkAccessPreflightBehavior() const;
+
   static absl::optional<std::string> GetHeaderString(
       const mojom::URLResponseHead& response,
       const std::string& header_name);
@@ -260,6 +271,9 @@
 
   bool has_authorization_covered_by_wildcard_ = false;
 
+  // Whether the current preflight request is 1) solely sent for PNA, not for
+  // CORS and PNA at the same time, and 2) in warning mode.
+  //
   // If set to true, then any and all errors raised by subsequent preflight
   // requests are ignored.
   //
@@ -272,8 +286,8 @@
   // `ShouldIgnorePrivateNetworkAccessErrors()` is also true.
   //
   // TODO(https://crbug.com/1268378): Remove this along with
-  // `should_ignore_private_network_access_errors_`.
-  bool should_ignore_preflight_errors_ = false;
+  // `ShouldIgnorePrivateNetworkAccessErrors()`.
+  bool sending_pna_only_warning_preflight_ = false;
 
   mojo::Remote<mojom::DevToolsObserver> devtools_observer_;
 
diff --git a/services/network/cors/cors_url_loader_private_network_access_unittest.cc b/services/network/cors/cors_url_loader_private_network_access_unittest.cc
index 2ba3f0b5..088b38c 100644
--- a/services/network/cors/cors_url_loader_private_network_access_unittest.cc
+++ b/services/network/cors/cors_url_loader_private_network_access_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "services/network/cors/cors_url_loader.h"
 
+#include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "net/base/net_errors.h"
 #include "services/network/cors/cors_url_loader_test_util.h"
@@ -652,9 +653,13 @@
             mojom::IPAddressSpace::kPublic);
 }
 
-// This test verifies that not having response for preflight will cause
-// PreflightLoader time out and the real request will continue being sent
-// under kPreflightWarn mode.
+// This test verifies that when:
+//
+//  - the private network request policy is set to `kPreflightWarn`
+//  - a simple request detects a private network request
+//  - the following PNA preflight response takes forever to arrive
+//
+// ... a short timeout is applied, the error ignored and the request proceeds.
 TEST_F(CorsURLLoaderPrivateNetworkAccessTest, PolicyWarnSimpleTimeout) {
   auto initiator_origin = url::Origin::Create(GURL("https://example.com"));
 
@@ -724,6 +729,76 @@
 // This test verifies that when:
 //
 //  - the private network request policy is set to `kPreflightWarn`
+//  - a CORS preflight request detects a private network request
+//  - the following PNA preflight response takes forever to arrive
+//
+// ... we wait as long as it takes for the response to arrive.
+TEST_F(CorsURLLoaderPrivateNetworkAccessTest, PolicyWarnPreflightNoTimeout) {
+  auto initiator_origin = url::Origin::Create(GURL("https://example.com"));
+
+  ResetFactoryParams factory_params;
+  factory_params.is_trusted = true;
+  ResetFactory(initiator_origin, kRendererProcessId, factory_params);
+
+  MockDevToolsObserver devtools_observer;
+  ResourceRequest request;
+  request.method = "PUT";
+  request.mode = mojom::RequestMode::kCors;
+  request.url = GURL("https://other.example/");
+  request.request_initiator = initiator_origin;
+  request.trusted_params =
+      RequestTrustedParamsBuilder()
+          .WithClientSecurityState(
+              ClientSecurityStateBuilder()
+                  .WithPrivateNetworkRequestPolicy(
+                      mojom::PrivateNetworkRequestPolicy::kPreflightWarn)
+                  .WithIsSecureContext(true)
+                  .WithIPAddressSpace(mojom::IPAddressSpace::kPublic)
+                  .Build())
+          .WithDevToolsObserver(devtools_observer.Bind())
+          .Build();
+
+  base::HistogramTester histogram_tester;
+
+  CreateLoaderAndStart(request);
+  RunUntilCreateLoaderAndStartCalled();
+  NotifyLoaderClientOnComplete(CorsErrorStatus(
+      mojom::CorsError::kUnexpectedPrivateNetworkAccess,
+      mojom::IPAddressSpace::kUnknown, mojom::IPAddressSpace::kPrivate));
+
+  RunUntilCreateLoaderAndStartCalled();
+
+  // Simulate a response that takes longer to arrive than the preflight timeout.
+  // This should still work, because the timeout should not be applied.
+  base::OneShotTimer timer;
+  timer.Start(FROM_HERE, base::Milliseconds(500),
+              base::BindLambdaForTesting([this] {
+                NotifyLoaderClientOnReceiveResponse({
+                    {"Access-Control-Allow-Methods", "PUT"},
+                    {"Access-Control-Allow-Origin", "https://example.com"},
+                    {"Access-Control-Allow-Credentials", "true"},
+                    {"Access-Control-Allow-Private-Network", "true"},
+                });
+              }));
+
+  RunUntilCreateLoaderAndStartCalled();
+  NotifyLoaderClientOnReceiveResponse({
+      {"Access-Control-Allow-Methods", "PUT"},
+      {"Access-Control-Allow-Origin", "https://example.com"},
+      {"Access-Control-Allow-Credentials", "true"},
+  });
+  NotifyLoaderClientOnComplete(net::OK);
+  RunUntilComplete();
+
+  EXPECT_EQ(client().completion_status().error_code, net::OK);
+
+  EXPECT_THAT(histogram_tester.GetAllSamples(kPreflightWarningHistogramName),
+              IsEmpty());
+}
+
+// This test verifies that when:
+//
+//  - the private network request policy is set to `kPreflightWarn`
 //  - a simple request detects a private network request
 //  - the following PNA preflight fails due to a non-PNA CORS error
 //
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index 8015618..f24e526 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -58,6 +58,19 @@
   return header_value;
 }
 
+bool ShouldEnforcePrivateNetworkAccessHeader(
+    PrivateNetworkAccessPreflightBehavior behavior) {
+  // Use a switch statement to guarantee this is updated when the enum
+  // definition changes.
+  switch (behavior) {
+    case PrivateNetworkAccessPreflightBehavior::kEnforce:
+      return true;
+    case PrivateNetworkAccessPreflightBehavior::kWarnWithTimeout:
+    case PrivateNetworkAccessPreflightBehavior::kWarn:
+      return false;
+  }
+}
+
 // Algorithm step 3 of the CORS-preflight fetch,
 // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0, that requires
 //  - CORS-safelisted request-headers excluded
@@ -301,8 +314,7 @@
     const mojom::URLResponseHead& head,
     const ResourceRequest& original_request,
     bool tainted,
-    PreflightController::EnforcePrivateNetworkAccessHeader
-        enforce_private_network_access_header,
+    PrivateNetworkAccessPreflightBehavior private_network_access_behavior,
     const mojom::ClientSecurityStatePtr& client_security_state,
     mojom::DevToolsObserver* devtools_observer,
     absl::optional<CorsErrorStatus>* detected_error_status) {
@@ -321,7 +333,8 @@
   absl::optional<CorsErrorStatus> status =
       CheckAllowPrivateNetworkHeader(head, original_request);
   if (status) {
-    if (enforce_private_network_access_header) {
+    if (ShouldEnforcePrivateNetworkAccessHeader(
+            private_network_access_behavior)) {
       *detected_error_status = std::move(status);
       return nullptr;
     }
@@ -380,7 +393,7 @@
       const ResourceRequest& request,
       WithTrustedHeaderClient with_trusted_header_client,
       NonWildcardRequestHeadersSupport non_wildcard_request_headers_support,
-      EnforcePrivateNetworkAccessHeader enforce_private_network_access_header,
+      PrivateNetworkAccessPreflightBehavior private_network_access_behavior,
       bool tainted,
       const net::NetworkTrafficAnnotationTag& annotation_tag,
       const net::NetworkIsolationKey& network_isolation_key,
@@ -392,8 +405,7 @@
         original_request_(request),
         non_wildcard_request_headers_support_(
             non_wildcard_request_headers_support),
-        enforce_private_network_access_header_(
-            enforce_private_network_access_header),
+        private_network_access_behavior_(private_network_access_behavior),
         tainted_(tainted),
         network_isolation_key_(network_isolation_key),
         client_security_state_(std::move(client_security_state)),
@@ -425,10 +437,8 @@
     // should not wait around forever for a response. Certain servers never
     // respond, and that should not fail the overall request. Instead, we should
     // wait a short while then move on. See also https://crbug.com/1299382.
-    if (request.target_ip_address_space != mojom::IPAddressSpace::kUnknown &&
-        client_security_state_ &&
-        client_security_state_->private_network_request_policy ==
-            mojom::PrivateNetworkRequestPolicy::kPreflightWarn) {
+    if (private_network_access_behavior_ ==
+        PrivateNetworkAccessPreflightBehavior::kWarnWithTimeout) {
       loader_->SetTimeoutDuration(base::Milliseconds(100));
     }
   }
@@ -487,7 +497,7 @@
     bool has_authorization_covered_by_wildcard = false;
     std::unique_ptr<PreflightResult> result = CreatePreflightResult(
         final_url, head, original_request_, tainted_,
-        enforce_private_network_access_header_, client_security_state_,
+        private_network_access_behavior_, client_security_state_,
         devtools_observer_ ? devtools_observer_.get() : nullptr,
         &detected_error_status);
 
@@ -556,8 +566,7 @@
   const ResourceRequest original_request_;
 
   const NonWildcardRequestHeadersSupport non_wildcard_request_headers_support_;
-  const EnforcePrivateNetworkAccessHeader
-      enforce_private_network_access_header_;
+  const PrivateNetworkAccessPreflightBehavior private_network_access_behavior_;
   const bool tainted_;
   absl::optional<base::UnguessableToken> devtools_request_id_;
   const net::NetworkIsolationKey network_isolation_key_;
@@ -585,10 +594,10 @@
     const mojom::URLResponseHead& head,
     const ResourceRequest& original_request,
     bool tainted,
-    EnforcePrivateNetworkAccessHeader enforce_private_network_access_header,
+    PrivateNetworkAccessPreflightBehavior private_network_access_behavior,
     absl::optional<CorsErrorStatus>* detected_error_status) {
   return CreatePreflightResult(final_url, head, original_request, tainted,
-                               enforce_private_network_access_header,
+                               private_network_access_behavior,
                                /*client_security_state=*/nullptr,
                                /*devtools_observer=*/nullptr,
                                detected_error_status);
@@ -618,7 +627,7 @@
     const ResourceRequest& request,
     WithTrustedHeaderClient with_trusted_header_client,
     NonWildcardRequestHeadersSupport non_wildcard_request_headers_support,
-    EnforcePrivateNetworkAccessHeader enforce_private_network_access_header,
+    PrivateNetworkAccessPreflightBehavior private_network_access_behavior,
     bool tainted,
     const net::NetworkTrafficAnnotationTag& annotation_tag,
     mojom::URLLoaderFactory* loader_factory,
@@ -645,10 +654,9 @@
 
   auto emplaced_pair = loaders_.emplace(std::make_unique<PreflightLoader>(
       this, std::move(callback), request, with_trusted_header_client,
-      non_wildcard_request_headers_support,
-      enforce_private_network_access_header, tainted, annotation_tag,
-      network_isolation_key, std::move(client_security_state),
-      std::move(devtools_observer), net_log));
+      non_wildcard_request_headers_support, private_network_access_behavior,
+      tainted, annotation_tag, network_isolation_key,
+      std::move(client_security_state), std::move(devtools_observer), net_log));
   (*emplaced_pair.first)->Request(loader_factory);
 }
 
diff --git a/services/network/cors/preflight_controller.h b/services/network/cors/preflight_controller.h
index d0adfbd..7b247ac 100644
--- a/services/network/cors/preflight_controller.h
+++ b/services/network/cors/preflight_controller.h
@@ -39,6 +39,21 @@
 // Name of a histogram that records suppressed preflight errors, aka warnings.
 extern const char kPreflightWarningHistogramName[];
 
+// Dictates how the PreflightController should treat PNA preflights.
+//
+// TODO(https://crbug.com/1268378): Remove this once enforcement is always on.
+enum class PrivateNetworkAccessPreflightBehavior {
+  // Enforce the presence of PNA headers for PNA preflights.
+  kEnforce,
+
+  // Check for PNA headers, but do not fail the request in case of error.
+  // Instead, only report a warning to DevTools.
+  kWarn,
+
+  // Same as `kWarn`, also apply a short timeout to PNA preflights.
+  kWarnWithTimeout,
+};
+
 // A class to manage CORS-preflight, making a CORS-preflight request, checking
 // its result, and owning a CORS-preflight cache.
 class COMPONENT_EXPORT(NETWORK_SERVICE) PreflightController final {
@@ -65,7 +80,7 @@
       const mojom::URLResponseHead& head,
       const ResourceRequest& original_request,
       bool tainted,
-      EnforcePrivateNetworkAccessHeader enforce_private_network_access_header,
+      PrivateNetworkAccessPreflightBehavior private_network_access_behavior,
       absl::optional<CorsErrorStatus>* detected_error_status);
 
   // Checks CORS aceess on the CORS-preflight response parameters for testing.
@@ -92,7 +107,7 @@
       const ResourceRequest& resource_request,
       WithTrustedHeaderClient with_trusted_header_client,
       NonWildcardRequestHeadersSupport non_wildcard_request_headers_support,
-      EnforcePrivateNetworkAccessHeader enforce_private_network_access_header,
+      PrivateNetworkAccessPreflightBehavior private_network_access_behavior,
       bool tainted,
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
       mojom::URLLoaderFactory* loader_factory,
diff --git a/services/network/cors/preflight_controller_unittest.cc b/services/network/cors/preflight_controller_unittest.cc
index 0815105e..a8d77d3 100644
--- a/services/network/cors/preflight_controller_unittest.cc
+++ b/services/network/cors/preflight_controller_unittest.cc
@@ -46,8 +46,6 @@
 
 using ::testing::Optional;
 using WithTrustedHeaderClient = PreflightController::WithTrustedHeaderClient;
-using EnforcePrivateNetworkAccessHeader =
-    PreflightController::EnforcePrivateNetworkAccessHeader;
 
 TEST(PreflightControllerCreatePreflightRequestTest, LexicographicalOrder) {
   ResourceRequest request;
@@ -236,7 +234,7 @@
       base::BindOnce([](int, absl::optional<CorsErrorStatus>, bool) {}),
       request, WithTrustedHeaderClient(false),
       NonWildcardRequestHeadersSupport(false),
-      EnforcePrivateNetworkAccessHeader(false), /*tainted=*/false,
+      PrivateNetworkAccessPreflightBehavior::kWarn, /*tainted=*/false,
       TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, net::IsolationInfo(),
       /*client_security_state=*/nullptr,
       /*devtools_observer=*/mojo::NullRemote(), net_log);
@@ -245,7 +243,7 @@
       base::BindOnce([](int, absl::optional<CorsErrorStatus>, bool) {}),
       request, WithTrustedHeaderClient(true),
       NonWildcardRequestHeadersSupport(false),
-      EnforcePrivateNetworkAccessHeader(false), /*tainted=*/false,
+      PrivateNetworkAccessPreflightBehavior::kWarn, /*tainted=*/false,
       TRAFFIC_ANNOTATION_FOR_TESTS, &url_loader_factory, net::IsolationInfo(),
       /*client_security_state=*/nullptr,
       /*devtools_observer=*/mojo::NullRemote(), net_log);
@@ -453,8 +451,8 @@
       const ResourceRequest& request,
       bool tainted = false,
       net::IsolationInfo isolation_info = net::IsolationInfo(),
-      EnforcePrivateNetworkAccessHeader enforce_private_network_access_header =
-          EnforcePrivateNetworkAccessHeader(false),
+      PrivateNetworkAccessPreflightBehavior private_network_access_behavior =
+          PrivateNetworkAccessPreflightBehavior::kWarn,
       mojom::ClientSecurityStatePtr client_security_state = nullptr) {
     DCHECK(preflight_controller_);
     run_loop_ = std::make_unique<base::RunLoop>();
@@ -462,9 +460,8 @@
         base::BindOnce(&PreflightControllerTest::HandleRequestCompletion,
                        base::Unretained(this)),
         request, WithTrustedHeaderClient(false),
-        non_wildcard_request_headers_support_,
-        enforce_private_network_access_header, tainted,
-        TRAFFIC_ANNOTATION_FOR_TESTS, url_loader_factory_remote_.get(),
+        non_wildcard_request_headers_support_, private_network_access_behavior,
+        tainted, TRAFFIC_ANNOTATION_FOR_TESTS, url_loader_factory_remote_.get(),
         isolation_info, std::move(client_security_state),
         devtools_observer_->Bind(),
         net::NetLogWithSource::Make(net::NetLog::Get(),
@@ -705,7 +702,7 @@
   std::unique_ptr<PreflightResult> result =
       PreflightController::CreatePreflightResultForTesting(
           url, response_head, request, tainted,
-          PreflightController::EnforcePrivateNetworkAccessHeader(true),
+          PrivateNetworkAccessPreflightBehavior::kEnforce,
           &detected_error_status);
 
   EXPECT_FALSE(result);
@@ -734,7 +731,7 @@
   request.trusted_params->client_security_state = client_security_state.Clone();
 
   PerformPreflightCheck(request, /*tainted=*/false, net::IsolationInfo(),
-                        EnforcePrivateNetworkAccessHeader(true),
+                        PrivateNetworkAccessPreflightBehavior::kEnforce,
                         std::move(client_security_state));
   EXPECT_EQ(net::ERR_FAILED, net_error());
 
@@ -758,7 +755,7 @@
 }
 
 TEST_F(PreflightControllerTest,
-       CheckPrivateNetworkAccessRequestPreflightWarnTimeout) {
+       CheckPrivateNetworkAccessRequestTimeoutBehaviorEnforce) {
   net::EmbeddedTestServer delayed_server;
   delayed_server.RegisterRequestHandler(
       base::BindRepeating(&AllowPrivateNetworkAccess));
@@ -786,13 +783,47 @@
   request.trusted_params->client_security_state = client_security_state.Clone();
 
   PerformPreflightCheck(request, /*tainted=*/false, net::IsolationInfo(),
-                        EnforcePrivateNetworkAccessHeader(true),
-                        std::move(client_security_state));
+                        PrivateNetworkAccessPreflightBehavior::kEnforce,
+                        /*client_security_state=*/nullptr);
+  EXPECT_EQ(net::OK, net_error());
+}
+
+TEST_F(PreflightControllerTest,
+       CheckPrivateNetworkAccessRequestTimeoutBehaviorWarnWithTimeout) {
+  net::EmbeddedTestServer delayed_server;
+  delayed_server.RegisterRequestHandler(
+      base::BindRepeating(&AllowPrivateNetworkAccess));
+  ASSERT_TRUE(delayed_server.Start());
+  ResourceRequest request;
+  request.method = std::string("GET");
+  GURL url = delayed_server.GetURL("/");
+  request.url = url;
+  request.request_initiator = url::Origin::Create(url);
+  request.mode = mojom::RequestMode::kCors;
+  request.credentials_mode = mojom::CredentialsMode::kOmit;
+  request.target_ip_address_space = network::mojom::IPAddressSpace::kLocal;
+
+  mojom::ClientSecurityStatePtr client_security_state =
+      ClientSecurityStateBuilder()
+          .WithPrivateNetworkRequestPolicy(
+              mojom::PrivateNetworkRequestPolicy::kPreflightWarn)
+          .Build();
+
+  // Set the client security state in the request's trusted params, because the
+  // test uses a shared factory with no client security state in its factory
+  // params, and URLLoader expects requests with a target IP address space to
+  // carry a client security state.
+  request.trusted_params = ResourceRequest::TrustedParams();
+  request.trusted_params->client_security_state = client_security_state.Clone();
+
+  PerformPreflightCheck(request, /*tainted=*/false, net::IsolationInfo(),
+                        PrivateNetworkAccessPreflightBehavior::kWarnWithTimeout,
+                        /*client_security_state=*/nullptr);
   EXPECT_EQ(net::ERR_TIMED_OUT, net_error());
 }
 
 TEST_F(PreflightControllerTest,
-       CheckPrivateNetworkAccessRequestPreflightBlockTimeout) {
+       CheckPrivateNetworkAccessRequestPreflightTimeoutBehaviorWarn) {
   net::EmbeddedTestServer delayed_server;
   delayed_server.RegisterRequestHandler(
       base::BindRepeating(&AllowPrivateNetworkAccess));
@@ -820,8 +851,8 @@
   request.trusted_params->client_security_state = client_security_state.Clone();
 
   PerformPreflightCheck(request, /*tainted=*/false, net::IsolationInfo(),
-                        EnforcePrivateNetworkAccessHeader(true),
-                        std::move(client_security_state));
+                        PrivateNetworkAccessPreflightBehavior::kWarn,
+                        /*client_security_state=*/nullptr);
   EXPECT_EQ(net::OK, net_error());
 }
 
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 2ceb81f..37012af 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -1649,6 +1649,8 @@
       string fontWeight
       # The font-stretch.
       string fontStretch
+      # The font-display.
+      string fontDisplay
       # The unicode-range.
       string unicodeRange
       # The src.
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 516ad9c..87c9a8e 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1401,55 +1401,6 @@
     "page/validation_message_overlay_delegate_test.cc",
     "page/viewport_test.cc",
     "page/window_features_test.cc",
-    "paint/block_painter_test.cc",
-    "paint/box_paint_invalidator_test.cc",
-    "paint/box_painter_test.cc",
-    "paint/clip_path_clipper_test.cc",
-    "paint/clip_rect_test.cc",
-    "paint/compositing/compositing_reason_finder_test.cc",
-    "paint/compositing/compositing_test.cc",
-    "paint/css_mask_painter_test.cc",
-    "paint/cull_rect_updater_test.cc",
-    "paint/first_meaningful_paint_detector_test.cc",
-    "paint/fragment_data_test.cc",
-    "paint/highlight_painting_utils_test.cc",
-    "paint/html_canvas_painter_test.cc",
-    "paint/image_element_timing_test.cc",
-    "paint/image_paint_timing_detector_test.cc",
-    "paint/inline_text_box_painter_test.cc",
-    "paint/largest_contentful_paint_calculator_test.cc",
-    "paint/link_highlight_impl_test.cc",
-    "paint/ng/ng_box_fragment_painter_test.cc",
-    "paint/ng/ng_highlight_overlay_test.cc",
-    "paint/ng/ng_inline_paint_context_test.cc",
-    "paint/ng/ng_text_fragment_painter_test.cc",
-    "paint/nine_piece_image_grid_test.cc",
-    "paint/object_paint_invalidator_test.cc",
-    "paint/outline_painter_test.cc",
-    "paint/paint_and_raster_invalidation_test.cc",
-    "paint/paint_and_raster_invalidation_test.h",
-    "paint/paint_auto_dark_mode_test.cc",
-    "paint/paint_controller_paint_test.cc",
-    "paint/paint_controller_paint_test.h",
-    "paint/paint_layer_clipper_test.cc",
-    "paint/paint_layer_painter_test.cc",
-    "paint/paint_layer_scrollable_area_test.cc",
-    "paint/paint_layer_test.cc",
-    "paint/paint_property_tree_builder_test.cc",
-    "paint/paint_property_tree_builder_test.h",
-    "paint/paint_property_tree_printer_test.cc",
-    "paint/paint_property_tree_update_tests.cc",
-    "paint/paint_timing_test_helper.h",
-    "paint/pre_paint_tree_walk_test.cc",
-    "paint/scrollable_area_painter_test.cc",
-    "paint/selection_bounds_recorder_test.cc",
-    "paint/svg_container_painter_test.cc",
-    "paint/table_painter_test.cc",
-    "paint/text_paint_timing_detector_test.cc",
-    "paint/text_painter_test.cc",
-    "paint/text_selection_repaint_test.cc",
-    "paint/video_painter_test.cc",
-    "paint/view_painter_test.cc",
     "permissions_policy/document_policy_parser_test.cc",
     "permissions_policy/document_policy_sim_test.cc",
     "permissions_policy/permissions_policy_devtools_support_test.cc",
@@ -1471,18 +1422,6 @@
     "speculation_rules/speculation_rules_origin_trial_test.cc",
     "speculation_rules/stub_speculation_host.cc",
     "speculation_rules/stub_speculation_host.h",
-    "svg/animation/priority_queue_test.cc",
-    "svg/animation/smil_time_container_test.cc",
-    "svg/animation/svg_smil_element_test.cc",
-    "svg/graphics/svg_image_test.cc",
-    "svg/svg_element_test.cc",
-    "svg/svg_foreign_object_element_test.cc",
-    "svg/svg_path_parser_test.cc",
-    "svg/svg_path_query_test.cc",
-    "svg/svg_resource_document_content_test.cc",
-    "svg/svg_text_content_element_test.cc",
-    "svg/svg_use_element_test.cc",
-    "svg/unsafe_svg_attribute_sanitization_test.cc",
     "timing/background_tracing_helper_test.cc",
     "timing/memory_info_test.cc",
     "timing/performance_entry_test.cc",
@@ -1524,9 +1463,11 @@
   sources += rebase_path(blink_core_tests_html, "", "html")
   sources += rebase_path(blink_core_tests_layout, "", "layout")
   sources += rebase_path(blink_core_tests_loader, "", "loader")
+  sources += rebase_path(blink_core_tests_paint, "", "paint")
   sources += rebase_path(blink_core_tests_scroll, "", "scroll")
   sources += rebase_path(blink_core_tests_streams, "", "streams")
   sources += rebase_path(blink_core_tests_style, "", "style")
+  sources += rebase_path(blink_core_tests_svg, "", "svg")
 
   if (is_debug || dcheck_always_on) {
     sources += [ "exported/web_disallow_transition_scope_test.cc" ]
diff --git a/third_party/blink/renderer/core/css/media_feature_overrides.cc b/third_party/blink/renderer/core/css/media_feature_overrides.cc
index ca391b6..36fcd42 100644
--- a/third_party/blink/renderer/core/css/media_feature_overrides.cc
+++ b/third_party/blink/renderer/core/css/media_feature_overrides.cc
@@ -4,17 +4,70 @@
 
 #include "third_party/blink/renderer/core/css/media_feature_overrides.h"
 
+#include "third_party/blink/renderer/core/css/media_features.h"
+#include "third_party/blink/renderer/core/css/media_query_exp.h"
+#include "third_party/blink/renderer/core/css/media_values.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_token_range.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
+#include "third_party/blink/renderer/platform/graphics/color_space_gamut.h"
 
 namespace blink {
 
-void MediaFeatureOverrides::Trace(Visitor* visitor) const {
-  visitor->Trace(overrides_);
+namespace {
+
+absl::optional<ColorSpaceGamut> ConvertColorGamut(
+    const MediaQueryExpValue& value) {
+  if (!value.IsValid())
+    return absl::nullopt;
+  if (value.Id() == CSSValueID::kSRGB)
+    return ColorSpaceGamut::SRGB;
+  if (value.Id() == CSSValueID::kP3)
+    return ColorSpaceGamut::P3;
+  // Rec. 2020 is also known as ITU-R-Empfehlung BT.2020.
+  if (value.Id() == CSSValueID::kRec2020)
+    return ColorSpaceGamut::BT2020;
+  return absl::nullopt;
 }
 
+absl::optional<mojom::blink::PreferredColorScheme> ConvertPreferredColorScheme(
+    const MediaQueryExpValue& value) {
+  if (!value.IsValid())
+    return absl::nullopt;
+  return CSSValueIDToPreferredColorScheme(value.Id());
+}
+
+absl::optional<mojom::blink::PreferredContrast> ConvertPreferredContrast(
+    const MediaQueryExpValue& value) {
+  if (!value.IsValid())
+    return absl::nullopt;
+  return CSSValueIDToPreferredContrast(value.Id());
+}
+
+absl::optional<bool> ConvertPrefersReducedMotion(
+    const MediaQueryExpValue& value) {
+  if (!value.IsValid())
+    return absl::nullopt;
+  return value.Id() == CSSValueID::kReduce;
+}
+
+absl::optional<bool> ConvertPrefersReducedData(
+    const MediaQueryExpValue& value) {
+  if (!value.IsValid())
+    return absl::nullopt;
+  return value.Id() == CSSValueID::kReduce;
+}
+
+absl::optional<ForcedColors> ConvertForcedColors(
+    const MediaQueryExpValue& value) {
+  if (!value.IsValid())
+    return absl::nullopt;
+  return CSSValueIDToForcedColors(value.Id());
+}
+
+}  // namespace
+
 void MediaFeatureOverrides::SetOverride(const AtomicString& feature,
                                         const String& value_string) {
   CSSTokenizer tokenizer(value_string);
@@ -35,14 +88,24 @@
   // Note that once a real CSSParserContext is plumbed through we can use its
   // Document to get the ExecutionContext so the extra parameter should be
   // removed.
-  auto value =
-      MediaQueryExp::Create(feature, range, *fake_context, nullptr).ExpValue();
+  MediaQueryExpBounds bounds =
+      MediaQueryExp::Create(feature, range, *fake_context, nullptr).Bounds();
+  DCHECK(!bounds.left.IsValid());
+  MediaQueryExpValue value = bounds.right.value;
 
-  if (value.IsValid()) {
-    overrides_.Set(feature,
-                   MakeGarbageCollected<MediaQueryExpValueWrapper>(value));
-  } else {
-    overrides_.erase(feature);
+  if (feature == media_feature_names::kColorGamutMediaFeature) {
+    color_gamut_ = ConvertColorGamut(value);
+  } else if (feature == media_feature_names::kPrefersColorSchemeMediaFeature) {
+    preferred_color_scheme_ = ConvertPreferredColorScheme(value);
+  } else if (feature == media_feature_names::kPrefersContrastMediaFeature) {
+    preferred_contrast_ = ConvertPreferredContrast(value);
+  } else if (feature ==
+             media_feature_names::kPrefersReducedMotionMediaFeature) {
+    prefers_reduced_motion_ = ConvertPrefersReducedMotion(value);
+  } else if (feature == media_feature_names::kPrefersReducedDataMediaFeature) {
+    prefers_reduced_data_ = ConvertPrefersReducedData(value);
+  } else if (feature == media_feature_names::kForcedColorsMediaFeature) {
+    forced_colors_ = ConvertForcedColors(value);
   }
 }
 
diff --git a/third_party/blink/renderer/core/css/media_feature_overrides.h b/third_party/blink/renderer/core/css/media_feature_overrides.h
index 5864b94d..12315bdf 100644
--- a/third_party/blink/renderer/core/css/media_feature_overrides.h
+++ b/third_party/blink/renderer/core/css/media_feature_overrides.h
@@ -5,30 +5,48 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_MEDIA_FEATURE_OVERRIDES_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_MEDIA_FEATURE_OVERRIDES_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom-blink-forward.h"
+#include "third_party/blink/public/mojom/css/preferred_contrast.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/css/media_query_exp.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
-#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
 namespace blink {
 
-class CORE_EXPORT MediaFeatureOverrides
-    : public GarbageCollected<MediaFeatureOverrides> {
+enum class ColorSpaceGamut;
+enum class ForcedColors;
+
+class CORE_EXPORT MediaFeatureOverrides {
+  USING_FAST_MALLOC(MediaFeatureOverrides);
+
  public:
-  void Trace(Visitor*) const;
   void SetOverride(const AtomicString& feature, const String& value_string);
-  MediaQueryExpValue GetOverride(const AtomicString& feature) const {
-    auto it = overrides_.find(feature);
-    return it != overrides_.end() ? it->value->value : MediaQueryExpValue();
+
+  absl::optional<ColorSpaceGamut> GetColorGamut() const { return color_gamut_; }
+  absl::optional<mojom::blink::PreferredColorScheme> GetPreferredColorScheme()
+      const {
+    return preferred_color_scheme_;
+  }
+  absl::optional<mojom::blink::PreferredContrast> GetPreferredContrast() const {
+    return preferred_contrast_;
+  }
+  absl::optional<bool> GetPrefersReducedMotion() const {
+    return prefers_reduced_motion_;
+  }
+  absl::optional<bool> GetPrefersReducedData() const {
+    return prefers_reduced_data_;
+  }
+  absl::optional<ForcedColors> GetForcedColors() const {
+    return forced_colors_;
   }
 
  private:
-  // TODO(crbug.com/1312000): HeapHashMap does not support storing
-  // MediaQueryExpValue (DISALLOW_NEW) directly. Refactor to avoid the
-  // wrapper.
-  HeapHashMap<AtomicString, Member<MediaQueryExpValueWrapper>> overrides_;
+  absl::optional<ColorSpaceGamut> color_gamut_;
+  absl::optional<mojom::blink::PreferredColorScheme> preferred_color_scheme_;
+  absl::optional<mojom::blink::PreferredContrast> preferred_contrast_;
+  absl::optional<bool> prefers_reduced_motion_;
+  absl::optional<bool> prefers_reduced_data_;
+  absl::optional<ForcedColors> forced_colors_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_feature_overrides_test.cc b/third_party/blink/renderer/core/css/media_feature_overrides_test.cc
index 925bb93a..c886baaf 100644
--- a/third_party/blink/renderer/core/css/media_feature_overrides_test.cc
+++ b/third_party/blink/renderer/core/css/media_feature_overrides_test.cc
@@ -5,55 +5,51 @@
 #include "third_party/blink/renderer/core/css/media_feature_overrides.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom-blink.h"
 
 namespace blink {
 
 TEST(MediaFeatureOverrides, GetOverrideInitial) {
   MediaFeatureOverrides overrides;
 
-  EXPECT_FALSE(overrides.GetOverride("unknown").IsValid());
-  EXPECT_FALSE(overrides.GetOverride("prefers-color-scheme").IsValid());
-  EXPECT_FALSE(overrides.GetOverride("display-mode").IsValid());
+  EXPECT_FALSE(overrides.GetColorGamut().has_value());
+  EXPECT_FALSE(overrides.GetPreferredColorScheme().has_value());
 }
 
 TEST(MediaFeatureOverrides, SetOverrideInvalid) {
   MediaFeatureOverrides overrides;
 
   overrides.SetOverride("prefers-color-scheme", "1px");
-  EXPECT_FALSE(overrides.GetOverride("prefers-color-scheme").IsValid());
+  EXPECT_FALSE(overrides.GetPreferredColorScheme().has_value());
 
   overrides.SetOverride("prefers-color-scheme", "orange");
-  EXPECT_FALSE(overrides.GetOverride("prefers-color-scheme").IsValid());
+  EXPECT_FALSE(overrides.GetPreferredColorScheme().has_value());
 }
 
 TEST(MediaFeatureOverrides, SetOverrideValid) {
   MediaFeatureOverrides overrides;
 
   overrides.SetOverride("prefers-color-scheme", "light");
-  auto light_override = overrides.GetOverride("prefers-color-scheme");
-  EXPECT_TRUE(light_override.IsValid());
-  ASSERT_TRUE(light_override.IsId());
-  EXPECT_EQ(CSSValueID::kLight, light_override.Id());
+  EXPECT_EQ(mojom::blink::PreferredColorScheme::kLight,
+            overrides.GetPreferredColorScheme());
 
   overrides.SetOverride("prefers-color-scheme", "dark");
-  auto dark_override = overrides.GetOverride("prefers-color-scheme");
-  ASSERT_TRUE(dark_override.IsValid());
-  EXPECT_TRUE(dark_override.IsId());
-  EXPECT_EQ(CSSValueID::kDark, dark_override.Id());
+  EXPECT_EQ(mojom::blink::PreferredColorScheme::kDark,
+            overrides.GetPreferredColorScheme());
 }
 
 TEST(MediaFeatureOverrides, ResetOverride) {
   MediaFeatureOverrides overrides;
 
   overrides.SetOverride("prefers-color-scheme", "light");
-  EXPECT_TRUE(overrides.GetOverride("prefers-color-scheme").IsValid());
+  EXPECT_TRUE(overrides.GetPreferredColorScheme().has_value());
   overrides.SetOverride("prefers-color-scheme", "");
-  EXPECT_FALSE(overrides.GetOverride("prefers-color-scheme").IsValid());
+  EXPECT_FALSE(overrides.GetPreferredColorScheme().has_value());
 
   overrides.SetOverride("prefers-color-scheme", "light");
-  EXPECT_TRUE(overrides.GetOverride("prefers-color-scheme").IsValid());
+  EXPECT_TRUE(overrides.GetPreferredColorScheme().has_value());
   overrides.SetOverride("prefers-color-scheme", "invalid");
-  EXPECT_FALSE(overrides.GetOverride("prefers-color-scheme").IsValid());
+  EXPECT_FALSE(overrides.GetPreferredColorScheme().has_value());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index 9794d35..627c0d0 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -506,9 +506,9 @@
   // <mf-plain>  e.g. (width: 100px)
   if (!bounds_.IsRange()) {
     result.Append(name);
-    if (ExpValue().IsValid()) {
+    if (bounds_.right.IsValid()) {
       result.Append(": ");
-      result.Append(ExpValue().CssText());
+      result.Append(bounds_.right.value.CssText());
     }
   } else {
     if (bounds_.left.IsValid()) {
diff --git a/third_party/blink/renderer/core/css/media_query_exp.h b/third_party/blink/renderer/core/css/media_query_exp.h
index f6381f0..d9f56a2 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.h
+++ b/third_party/blink/renderer/core/css/media_query_exp.h
@@ -175,16 +175,6 @@
   kGe,
 };
 
-class CORE_EXPORT MediaQueryExpValueWrapper
-    : public GarbageCollected<MediaQueryExpValueWrapper> {
- public:
-  explicit MediaQueryExpValueWrapper(MediaQueryExpValue value) : value(value) {}
-
-  void Trace(Visitor* visitor) const { visitor->Trace(value); }
-
-  const MediaQueryExpValue value;
-};
-
 // This represents the following part of a <media-feature> (example):
 //
 //  (width >= 10px)
@@ -278,12 +268,6 @@
 
   const String& MediaFeature() const { return media_feature_; }
 
-  // TODO(crbug.com/1034465): Replace with MediaQueryExpBounds.
-  MediaQueryExpValue ExpValue() const {
-    DCHECK(!bounds_.left.IsValid());
-    return bounds_.right.value;
-  }
-
   const MediaQueryExpBounds& Bounds() const { return bounds_; }
 
   bool IsValid() const { return !media_feature_.IsNull(); }
diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc
index 9559c1a8..362dbb8 100644
--- a/third_party/blink/renderer/core/css/media_values.cc
+++ b/third_party/blink/renderer/core/css/media_values.cc
@@ -298,21 +298,12 @@
 ColorSpaceGamut MediaValues::CalculateColorGamut(LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->GetPage());
-  if (const auto* overrides = frame->GetPage()->GetMediaFeatureOverrides()) {
-    MediaQueryExpValue value = overrides->GetOverride("color-gamut");
-    if (value.IsValid()) {
-      if (value.Id() == CSSValueID::kSRGB)
-        return ColorSpaceGamut::SRGB;
-      if (value.Id() == CSSValueID::kP3)
-        return ColorSpaceGamut::P3;
-      // Rec. 2020 is also known as ITU-R-Empfehlung BT.2020.
-      if (value.Id() == CSSValueID::kRec2020)
-        return ColorSpaceGamut::BT2020;
-      NOTREACHED();
-    }
-  }
-  return color_space_utilities::GetColorSpaceGamut(
-      frame->GetPage()->GetChromeClient().GetScreenInfo(*frame));
+  const MediaFeatureOverrides* overrides =
+      frame->GetPage()->GetMediaFeatureOverrides();
+  absl::optional<ColorSpaceGamut> override_value =
+      overrides ? overrides->GetColorGamut() : absl::nullopt;
+  return override_value.value_or(color_space_utilities::GetColorSpaceGamut(
+      frame->GetPage()->GetChromeClient().GetScreenInfo(*frame)));
 }
 
 mojom::blink::PreferredColorScheme MediaValues::CalculatePreferredColorScheme(
@@ -321,12 +312,12 @@
   DCHECK(frame->GetSettings());
   DCHECK(frame->GetDocument());
   DCHECK(frame->GetPage());
-  if (const auto* overrides = frame->GetPage()->GetMediaFeatureOverrides()) {
-    MediaQueryExpValue value = overrides->GetOverride("prefers-color-scheme");
-    if (value.IsValid())
-      return CSSValueIDToPreferredColorScheme(value.Id());
-  }
-  return frame->GetDocument()->GetStyleEngine().GetPreferredColorScheme();
+  const MediaFeatureOverrides* overrides =
+      frame->GetPage()->GetMediaFeatureOverrides();
+  absl::optional<mojom::blink::PreferredColorScheme> override_value =
+      overrides ? overrides->GetPreferredColorScheme() : absl::nullopt;
+  return override_value.value_or(
+      frame->GetDocument()->GetStyleEngine().GetPreferredColorScheme());
 }
 
 mojom::blink::PreferredContrast MediaValues::CalculatePreferredContrast(
@@ -334,45 +325,43 @@
   DCHECK(frame);
   DCHECK(frame->GetSettings());
   DCHECK(frame->GetPage());
-  if (const auto* overrides = frame->GetPage()->GetMediaFeatureOverrides()) {
-    MediaQueryExpValue value = overrides->GetOverride("prefers-contrast");
-    if (value.IsValid())
-      return CSSValueIDToPreferredContrast(value.Id());
-  }
-  return frame->GetSettings()->GetPreferredContrast();
+  const MediaFeatureOverrides* overrides =
+      frame->GetPage()->GetMediaFeatureOverrides();
+  absl::optional<mojom::blink::PreferredContrast> override_value =
+      overrides ? overrides->GetPreferredContrast() : absl::nullopt;
+  return override_value.value_or(frame->GetSettings()->GetPreferredContrast());
 }
 
 bool MediaValues::CalculatePrefersReducedMotion(LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->GetSettings());
-  if (const auto* overrides = frame->GetPage()->GetMediaFeatureOverrides()) {
-    MediaQueryExpValue value = overrides->GetOverride("prefers-reduced-motion");
-    if (value.IsValid())
-      return value.Id() == CSSValueID::kReduce;
-  }
-  return frame->GetSettings()->GetPrefersReducedMotion();
+  const MediaFeatureOverrides* overrides =
+      frame->GetPage()->GetMediaFeatureOverrides();
+  absl::optional<bool> override_value =
+      overrides ? overrides->GetPrefersReducedMotion() : absl::nullopt;
+  return override_value.value_or(
+      frame->GetSettings()->GetPrefersReducedMotion());
 }
 
 bool MediaValues::CalculatePrefersReducedData(LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->GetSettings());
-  if (const auto* overrides = frame->GetPage()->GetMediaFeatureOverrides()) {
-    MediaQueryExpValue value = overrides->GetOverride("prefers-reduced-data");
-    if (value.IsValid())
-      return value.Id() == CSSValueID::kReduce;
-  }
-  return GetNetworkStateNotifier().SaveDataEnabled();
+  const MediaFeatureOverrides* overrides =
+      frame->GetPage()->GetMediaFeatureOverrides();
+  absl::optional<bool> override_value =
+      overrides ? overrides->GetPrefersReducedData() : absl::nullopt;
+  return override_value.value_or(GetNetworkStateNotifier().SaveDataEnabled());
 }
 
 ForcedColors MediaValues::CalculateForcedColors(LocalFrame* frame) {
   DCHECK(frame);
   DCHECK(frame->GetSettings());
-  if (const auto* overrides = frame->GetPage()->GetMediaFeatureOverrides()) {
-    MediaQueryExpValue value = overrides->GetOverride("forced-colors");
-    if (value.IsValid())
-      return CSSValueIDToForcedColors(value.Id());
-  }
-  return WebThemeEngineHelper::GetNativeThemeEngine()->GetForcedColors();
+  const MediaFeatureOverrides* overrides =
+      frame->GetPage()->GetMediaFeatureOverrides();
+  absl::optional<ForcedColors> override_value =
+      overrides ? overrides->GetForcedColors() : absl::nullopt;
+  return override_value.value_or(
+      WebThemeEngineHelper::GetNativeThemeEngine()->GetForcedColors());
 }
 
 NavigationControls MediaValues::CalculateNavigationControls(LocalFrame* frame) {
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h
index 3abad40..8ab2547 100644
--- a/third_party/blink/renderer/core/css/media_values.h
+++ b/third_party/blink/renderer/core/css/media_values.h
@@ -30,6 +30,7 @@
 
 mojom::blink::PreferredColorScheme CSSValueIDToPreferredColorScheme(
     CSSValueID id);
+mojom::blink::PreferredContrast CSSValueIDToPreferredContrast(CSSValueID);
 ForcedColors CSSValueIDToForcedColors(CSSValueID id);
 
 class CORE_EXPORT MediaValues : public GarbageCollected<MediaValues>,
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
index cc17be6..4fb23fe 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1847,6 +1847,8 @@
 
       // If the child style is a cache hit, we'll never reach StyleBuilder::
       // ApplyProperty, hence we'll never set the flag on the parent.
+      // (We do the same thing for independently inherited properties in
+      // Element::RecalcOwnStyle().)
       if (state.Style()->HasExplicitInheritance())
         state.ParentStyle()->SetChildHasExplicitInheritance();
       is_non_inherited_cache_hit = true;
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 066d50e..f51b8878 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3077,16 +3077,14 @@
 
   if (const auto* overrides =
           GetDocument().GetPage()->GetMediaFeatureOverrides()) {
-    MediaQueryExpValue forced_colors_value =
-        overrides->GetOverride("forced-colors");
-    if (forced_colors_value.IsValid())
-      forced_colors_ = CSSValueIDToForcedColors(forced_colors_value.Id());
-
-    MediaQueryExpValue preferred_color_scheme_value =
-        overrides->GetOverride("prefers-color-scheme");
-    if (preferred_color_scheme_value.IsValid()) {
-      preferred_color_scheme_ =
-          CSSValueIDToPreferredColorScheme(preferred_color_scheme_value.Id());
+    if (absl::optional<ForcedColors> forced_color_override =
+            overrides->GetForcedColors()) {
+      forced_colors_ = forced_color_override.value();
+    }
+    if (absl::optional<mojom::blink::PreferredColorScheme>
+            preferred_color_scheme_override =
+                overrides->GetPreferredColorScheme()) {
+      preferred_color_scheme_ = preferred_color_scheme_override.value();
     }
   }
 
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
index d3c8800..016090c 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.cc
@@ -173,6 +173,10 @@
   return ScopedForceActivatableDisplayLocks(this);
 }
 
+bool DisplayLockDocumentState::HasActivatableLocks() const {
+  return LockedDisplayLockCount() != DisplayLockBlockingAllActivationCount();
+}
+
 bool DisplayLockDocumentState::ActivatableDisplayLocksForced() const {
   return activatable_display_locks_forced_;
 }
@@ -396,7 +400,7 @@
 void DisplayLockDocumentState::UnlockShapingDeferredElements() {
   if (!RuntimeEnabledFeatures::DeferredShapingEnabled())
     return;
-  if (LockedDisplayLockCount() == DisplayLockBlockingAllActivationCount())
+  if (!HasActivatableLocks())
     return;
 
   size_t count = 0;
@@ -418,18 +422,18 @@
     CSSPropertyID property_id) {
   if (!RuntimeEnabledFeatures::DeferredShapingEnabled())
     return;
-  if (LockedDisplayLockCount() == DisplayLockBlockingAllActivationCount())
+  if (!HasActivatableLocks())
     return;
   // Need to update layout tree because we access the tree and style.
   target.GetDocument().UpdateStyleAndLayoutTreeForNode(&target);
-  if (LockedDisplayLockCount() == DisplayLockBlockingAllActivationCount())
+  if (!HasActivatableLocks())
     return;
   LayoutObject* target_object = target.GetLayoutObject();
   if (!target_object)
     return;
 
   UnlockShapingDeferredInclusiveDescendants(*target_object);
-  if (LockedDisplayLockCount() == DisplayLockBlockingAllActivationCount())
+  if (!HasActivatableLocks())
     return;
 
   const ComputedStyle& style = target_object->StyleRef();
@@ -516,7 +520,7 @@
     const LayoutObject& object) {
   if (!RuntimeEnabledFeatures::DeferredShapingEnabled())
     return;
-  if (LockedDisplayLockCount() == DisplayLockBlockingAllActivationCount())
+  if (!HasActivatableLocks())
     return;
 
   if (object.IsInline()) {
@@ -550,7 +554,7 @@
     const LayoutObject& object) {
   if (!RuntimeEnabledFeatures::DeferredShapingEnabled())
     return;
-  if (LockedDisplayLockCount() == DisplayLockBlockingAllActivationCount())
+  if (!HasActivatableLocks())
     return;
 
   if (object.IsInline()) {
@@ -569,7 +573,7 @@
     if ((style.PaddingTop().IsPercent() || style.PaddingBottom().IsPercent()) &&
         object.ContainingBlock()) {
       UnlockToDetermineWidth(*object.ContainingBlock());
-      if (LockedDisplayLockCount() == DisplayLockBlockingAllActivationCount())
+      if (!HasActivatableLocks())
         return;
     }
   }
@@ -584,7 +588,7 @@
 void DisplayLockDocumentState::UnlockShapingDeferredInclusiveDescendants(
     const LayoutObject& ancestor) {
   DCHECK(RuntimeEnabledFeatures::DeferredShapingEnabled());
-  DCHECK_NE(LockedDisplayLockCount(), DisplayLockBlockingAllActivationCount());
+  DCHECK(HasActivatableLocks());
 
   size_t count = 0;
   for (auto& context : display_lock_contexts_) {
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_document_state.h b/third_party/blink/renderer/core/display_lock/display_lock_document_state.h
index 776eda33..fecb603 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_document_state.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_document_state.h
@@ -52,6 +52,11 @@
   void RegisterDisplayLockActivationObservation(Element*);
   void UnregisterDisplayLockActivationObservation(Element*);
 
+  // Returns true if we have activatable locks.
+  // This compares LockedDisplayLockCount() and
+  // DisplayLockBlockingAllActivationCount().
+  bool HasActivatableLocks() const;
+
   // Returns true if all activatable locks have been forced.
   bool ActivatableDisplayLocksForced() const;
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 132624c1..14fc429 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -2682,8 +2682,7 @@
 
   if (RuntimeEnabledFeatures::DeferredShapingEnabled()) {
     auto& state = GetDisplayLockDocumentState();
-    if (state.LockedDisplayLockCount() !=
-        state.DisplayLockBlockingAllActivationCount()) {
+    if (state.HasActivatableLocks()) {
       UpdateStyleAndLayoutTree();
       if (node->GetLayoutObject()) {
         if (property_id == CSSPropertyID::kWidth)
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index a61165eb..8ae699a 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -3891,6 +3891,14 @@
     // recalc if the only changed properties are independent. In this case, we
     // can simply clone the old ComputedStyle and set these directly.
     new_style = PropagateInheritedProperties();
+    if (new_style) {
+      // If the child style is copied from the old one, we'll never
+      // reach StyleBuilder::ApplyProperty(), hence we'll
+      // never set the flag on the parent. this is completely analogous
+      // to the code in StyleResolver::ApplyMatchedCache().
+      if (new_style->HasExplicitInheritance())
+        parent_style->SetChildHasExplicitInheritance();
+    }
   }
   if (!new_style && (parent_style || (GetDocument().documentElement() == this &&
                                       LayoutViewCanHaveChildren(*this)))) {
@@ -5452,8 +5460,7 @@
 bool Element::ActivateDisplayLockIfNeeded(DisplayLockActivationReason reason) {
   auto& state = GetDocument().GetDisplayLockDocumentState();
   state.UnlockShapingDeferredElements(*this);
-  if (state.LockedDisplayLockCount() ==
-      state.DisplayLockBlockingAllActivationCount())
+  if (!state.HasActivatableLocks())
     return false;
 
   HeapVector<Member<Element>> activatable_targets;
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index 61abe2b..f8cffe4b 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -762,10 +762,12 @@
   if (options->includeUserActivation())
     user_activation = UserActivation::CreateSnapshot(source);
 
-  // TODO(mustaq): This is an ad-hoc mechanism to support delegating a single
-  // capability.  We need to add a structure to support passing multiple
-  // capabilities.  An explainer for the general delegation API is here:
-  // https://github.com/mustaqahmed/capability-delegation
+  // Capability Delegation permits a script to delegate its ability to call a
+  // restricted API to another browsing context it trusts. User activation is
+  // currently consumed when a supported capability is specified, to prevent
+  // potentially abusive repeated delegation attempts.
+  // https://wicg.github.io/capability-delegation/spec.html
+  // TODO(mustaq): Explore use cases for delegating multiple capabilities.
   mojom::blink::DelegatedCapability delegated_capability =
       mojom::blink::DelegatedCapability::kNone;
   if (LocalFrame::HasTransientUserActivation(source_frame) &&
@@ -774,9 +776,11 @@
     options->delegate().Split(' ', capability_list);
     if (capability_list.Contains("payment")) {
       delegated_capability = mojom::blink::DelegatedCapability::kPaymentRequest;
+      LocalFrame::ConsumeTransientUserActivation(source_frame);
     } else if (capability_list.Contains("fullscreen")) {
       delegated_capability =
           mojom::blink::DelegatedCapability::kFullscreenRequest;
+      LocalFrame::ConsumeTransientUserActivation(source_frame);
     }
   }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 8858b02..730c2755 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -737,6 +737,7 @@
           .setFontVariant(font->variant())
           .setFontWeight(font->weight())
           .setFontStretch(font->stretch())
+          .setFontDisplay(font->display())
           .setUnicodeRange(font->unicodeRange())
           .setSrc(src)
           .setPlatformFontFamily(
@@ -1881,7 +1882,7 @@
     bool has_expression_items = false;
     for (wtf_size_t j = 0; j < expressions.size(); ++j) {
       const MediaQueryExp& media_query_exp = expressions.at(j);
-      MediaQueryExpValue exp_value = media_query_exp.ExpValue();
+      MediaQueryExpValue exp_value = media_query_exp.Bounds().right.value;
       if (!exp_value.IsNumeric())
         continue;
       const char* value_name =
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index 277ab83..f0a9c99 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -546,6 +546,8 @@
   "ng/ng_floats_utils.h",
   "ng/ng_fragment.h",
   "ng/ng_fragment_builder.h",
+  "ng/ng_fragment_repeater.cc",
+  "ng/ng_fragment_repeater.h",
   "ng/ng_fragmentation_utils.cc",
   "ng/ng_fragmentation_utils.h",
   "ng/ng_ink_overflow.cc",
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 803fdaf..6e52015 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -286,11 +286,8 @@
     // See SVGLayoutSupport::CalculateScreenFontSizeScalingFactor().
     if (old_squared_scale != new_affine_transform.XScaleSquared() +
                                  new_affine_transform.YScaleSquared()) {
-      for (LayoutBox* box : *View()->SvgTextDescendantsMap().at(this)) {
-        box->SetNeedsLayout(layout_invalidation_reason::kStyleChange,
-                            kMarkContainerChain);
+      for (LayoutBox* box : *View()->SvgTextDescendantsMap().at(this))
         To<LayoutNGSVGText>(box)->SetNeedsTextMetricsUpdate();
-      }
     }
   }
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 82887d2f..6fafab99 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -6910,6 +6910,11 @@
       layout_overflow->UniteEvenIfEmpty(fragment_layout_overflow);
 
     if (const auto* break_token = fragment.BreakToken()) {
+      // The legacy engine doesn't understand our concept of repeated
+      // fragments. Stop now. The overflow rectangle will represent the
+      // fragment(s) generated under the first repeated root.
+      if (break_token->IsRepeated())
+        break;
       consumed_block_size = break_token->ConsumedBlockSize();
     }
   }
@@ -7176,6 +7181,12 @@
 
     self_rect.Unite(fragment_self_rect);
     contents_rect.Unite(fragment_contents_rect);
+
+    // The legacy engine doesn't understand our concept of repeated
+    // fragments. Stop now. The overflow rectangle will represent the
+    // fragment(s) generated under the first repeated root.
+    if (fragment.BreakToken() && fragment.BreakToken()->IsRepeated())
+      break;
   }
 
   if (!has_overflow) {
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index 901fd81..2559c1e 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1308,6 +1308,10 @@
     return NGPhysicalFragmentList(layout_results_);
   }
   const NGLayoutResult* GetLayoutResult(wtf_size_t i) const;
+  const NGLayoutResultList& GetLayoutResults() const {
+    NOT_DESTROYED();
+    return layout_results_;
+  }
   const NGPhysicalBoxFragment* GetPhysicalFragment(wtf_size_t i) const;
   const FragmentData* FragmentDataFromPhysicalFragment(
       const NGPhysicalBoxFragment&) const;
diff --git a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
index 3a6f535..16a75834 100644
--- a/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
+++ b/third_party/blink/renderer/core/layout/layout_shift_tracker.cc
@@ -582,9 +582,12 @@
   DCHECK(performance);
 
   double input_timestamp = LastInputTimestamp();
-  LayoutShift* entry =
-      LayoutShift::Create(performance->now(), score_delta, had_recent_input,
-                          input_timestamp, CreateAttributionList());
+  LayoutShift* entry = LayoutShift::Create(
+      performance->now(), score_delta, had_recent_input, input_timestamp,
+      CreateAttributionList(),
+      PerformanceEntry::GetNavigationId(window));  // Add WPT for
+                                                   //  LayoutShift. See
+                                                   //  crbug.com/1320878.
 
   performance->AddLayoutShiftEntry(entry);
 }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
index ccc8709..315803a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.cc
@@ -32,6 +32,11 @@
   return MakeGarbageCollected<NGPhysicalLineBoxFragment>(PassKey(), builder);
 }
 
+const NGPhysicalLineBoxFragment* NGPhysicalLineBoxFragment::Clone(
+    const NGPhysicalLineBoxFragment& other) {
+  return MakeGarbageCollected<NGPhysicalLineBoxFragment>(PassKey(), other);
+}
+
 NGPhysicalLineBoxFragment::NGPhysicalLineBoxFragment(
     PassKey key,
     NGLineBoxFragmentBuilder* builder)
@@ -49,6 +54,15 @@
                                 builder->unpositioned_list_marker_;
 }
 
+NGPhysicalLineBoxFragment::NGPhysicalLineBoxFragment(
+    PassKey key,
+    const NGPhysicalLineBoxFragment& other)
+    : NGPhysicalFragment(other), metrics_(other.metrics_) {
+  base_direction_ = other.base_direction_;
+  has_hanging_ = other.has_hanging_;
+  has_propagated_descendants_ = other.has_propagated_descendants_;
+}
+
 NGPhysicalLineBoxFragment::~NGPhysicalLineBoxFragment() = default;
 
 void NGPhysicalLineBoxFragment::Dispose() {}
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
index 5424acbb..f5a725a 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h
@@ -30,8 +30,12 @@
   static const NGPhysicalLineBoxFragment* Create(
       NGLineBoxFragmentBuilder* builder);
 
+  static const NGPhysicalLineBoxFragment* Clone(
+      const NGPhysicalLineBoxFragment&);
+
   using PassKey = base::PassKey<NGPhysicalLineBoxFragment>;
   NGPhysicalLineBoxFragment(PassKey, NGLineBoxFragmentBuilder* builder);
+  NGPhysicalLineBoxFragment(PassKey, const NGPhysicalLineBoxFragment&);
   ~NGPhysicalLineBoxFragment();
 
   void TraceAfterDispatch(Visitor*) const;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
index 2f9548f..e8cfeb9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.cc
@@ -34,6 +34,29 @@
       PassKey(), builder);
 }
 
+NGBlockBreakToken* NGBlockBreakToken::CreateRepeated(const NGBlockNode& node,
+                                                     unsigned sequence_number) {
+  auto* token = MakeGarbageCollected<NGBlockBreakToken>(PassKey(), node);
+  token->data_ = MakeGarbageCollected<NGBlockBreakTokenData>();
+  token->data_->sequence_number = sequence_number;
+  token->is_repeated_ = true;
+  return token;
+}
+
+NGBlockBreakToken* NGBlockBreakToken::CreateForBreakInRepeatedFragment(
+    const NGBlockNode& node,
+    unsigned sequence_number,
+    LayoutUnit consumed_block_size) {
+  auto* token = MakeGarbageCollected<NGBlockBreakToken>(PassKey(), node);
+  token->data_ = MakeGarbageCollected<NGBlockBreakTokenData>();
+  token->data_->sequence_number = sequence_number;
+  token->data_->consumed_block_size = consumed_block_size;
+#if DCHECK_IS_ON()
+  token->is_repeated_actual_break_ = true;
+#endif
+  return token;
+}
+
 NGBlockBreakToken::NGBlockBreakToken(PassKey key, NGBoxFragmentBuilder* builder)
     : NGBreakToken(kBlockBreakToken, builder->node_),
       const_num_children_(builder->child_break_tokens_.size()) {
@@ -103,9 +126,9 @@
 
 void NGBlockBreakToken::TraceAfterDispatch(Visitor* visitor) const {
   visitor->Trace(data_);
-  // Looking up |ChildBreakTokens()| in Trace() here is safe because
+  // Looking up |ChildBreakTokensInternal()| in Trace() here is safe because
   // |const_num_children_| is const.
-  for (auto& child : ChildBreakTokens())
+  for (auto& child : ChildBreakTokensInternal())
     visitor->Trace(child);
   NGBreakToken::TraceAfterDispatch(visitor);
 }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h
index 92385b9..9aaba70c 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_break_token.h
@@ -41,6 +41,30 @@
     return token;
   }
 
+  // Create a "repeat" break token. This is created at each fragment (that
+  // didn't otherwise break) generated by repeated content, unless it's the very
+  // last fragment. This is needed in order to get the sequence numbers right.
+  static NGBlockBreakToken* CreateRepeated(const NGBlockNode&,
+                                           unsigned sequence_number);
+
+  // Create a break token for a "regular" break in a repeated fragment.
+  //
+  // This is needed when repeated content has another fragmentation context
+  // inside, and there are actual breaks inside that fragmentation context.
+  //
+  // Note: Although the break token created here corresponds with one inside the
+  // first fragment, this break token is "crippled" in many ways. There'll never
+  // be any child break tokens, for instance. The only information that's
+  // carried over from the original break token is consumed block-size, and we
+  // also set the correct sequence number. Break tokens created by this function
+  // aren't meant to be used in layout. They are just here to keep pre-paint and
+  // paint happy (which rely on sequence numbers and consumed block-size). Any
+  // other use of this break token is undefined (and likely to fail DCHECKs).
+  static NGBlockBreakToken* CreateForBreakInRepeatedFragment(
+      const NGBlockNode&,
+      unsigned sequence_number,
+      LayoutUnit consumed_block_size);
+
   // Represents the amount of block-size consumed by previous fragments.
   //
   // E.g. if the node specifies a block-size of 200px, and the previous
@@ -61,6 +85,9 @@
   // size when used for legacy. This difference is represented by
   // |consumed_block_size_legacy_adjustment_|.
   LayoutUnit ConsumedBlockSizeForLegacy() const {
+#if DCHECK_IS_ON()
+    DCHECK(!is_repeated_actual_break_);
+#endif
     DCHECK(data_);
     return data_->consumed_block_size +
            data_->consumed_block_size_legacy_adjustment;
@@ -73,12 +100,17 @@
   // want a fragmentainer break before laying out the node). What the sequence
   // number is for such a break token is undefined.
   unsigned SequenceNumber() const {
-    DCHECK(!IsBreakBefore());
+#if DCHECK_IS_ON()
+    DCHECK(is_repeated_actual_break_ || !IsBreakBefore());
+#endif
     DCHECK(data_);
     return data_->sequence_number;
   }
 
   const NGBlockBreakTokenData* TokenData() const {
+#if DCHECK_IS_ON()
+    DCHECK(!is_repeated_actual_break_);
+#endif
     DCHECK(data_);
     return data_;
   }
@@ -91,13 +123,29 @@
 
   bool IsForcedBreak() const { return is_forced_break_; }
 
-  bool IsCausedByColumnSpanner() const { return is_caused_by_column_spanner_; }
+  // Return true if the node didn't actually break, but is repeated in the next
+  // fragmentainer in the fragmentation context in which the repeated content
+  // root (table header / footer, or fixed-positioned element when printing)
+  // lives.
+  bool IsRepeated() const { return is_repeated_; }
+
+  bool IsCausedByColumnSpanner() const {
+#if DCHECK_IS_ON()
+    DCHECK(!is_repeated_actual_break_);
+#endif
+    return is_caused_by_column_spanner_;
+  }
 
   // Return true if all children have been "seen". When we have reached this
   // point, and resume layout in a fragmentainer, we should only process child
   // break tokens, if any, and not attempt to start laying out nodes that don't
   // have one (since all children are either finished, or have a break token).
-  bool HasSeenAllChildren() const { return has_seen_all_children_; }
+  bool HasSeenAllChildren() const {
+#if DCHECK_IS_ON()
+    DCHECK(!is_repeated_actual_break_);
+#endif
+    return has_seen_all_children_;
+  }
 
   // Return true if layout was past the block-end border edge of the node when
   // it fragmented. This typically means that something is overflowing the node,
@@ -121,10 +169,18 @@
   // not ready to proceed to the next column. Anything that can fit at the
   // bottom of a column (either because it actually has 0 height, or e.g. a
   // negative top margin) will be put into that column, not the next.
-  bool IsAtBlockEnd() const { return is_at_block_end_; }
+  bool IsAtBlockEnd() const {
+#if DCHECK_IS_ON()
+    DCHECK(!is_repeated_actual_break_);
+#endif
+    return is_at_block_end_;
+  }
 
   // True if earlier fragments could not position the list marker.
   bool HasUnpositionedListMarker() const {
+#if DCHECK_IS_ON()
+    DCHECK(!is_repeated_actual_break_);
+#endif
     return has_unpositioned_list_marker_;
   }
 
@@ -137,7 +193,10 @@
   //
   // A child which we haven't visited yet doesn't have a break token here.
   const base::span<const Member<const NGBreakToken>> ChildBreakTokens() const {
-    return base::make_span(child_break_tokens_, const_num_children_);
+#if DCHECK_IS_ON()
+    DCHECK(!is_repeated_actual_break_);
+#endif
+    return ChildBreakTokensInternal();
   }
 
   // Find the child NGInlineBreakToken for the specified node.
@@ -159,6 +218,11 @@
   void TraceAfterDispatch(Visitor*) const;
 
  private:
+  const base::span<const Member<const NGBreakToken>> ChildBreakTokensInternal()
+      const {
+    return base::make_span(child_break_tokens_, const_num_children_);
+  }
+
   Member<NGBlockBreakTokenData> data_;
 
   const wtf_size_t const_num_children_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 5d49a256..7b9ff19f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -55,6 +55,7 @@
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_disable_side_effects_scope.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
@@ -672,6 +673,67 @@
   return result;
 }
 
+const NGLayoutResult* NGBlockNode::LayoutRepeatableRoot(
+    const NGConstraintSpace& constraint_space,
+    const NGBlockBreakToken* break_token) const {
+  // When laying out repeatable content, we cannot at the same time allow it to
+  // break inside.
+  DCHECK(!constraint_space.HasBlockFragmentation());
+
+  // We can't both resume and repeat!
+  DCHECK(!IsResumingLayout(break_token));
+
+  bool is_first = !break_token || !break_token->IsRepeated();
+  const NGLayoutResult* result;
+  if (is_first) {
+    // We're generating the first fragment for repeated content. Perform regular
+    // layout.
+    result = Layout(constraint_space, break_token);
+    DCHECK(!result->PhysicalFragment().BreakToken());
+  } else {
+    // We're repeating. Create a shallow clone of the first result. Once we're
+    // at the last fragment, we'll actually create a deep clone.
+    result = NGLayoutResult::Clone(*box_->GetLayoutResult(0));
+  }
+
+  wtf_size_t index = FragmentIndex(break_token);
+  const auto& fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment());
+  // Unless it's the very last fragment to be generated, we need to create a
+  // special "repeat" break token, which will be the incoming break token when
+  // generating the next fragment. This is needed in order to get the sequence
+  // numbers right, which is important when adding the result to the LayoutBox,
+  // and it's also needed by pre-paint / paint.
+  const NGBlockBreakToken* outgoing_break_token = nullptr;
+  if (constraint_space.IsRepeatable())
+    outgoing_break_token = NGBlockBreakToken::CreateRepeated(*this, index);
+  auto mutator = fragment.GetMutableForCloning();
+  mutator.SetBreakToken(outgoing_break_token);
+  if (!is_first) {
+    mutator.ClearIsFirstForNode();
+    box_->SetLayoutResult(result, index);
+  }
+
+  if (!constraint_space.IsRepeatable()) {
+    // This is the last fragment. It won't be repeated again. We have already
+    // created fragments for the repeated nodes, but the cloning was shallow.
+    // We're now ready to deep-clone the entire subtree for each repeated
+    // fragment, and update the layout result vector in the LayoutBox, including
+    // setting correct break tokens with sequence numbers.
+    wtf_size_t fragment_count = box_->PhysicalFragmentCount();
+    DCHECK_GE(fragment_count, 1u);
+    box_->ClearNeedsLayout();
+    for (wtf_size_t i = 1; i < fragment_count; i++) {
+      const NGPhysicalBoxFragment& fragment = *box_->GetPhysicalFragment(i);
+      bool is_first = i == 1;
+      bool is_last = i + 1 == fragment_count;
+      NGFragmentRepeater repeater(is_first, is_last);
+      repeater.CloneChildFragments(fragment);
+    }
+  }
+
+  return result;
+}
+
 const NGLayoutResult* NGBlockNode::CachedLayoutResultForOutOfFlowPositioned(
     LogicalSize container_content_size) const {
   DCHECK(IsOutOfFlowPositioned());
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.h b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
index 50577ad3..58d586d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.h
@@ -50,6 +50,34 @@
   const NGLayoutResult* SimplifiedLayout(
       const NGPhysicalFragment& previous_fragment) const;
 
+  // Lay out a repeatable node during block fragmentation (fixed positioned
+  // element during printing, or table header / footer). To be called once for
+  // each container fragment in which it repeats.
+  //
+  // NGConstraintSpace::IsRepeatable() will tell whether the node is
+  // (potentially [1]) going to repeat again (in which case an outgoing "repeat"
+  // break token will be created, or if this is the last time (no outgoing break
+  // token will be created).
+  //
+  // [1] Depending on the type of content, and depending on the way we implement
+  // it, we may or may not be able to tell up-front whether it's going to repeat
+  // again.
+  //
+  // Note that we only actually lay it out once - when at the first container
+  // fragment. Any subsequent call will just clone the previous result. When
+  // we're done repeating, when at the last fragment, we'll finalize the cloned
+  // results, by deep-cloning and setting the correct break token sequence
+  // numbers.
+  //
+  // Ideally, there should only be one fragment subtree generated from a
+  // repeated element (which could simply be inserted inside every relevant
+  // container fragment), but due to requirements from pre-paint and paint
+  // (mainly), we need to clone the fragment as many times as it repeats, and we
+  // also need to make sure that the break tokens are reasonably intact -
+  // including the sequence numbers. This is why we need this.
+  const NGLayoutResult* LayoutRepeatableRoot(const NGConstraintSpace&,
+                                             const NGBlockBreakToken*) const;
+
   // This method is just for use within the |NGOutOfFlowLayoutPart|.
   //
   // As OOF-positioned objects have their position, and size computed
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index b47b183..e106ad0 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -396,7 +396,8 @@
   if (!child_box_fragment)
     return;
 
-  if (const NGBlockBreakToken* token = child_box_fragment->BreakToken()) {
+  const NGBlockBreakToken* token = child_box_fragment->BreakToken();
+  if (token && !token->IsRepeated()) {
     // Figure out if this child break is in the same flow as this parent. If
     // it's an out-of-flow positioned box, it's not. If it's in a parallel flow,
     // it's also not.
@@ -671,7 +672,7 @@
   DCHECK(!HasInflowChildBreakInside());
   DCHECK(!DidBreakSelf());
   DCHECK(!has_forced_break_);
-  DCHECK(!HasBreakTokenData());
+  DCHECK(ConstraintSpace().IsRepeatable() || !HasBreakTokenData());
   DCHECK_EQ(minimal_space_shortage_, kIndefiniteSize);
   if (!ConstraintSpace().ShouldPropagateChildBreakValues()) {
     DCHECK(!initial_break_before_);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_break_token.h b/third_party/blink/renderer/core/layout/ng/ng_break_token.h
index cf0b729..c3706ce 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_break_token.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_break_token.h
@@ -61,9 +61,13 @@
                unsigned flags = 0)
       : box_(node.GetLayoutBox()),
         type_(type),
+#if DCHECK_IS_ON()
+        is_repeated_actual_break_(false),
+#endif
         flags_(flags),
         is_break_before_(false),
         is_forced_break_(false),
+        is_repeated_(false),
         is_caused_by_column_spanner_(false),
         is_at_block_end_(false),
         has_seen_all_children_(false) {
@@ -78,6 +82,14 @@
   unsigned type_ : 1;
 
  protected:
+#if DCHECK_IS_ON()
+  // If true, this is a break token for an actual break in a cloned fragment. In
+  // such cases, only a few of the members here have been set up correctly, and
+  // the rest should therefore not be accessed. Such break tokens are never used
+  // in layout, only by pre-paint / paint.
+  unsigned is_repeated_actual_break_ : 1;
+#endif
+
   // The following bitfields are only to be used by NGInlineBreakToken (it's
   // defined here to save memory, since that class has no bitfields).
 
@@ -90,6 +102,8 @@
 
   unsigned is_forced_break_ : 1;
 
+  unsigned is_repeated_ : 1;
+
   unsigned is_caused_by_column_spanner_ : 1;
 
   // Set when layout is past the block-end border edge. If we break when we're
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
index 64d2397..7abd1368 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -393,6 +393,13 @@
     return HasRareData() && rare_data_->is_at_fragmentainer_start;
   }
 
+  // Return true if the content is repeatable inside block fragmentation, which
+  // is the case when an element is fixed positioned (printing only), or a
+  // repeatable table header / footer.
+  bool IsRepeatable() const {
+    return HasRareData() && rare_data_->is_repeatable;
+  }
+
   // Whether the current constraint space is for the newly established
   // Formatting Context.
   bool IsNewFormattingContext() const {
@@ -841,7 +848,8 @@
           min_block_size_should_encompass_intrinsic_size(false),
           min_break_appeal(kBreakAppealLastResort),
           propagate_child_break_values(false),
-          is_at_fragmentainer_start(false) {}
+          is_at_fragmentainer_start(false),
+          is_repeatable(false) {}
     RareData(const RareData& other)
         : percentage_resolution_size(other.percentage_resolution_size),
           replaced_percentage_resolution_block_size(
@@ -866,7 +874,8 @@
               other.min_block_size_should_encompass_intrinsic_size),
           min_break_appeal(other.min_break_appeal),
           propagate_child_break_values(other.propagate_child_break_values),
-          is_at_fragmentainer_start(other.is_at_fragmentainer_start) {
+          is_at_fragmentainer_start(other.is_at_fragmentainer_start),
+          is_repeatable(other.is_repeatable) {
       switch (data_union_type) {
         case kNone:
           break;
@@ -940,7 +949,8 @@
           should_ignore_forced_breaks != other.should_ignore_forced_breaks ||
           is_in_column_bfc != other.is_in_column_bfc ||
           min_break_appeal != other.min_break_appeal ||
-          propagate_child_break_values != other.propagate_child_break_values)
+          propagate_child_break_values != other.propagate_child_break_values ||
+          is_repeatable != other.is_repeatable)
         return false;
 
       switch (data_union_type) {
@@ -974,7 +984,8 @@
           requires_content_before_breaking || is_inside_balanced_columns ||
           should_ignore_forced_breaks || is_in_column_bfc ||
           min_break_appeal != kBreakAppealLastResort ||
-          propagate_child_break_values || is_at_fragmentainer_start)
+          propagate_child_break_values || is_at_fragmentainer_start ||
+          is_repeatable)
         return false;
 
       switch (data_union_type) {
@@ -1226,6 +1237,7 @@
     unsigned min_break_appeal : kNGBreakAppealBitsNeeded;
     unsigned propagate_child_break_values : 1;
     unsigned is_at_fragmentainer_start : 1;
+    unsigned is_repeatable : 1;
 
    private:
     struct BlockData {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
index bc6595c4..6190b31 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
@@ -38,6 +38,8 @@
                                  adjust_inline_size_if_needed) {
     if (parent_space.ShouldPropagateChildBreakValues())
       SetShouldPropagateChildBreakValues();
+    if (parent_space.IsRepeatable())
+      SetIsRepeatable();
   }
 
   // The setters on this builder are in the writing mode of parent_writing_mode.
@@ -124,6 +126,16 @@
       space_.EnsureRareData()->fragmentainer_block_size = size;
   }
 
+  // Shrink the fragmentainer block-size, to reserve space at the end. This is
+  // needed for repeated table footers.
+  void ReserveSpaceAtFragmentainerEnd(LayoutUnit space) {
+#if DCHECK_IS_ON()
+    DCHECK(is_fragmentainer_block_size_set_);
+#endif
+    DCHECK_GE(space_.rare_data_->fragmentainer_block_size, space);
+    space_.rare_data_->fragmentainer_block_size -= space;
+  }
+
   void SetFragmentainerOffsetAtBfc(LayoutUnit offset) {
 #if DCHECK_IS_ON()
     DCHECK(!is_fragmentainer_offset_at_bfc_set_);
@@ -137,6 +149,8 @@
     space_.EnsureRareData()->is_at_fragmentainer_start = true;
   }
 
+  void SetIsRepeatable() { space_.EnsureRareData()->is_repeatable = true; }
+
   void SetIsFixedInlineSize(bool b) {
     if (LIKELY(is_in_parallel_flow_))
       space_.bitfields_.is_fixed_inline_size = b;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.cc b/third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.cc
new file mode 100644
index 0000000..e2ee2c3
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.cc
@@ -0,0 +1,217 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.h"
+
+#include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h"
+#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_link.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
+
+namespace blink {
+
+namespace {
+
+const NGLayoutResult* GetClonableLayoutResult(
+    const LayoutBox& layout_box,
+    const NGPhysicalBoxFragment& fragment) {
+  if (const NGBlockBreakToken* break_token = fragment.BreakToken()) {
+    if (!break_token->IsRepeated())
+      return layout_box.GetLayoutResult(break_token->SequenceNumber());
+  }
+  // Cloned results may already have been added (so we can't just pick the last
+  // one), but the break tokens have not yet been updated. Look for the first
+  // result without a break token (or with a repeated break token, in case we've
+  // already been through this). This will actually be the very first result,
+  // unless there's a fragmentation context established inside the repeated
+  // root.
+  for (const NGLayoutResult* result : layout_box.GetLayoutResults()) {
+    const NGBlockBreakToken* break_token =
+        To<NGPhysicalBoxFragment>(result->PhysicalFragment()).BreakToken();
+    if (!break_token || break_token->IsRepeated())
+      return result;
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
+// Remove all cloned results, but keep the first original one(s).
+void RemoveClonedResults(LayoutBox& layout_box) {
+  for (wtf_size_t idx = 0; idx < layout_box.PhysicalFragmentCount(); idx++) {
+    const NGBlockBreakToken* break_token =
+        layout_box.GetPhysicalFragment(idx)->BreakToken();
+    if (!break_token || break_token->IsRepeated()) {
+      layout_box.ShrinkLayoutResults(idx + 1);
+      return;
+    }
+  }
+  NOTREACHED();
+}
+
+void UpdateBreakTokens(LayoutBox& layout_box) {
+  NGBlockNode node(&layout_box);
+  wtf_size_t sequence_number = 0;
+  wtf_size_t fragment_count = layout_box.PhysicalFragmentCount();
+
+  // If this box is a fragmentation context root, we also need to update the
+  // break tokens of the fragmentainers, since they aren't associated with a
+  // layout object on their own.
+  const NGPhysicalBoxFragment* last_fragmentainer = nullptr;
+  wtf_size_t fragmentainer_sequence_number = 0;
+
+  for (wtf_size_t idx = 0; idx < fragment_count; idx++, sequence_number++) {
+    const auto& fragment = *layout_box.GetPhysicalFragment(idx);
+    const NGBlockBreakToken* break_token = fragment.BreakToken();
+    if (break_token && break_token->IsRepeated())
+      break_token = nullptr;
+    if (break_token) {
+      // It may already have a break token, if there's another fragmentation
+      // context inside the repeated root. But we need to update the sequence
+      // number, unless we're inside the very first fragment generated for the
+      // repeated root.
+      if (break_token->SequenceNumber() != sequence_number) {
+        break_token = NGBlockBreakToken::CreateForBreakInRepeatedFragment(
+            node, sequence_number, break_token->ConsumedBlockSize());
+      }
+    } else if (idx + 1 < fragment_count) {
+      // Unless it's the very last fragment, it needs a break token.
+      break_token = NGBlockBreakToken::CreateRepeated(node, sequence_number);
+    }
+    fragment.GetMutableForCloning().SetBreakToken(break_token);
+
+    // That's all we have to do, unless this is a fragmentation context root.
+
+    if (!fragment.IsFragmentationContextRoot())
+      continue;
+
+    // If this is a fragmentation context root, we also need to update the
+    // fragmentainers (which don't have a LayoutBox associated with them).
+
+    for (const auto& child_link : fragment.Children()) {
+      if (!child_link->IsFragmentainerBox())
+        continue;
+      const auto& fragmentainer =
+          *To<NGPhysicalBoxFragment>(child_link.fragment.Get());
+      const NGBlockBreakToken* fragmentainer_break_token =
+          fragmentainer.BreakToken();
+      if (fragmentainer_break_token && fragmentainer_break_token->IsRepeated())
+        fragmentainer_break_token = nullptr;
+      if (fragmentainer_break_token) {
+        if (fragmentainer_break_token->SequenceNumber() !=
+            fragmentainer_sequence_number) {
+          fragmentainer_break_token =
+              NGBlockBreakToken::CreateForBreakInRepeatedFragment(
+                  node, fragmentainer_sequence_number,
+                  fragmentainer_break_token->ConsumedBlockSize());
+          fragmentainer.GetMutableForCloning().SetBreakToken(
+              fragmentainer_break_token);
+        }
+      } else {
+        fragmentainer_break_token = NGBlockBreakToken::CreateRepeated(
+            node, fragmentainer_sequence_number);
+        fragmentainer.GetMutableForCloning().SetBreakToken(
+            fragmentainer_break_token);
+
+        // Since this fragmentainer didn't have a break token, it might be the
+        // very last one, but it's not straight-forward to figure out whether
+        // this is actually the case. So just keep track of what we're visiting.
+        // It's been given a break token for now. If it turns out that this was
+        // the last fragmentainer, we'll remove it again further below.
+        last_fragmentainer = &fragmentainer;
+      }
+      fragmentainer_sequence_number++;
+    }
+  }
+
+  // The last fragmentainer shouldn't have an outgoing break token, but it got
+  // one above.
+  if (last_fragmentainer)
+    last_fragmentainer->GetMutableForCloning().SetBreakToken(nullptr);
+}
+
+}  // anonymous namespace
+
+void NGFragmentRepeater::CloneChildFragments(
+    const NGPhysicalBoxFragment& cloned_fragment) {
+  for (NGLink& child : cloned_fragment.GetMutableForCloning().Children()) {
+    if (const auto* child_box =
+            DynamicTo<NGPhysicalBoxFragment>(child.fragment.Get())) {
+      if (child_box->IsCSSBox()) {
+        const auto* child_layout_box =
+            To<LayoutBox>(child_box->GetLayoutObject());
+        const NGLayoutResult* child_result =
+            GetClonableLayoutResult(*child_layout_box, *child_box);
+        child_result = Repeat(*child_result);
+        child.fragment = &child_result->PhysicalFragment();
+      } else if (child_box->IsFragmentainerBox()) {
+        child_box = NGPhysicalBoxFragment::Clone(*child_box);
+        CloneChildFragments(*child_box);
+        child.fragment = child_box;
+      }
+    } else if (child->IsLineBox()) {
+      child.fragment = NGPhysicalLineBoxFragment::Clone(
+          To<NGPhysicalLineBoxFragment>(*child.fragment.Get()));
+    }
+  }
+}
+
+const NGLayoutResult* NGFragmentRepeater::Repeat(const NGLayoutResult& other) {
+  const NGLayoutResult* cloned_result = NGLayoutResult::Clone(other);
+  const auto& cloned_fragment =
+      To<NGPhysicalBoxFragment>(cloned_result->PhysicalFragment());
+  auto& layout_box = *To<LayoutBox>(cloned_fragment.GetMutableLayoutObject());
+
+  if (is_first_clone_ && cloned_fragment.IsFirstForNode()) {
+    // We're (re-)inserting cloned results, and we're at the first clone. Remove
+    // the old results first.
+    RemoveClonedResults(layout_box);
+  }
+
+  if (cloned_fragment.HasItems()) {
+    // Fragment items have already been cloned, but any atomic inlines were
+    // shallowly cloned. Deep-clone them now, if any.
+    for (auto& cloned_item : cloned_fragment.Items()->Items()) {
+      const NGPhysicalBoxFragment* child_box_fragment =
+          cloned_item.BoxFragment();
+      if (!child_box_fragment)
+        continue;
+      const auto* child_layout_box =
+          DynamicTo<LayoutBox>(child_box_fragment->GetLayoutObject());
+      if (!child_layout_box) {
+        // We don't need to clone non-atomic inlines.
+        DCHECK(child_box_fragment->GetLayoutObject()->IsLayoutInline());
+        continue;
+      }
+      const NGLayoutResult* child_result =
+          GetClonableLayoutResult(*child_layout_box, *child_box_fragment);
+      child_result = Repeat(*child_result);
+      child_box_fragment =
+          &To<NGPhysicalBoxFragment>(child_result->PhysicalFragment());
+      cloned_item.GetMutableForCloning().ReplaceBoxFragment(
+          *child_box_fragment);
+    }
+  }
+
+  CloneChildFragments(cloned_fragment);
+
+  // The first-for-node bit has also been cloned. But we're obviously not the
+  // first anymore if we're repeated.
+  cloned_fragment.GetMutableForCloning().ClearIsFirstForNode();
+
+  layout_box.AppendLayoutResult(cloned_result);
+  if (is_last_fragment_ && (!cloned_fragment.BreakToken() ||
+                            cloned_fragment.BreakToken()->IsRepeated())) {
+    // We've reached the end. We can finally add missing break tokens, and
+    // update cloned sequence numbers.
+    UpdateBreakTokens(layout_box);
+    layout_box.ClearNeedsLayout();
+    layout_box.FinalizeLayoutResults();
+  }
+  return cloned_result;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.h b/third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.h
new file mode 100644
index 0000000..2a5f471
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragment_repeater.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENT_REPEATER_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENT_REPEATER_H_
+
+#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+
+namespace blink {
+
+class NGLayoutResult;
+class NGPhysicalBoxFragment;
+
+// Fragment tree mutator / cloner / repeater.
+//
+// This is needed in order to implement repeated content in block fragmentation
+// (repeated table headers / footers, and also fixed-positioned elements when
+// printing).
+//
+// On the layout side, we only lay out the element once, but pre-paint and paint
+// require one unique fragment for each time it repeats, since we need one
+// FragmentData object for each, each with its own global-ish paint offset.
+class NGFragmentRepeater {
+  STACK_ALLOCATED();
+
+ public:
+  explicit NGFragmentRepeater(bool is_first_clone, bool is_last_fragment)
+      : is_first_clone_(is_first_clone), is_last_fragment_(is_last_fragment) {}
+
+  // Deep-clone the subtree of an already shallowly cloned fragment. This will
+  // also create new break tokens inside, in order to set unique sequence
+  // numbers. The result is only usable by pre-paint / painting, not by actual
+  // layout.
+  void CloneChildFragments(const NGPhysicalBoxFragment& cloned_fragment);
+
+ private:
+  const NGLayoutResult* Repeat(const NGLayoutResult& other);
+
+  // True when at the first cloned fragment.
+  bool is_first_clone_;
+
+  // True when at the last container fragment. No outgoing "repeat" break tokens
+  // should be created then.
+  bool is_last_fragment_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_FRAGMENT_REPEATER_H_
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
index 8556131..8aedfa23 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.cc
@@ -210,7 +210,7 @@
     consider_break_inside_avoidance = true;
   } else {
     appeal = layout_result.BreakAppeal();
-    consider_break_inside_avoidance = break_token;
+    consider_break_inside_avoidance = break_token && !break_token->IsRepeated();
   }
 
   // We don't let break-inside:avoid affect the child's stored break appeal, but
@@ -847,7 +847,8 @@
 
   NGBreakAppeal appeal_inside =
       CalculateBreakAppealInside(space, layout_result);
-  if (break_token || appeal_inside < kBreakAppealPerfect) {
+  if ((break_token && !break_token->IsRepeated()) ||
+      appeal_inside < kBreakAppealPerfect) {
     // The block child broke inside, either in this fragmentation context, or in
     // an inner one. We now need to decide whether to keep that break, or if it
     // would be better to break before it. Allow breaking inside if it has the
@@ -921,7 +922,9 @@
     NGBoxFragmentBuilder* builder,
     NGFlexColumnBreakInfo* flex_column_break_info) {
   // If the child already broke, it's a little too late to look for breakpoints.
-  DCHECK(!layout_result.PhysicalFragment().BreakToken());
+  DCHECK(!layout_result.PhysicalFragment().BreakToken() ||
+         To<NGBlockBreakToken>(layout_result.PhysicalFragment().BreakToken())
+             ->IsRepeated());
 
   // See if there's a good breakpoint inside the child.
   NGBreakAppeal appeal_inside = kBreakAppealLastResort;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
index 97d7f0d..7c720ed9 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h
@@ -61,7 +61,7 @@
 
 // Return true if we're resuming layout after a previous break.
 inline bool IsResumingLayout(const NGBlockBreakToken* token) {
-  return token && !token->IsBreakBefore();
+  return token && !token->IsBreakBefore() && !token->IsRepeated();
 }
 
 // Return true if the node may break into multiple fragments (or has already
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
index b0cd73b..884c3b2f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -38,6 +38,13 @@
 }  // namespace
 
 // static
+const NGLayoutResult* NGLayoutResult::Clone(const NGLayoutResult& other) {
+  return MakeGarbageCollected<NGLayoutResult>(
+      other, NGPhysicalBoxFragment::Clone(
+                 To<NGPhysicalBoxFragment>(other.PhysicalFragment())));
+}
+
+// static
 const NGLayoutResult* NGLayoutResult::CloneWithPostLayoutFragments(
     const NGLayoutResult& other,
     const absl::optional<PhysicalRect> updated_layout_overflow) {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
index dee9e42..7ce331f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -51,12 +51,20 @@
     kNeedsRelayoutWithNoForcedTruncateAtLineClamp = 4,
     kDisableFragmentation = 5,
     kNeedsRelayoutWithNoChildScrollbarChanges = 6,
-    kNeedsRelayoutWithRowCrossSizeChanges = 7,
+    kAlgorithmSpecific1 = 7,  // Save bits by using the same value for mutually
+                              // exclusive results.
+    kNeedsRelayoutWithRowCrossSizeChanges = kAlgorithmSpecific1,
+    kNeedsRelayoutAsLastTableBox = kAlgorithmSpecific1,
     // When adding new values, make sure the bit size of |Bitfields::status| is
     // large enough to store.
   };
 
-  // Creates a copy of |other| but uses the "post-layout" fragments to ensure
+  // Make a shallow clone of the result. The fragment is cloned. Fragment
+  // *items* are also cloned, but child fragments are not. Apart from that it's
+  // truly shallow. Pinky promise.
+  static const NGLayoutResult* Clone(const NGLayoutResult&);
+
+  // Same as Clone(), but uses the "post-layout" fragments to ensure
   // fragment-tree consistency.
   static const NGLayoutResult* CloneWithPostLayoutFragments(
       const NGLayoutResult& other,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 9cc42c8b..879c1b9d 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -219,6 +219,21 @@
 }
 
 // static
+const NGPhysicalBoxFragment* NGPhysicalBoxFragment::Clone(
+    const NGPhysicalBoxFragment& other) {
+  // The size of the new fragment shouldn't differ from the old one.
+  wtf_size_t num_fragment_items = other.Items() ? other.Items()->Size() : 0;
+  size_t byte_size = AdditionalByteSize(
+      num_fragment_items, other.const_num_children_, other.has_layout_overflow_,
+      other.has_borders_, other.has_padding_, other.has_inflow_bounds_,
+      other.const_has_rare_data_);
+
+  return MakeGarbageCollected<NGPhysicalBoxFragment>(
+      AdditionalBytes(byte_size), PassKey(), other, other.HasLayoutOverflow(),
+      other.LayoutOverflow());
+}
+
+// static
 const NGPhysicalBoxFragment*
 NGPhysicalBoxFragment::CloneWithPostLayoutFragments(
     const NGPhysicalBoxFragment& other,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
index ac2760b..621df07 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -31,8 +31,12 @@
   static const NGPhysicalBoxFragment* Create(
       NGBoxFragmentBuilder* builder,
       WritingMode block_or_line_writing_mode);
-  // Creates a copy of |other| but uses the "post-layout" fragments to ensure
-  // fragment-tree consistency.
+
+  // Creates a shallow copy of |other|.
+  static const NGPhysicalBoxFragment* Clone(const NGPhysicalBoxFragment& other);
+
+  // Creates a shallow copy of |other| but uses the "post-layout" fragments to
+  // ensure fragment-tree consistency.
   static const NGPhysicalBoxFragment* CloneWithPostLayoutFragments(
       const NGPhysicalBoxFragment& other,
       const absl::optional<PhysicalRect> updated_layout_overflow =
@@ -424,9 +428,14 @@
 
   class MutableForCloning {
     STACK_ALLOCATED();
+    friend class NGFragmentRepeater;
     friend class NGPhysicalBoxFragment;
 
    public:
+    void ClearIsFirstForNode() { fragment_.is_first_for_node_ = false; }
+    void SetBreakToken(const NGBlockBreakToken* token) {
+      fragment_.break_token_ = token;
+    }
     base::span<NGLink> Children() const {
       DCHECK(fragment_.children_valid_);
       return base::make_span(fragment_.children_,
diff --git a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc
index b4fb1c4..18650dd8 100644
--- a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc
+++ b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.cc
@@ -32,6 +32,8 @@
 void LayoutNGSVGText::StyleDidChange(StyleDifference diff,
                                      const ComputedStyle* old_style) {
   NOT_DESTROYED();
+  if (needs_text_metrics_update_ && diff.HasDifference())
+    diff.SetNeedsFullLayout();
   LayoutNGBlockFlowMixin<LayoutSVGBlock>::StyleDidChange(diff, old_style);
   SVGResources::UpdatePaints(*GetElement(), old_style, StyleRef());
 }
@@ -337,4 +339,9 @@
   SetNeedsCollectInlines(true);
 }
 
+bool LayoutNGSVGText::NeedsTextMetricsUpdate() const {
+  NOT_DESTROYED();
+  return needs_text_metrics_update_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h
index 2454d18..03beb417 100644
--- a/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h
+++ b/third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h
@@ -25,6 +25,7 @@
   // descendant <tspan> is changed.
   void SetNeedsPositioningValuesUpdate();
   void SetNeedsTextMetricsUpdate();
+  bool NeedsTextMetricsUpdate() const;
 
   bool IsObjectBoundingBoxValid() const;
 
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index f7fc51b5..9de9193 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -623,11 +623,16 @@
   }
 #endif
 
-  return GenerateFragment(container_builder_.InlineSize(),
-                          minimal_table_grid_block_size, grouped_children,
-                          column_locations, rows, cell_block_constraints,
-                          sections, captions, *table_borders,
-                          is_grid_empty ? LogicalSize() : border_spacing);
+  const NGLayoutResult* result = GenerateFragment(
+      container_builder_.InlineSize(), minimal_table_grid_block_size,
+      grouped_children, column_locations, rows, cell_block_constraints,
+      sections, captions, *table_borders,
+      is_grid_empty ? LogicalSize() : border_spacing);
+
+  if (result->Status() == NGLayoutResult::kNeedsRelayoutAsLastTableBox)
+    return RelayoutAsLastTableBox();
+
+  return result;
 }
 
 MinMaxSizesResult NGTableLayoutAlgorithm::ComputeMinMaxSizes(
@@ -670,6 +675,16 @@
                            /* depends_on_block_constraints */ false};
 }
 
+const NGLayoutResult* NGTableLayoutAlgorithm::RelayoutAsLastTableBox() {
+  DCHECK(!is_known_to_be_last_table_box_);
+  NGLayoutAlgorithmParams params(
+      Node(), container_builder_.InitialFragmentGeometry(), ConstraintSpace(),
+      BreakToken(), /* early_break */ nullptr);
+  NGTableLayoutAlgorithm algorithm(params);
+  algorithm.is_known_to_be_last_table_box_ = true;
+  return algorithm.Layout();
+}
+
 void NGTableLayoutAlgorithm::ComputeRows(
     const LayoutUnit table_grid_inline_size,
     const NGTableGroupedChildren& grouped_children,
@@ -685,6 +700,13 @@
   DCHECK_EQ(rows->size(), 0u);
   DCHECK_EQ(cell_block_constraints->size(), 0u);
 
+  // If this isn't the first fragment, avoid side-effects. We need to leave the
+  // NGLayoutResult vector in LayoutBox objects alone, since we're in the middle
+  // of building those.
+  absl::optional<NGDisableSideEffectsScope> disable_side_effects;
+  if (IsResumingLayout(BreakToken()))
+    disable_side_effects.emplace();
+
   const bool is_table_block_size_specified = !Style().LogicalHeight().IsAuto();
   LayoutUnit total_table_block_size;
   wtf_size_t section_index = 0;
@@ -844,6 +866,7 @@
   LayoutUnit border_spacing_after_last_section;
 
   bool has_processed_first_child = false;
+  bool has_container_separation = false;
 
   auto AddCaptionResult = [&](const CaptionResult& caption,
                               LayoutUnit* block_offset) -> void {
@@ -882,12 +905,33 @@
        border_spacing.inline_size * 2)
           .ClampNegativeToZero();
 
+  struct RepeatedFooter {
+    STACK_ALLOCATED();
+
+   public:
+    RepeatedFooter(wtf_size_t section_idx,
+                   LayoutUnit block_size,
+                   const NGBlockBreakToken* incoming_break_token)
+        : section_idx(section_idx),
+          block_size(block_size),
+          incoming_break_token(incoming_break_token) {}
+
+    wtf_size_t section_idx;  // Index into |sections|.
+    LayoutUnit block_size;
+    const NGBlockBreakToken* incoming_break_token;
+  };
+
+  enum ESectionRepeatMode { kNotRepeated, kMayRepeatAgain, kRepeatedLast };
+
   auto CreateSectionConstraintSpace = [&table_writing_direction,
                                        &section_available_inline_size,
-                                       &constraint_space_data, &sections,
-                                       this](const NGBlockNode& section,
-                                             LayoutUnit block_offset,
-                                             wtf_size_t section_index) {
+                                       &constraint_space_data, &sections, this](
+                                          const NGBlockNode& section,
+                                          LayoutUnit block_offset,
+                                          wtf_size_t section_index,
+                                          const absl::optional<RepeatedFooter>
+                                              repeated_footer,
+                                          ESectionRepeatMode repeat_mode) {
     NGConstraintSpaceBuilder section_space_builder(
         ConstraintSpace(), table_writing_direction, /* is_new_fc */ true);
 
@@ -907,11 +951,21 @@
     section_space_builder.SetTableSectionData(constraint_space_data,
                                               section_index);
 
-    if (ConstraintSpace().HasBlockFragmentation()) {
+    if (repeat_mode != kNotRepeated) {
+      if (repeat_mode == kMayRepeatAgain)
+        section_space_builder.SetIsRepeatable();
+    } else if (ConstraintSpace().HasBlockFragmentation()) {
       SetupSpaceBuilderForFragmentation(
           ConstraintSpace(), section, block_offset, &section_space_builder,
           /* is_new_fc */ true,
           container_builder_.RequiresContentBeforeBreaking());
+
+      if (repeated_footer) {
+        // Reserve space for the repeated footer at the block-end of the
+        // fragmentainer. No other section may extend into this area.
+        section_space_builder.ReserveSpaceAtFragmentainerEnd(
+            repeated_footer->block_size);
+      }
     }
 
     return section_space_builder.ToConstraintSpace();
@@ -945,6 +999,49 @@
   absl::optional<TableBoxExtent> table_box_extent;
   absl::optional<LayoutUnit> table_baseline;
 
+  bool has_repeated_header = false;
+  absl::optional<RepeatedFooter> pending_repeated_footer;
+
+  if (ConstraintSpace().HasKnownFragmentainerBlockSize() &&
+      (grouped_children.header || grouped_children.footer)) {
+    // Before layout, we need to go through the table's children, to look for
+    // repeatable headers and footers. This is especially important for footers,
+    // since we need to reserve space for it after any preceding non-repeated
+    // sections (typically tbody).
+    LayoutUnit max_section_block_size =
+        ConstraintSpace().FragmentainerBlockSize() / 4;
+    NGTableChildIterator child_iterator(grouped_children, BreakToken());
+    for (auto entry = child_iterator.NextChild();
+         NGBlockNode child = entry.GetNode();
+         entry = child_iterator.NextChild()) {
+      if (child != grouped_children.header && child != grouped_children.footer)
+        continue;
+
+      // Headers and footers may be repeated if their block-size is one quarter
+      // or less than that of the fragmentainer, AND 'break-inside' has an
+      // applicable avoid* value. Being repeated means that the section is
+      // monolithic, and nothing inside can break.
+      //
+      // See https://www.w3.org/TR/css-tables-3/#repeated-headers
+      LayoutUnit block_size = sections[entry.GetSectionIndex()].block_size;
+      if (block_size > max_section_block_size)
+        continue;
+      if (!IsAvoidBreakValue(ConstraintSpace(), child.Style().BreakInside()))
+        continue;
+
+      if (child == grouped_children.header) {
+        has_repeated_header = true;
+      } else {
+        DCHECK_EQ(child, grouped_children.footer);
+        // We need to reserve space for the repeated footer at the end of the
+        // fragmentainer.
+        pending_repeated_footer.emplace(entry.GetSectionIndex(),
+                                        block_size + border_spacing.block_size,
+                                        entry.GetBreakToken());
+      }
+    }
+  }
+
   LayoutUnit grid_block_size;
   bool broke_inside = false;
   bool is_past_last_section_end = false;
@@ -971,6 +1068,7 @@
     const NGLayoutResult* child_result;
     LayoutUnit child_inline_offset;
     absl::optional<TableBoxExtent> new_table_box_extent;
+    bool is_repeated_section = false;
     if (child.IsTableCaption()) {
       if (!relayout_captions)
         continue;
@@ -1023,8 +1121,10 @@
       } else {
         // Entering the first section in this fragment. This is where the "table
         // box" starts. First check if we already added the block-start border
-        // in a previous fragment.
-        if (entry.GetSectionIndex() > 0 || IsResumingLayout(child_break_token))
+        // in a previous fragment. If we're past the first section, or if we're
+        // resuming or repeating the first section, this is the case.
+        if (entry.GetSectionIndex() > 0 ||
+            (child_break_token && !child_break_token->IsBreakBefore()))
           border_padding_sides_to_include.block_start = false;
         new_table_box_extent =
             BeginTableBoxLayout(child_block_offset, TableBoxBorderPadding());
@@ -1041,10 +1141,44 @@
       LayoutUnit offset_for_childless_section = child_block_offset;
       child_block_offset += collapsible_border_spacing;
 
+      is_repeated_section =
+          (pending_repeated_footer && child == grouped_children.footer) ||
+          (has_repeated_header && child == grouped_children.header);
+
+      bool may_repeat_again = false;
+      if (is_repeated_section) {
+        if (child == grouped_children.header) {
+          // Unless we've already been at the end, we cannot tell whether this
+          // is the last time the header will repeat. We will tentatively have
+          // to make it repeatable. If this turns out to be wrong, because we
+          // reach the end in this fragment, we need to abort and relayout.
+          may_repeat_again = !is_known_to_be_last_table_box_;
+        } else if (child == grouped_children.footer) {
+          // For footers it's easier, though. Since we got all the way to the
+          // footer during layout, this means that this will be the last time
+          // the footer is repeated. We can finish it right away, unless we have
+          // a repeated header as well (which means that we're going to
+          // relayout).
+          pending_repeated_footer.reset();
+          may_repeat_again =
+              !is_known_to_be_last_table_box_ && has_repeated_header;
+        }
+      }
+
+      ESectionRepeatMode repeat_mode = kNotRepeated;
+      if (is_repeated_section)
+        repeat_mode = may_repeat_again ? kMayRepeatAgain : kRepeatedLast;
+
       NGConstraintSpace child_space = CreateSectionConstraintSpace(
-          child, child_block_offset, entry.GetSectionIndex());
-      child_result =
-          child.Layout(child_space, child_break_token, early_break_in_child);
+          child, child_block_offset, entry.GetSectionIndex(),
+          pending_repeated_footer, repeat_mode);
+      if (is_repeated_section) {
+        child_result =
+            child.LayoutRepeatableRoot(child_space, child_break_token);
+      } else {
+        child_result =
+            child.Layout(child_space, child_break_token, early_break_in_child);
+      }
       child_inline_offset = section_inline_offset;
 
       border_spacing_after_last_section = border_spacing.block_size;
@@ -1072,7 +1206,7 @@
           ConstraintSpace().FragmentainerOffsetAtBfc() + child_block_offset;
       NGBreakStatus break_status = BreakBeforeChildIfNeeded(
           ConstraintSpace(), child, *child_result, fragmentainer_block_offset,
-          has_processed_first_child, &container_builder_);
+          has_container_separation, &container_builder_);
       if (break_status == NGBreakStatus::kNeedsEarlierBreak) {
         return RelayoutAndBreakEarlier<NGTableLayoutAlgorithm>(
             container_builder_.EarlyBreak());
@@ -1109,6 +1243,8 @@
 
     if (ConstraintSpace().HasBlockFragmentation()) {
       has_processed_first_child = true;
+      if (!has_container_separation)
+        has_container_separation = !is_repeated_section;
       if (container_builder_.HasInflowChildBreakInside()) {
         broke_inside = true;
         break;
@@ -1140,6 +1276,48 @@
     }
   }
 
+  bool table_box_will_continue =
+      table_box_extent && !is_past_last_section_end && broke_inside;
+  if (has_repeated_header && !table_box_will_continue &&
+      !is_known_to_be_last_table_box_) {
+    // We have already laid out the header in a repeatable manner (with an
+    // outgoing "repeat" break token). However, we managed to finish the table
+    // box in this fragment, so it shouldn't repeat anymore. We now need to
+    // re-layout, with this in mind.
+    return container_builder_.Abort(
+        NGLayoutResult::kNeedsRelayoutAsLastTableBox);
+  }
+
+  if (pending_repeated_footer && table_box_extent) {
+    DCHECK(table_box_will_continue);
+    // We broke before we got to the footer. Add it now.
+    LogicalOffset offset(section_inline_offset, child_block_offset);
+    NGConstraintSpace child_space = CreateSectionConstraintSpace(
+        grouped_children.footer, offset.block_offset,
+        pending_repeated_footer->section_idx,
+        /* repeated_footer */ absl::nullopt, kMayRepeatAgain);
+    const NGLayoutResult* result = grouped_children.footer.LayoutRepeatableRoot(
+        child_space, pending_repeated_footer->incoming_break_token);
+
+    LayoutUnit fragmentainer_block_offset =
+        ConstraintSpace().FragmentainerOffsetAtBfc() + offset.block_offset;
+    NGBreakStatus break_status =
+        BreakBeforeChildIfNeeded(ConstraintSpace(), grouped_children.footer,
+                                 *result, fragmentainer_block_offset,
+                                 has_container_separation, &container_builder_);
+    if (break_status == NGBreakStatus::kContinue) {
+      container_builder_.AddResult(*result, offset);
+    } else {
+      DCHECK_EQ(break_status, NGBreakStatus::kBrokeBefore);
+      // We should only get a break before the footer when laying out the first
+      // table box fragment. While there are rules that make sure that a footer
+      // normally fits (it should only be a quarter of the fragmentainer's
+      // block-size), if the table box starts near the end of the fragmentainer,
+      // we may still run out of space before a repeatable footer.
+      DCHECK(!pending_repeated_footer->incoming_break_token);
+    }
+  }
+
   LayoutUnit column_block_size;
   LogicalRect table_grid_rect;
 
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h
index 28b25c63a..46be8db 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.h
@@ -57,6 +57,8 @@
   };
 
  private:
+  const NGLayoutResult* RelayoutAsLastTableBox();
+
   void ComputeRows(const LayoutUnit table_grid_inline_size,
                    const NGTableGroupedChildren& grouped_children,
                    const Vector<NGTableColumnLocation>& column_locations,
@@ -88,6 +90,10 @@
       const HeapVector<CaptionResult>& captions,
       const NGTableBorders& table_borders,
       const LogicalSize& border_spacing);
+
+  // Set to true when we're re-laying out without repeating table headers and
+  // footers.
+  bool is_known_to_be_last_table_box_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
index c1ac3a1..ad2ad57 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_inline.cc
@@ -23,6 +23,7 @@
 
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
+#include "third_party/blink/renderer/core/layout/ng/svg/layout_ng_svg_text.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_inline_text.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h"
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
@@ -189,6 +190,13 @@
 void LayoutSVGInline::StyleDidChange(StyleDifference diff,
                                      const ComputedStyle* old_style) {
   NOT_DESTROYED();
+  if (diff.HasDifference()) {
+    if (auto* svg_text = DynamicTo<LayoutNGSVGText>(
+            LayoutSVGText::LocateLayoutSVGTextAncestor(this))) {
+      if (svg_text->NeedsTextMetricsUpdate())
+        diff.SetNeedsFullLayout();
+    }
+  }
   LayoutInline::StyleDidChange(diff, old_style);
 
   if (diff.NeedsFullLayout())
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index ee77f04..3a02bd83 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -941,7 +941,6 @@
   visitor->Trace(plugins_changed_observers_);
   visitor->Trace(next_related_page_);
   visitor->Trace(prev_related_page_);
-  visitor->Trace(media_feature_overrides_);
   Supplementable<Page>::Trace(visitor);
 }
 
@@ -1101,7 +1100,7 @@
   if (!media_feature_overrides_) {
     if (value.IsEmpty())
       return;
-    media_feature_overrides_ = MakeGarbageCollected<MediaFeatureOverrides>();
+    media_feature_overrides_ = std::make_unique<MediaFeatureOverrides>();
   }
   media_feature_overrides_->SetOverride(media_feature, value);
   if (media_feature == "prefers-color-scheme" ||
@@ -1112,7 +1111,7 @@
 }
 
 void Page::ClearMediaFeatureOverrides() {
-  media_feature_overrides_ = nullptr;
+  media_feature_overrides_.reset();
   SettingsChanged(ChangeType::kMediaQuery);
   SettingsChanged(ChangeType::kColorScheme);
 }
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index bb22327..96fd41a 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -361,7 +361,7 @@
   void SetMediaFeatureOverride(const AtomicString& media_feature,
                                const String& value);
   const MediaFeatureOverrides* GetMediaFeatureOverrides() const {
-    return media_feature_overrides_.Get();
+    return media_feature_overrides_.get();
   }
   void ClearMediaFeatureOverrides();
 
@@ -516,7 +516,7 @@
   std::unique_ptr<PageScheduler> page_scheduler_;
 
   // Overrides for various media features, set from DevTools.
-  Member<MediaFeatureOverrides> media_feature_overrides_;
+  std::unique_ptr<MediaFeatureOverrides> media_feature_overrides_;
 
   // Emulated vision deficiency, set from DevTools.
   VisionDeficiency vision_deficiency_ = VisionDeficiency::kNoVisionDeficiency;
diff --git a/third_party/blink/renderer/core/paint/build.gni b/third_party/blink/renderer/core/paint/build.gni
index 14084963..b005dce 100644
--- a/third_party/blink/renderer/core/paint/build.gni
+++ b/third_party/blink/renderer/core/paint/build.gni
@@ -259,3 +259,55 @@
   "view_painter.cc",
   "view_painter.h",
 ]
+
+blink_core_tests_paint = [
+  "block_painter_test.cc",
+  "box_paint_invalidator_test.cc",
+  "box_painter_test.cc",
+  "clip_path_clipper_test.cc",
+  "clip_rect_test.cc",
+  "compositing/compositing_reason_finder_test.cc",
+  "compositing/compositing_test.cc",
+  "css_mask_painter_test.cc",
+  "cull_rect_updater_test.cc",
+  "first_meaningful_paint_detector_test.cc",
+  "fragment_data_test.cc",
+  "highlight_painting_utils_test.cc",
+  "html_canvas_painter_test.cc",
+  "image_element_timing_test.cc",
+  "image_paint_timing_detector_test.cc",
+  "inline_text_box_painter_test.cc",
+  "largest_contentful_paint_calculator_test.cc",
+  "link_highlight_impl_test.cc",
+  "ng/ng_box_fragment_painter_test.cc",
+  "ng/ng_highlight_overlay_test.cc",
+  "ng/ng_inline_paint_context_test.cc",
+  "ng/ng_text_fragment_painter_test.cc",
+  "nine_piece_image_grid_test.cc",
+  "object_paint_invalidator_test.cc",
+  "outline_painter_test.cc",
+  "paint_and_raster_invalidation_test.cc",
+  "paint_and_raster_invalidation_test.h",
+  "paint_auto_dark_mode_test.cc",
+  "paint_controller_paint_test.cc",
+  "paint_controller_paint_test.h",
+  "paint_layer_clipper_test.cc",
+  "paint_layer_painter_test.cc",
+  "paint_layer_scrollable_area_test.cc",
+  "paint_layer_test.cc",
+  "paint_property_tree_builder_test.cc",
+  "paint_property_tree_builder_test.h",
+  "paint_property_tree_printer_test.cc",
+  "paint_property_tree_update_tests.cc",
+  "paint_timing_test_helper.h",
+  "pre_paint_tree_walk_test.cc",
+  "scrollable_area_painter_test.cc",
+  "selection_bounds_recorder_test.cc",
+  "svg_container_painter_test.cc",
+  "table_painter_test.cc",
+  "text_paint_timing_detector_test.cc",
+  "text_painter_test.cc",
+  "text_selection_repaint_test.cc",
+  "video_painter_test.cc",
+  "view_painter_test.cc",
+]
diff --git a/third_party/blink/renderer/core/svg/build.gni b/third_party/blink/renderer/core/svg/build.gni
index 76efc7db..fdb0f0e9 100644
--- a/third_party/blink/renderer/core/svg/build.gni
+++ b/third_party/blink/renderer/core/svg/build.gni
@@ -345,3 +345,18 @@
   "svg_zoom_and_pan.cc",
   "svg_zoom_and_pan.h",
 ]
+
+blink_core_tests_svg = [
+  "animation/priority_queue_test.cc",
+  "animation/smil_time_container_test.cc",
+  "animation/svg_smil_element_test.cc",
+  "graphics/svg_image_test.cc",
+  "svg_element_test.cc",
+  "svg_foreign_object_element_test.cc",
+  "svg_path_parser_test.cc",
+  "svg_path_query_test.cc",
+  "svg_resource_document_content_test.cc",
+  "svg_text_content_element_test.cc",
+  "svg_use_element_test.cc",
+  "unsafe_svg_attribute_sanitization_test.cc",
+]
diff --git a/third_party/blink/renderer/core/timing/largest_contentful_paint.cc b/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
index e3d3dfd2..07355839 100644
--- a/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
+++ b/third_party/blink/renderer/core/timing/largest_contentful_paint.cc
@@ -19,8 +19,9 @@
     base::TimeDelta first_animated_frame_time,
     const AtomicString& id,
     const String& url,
-    Element* element)
-    : PerformanceEntry(g_empty_atom, start_time, start_time),
+    Element* element,
+    uint32_t navigation_id)
+    : PerformanceEntry(g_empty_atom, start_time, start_time, navigation_id),
       size_(size),
       render_time_(render_time),
       load_time_(load_time),
diff --git a/third_party/blink/renderer/core/timing/largest_contentful_paint.h b/third_party/blink/renderer/core/timing/largest_contentful_paint.h
index 506b44c4..763a4368 100644
--- a/third_party/blink/renderer/core/timing/largest_contentful_paint.h
+++ b/third_party/blink/renderer/core/timing/largest_contentful_paint.h
@@ -26,7 +26,8 @@
                          base::TimeDelta first_animated_frame_time,
                          const AtomicString& id,
                          const String& url,
-                         Element*);
+                         Element* element,
+                         uint32_t navigation_id);
   ~LargestContentfulPaint() override;
 
   AtomicString entryType() const override;
diff --git a/third_party/blink/renderer/core/timing/layout_shift.cc b/third_party/blink/renderer/core/timing/layout_shift.cc
index 16485fd..9098887 100644
--- a/third_party/blink/renderer/core/timing/layout_shift.cc
+++ b/third_party/blink/renderer/core/timing/layout_shift.cc
@@ -15,17 +15,20 @@
                                  double value,
                                  bool input_detected,
                                  double input_timestamp,
-                                 AttributionList sources) {
+                                 AttributionList sources,
+                                 uint32_t navigation_id) {
   return MakeGarbageCollected<LayoutShift>(start_time, value, input_detected,
-                                           input_timestamp, sources);
+                                           input_timestamp, sources,
+                                           navigation_id);
 }
 
 LayoutShift::LayoutShift(double start_time,
                          double value,
                          bool input_detected,
                          double input_timestamp,
-                         AttributionList sources)
-    : PerformanceEntry(g_empty_atom, start_time, start_time),
+                         AttributionList sources,
+                         uint32_t navigation_id)
+    : PerformanceEntry(g_empty_atom, start_time, start_time, navigation_id),
       value_(value),
       had_recent_input_(input_detected),
       most_recent_input_timestamp_(input_timestamp),
diff --git a/third_party/blink/renderer/core/timing/layout_shift.h b/third_party/blink/renderer/core/timing/layout_shift.h
index 912b018..1e6adbe 100644
--- a/third_party/blink/renderer/core/timing/layout_shift.h
+++ b/third_party/blink/renderer/core/timing/layout_shift.h
@@ -30,13 +30,15 @@
                              double value,
                              bool input_detected,
                              double input_timestamp,
-                             AttributionList sources);
+                             AttributionList sources,
+                             uint32_t navigation_id);
 
   explicit LayoutShift(double start_time,
                        double value,
                        bool input_detected,
                        double input_timestamp,
-                       AttributionList sources);
+                       AttributionList sources,
+                       uint32_t navigation_id);
 
   ~LayoutShift() override;
 
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index 04b3626..e66d8b83 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -67,6 +67,7 @@
 #include "third_party/blink/renderer/core/timing/layout_shift.h"
 #include "third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.h"
 #include "third_party/blink/renderer/core/timing/performance_element_timing.h"
+#include "third_party/blink/renderer/core/timing/performance_entry.h"
 #include "third_party/blink/renderer/core/timing/performance_event_timing.h"
 #include "third_party/blink/renderer/core/timing/performance_long_task_timing.h"
 #include "third_party/blink/renderer/core/timing/performance_mark.h"
@@ -732,7 +733,8 @@
 void Performance::AddPaintTiming(PerformancePaintTiming::PaintType type,
                                  base::TimeTicks start_time) {
   PerformanceEntry* entry = MakeGarbageCollected<PerformancePaintTiming>(
-      type, MonotonicTimeToDOMHighResTimeStamp(start_time));
+      type, MonotonicTimeToDOMHighResTimeStamp(start_time),
+      PerformanceEntry::GetNavigationId(GetExecutionContext()));
   // Always buffer First Paint & First Contentful Paint.
   if (type == PerformancePaintTiming::PaintType::kFirstPaint)
     first_paint_timing_ = entry;
@@ -755,14 +757,16 @@
                                     const AtomicString& container_name) {
   double dom_high_res_start_time =
       MonotonicTimeToDOMHighResTimeStamp(start_time);
+
+  ExecutionContext* execution_context = GetExecutionContext();
   auto* entry = MakeGarbageCollected<PerformanceLongTaskTiming>(
       dom_high_res_start_time,
       // Convert the delta between start and end times to an int to reduce the
       // granularity of the duration to 1 ms.
       static_cast<int>(MonotonicTimeToDOMHighResTimeStamp(end_time) -
                        dom_high_res_start_time),
-      name, container_type, container_src, container_id, container_name);
-  ExecutionContext* execution_context = GetExecutionContext();
+      name, container_type, container_src, container_id, container_name,
+      PerformanceEntry::GetNavigationId(execution_context));
   if (longtask_buffer_.size() < kDefaultLongTaskBufferSize) {
     longtask_buffer_.push_back(entry);
   } else {
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.cc b/third_party/blink/renderer/core/timing/performance_element_timing.cc
index 2565ad5..da6995f 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.cc
@@ -22,7 +22,8 @@
     int naturalWidth,
     int naturalHeight,
     const AtomicString& id,
-    Element* element) {
+    Element* element,
+    uint32_t navigation_id) {
   // It is possible to 'paint' images which have naturalWidth or naturalHeight
   // equal to 0.
   DCHECK_GE(naturalWidth, 0);
@@ -31,7 +32,7 @@
   double start_time = render_time != 0.0 ? render_time : load_time;
   return MakeGarbageCollected<PerformanceElementTiming>(
       name, start_time, url, intersection_rect, render_time, load_time,
-      identifier, naturalWidth, naturalHeight, id, element);
+      identifier, naturalWidth, naturalHeight, id, element, navigation_id);
 }
 
 PerformanceElementTiming::PerformanceElementTiming(
@@ -45,8 +46,9 @@
     int naturalWidth,
     int naturalHeight,
     const AtomicString& id,
-    Element* element)
-    : PerformanceEntry(name, start_time, start_time),
+    Element* element,
+    uint32_t navigation_id)
+    : PerformanceEntry(name, start_time, start_time, navigation_id),
       element_(element),
       intersection_rect_(DOMRectReadOnly::FromRectF(intersection_rect)),
       render_time_(render_time),
diff --git a/third_party/blink/renderer/core/timing/performance_element_timing.h b/third_party/blink/renderer/core/timing/performance_element_timing.h
index 3dfb3f07..14d2bf3 100644
--- a/third_party/blink/renderer/core/timing/performance_element_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_element_timing.h
@@ -31,7 +31,8 @@
                                           int naturalWidth,
                                           int naturalHeight,
                                           const AtomicString& id,
-                                          Element*);
+                                          Element*,
+                                          uint32_t navigation_id);
   PerformanceElementTiming(const AtomicString& name,
                            DOMHighResTimeStamp start_time,
                            const String& url,
@@ -42,7 +43,8 @@
                            int naturalWidth,
                            int naturalHeight,
                            const AtomicString& id,
-                           Element*);
+                           Element*,
+                           uint32_t navigation_id);
 
   ~PerformanceElementTiming() override;
 
diff --git a/third_party/blink/renderer/core/timing/performance_entry.cc b/third_party/blink/renderer/core/timing/performance_entry.cc
index 99567cbc..f2bf4b7 100644
--- a/third_party/blink/renderer/core/timing/performance_entry.cc
+++ b/third_party/blink/renderer/core/timing/performance_entry.cc
@@ -34,8 +34,10 @@
 #include "third_party/blink/public/mojom/timing/performance_mark_or_measure.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/performance_entry_names.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 
 namespace blink {
 
@@ -126,11 +128,31 @@
   return kInvalid;
 }
 
+// static
 uint32_t PerformanceEntry::GetNavigationId(ScriptState* script_state) {
   const auto* local_dom_window = LocalDOMWindow::From(script_state);
-  if (!local_dom_window || !local_dom_window->GetFrame()) {
-    return 1;
-  }
+  // local_dom_window is null in some browser tests and unit tests.
+  // The navigation_id starts from 1. Without a window, there would be no
+  // subsequent navigations.
+  if (!local_dom_window)
+    return kNavigationIdDefaultValue;
+
+  // Calling GetFrame() on a window of a detached frame returns null.
+  if (!local_dom_window->GetFrame())
+    return kNavigationIdDefaultValue;
+
+  return local_dom_window->GetFrame()->GetNavigationId();
+}
+
+// static
+uint32_t PerformanceEntry::GetNavigationId(ExecutionContext* context) {
+  const auto* local_dom_window = DynamicTo<LocalDOMWindow>(context);
+  if (!local_dom_window)
+    return kNavigationIdDefaultValue;
+
+  if (!local_dom_window->GetFrame())
+    return kNavigationIdDefaultValue;
+
   return local_dom_window->GetFrame()->GetNavigationId();
 }
 
diff --git a/third_party/blink/renderer/core/timing/performance_entry.h b/third_party/blink/renderer/core/timing/performance_entry.h
index de6b2a3..cd321663 100644
--- a/third_party/blink/renderer/core/timing/performance_entry.h
+++ b/third_party/blink/renderer/core/timing/performance_entry.h
@@ -44,10 +44,13 @@
 class ScriptState;
 class ScriptValue;
 class V8ObjectBuilder;
+class ExecutionContext;
 
 using PerformanceEntryType = unsigned;
 using PerformanceEntryTypeMask = unsigned;
 
+constexpr uint32_t kNavigationIdDefaultValue = 1;
+
 class CORE_EXPORT PerformanceEntry : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -115,6 +118,7 @@
   }
 
   static uint32_t GetNavigationId(ScriptState* script_state);
+  static uint32_t GetNavigationId(ExecutionContext* context);
 
   // PerformanceMark/Measure override this and it returns Mojo structure pointer
   // which has all members of PerformanceMark/Measure. Common data members are
@@ -127,11 +131,11 @@
   PerformanceEntry(const AtomicString& name,
                    double start_time,
                    double finish_time,
-                   uint32_t navigation_id = 1);
+                   uint32_t navigation_id);
   PerformanceEntry(double duration,
                    const AtomicString& name,
                    double start_time,
-                   uint32_t navigation_id = 1);
+                   uint32_t navigation_id);
 
   virtual void BuildJSONValue(V8ObjectBuilder&) const;
 
diff --git a/third_party/blink/renderer/core/timing/performance_entry_test.cc b/third_party/blink/renderer/core/timing/performance_entry_test.cc
index 42d6ad4c..a6bd0e0 100644
--- a/third_party/blink/renderer/core/timing/performance_entry_test.cc
+++ b/third_party/blink/renderer/core/timing/performance_entry_test.cc
@@ -5,24 +5,18 @@
 #include "third_party/blink/renderer/core/timing/performance_entry.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
 
 class PerformanceEntryTest : public testing::Test {};
-TEST(PerformanceEntryTest, GetNavigationCounter) {
+TEST(PerformanceEntryTest, GetNavigationId) {
   V8TestingScope scope;
-  ScriptState* script_state = scope.GetScriptState();
 
-  EXPECT_EQ(1u, PerformanceEntry::GetNavigationId(script_state));
+  EXPECT_EQ(1u, PerformanceEntry::GetNavigationId(scope.GetScriptState()));
 
   scope.GetFrame().IncrementNavigationId();
-  EXPECT_EQ(2u, PerformanceEntry::GetNavigationId(script_state));
-
-  scope.GetFrame().IncrementNavigationId();
-  EXPECT_EQ(3u, PerformanceEntry::GetNavigationId(script_state));
+  EXPECT_EQ(2u, PerformanceEntry::GetNavigationId(scope.GetExecutionContext()));
 }
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/performance_event_timing.cc b/third_party/blink/renderer/core/timing/performance_event_timing.cc
index 502ede3f..6314ae2 100644
--- a/third_party/blink/renderer/core/timing/performance_event_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_event_timing.cc
@@ -21,13 +21,14 @@
     DOMHighResTimeStamp processing_start,
     DOMHighResTimeStamp processing_end,
     bool cancelable,
-    Node* target) {
+    Node* target,
+    uint32_t navigation_id) {
   // TODO(npm): enable this DCHECK once https://crbug.com/852846 is fixed.
   // DCHECK_LE(start_time, processing_start);
   DCHECK_LE(processing_start, processing_end);
   return MakeGarbageCollected<PerformanceEventTiming>(
       event_type, performance_entry_names::kEvent, start_time, processing_start,
-      processing_end, cancelable, target);
+      processing_end, cancelable, target, navigation_id);
 }
 
 // static
@@ -37,7 +38,7 @@
       MakeGarbageCollected<PerformanceEventTiming>(
           entry->name(), performance_entry_names::kFirstInput,
           entry->startTime(), entry->processingStart(), entry->processingEnd(),
-          entry->cancelable(), entry->target());
+          entry->cancelable(), entry->target(), entry->navigationId());
   first_input->SetDuration(entry->duration());
   return first_input;
 }
@@ -49,8 +50,9 @@
     DOMHighResTimeStamp processing_start,
     DOMHighResTimeStamp processing_end,
     bool cancelable,
-    Node* target)
-    : PerformanceEntry(event_type, start_time, 0.0),
+    Node* target,
+    uint32_t navigation_id)
+    : PerformanceEntry(event_type, start_time, 0.0, navigation_id),
       entry_type_(entry_type),
       processing_start_(processing_start),
       processing_end_(processing_end),
diff --git a/third_party/blink/renderer/core/timing/performance_event_timing.h b/third_party/blink/renderer/core/timing/performance_event_timing.h
index 4aac5b65f..f3b27ae 100644
--- a/third_party/blink/renderer/core/timing/performance_event_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_event_timing.h
@@ -23,7 +23,8 @@
                                         DOMHighResTimeStamp processing_start,
                                         DOMHighResTimeStamp processing_end,
                                         bool cancelable,
-                                        Node* target);
+                                        Node* target,
+                                        uint32_t navigation_id);
 
   static PerformanceEventTiming* CreateFirstInputTiming(
       PerformanceEventTiming* entry);
@@ -34,7 +35,8 @@
                          DOMHighResTimeStamp processing_start,
                          DOMHighResTimeStamp processing_end,
                          bool cancelable,
-                         Node* target);
+                         Node* target,
+                         uint32_t navigation_id);
   ~PerformanceEventTiming() override;
 
   AtomicString entryType() const override { return entry_type_; }
diff --git a/third_party/blink/renderer/core/timing/performance_long_task_timing.cc b/third_party/blink/renderer/core/timing/performance_long_task_timing.cc
index 7fa59e3..0cf5275 100644
--- a/third_party/blink/renderer/core/timing/performance_long_task_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_long_task_timing.cc
@@ -19,10 +19,12 @@
     const AtomicString& culprit_type,
     const AtomicString& culprit_src,
     const AtomicString& culprit_id,
-    const AtomicString& culprit_name)
-    : PerformanceEntry(duration, name, start_time) {
+    const AtomicString& culprit_name,
+    const uint32_t navigation_id)
+    : PerformanceEntry(duration, name, start_time, navigation_id) {
   auto* attribution_entry = MakeGarbageCollected<TaskAttributionTiming>(
-      "unknown", culprit_type, culprit_src, culprit_id, culprit_name);
+      "unknown", culprit_type, culprit_src, culprit_id, culprit_name,
+      navigation_id);
   attribution_.push_back(*attribution_entry);
 }
 
diff --git a/third_party/blink/renderer/core/timing/performance_long_task_timing.h b/third_party/blink/renderer/core/timing/performance_long_task_timing.h
index 4d0dc2b..f2137a3 100644
--- a/third_party/blink/renderer/core/timing/performance_long_task_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_long_task_timing.h
@@ -30,7 +30,8 @@
                             const AtomicString& culprit_type,
                             const AtomicString& culprit_src,
                             const AtomicString& culprit_id,
-                            const AtomicString& culprit_name);
+                            const AtomicString& culprit_name,
+                            const uint32_t navigation_id);
   ~PerformanceLongTaskTiming() override;
 
   AtomicString entryType() const override;
diff --git a/third_party/blink/renderer/core/timing/performance_observer_test.cc b/third_party/blink/renderer/core/timing/performance_observer_test.cc
index 4455236..312781b 100644
--- a/third_party/blink/renderer/core/timing/performance_observer_test.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer_test.cc
@@ -77,8 +77,9 @@
   EXPECT_EQ(0, NumPerformanceEntries());
 
   // add a layout-shift to performance so getEntries() returns it
-  auto* entry = LayoutShift::Create(0.0, 1234, true, 5678,
-                                    LayoutShift::AttributionList());
+  auto* entry =
+      LayoutShift::Create(0.0, 1234, true, 5678, LayoutShift::AttributionList(),
+                          /*navigation_id=*/1);
   base_->AddLayoutShiftBuffer(*entry);
 
   // call observe with the buffered flag
diff --git a/third_party/blink/renderer/core/timing/performance_paint_timing.cc b/third_party/blink/renderer/core/timing/performance_paint_timing.cc
index 7d74bbb..5be18ad 100644
--- a/third_party/blink/renderer/core/timing/performance_paint_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_paint_timing.cc
@@ -31,10 +31,12 @@
 }  // namespace
 
 PerformancePaintTiming::PerformancePaintTiming(PaintType type,
-                                               double start_time)
+                                               double start_time,
+                                               uint32_t navigation_id)
     : PerformanceEntry(FromPaintTypeToString(type),
                        start_time,
-                       start_time) {}
+                       start_time,
+                       navigation_id) {}
 
 PerformancePaintTiming::~PerformancePaintTiming() = default;
 
diff --git a/third_party/blink/renderer/core/timing/performance_paint_timing.h b/third_party/blink/renderer/core/timing/performance_paint_timing.h
index 30db71c..2c2a1a5 100644
--- a/third_party/blink/renderer/core/timing/performance_paint_timing.h
+++ b/third_party/blink/renderer/core/timing/performance_paint_timing.h
@@ -16,7 +16,7 @@
  public:
   enum class PaintType { kFirstPaint, kFirstContentfulPaint };
 
-  PerformancePaintTiming(PaintType, double start_time);
+  PerformancePaintTiming(PaintType, double start_time, uint32_t navigation_id);
   ~PerformancePaintTiming() override;
 
   AtomicString entryType() const override;
diff --git a/third_party/blink/renderer/core/timing/performance_resource_timing.cc b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
index af1fa42..95a3b941 100644
--- a/third_party/blink/renderer/core/timing/performance_resource_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_resource_timing.cc
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/performance_entry_names.h"
 #include "third_party/blink/renderer/core/timing/performance.h"
+#include "third_party/blink/renderer/core/timing/performance_entry.h"
 #include "third_party/blink/renderer/core/timing/performance_mark.h"
 #include "third_party/blink/renderer/core/timing/performance_measure.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -66,7 +67,8 @@
                            time_origin,
                            info.response_end,
                            info.allow_negative_values,
-                           cross_origin_isolated_capability)),
+                           cross_origin_isolated_capability),
+                       PerformanceEntry::GetNavigationId(context)),
       initiator_type_(initiator_type.IsEmpty()
                           ? fetch_initiator_type_names::kOther
                           : initiator_type),
@@ -101,6 +103,7 @@
 // TODO(https://crbug.com/900700): Set a Mojo pending receiver for
 // WorkerTimingContainer in |worker_timing_receiver_| when a service worker
 // controls a page.
+// The navigation_id for navigation timing is always 1.
 PerformanceResourceTiming::PerformanceResourceTiming(
     const AtomicString& name,
     base::TimeTicks time_origin,
@@ -108,7 +111,7 @@
     bool is_secure_transport,
     HeapVector<Member<PerformanceServerTiming>> server_timing,
     ExecutionContext* context)
-    : PerformanceEntry(name, 0.0, 0.0),
+    : PerformanceEntry(name, 0.0, 0.0, kNavigationIdDefaultValue),
       time_origin_(time_origin),
       cross_origin_isolated_capability_(cross_origin_isolated_capability),
       context_type_(mojom::blink::RequestContextType::HYPERLINK),
diff --git a/third_party/blink/renderer/core/timing/task_attribution_timing.cc b/third_party/blink/renderer/core/timing/task_attribution_timing.cc
index 67c00aa..6b852fbc 100644
--- a/third_party/blink/renderer/core/timing/task_attribution_timing.cc
+++ b/third_party/blink/renderer/core/timing/task_attribution_timing.cc
@@ -14,8 +14,9 @@
                                              const AtomicString& container_type,
                                              const AtomicString& container_src,
                                              const AtomicString& container_id,
-                                             const AtomicString& container_name)
-    : PerformanceEntry(name, 0.0, 0.0),
+                                             const AtomicString& container_name,
+                                             const uint32_t navigation_id)
+    : PerformanceEntry(name, 0.0, 0.0, navigation_id),
       container_type_(container_type),
       container_src_(container_src),
       container_id_(container_id),
diff --git a/third_party/blink/renderer/core/timing/task_attribution_timing.h b/third_party/blink/renderer/core/timing/task_attribution_timing.h
index c73d4fcd..61e4e51f 100644
--- a/third_party/blink/renderer/core/timing/task_attribution_timing.h
+++ b/third_party/blink/renderer/core/timing/task_attribution_timing.h
@@ -30,7 +30,8 @@
                         const AtomicString& container_type,
                         const AtomicString& container_src,
                         const AtomicString& container_id,
-                        const AtomicString& container_name);
+                        const AtomicString& container_name,
+                        const uint32_t navigation_id);
   ~TaskAttributionTiming() override;
 
  private:
diff --git a/third_party/blink/renderer/core/timing/visibility_state_entry.cc b/third_party/blink/renderer/core/timing/visibility_state_entry.cc
index 5f23911f..828f16d 100644
--- a/third_party/blink/renderer/core/timing/visibility_state_entry.cc
+++ b/third_party/blink/renderer/core/timing/visibility_state_entry.cc
@@ -9,8 +9,10 @@
 
 namespace blink {
 
-VisibilityStateEntry::VisibilityStateEntry(AtomicString name, double start_time)
-    : PerformanceEntry(name, start_time, start_time) {}
+VisibilityStateEntry::VisibilityStateEntry(AtomicString name,
+                                           double start_time,
+                                           uint32_t navigation_id)
+    : PerformanceEntry(name, start_time, start_time, navigation_id) {}
 
 VisibilityStateEntry::~VisibilityStateEntry() = default;
 
diff --git a/third_party/blink/renderer/core/timing/visibility_state_entry.h b/third_party/blink/renderer/core/timing/visibility_state_entry.h
index 123515a6..96d03b76 100644
--- a/third_party/blink/renderer/core/timing/visibility_state_entry.h
+++ b/third_party/blink/renderer/core/timing/visibility_state_entry.h
@@ -14,7 +14,9 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  VisibilityStateEntry(AtomicString name, double start_time);
+  VisibilityStateEntry(AtomicString name,
+                       double start_time,
+                       uint32_t navigation_id);
   ~VisibilityStateEntry() override;
 
   AtomicString entryType() const override;
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index 0fb71fca..52981d0 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -63,6 +63,7 @@
 #include "third_party/blink/renderer/core/timing/largest_contentful_paint.h"
 #include "third_party/blink/renderer/core/timing/layout_shift.h"
 #include "third_party/blink/renderer/core/timing/performance_element_timing.h"
+#include "third_party/blink/renderer/core/timing/performance_entry.h"
 #include "third_party/blink/renderer/core/timing/performance_event_timing.h"
 #include "third_party/blink/renderer/core/timing/performance_observer.h"
 #include "third_party/blink/renderer/core/timing/performance_timing.h"
@@ -406,7 +407,10 @@
       event_type, MonotonicTimeToDOMHighResTimeStamp(start_time),
       MonotonicTimeToDOMHighResTimeStamp(processing_start),
       MonotonicTimeToDOMHighResTimeStamp(processing_end), event.cancelable(),
-      event.target() ? event.target()->ToNode() : nullptr);
+      event.target() ? event.target()->ToNode() : nullptr,
+      PerformanceEntry::GetNavigationId(
+          GetExecutionContext()));  // TODO(haoliuk): Add WPT for Event Timing.
+                                    // See crbug.com/1320878.
   absl::optional<int> key_code;
   if (event.IsKeyboardEvent())
     key_code = DynamicTo<KeyboardEvent>(event)->keyCode();
@@ -614,7 +618,8 @@
   PerformanceElementTiming* entry = PerformanceElementTiming::Create(
       name, url, rect, MonotonicTimeToDOMHighResTimeStamp(start_time),
       MonotonicTimeToDOMHighResTimeStamp(load_time), identifier,
-      intrinsic_size.width(), intrinsic_size.height(), id, element);
+      intrinsic_size.width(), intrinsic_size.height(), id, element,
+      PerformanceEntry::GetNavigationId(GetExecutionContext()));
   TRACE_EVENT2("loading", "PerformanceElementTiming", "data",
                entry->ToTracedValue(), "frame",
                ToTraceValue(DomWindow()->GetFrame()));
@@ -652,7 +657,11 @@
   DCHECK(RuntimeEnabledFeatures::VisibilityStateEntryEnabled());
   VisibilityStateEntry* entry = MakeGarbageCollected<VisibilityStateEntry>(
       PageHiddenStateString(!is_visible),
-      MonotonicTimeToDOMHighResTimeStamp(timestamp));
+      MonotonicTimeToDOMHighResTimeStamp(timestamp),
+      PerformanceEntry::GetNavigationId(
+          GetExecutionContext()));  // Todo(haoliuk): Add WPT for
+                                    // VisibilityStateEntry. See
+                                    // crbug.com/1320878.
   if (HasObserverFor(PerformanceEntry::kVisibilityState))
     NotifyObserversOfEntry(*entry);
 
@@ -692,7 +701,8 @@
       render_timestamp.is_zero() ? load_timestamp : render_timestamp;
   auto* entry = MakeGarbageCollected<LargestContentfulPaint>(
       start_timestamp.InMillisecondsF(), render_timestamp, paint_size,
-      load_timestamp, first_animated_frame_timestamp, id, url, element);
+      load_timestamp, first_animated_frame_timestamp, id, url, element,
+      PerformanceEntry::GetNavigationId(GetExecutionContext()));
   if (HasObserverFor(PerformanceEntry::kLargestContentfulPaint))
     NotifyObserversOfEntry(*entry);
   AddLargestContentfulPaint(entry);
diff --git a/third_party/blink/renderer/core/timing/window_performance_test.cc b/third_party/blink/renderer/core/timing/window_performance_test.cc
index ca568bd..a6d9334 100644
--- a/third_party/blink/renderer/core/timing/window_performance_test.cc
+++ b/third_party/blink/renderer/core/timing/window_performance_test.cc
@@ -132,7 +132,8 @@
 
   PerformanceEventTiming* CreatePerformanceEventTiming(
       const AtomicString& name) {
-    return PerformanceEventTiming::Create(name, 0.0, 0.0, 0.0, false, nullptr);
+    return PerformanceEventTiming::Create(name, 0.0, 0.0, 0.0, false, nullptr,
+                                          1);
   }
 
   LocalFrame* GetFrame() const { return &page_holder_->GetFrame(); }
diff --git a/third_party/blink/renderer/modules/mediarecorder/OWNERS b/third_party/blink/renderer/modules/mediarecorder/OWNERS
index 072fd09f..75deb9a 100644
--- a/third_party/blink/renderer/modules/mediarecorder/OWNERS
+++ b/third_party/blink/renderer/modules/mediarecorder/OWNERS
@@ -1,3 +1,4 @@
+file://media/OWNERS
 guidou@chromium.org
 handellm@google.com
 
diff --git a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc
index 9ef9b9e..4f0edb2 100644
--- a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc
@@ -30,10 +30,11 @@
   kInvalidCropTargetFormat = 2,
   kRejectedWithErrorGeneric = 3,
   kRejectedWithUnsupportedCaptureDevice = 4,
-  kRejectedWithErrorUnknownDeviceId = 5,
+  kRejectedWithErrorUnknownDeviceId_DEPRECATED = 5,
   kRejectedWithNotImplemented = 6,
   kNonIncreasingCropVersion = 7,
-  kMaxValue = kNonIncreasingCropVersion
+  kInvalidCropTarget = 8,
+  kMaxValue = kInvalidCropTarget
 };
 
 void RecordUma(CropToResult result) {
@@ -84,18 +85,18 @@
                          "Unknown error.");
       return;
     case media::mojom::CropRequestResult::kUnsupportedCaptureDevice:
+      // Note that this is an unsupported device; not an unsupported Element.
+      // This should essentially not happen. If it happens, it indicates
+      // something in the capture pipeline has been changed.
       RecordUma(CropToResult::kRejectedWithUnsupportedCaptureDevice);
       RaiseCropException(resolver, DOMExceptionCode::kAbortError,
                          "Unsupported device.");
       return;
-    case media::mojom::CropRequestResult::kErrorUnknownDeviceId:
-      RecordUma(CropToResult::kRejectedWithErrorUnknownDeviceId);
-      RaiseCropException(resolver, DOMExceptionCode::kAbortError,
-                         "Unknown device.");
-      return;
     case media::mojom::CropRequestResult::kNotImplemented:
+      // Unimplemented codepath reached, OTHER than lacking support for
+      // a specific Element subtype.
       RecordUma(CropToResult::kRejectedWithNotImplemented);
-      RaiseCropException(resolver, DOMExceptionCode::kAbortError,
+      RaiseCropException(resolver, DOMExceptionCode::kOperationError,
                          "Not implemented.");
       return;
     case media::mojom::CropRequestResult::kNonIncreasingCropVersion:
@@ -108,6 +109,11 @@
       RaiseCropException(resolver, DOMExceptionCode::kAbortError,
                          "Non-increasing crop version.");
       return;
+    case media::mojom::CropRequestResult::kInvalidCropTarget:
+      RecordUma(CropToResult::kNonIncreasingCropVersion);
+      RaiseCropException(resolver, DOMExceptionCode::kNotAllowedError,
+                         "Invalid CropTarget.");
+      return;
   }
 
   NOTREACHED();
diff --git a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.idl b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.idl
index 6a72080..5bda5d5 100644
--- a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.idl
+++ b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.idl
@@ -7,7 +7,7 @@
     Exposed = Window,
     RuntimeEnabled = RegionCapture
 ]
-interface BrowserCaptureMediaStreamTrack : FocusableMediaStreamTrack {
+interface BrowserCaptureMediaStreamTrack : MediaStreamTrack {
   // 1. If |cropTarget| is a non-empty string, start cropping the track
   //    to the coordinates of the element represented by |cropTarget|.
   //    Return a Promise that resolves once cropping has been fully initiated
@@ -18,4 +18,17 @@
   //    propagated and subsequent frames are guaranteed to be uncropped.
   [CallWith = ScriptState, RaisesException, MeasureAs = RegionCapture]
   Promise<void> cropTo(CropTarget? crop_id);
+
+  // See FocusableMediaStreamTrack.focus() for more details of what this method
+  // does and how it behaves. It is here because:
+  // * BrowserCaptureMediaStreamTrack is for tab-capture, and is shipped.
+  // * FocusableMediaStreamTrack is for window-capture, and is in Origin Trial.
+  // Both tabs and windows are focusable, so both expose focus() if the
+  // ConditionalFocus OT is active.
+  [
+    CallWith = ExecutionContext,
+    RaisesException,
+    MeasureAs = ConditionalFocus,
+    RuntimeEnabled = ConditionalFocus
+  ] void focus(CaptureStartFocusBehavior focus_behavior);
 };
diff --git a/third_party/blink/renderer/modules/mediastream/focusable_media_stream_track.idl b/third_party/blink/renderer/modules/mediastream/focusable_media_stream_track.idl
index a1df9953..321201c7 100644
--- a/third_party/blink/renderer/modules/mediastream/focusable_media_stream_track.idl
+++ b/third_party/blink/renderer/modules/mediastream/focusable_media_stream_track.idl
@@ -23,6 +23,10 @@
   // Promise<MediaStream> is resolved, or if the call comes too late due
   // to processing delays, the user agent assumes an implicit call with |focus|
   // set to a value decided by the user agent itself.
-  [CallWith = ExecutionContext, RaisesException, MeasureAs = ConditionalFocus]
-  void focus(CaptureStartFocusBehavior focus_behavior);
+  [
+    CallWith = ExecutionContext,
+    RaisesException,
+    MeasureAs = ConditionalFocus,
+    RuntimeEnabled = ConditionalFocus  // Gate exposure in sub-classes.
+  ] void focus(CaptureStartFocusBehavior focus_behavior);
 };
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
index 07db88dc..0a7ebd28 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
@@ -240,12 +240,6 @@
       (display_surface_type == media::mojom::DisplayCaptureSurfaceType::WINDOW);
 
   if (is_tab_capture && RuntimeEnabledFeatures::RegionCaptureEnabled(context)) {
-    // Note:
-    // * ConditionalFocus is `implied_by` RegionCapture.
-    // * BrowserCaptureMediaStreamTrack a subclass of FocusableMediaStreamTrack.
-    // Therefore, tab-capture with ConditionalFocus/RegionCapture active
-    // instantiates a track on which focus() is exposed - as intended.
-    DCHECK(RuntimeEnabledFeatures::ConditionalFocusEnabled(context));
     return MakeGarbageCollected<BrowserCaptureMediaStreamTrack>(
         context, component, std::move(callback), descriptor_id);
   } else if ((is_tab_capture || is_window_capture) &&
diff --git a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
index afcd09d..36aca86 100644
--- a/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
+++ b/third_party/blink/renderer/modules/scheduler/task_attribution_tracker_impl.cc
@@ -194,6 +194,9 @@
   }
   v8::Isolate* isolate = script_state->GetIsolate();
   DCHECK(isolate);
+  if (isolate->IsExecutionTerminating()) {
+    return absl::nullopt;
+  }
   // If not empty, the value must be a ScriptWrappableTaskId.
   NonThrowableExceptionState exception_state;
   ScriptWrappableTaskId* script_wrappable_task_id =
@@ -214,6 +217,9 @@
   ScriptState::Scope scope(script_state);
   v8::Isolate* isolate = script_state->GetIsolate();
   DCHECK(isolate);
+  if (isolate->IsExecutionTerminating()) {
+    return;
+  }
   v8::Local<v8::Context> context = script_state->GetContext();
   DCHECK(!context.IsEmpty());
 
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
index eafb415..5ca8acb9 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_external_texture.cc
@@ -203,6 +203,17 @@
   }
 
   gfx::ColorSpace srcColorSpace = media_video_frame->ColorSpace();
+  // It should be very rare that a frame didn't get a valid colorspace through
+  // the guessing process:
+  // https://source.chromium.org/chromium/chromium/src/+/main:media/base/video_color_space.cc;l=69;drc=6c9cfff09be8397270b376a4e4407328694e97fa
+  // The historical rule for this was to use BT.601 for SD content and BT.709
+  // for HD content:
+  // https://source.chromium.org/chromium/chromium/src/+/main:media/ffmpeg/ffmpeg_common.cc;l=683;drc=1946212ac0100668f14eb9e2843bdd846e510a1e)
+  // We prefer always using BT.709 since SD content in practice is down-scaled
+  // HD content, not NTSC broadcast content.
+  if (!srcColorSpace.IsValid()) {
+    srcColorSpace = gfx::ColorSpace::CreateREC709();
+  }
   gfx::ColorSpace dstColorSpace =
       PredefinedColorSpaceToGfxColorSpace(dst_predefined_color_space);
 
diff --git a/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc b/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
index 8224c0e..9857899 100644
--- a/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
+++ b/third_party/blink/renderer/platform/bindings/v8_object_constructor.cc
@@ -77,14 +77,18 @@
       type->GetV8ClassTemplate(isolate, world).As<v8::FunctionTemplate>();
   // Getting the function might fail if we're running out of stack or memory.
   v8::Local<v8::Function> interface_object;
-  bool get_interface_object =
-      interface_template->GetFunction(context).ToLocal(&interface_object);
-  if (UNLIKELY(!get_interface_object)) {
-    // For investigation of crbug.com/1247628
-    static crash_reporter::CrashKeyString<64> crash_key(
-        "blink__create_interface_object");
-    crash_key.Set(type->interface_name);
-    CHECK(get_interface_object);
+  {
+    v8::TryCatch try_catch(isolate);
+    bool get_interface_object =
+        interface_template->GetFunction(context).ToLocal(&interface_object);
+    if (UNLIKELY(!get_interface_object)) {
+      // For investigation of crbug.com/1247628
+      static crash_reporter::CrashKeyString<64> crash_key(
+          "blink__create_interface_object");
+      crash_key.Set(type->interface_name);
+      CHECK(!try_catch.HasCaught());
+      CHECK(get_interface_object);
+    }
   }
 
   if (type->parent_class) {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_data.h b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_data.h
index c05740c..57702e79 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_data.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_font_data.h
@@ -69,14 +69,6 @@
     }
   }
 
-  float SizePerUnit(const SkTypeface& typeface) const {
-    if (size_per_unit_ != kInvalidFallbackMetricsValue)
-      return size_per_unit_;
-    int units_per_em = typeface.getUnitsPerEm();
-    size_per_unit_ = font_.getSize() / units_per_em;
-    return size_per_unit_;
-  }
-
   scoped_refptr<OpenTypeVerticalData> VerticalData() {
     if (!vertical_data_) {
       DCHECK_NE(ascent_fallback_, kInvalidFallbackMetricsValue);
@@ -96,7 +88,7 @@
 
   // Capture these scaled fallback metrics from FontPlatformData so that a
   // OpenTypeVerticalData object can be constructed from them when needed.
-  mutable float size_per_unit_;
+  float size_per_unit_;
   float ascent_fallback_;
   float height_fallback_;
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index a06dbe7..3d6e72c 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -448,7 +448,6 @@
       name: "ConditionalFocus",
       origin_trial_feature_name: "ConditionalFocus",
       status: {"Android": "", "default": "experimental"},
-      implied_by: ["RegionCapture"],
     },
     {
       name: "ConsolidatedMovementXY",
@@ -1326,6 +1325,7 @@
     {
       // crbug.com/989916
       name: "LayoutNGForeignObject",
+      status: "test",
     },
     {
       // Block fragmentation support in the grid layout algorithm.
@@ -1951,7 +1951,7 @@
     {
       name: "RegionCapture",
       origin_trial_feature_name: "RegionCapture",
-      status: "experimental",
+      status: {"Android": "", "default": "stable"},
     },
     {
       name: "RemotePlayback",
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 1557a2f..757c64b 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -72,7 +72,11 @@
 # Activation v2 (UAv2).
 crbug.com/906791 external/wpt/fullscreen/api/element-ready-check-allowed-cross-origin-manual.sub.html [ Timeout ]
 
-crbug.com/1293083 external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html [ Failure ]
+# These pass when capability delegation of fullscreen requests is enabled.
+crbug.com/1293083 external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html [ Failure ]
+crbug.com/1293083 external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html [ Failure ]
+crbug.com/1293083 external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html [ Failure ]
+crbug.com/1293083 external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html [ Failure ]
 
 # These two are left over from crbug.com/881040, I rebaselined them twice and
 # they continue to fail.
@@ -1586,6 +1590,15 @@
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-expansion-002.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-table-cell.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/break-before-table-cell-child.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/abspos.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/block-in-inline.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/captions.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/footer.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/header-after-break.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/header-footer.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/header.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/inline-block.tentative.html [ Pass ]
+virtual/layout_ng_table_frag/external/wpt/css/css-break/table/repeated-section/multicol.tentative.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-border-005.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-cell-expansion-003.html [ Pass ]
 virtual/layout_ng_table_frag/external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Pass ]
@@ -1611,38 +1624,17 @@
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/fragmented-table-with-fixed-height.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead-2.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead-3.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead-4.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead-tfoot-4.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead-tfoot.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/nested-repeating-thead.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/no-repeating-table-header-after-sections.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/repeating-thead-multiple-tables-page-border.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/repeating-thead-multiple-tables.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/repeating-thead-tfoot-different-fragment-height.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-cell-repeating-thead-break-inside-avoid-content.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-cell-repeating-thead-break-inside-content-first-line.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-cell-repeating-thead-break-inside-content.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-cell-too-large-for-page.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-cells-multiple-tables-no-repeating-thead.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-large-cell-with-header.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-2.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-3.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row-4.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-caption-repeating-thead-tfoot-with-border-spacing-at-top-of-row.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-multiple-tables-repeating-thead-with-border-spacing-at-top-of-row.html [ Crash Pass ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-2.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-3.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-4.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-nested-repeating-tfoot.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-tfoot-2.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-tfoot-3.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-tfoot-4.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead-tfoot.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-nested-repeating-thead.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-tfoot-rows-allowing-break.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-cell-straddles-page-unsplittable-div.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-cell-straddles-page.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page-break-after-avoid-2.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page-break-after-avoid-3.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page-break-after-avoid.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-starts-middle-of-page.html [ Failure ]
@@ -1654,7 +1646,6 @@
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-tfoot-with-caption.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-tfoot-with-two-captions.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-tfoot.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-with-border-spacing-at-top-of-row.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-with-caption.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead-with-two-captions.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/fragmentation/single-line-cells-repeating-thead.html [ Failure ]
@@ -3953,6 +3944,15 @@
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-expansion-002.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-table-cell.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/break-before-table-cell-child.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/abspos.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/block-in-inline.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/captions.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/footer.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/header-after-break.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/header-footer.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/header.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/inline-block.tentative.html [ Failure ]
+crbug.com/1078927 external/wpt/css/css-break/table/repeated-section/multicol.tentative.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-border-005.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-cell-expansion-003.html [ Failure ]
 crbug.com/1078927 external/wpt/css/css-break/table/table-col-paint-htb-ltr.html [ Failure ]
@@ -7093,3 +7093,7 @@
 
 # Temporarily allow pass or failure while new WebRTC stats are added and rolled into Chromium.
 crbug.com/webrtc/14147 external/wpt/webrtc-stats/supported-stats.html [ Failure Pass ]
+
+# Sheriff 2022-06-08
+crbug.com/1229084 [ Linux ] external/wpt/storage-access-api/requestStorageAccess.sub.window.html [ Failure Pass ]
+crbug.com/1229084 [ Win ] external/wpt/storage-access-api/requestStorageAccess.sub.window.html [ Failure Pass ]
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/abspos.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/abspos.tentative.html
new file mode 100644
index 0000000..a7297f1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/abspos.tentative.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="position:relative; display:table-header-group; break-inside:avoid;">
+      <div style="height:20px;">
+        <div style="position:absolute; width:25px; height:20px; background:green;"></div>
+      </div>
+    </div>
+    <div style="width:25px; height:320px; background:green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/block-in-inline.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/block-in-inline.tentative.html
new file mode 100644
index 0000000..1a94c69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/block-in-inline.tentative.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="position:relative; top:5px; left:5px; display:table-header-group; break-inside:avoid;">
+      <span style="position:relative; left:5px; top:5px;">
+        <div style="position:relative; left:-10px; top:-10px; width:100%; height:20px; background:green;"></div>
+      </span>
+    </div>
+    <div style="width:25px; height:320px; background:green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/captions.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/captions.tentative.html
new file mode 100644
index 0000000..1f28d568
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/captions.tentative.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="display:table-caption; caption-side:top; height:150px; background:green;"></div>
+    <div style="display:table-header-group; break-inside:avoid;">
+      <div style="height:20px; background:green;"></div>
+    </div>
+    <div style="width:25px; height:20px; background:green;"></div>
+    <div style="display:table-footer-group; break-inside:avoid;">
+      <div style="height:20px; background:green;"></div>
+    </div>
+    <div style="display:table-caption; caption-side:bottom; height:150px; background:green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/footer.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/footer.tentative.html
new file mode 100644
index 0000000..8789ef2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/footer.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="display:table-footer-group; break-inside:avoid;">
+      <div style="height:20px; background:green;"></div>
+    </div>
+    <div style="width:25px; height:320px; background:green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header-after-break.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header-after-break.tentative.html
new file mode 100644
index 0000000..6a5abc01
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header-after-break.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width:100px; height:100px; background:red;">
+  <div style="columns:4; gap:0; column-fill:auto; height:130px;">
+    <div style="display:table; width:100%; border-top:20px solid green;">
+      <div style="display:table-caption; height:230px;">
+        <div style="height:100px; background:green;"></div>
+        <div style="height:30px;"></div>
+        <div style="height:100px; background:green;"></div>
+      </div>
+      <div style="display:table-header-group; break-inside:avoid;">
+        <div style="height:20px; background:green;"></div>
+      </div>
+      <div style="height:200px;">
+        <div style="height:60px; background:green;"></div>
+        <div style="height:30px;"></div>
+        <div style="height:80px; background:green;"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header-footer.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header-footer.tentative.html
new file mode 100644
index 0000000..7a1e956
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header-footer.tentative.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="display:table-header-group; break-inside:avoid;">
+      <div style="height:20px; background:green;"></div>
+    </div>
+    <div style="width:25px; height:240px; background:green;"></div>
+    <div style="display:table-footer-group; break-inside:avoid;">
+      <div style="height:20px; background:green;"></div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header.tentative.html
new file mode 100644
index 0000000..6f4ad010
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/header.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="display:table-header-group; break-inside:avoid;">
+      <div style="height:20px; background:green;"></div>
+    </div>
+    <div style="width:25px; height:320px; background:green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/inline-block.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/inline-block.tentative.html
new file mode 100644
index 0000000..6dee47e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/inline-block.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="display:table-header-group; break-inside:avoid;">
+      <div style="display:inline-block; vertical-align:top; width:100%; height:20px; background:green;"></div>
+    </div>
+    <div style="width:25px; height:320px; background:green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/multicol.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/multicol.tentative.html
new file mode 100644
index 0000000..6dcd51bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/repeated-section/multicol.tentative.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-tables-3/#repeated-headers">
+<link rel="match" href="../../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-fill:auto; gap:0; width:100px; height:100px; background:red;">
+  <div style="display:table;">
+    <div style="display:table-header-group; break-inside:avoid;">
+      <div style="columns:5; height:20px; gap:0; background:red;">
+        <div style="height:100px; background:green;"></div>
+      </div>
+    </div>
+    <div style="width:25px; height:320px; background:green;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/inherit-background-color-transition-ref.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/inherit-background-color-transition-ref.html
new file mode 100644
index 0000000..b7a5824
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/inherit-background-color-transition-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+  <body style="color: black; background-color: white">
+    <div><div>PASS if black on white</div></div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/inherit-background-color-transition.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/inherit-background-color-transition.html
new file mode 100644
index 0000000..2c0e4aed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/inherit-background-color-transition.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html id="html" class="reftest-wait">
+  <title>Verifies that 'background-color' from a transition inherits explicitly down if requested</title>
+  <link rel="help" href="https://crbug.com/1325340">
+  <link rel="match" href="inherit-background-color-transition-ref.html">
+  <script src="support/helper.js"></script>
+  <style>
+    body { transition: background-color 1s; color: white; background-color: black; }
+    .light { color: black; background-color: white; }
+  </style>
+</head>
+<body id="body">
+  <div style="background-color: inherit">
+    <div style="background-color: inherit">PASS if black on white</div>
+  </div>
+  <script>
+    body.offsetTop;
+
+    async function run() {
+      let transitionEnd = new Promise((resolve) => {
+        body.addEventListener('transitionend', resolve);
+      });
+
+      // Trigger transition:
+      body.classList.toggle('light');
+
+      const transition = body.getAnimations()[0];
+      await transition.ready;
+      await waitForFrame();
+
+      // Expedite transition, but let it finish naturally.
+      transition.currentTime = transition.effect.getTiming().duration - 1;
+      await transitionEnd;
+
+      await waitForFrame();
+    }
+
+    run().then(() => html.classList.toggle('reftest-wait'));
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html b/third_party/blink/web_tests/external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html
deleted file mode 100644
index 37e0099..0000000
--- a/third_party/blink/web_tests/external/wpt/fullscreen/api/delegate-request.https.sub.tentative.html
+++ /dev/null
@@ -1,88 +0,0 @@
-<!DOCTYPE html>
-<!--
-   Tentative due to:
-     https://github.com/WICG/capability-delegation/issues/10
--->
-<title>Fullscreen request delegation test</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-actions.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-
-<div>
-  Verifies that element.requestFullscreen() call from a cross-origin subframe without user
-  activation works if and only if the top frame has user activation and it delegates the capability
-  to the subframe.
-
-  https://wicg.github.io/capability-delegation/spec.html
-
-  See wpt/html/user-activation/propagation*.html for child->parent user activation visibility tests.
-  TODO: Check same-origin iframes, sibling frames, and popup<->opener delegation.
-</div>
-
-<iframe allow="fullscreen" width="300px" height="50px"
-        src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/fullscreen/api/resources/delegate-request-subframe.html">
-</iframe>
-
-<script>
-  // Returns a |Promise| that gets resolved with |event.data| when |window|
-  // receives from |source| a "message" event whose |event.data.type| matches the string
-  // |message_data_type|.
-  function getMessageData(message_data_type, source) {
-      return new Promise(resolve => {
-          function waitAndRemove(e) {
-              if (e.source != source || !e.data || e.data.type != message_data_type)
-                  return;
-              window.removeEventListener("message", waitAndRemove);
-              resolve(e.data);
-          }
-          window.addEventListener("message", waitAndRemove);
-      });
-  }
-
-  promise_setup(async () => {
-      // Make sure the iframe has loaded.
-      await getMessageData("subframe-loaded", frames[0]);
-  });
-
-  const target_origin = "https://{{hosts[alt][www]}}:{{ports[https][0]}}";
-  const request = {"type": "make-fullscreen-request"};
-
-  promise_test(async () => {
-      let result_promise = getMessageData("result", frames[0]);
-      frames[0].postMessage(request, {targetOrigin: target_origin});
-      let data = await result_promise;
-
-      assert_equals(data.result, "failure");
-  }, "Fullscreen-request from a subframe fails without delegation when the top frame has no user activation");
-
-  promise_test(async () => {
-      let result_promise = getMessageData("result", frames[0]);
-      frames[0].postMessage(request, {targetOrigin: target_origin,
-                                      delegate: "fullscreen"});
-      let data = await result_promise;
-
-      assert_equals(data.result, "failure");
-  }, "Fullscreen-request from a subframe fails with delegation when the top frame has no user activation");
-
-  promise_test(async () => {
-      let result_promise = getMessageData("result", frames[0]);
-      await test_driver.bless();
-      frames[0].postMessage(request, {targetOrigin: target_origin});
-      let data = await result_promise;
-
-      assert_equals(data.result, "failure");
-  }, "Fullscreen-request from a subframe fails without delegation when the top frame has user activation");
-
-  promise_test(async () => {
-      let result_promise = getMessageData("result", frames[0]);
-      await test_driver.bless();
-      frames[0].postMessage(request, {targetOrigin: target_origin,
-                                      delegate: "fullscreen"});
-      let data = await result_promise;
-
-      assert_equals(data.result, "success");
-  }, "Fullscreen-request from a subframe succeeds with delegation when the top frame has user activation");
-
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/fullscreen/api/resources/delegate-request-subframe.html b/third_party/blink/web_tests/external/wpt/fullscreen/api/resources/delegate-request-subframe.html
deleted file mode 100644
index 9bb9d781..0000000
--- a/third_party/blink/web_tests/external/wpt/fullscreen/api/resources/delegate-request-subframe.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<title>Fullscreen request delegation test: subframe</title>
-<body>Subframe body</body>
-
-<script>
-  function reportResult(msg) {
-      window.top.postMessage({"type": "result", "result": msg}, "*");
-  }
-
-  document.addEventListener('fullscreenchange', () => {
-      if (document.fullscreenElement) {
-          reportResult("success");
-          document.exitFullscreen();
-      }
-  });
-
-  document.addEventListener('fullscreenerror', () => {
-      reportResult("failure");
-  });
-
-  window.addEventListener("message", e => {
-      if (e.data.type == "make-fullscreen-request") {
-          document.body.requestFullscreen();
-      }
-  });
-
-  window.top.postMessage({"type": "subframe-loaded"}, "*");
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html
new file mode 100644
index 0000000..2d531a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-cross-origin.https.sub.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+     https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Popup Cross-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+  Verifies that element.requestFullscreen() calls from a cross-origin popup without user activation
+  work if and only if the opener has user activation and it delegates the capability.
+
+  https://wicg.github.io/capability-delegation/spec.html
+</div>
+
+<script>
+  let popup = null;
+
+  function testCrossOriginPopupFullscreenDelegation(capability, activate, expectation) {
+      const message = {"type": "make-fullscreen-request"};
+      const origin = "https://{{hosts[alt][www]}}:{{ports[https][0]}}";
+      const expectationType = expectation ? "succeeds" : "fails";
+      const delegationType = capability ? "with delegation" : "without delegation";
+      const activationType = activate ? "with user activation" : "with no user activation";
+
+      promise_test(async () => {
+          const data = await postCapabilityDelegationMessage(popup, message, origin, capability, activate);
+          assert_equals(data.result, expectation ? "success" : "failure");
+      }, `Fullscreen requests from a cross-origin popup ${expectationType} ${delegationType} from an opener ${activationType}`);
+  }
+
+  promise_setup(async () => {
+      // Make sure the recipient popup has loaded.
+      popup = window.open("https://{{hosts[alt][www]}}:{{ports[https][0]}}/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html",
+                          "", "width=300,height=200");
+      return getMessageData("recipient-loaded", popup);
+  });
+
+  testCrossOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+  testCrossOriginPopupFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/false, /*expectation=*/false);
+  testCrossOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/false);
+  testCrossOriginPopupFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html
new file mode 100644
index 0000000..d429095
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-popup-same-origin.https.tentative.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+     https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Popup Same-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+  Verifies that element.requestFullscreen() calls from a same-origin popup without user activation
+  work if and only if the opener has user activation and it delegates the capability.
+
+  https://wicg.github.io/capability-delegation/spec.html
+</div>
+
+<script>
+  let popup = null;
+
+  function testSameOriginPopupFullscreenDelegation(capability, activate, expectation) {
+      const message = {"type": "make-fullscreen-request"};
+      const expectationType = expectation ? "succeeds" : "fails";
+      const delegationType = capability ? "with delegation" : "without delegation";
+      const activationType = activate ? "with user activation" : "with no user activation";
+
+      promise_test(async () => {
+          const data = await postCapabilityDelegationMessage(popup, message, location.origin, capability, activate);
+          assert_equals(data.result, expectation ? "success" : "failure");
+      }, `Fullscreen requests from a same-origin popup ${expectationType} ${delegationType} from an opener ${activationType}`);
+  }
+
+  promise_setup(async () => {
+      // Make sure the recipient popup has loaded.
+      popup = window.open("./resources/delegate-fullscreen-request-recipient.html",
+                          "", "width=300,height=200");
+      return getMessageData("recipient-loaded", popup);
+  });
+
+  testSameOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+  testSameOriginPopupFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/false, /*expectation=*/false);
+  testSameOriginPopupFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/false);
+  testSameOriginPopupFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html
new file mode 100644
index 0000000..5f2d2f61
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-cross-origin.https.sub.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+     https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Subframe Cross-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+  Verifies that element.requestFullscreen() calls from a cross-origin subframe without user
+  activation work if and only if the top frame has user activation and it delegates the capability.
+
+  https://wicg.github.io/capability-delegation/spec.html
+
+  See wpt/html/user-activation/propagation*.html for frame tree user activation visibility tests.
+</div>
+
+<iframe allow="fullscreen" width="300px" height="50px"
+        src="https://{{hosts[alt][www]}}:{{ports[https][0]}}/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html">
+</iframe>
+
+<script>
+  function testCrossOriginSubframeFullscreenDelegation(capability, activate, expectation) {
+      const message = {"type": "make-fullscreen-request"};
+      const origin = "https://{{hosts[alt][www]}}:{{ports[https][0]}}";
+      const expectationType = expectation ? "succeeds" : "fails";
+      const delegationType = capability ? "with delegation" : "without delegation";
+      const activationType = activate ? "with user activation" : "with no user activation";
+
+      promise_test(async () => {
+          const data = await postCapabilityDelegationMessage(frames[0], message, origin, capability, activate);
+          assert_equals(data.result, expectation ? "success" : "failure");
+      }, `Fullscreen requests from a cross-origin subframe ${expectationType} ${delegationType} from an opener ${activationType}`);
+  }
+
+  promise_setup(async () => {
+      // Make sure the recipient iframe has loaded.
+      return getMessageData("recipient-loaded", frames[0]);
+  });
+
+  testCrossOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+  testCrossOriginSubframeFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/false, /*expectation=*/false);
+  testCrossOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/false);
+  testCrossOriginSubframeFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html
new file mode 100644
index 0000000..9e5482d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegate-fullscreen-request-subframe-same-origin.https.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+     https://github.com/WICG/capability-delegation/issues/10
+-->
+<title>Capability Delegation of Fullscreen Requests: Subframe Same-Origin</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+  Verifies that element.requestFullscreen() calls from a same-origin subframe without user
+  activation work if and only if the top frame has user activation, regardless of whether it
+  delegates the capability or not.
+
+  https://wicg.github.io/capability-delegation/spec.html
+
+  See wpt/html/user-activation/propagation*.html for frame tree user activation visibility tests.
+</div>
+
+<iframe allow="fullscreen" width="300px" height="50px"
+        src="./resources/delegate-fullscreen-request-recipient.html">
+</iframe>
+
+<script>
+  function testSameOriginSubframeFullscreenDelegation(capability, activate, expectation) {
+      const message = {"type": "make-fullscreen-request"};
+      const expectationType = expectation ? "succeeds" : "fails";
+      const delegationType = capability ? "with delegation" : "without delegation";
+      const activationType = activate ? "with user activation" : "with no user activation";
+
+      promise_test(async () => {
+          const data = await postCapabilityDelegationMessage(frames[0], message, window.location, capability, activate);
+          assert_equals(data.result, expectation ? "success" : "failure");
+      }, `Fullscreen requests from a same-origin subframe ${expectationType} ${delegationType} from an opener ${activationType}`);
+  }
+
+  promise_setup(async () => {
+      // Make sure the recipient iframe has loaded.
+      return getMessageData("recipient-loaded", frames[0]);
+  });
+
+  testSameOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/false, /*expectation=*/false);
+  testSameOriginSubframeFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/false, /*expectation=*/false);
+  testSameOriginSubframeFullscreenDelegation(/*capability=*/"", /*activate=*/true, /*expectation=*/true);
+  testSameOriginSubframeFullscreenDelegation(/*capability=*/"fullscreen", /*activate=*/true, /*expectation=*/true);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegation-consumes-activation.https.tentative.html b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegation-consumes-activation.https.tentative.html
new file mode 100644
index 0000000..1a8805d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/capability-delegation/delegation-consumes-activation.https.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+     https://github.com/whatwg/html/issues/4008
+-->
+<title>Capability Delegation: Consumes User Activation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/utils.js"></script>
+
+<div>
+  Test that capability delegation consumes transient user activation.
+
+  https://wicg.github.io/capability-delegation/spec.html
+</div>
+
+<iframe allow="payment" width="300px" height="50px" src="about:blank"></iframe>
+
+<script>
+  promise_test(async () => {
+      assert_false(navigator.userActivation.isActive);
+      await test_driver.bless();
+      assert_true(navigator.userActivation.isActive);
+      frames[0].postMessage({"type": "none"}, {targetOrigin: location.origin, delegate: ""});
+      assert_true(navigator.userActivation.isActive);
+      frames[0].postMessage({"type": "none"}, {targetOrigin: location.origin, delegate: "payment"});
+      assert_false(navigator.userActivation.isActive);
+  }, `capability delegation consumes transient user activation`);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html b/third_party/blink/web_tests/external/wpt/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html
new file mode 100644
index 0000000..11daf73
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/capability-delegation/resources/delegate-fullscreen-request-recipient.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Capability Delegation of Fullscreen Requests test recipient</title>
+<body>Capability Delegation of Fullscreen Requests test recipient body</body>
+
+<script>
+  const initiator = window.opener ? window.opener : window.top;
+  initiator.postMessage({"type": "recipient-loaded"}, "*");
+
+  function reportResult(msg) {
+      initiator.postMessage({"type": "result", "result": msg}, "*");
+  }
+
+  document.addEventListener('fullscreenchange', async () => {
+      if (document.fullscreenElement) {
+          await document.exitFullscreen();
+          reportResult("success");
+      }
+  });
+
+  document.addEventListener('fullscreenerror', () => {
+      reportResult("failure");
+  });
+
+  window.addEventListener("message", e => {
+      if (e.data.type == "make-fullscreen-request") {
+          document.body.requestFullscreen();
+      }
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/capability-delegation/resources/utils.js b/third_party/blink/web_tests/external/wpt/html/capability-delegation/resources/utils.js
new file mode 100644
index 0000000..3add3eb6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/capability-delegation/resources/utils.js
@@ -0,0 +1,24 @@
+// Returns a Promise that gets resolved with `event.data` when `window` receives from `source` a
+// "message" event whose `event.data.type` matches the string `message_data_type`.
+function getMessageData(message_data_type, source) {
+    return new Promise(resolve => {
+        function waitAndRemove(e) {
+            if (e.source != source || !e.data || e.data.type != message_data_type)
+                return;
+            window.removeEventListener("message", waitAndRemove);
+            resolve(e.data);
+        }
+        window.addEventListener("message", waitAndRemove);
+    });
+}
+
+// A helper that simulates user activation on the current frame if `activate` is true, then posts
+// `message` to `frame` with the target `origin` and specified `capability` to delegate. This helper
+// awaits and returns the result message sent in reply from `frame`.
+async function postCapabilityDelegationMessage(frame, message, origin, capability, activate) {
+    let result_promise = getMessageData("result", frame);
+    if (activate)
+        await test_driver.bless();
+    frame.postMessage(message, {targetOrigin: origin, delegate: capability});
+    return await result_promise;
+}
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index fdba97d..07ad220e 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -199,6 +199,7 @@
 SET TIMEOUT: navigation-timing/*
 SET TIMEOUT: old-tests/submission/Microsoft/history/history_000.htm
 SET TIMEOUT: paint-timing/resources/subframe-painting.html
+SET TIMEOUT: performance-timeline/resources/navigation-id-detached-frame-page.html
 SET TIMEOUT: portals/resources/portals-adopt-predecessor-portal.html
 SET TIMEOUT: preload/single-download-preload.html
 SET TIMEOUT: preload/resources/slow-exec.js
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-detached-frame.tentative.html b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-detached-frame.tentative.html
new file mode 100644
index 0000000..add1125
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-detached-frame.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <title>The navigation_id Detached iframe Parent Page.</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+  <script>
+    promise_test(t => {
+      return new Promise(resolve => {
+        const frame = document.createElement("iframe");
+        frame.addEventListener("load", async () => {
+          // Wait for iframe to be detached.
+          while (frame.contentWindow) {
+            await new Promise(r => t.step_timeout(r, 10));
+          }
+          resolve();
+        });
+        frame.src = "resources/navigation-id-detached-frame-page.html";
+        document.body.appendChild(frame);
+      });
+    }, "The navigation_id getter does not crash a window of detached frame");
+  </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-element-timing.tentative.html
similarity index 81%
copy from third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
copy to third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-element-timing.tentative.html
index 08dbd4b0..bc52f20 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-element-timing.tentative.html
@@ -7,7 +7,8 @@
 <script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
 <script src="navigation-id.helper.js"></script>
 <script>
-    runNavigationIdTest({
+  runNavigationIdTest({
     navigationTimes: 4,
-  }, "navigation id test");
-</script>
+    testName: 'element_timing',
+  }, "Element Timing navigation id test");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-long-task-task-attribution.tentative.html
similarity index 78%
copy from third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
copy to third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-long-task-task-attribution.tentative.html
index 08dbd4b0..662e175 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-long-task-task-attribution.tentative.html
@@ -7,7 +7,8 @@
 <script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
 <script src="navigation-id.helper.js"></script>
 <script>
-    runNavigationIdTest({
+  runNavigationIdTest({
     navigationTimes: 4,
-  }, "navigation id test");
-</script>
+    testName: 'long_task_task_attribution',
+  }, "Long Task/Task Attribution navigation id test");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-mark-measure.tentative.html
similarity index 82%
rename from third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
rename to third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-mark-measure.tentative.html
index 08dbd4b0..42795f9 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-mark-measure.tentative.html
@@ -7,7 +7,8 @@
 <script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
 <script src="navigation-id.helper.js"></script>
 <script>
-    runNavigationIdTest({
+  runNavigationIdTest({
     navigationTimes: 4,
-  }, "navigation id test");
-</script>
+    testName: 'mark_measure',
+  }, "Mark/Measure navigation id test");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-resource-timing.tentative.html
similarity index 81%
copy from third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
copy to third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-resource-timing.tentative.html
index 08dbd4b0..1ec906e 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id-resource-timing.tentative.html
@@ -7,7 +7,8 @@
 <script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
 <script src="navigation-id.helper.js"></script>
 <script>
-    runNavigationIdTest({
+  runNavigationIdTest({
     navigationTimes: 4,
-  }, "navigation id test");
-</script>
+    testName: 'resource_timing',
+  }, "Resource Timing navigation id test");
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.helper.js b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.helper.js
index fd6e24d..53099ca 100644
--- a/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.helper.js
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/navigation-id.helper.js
@@ -3,51 +3,99 @@
 // '/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js'
 // which should be included before this file to use these functions.
 
-function runNavigationIdTest(params, description) {
-  const defaultParams = {
-    constants: {
-      performanceMarkName: 'mark_navigation_id',
-      performanceMeasureName: 'measure_navigation_id',
-    },
-    // This function is to make and obtain the navigation counter value for a
-    // performance entries of mark and measure type. It is to be extended for
-    // other types of performance entry in future.
-    funcBeforeNavigation: (constants) => {
-      window.performance.mark(constants.performanceMarkName);
-      return window.performance
-        .getEntriesByName(constants.performanceMarkName)[0]
-        .navigationId;
-    },
-    funcAfterBFCacheLoad: (expectedNavigationId, constants) => {
-      window.performance.mark(
-        constants.performanceMarkName + expectedNavigationId);
-      window.performance.measure(
-        constants.performanceMeasureName + expectedNavigationId,
-        constants.performanceMarkName,
-        constants.performanceMarkName + expectedNavigationId);
-      return [
-        window.performance
-          .getEntriesByName(
-            constants.performanceMarkName + expectedNavigationId)[0]
-          .navigationId,
-        window.performance
-          .getEntriesByName(
-            constants.performanceMeasureName + expectedNavigationId)[0]
-          .navigationId
-      ];
-    },
-  };
-  params = { ...defaultParams, ...params };
-  runBfcacheWithMultipleNavigationTest(params, description);
+// This function is to obtain navigation ids of all performance entries to
+// verify.
+let testInitial = () => {
+  return window.performance.getEntries().map(e => e.navigationId);
 }
 
-function runBfcacheWithMultipleNavigationTest(params, description) {
+let testMarkMeasure = (expectedNavigationId, markName, MeasureName) => {
+  const markName1 = 'test-mark';
+  const markName2 = 'test-mark' + expectedNavigationId;
+  const measureName = 'test-measure' + expectedNavigationId;
+
+  window.performance.mark(markName1);
+  window.performance.mark(markName2);
+  window.performance.measure(measureName, markName1, markName2);
+  return window.performance.getEntriesByName(markName2).concat(
+    window.performance.getEntriesByName(measureName)).map(e => e.navigationId);
+}
+
+let testResourceTiming = async (expectedNavigationId) => {
+  let navigationId = -1;
+
+  let p = new Promise(resolve => {
+    new PerformanceObserver((list) => {
+      const entry = list.getEntries().find(e => e.name.includes('json_resource') && e.navigationId == expectedNavigationId);
+      if (entry) {
+        navigationId = entry.navigationId;
+        resolve();
+      }
+    }).observe({ type: 'resource' });
+  });
+
+  const resp = await fetch('/performance-timeline/resources/json_resource.json');
+  await p;
+  return [navigationId];
+}
+
+let testElementTiming = async (expectedNavigationId) => {
+  let navigationId = -1;
+  let p = new Promise(resolve => {
+    new PerformanceObserver((list) => {
+      const entry = list.getEntries().find(e => e.entryType === 'element' && e.identifier === 'test-element-timing' + expectedNavigationId);
+      if (entry) {
+        navigationId = entry.navigationId;
+        resolve();
+      }
+    }).observe({ type: 'element' });
+  });
+
+  let el = document.createElement('p');
+  el.setAttribute('elementtiming', 'test-element-timing' + expectedNavigationId);
+  el.textContent = 'test element timing text';
+  document.body.appendChild(el);
+  await p;
+  return [navigationId];
+}
+
+let testLongTask = async () => {
+  let navigationIds = [];
+
+  let p = new Promise(resolve => {
+    new PerformanceObserver((list) => {
+      const entry = list.getEntries().find(e => e.entryType === 'longtask')
+      if (entry) {
+        navigationIds.push(entry.navigationId);
+        navigationIds = navigationIds.concat(
+          entry.attribution.map(a => a.navigationId));
+        resolve();
+      }
+    }).observe({ type: 'longtask' });
+  });
+
+  const script = document.createElement('script');
+  script.src = '/performance-timeline/resources/make_long_task.js';
+  document.body.appendChild(script);
+  await p;
+  document.body.removeChild(script);
+  return navigationIds;
+}
+
+const testFunctionMap = {
+  'mark_measure': testMarkMeasure,
+  'resource_timing': testResourceTiming,
+  'element_timing': testElementTiming,
+  'long_task_task_attribution': testLongTask,
+};
+
+function runNavigationIdTest(params, description) {
   const defaultParams = {
     openFunc: url => window.open(url, '_blank', 'noopener'),
     scripts: [],
     funcBeforeNavigation: () => { },
     targetOrigin: originCrossSite,
-    navigationTimes: 1,
+    navigationTimes: 4,
     funcAfterAssertion: () => { },
   }  // Apply defaults.
   params = { ...defaultParams, ...params };
@@ -58,25 +106,25 @@
 
     const urlA = executorPath + pageA.context_id;
     const urlB = params.targetOrigin + executorPath + pageB.context_id;
-
+    // Open url A.
     params.openFunc(urlA);
-
     await pageA.execute_script(waitForPageShow);
 
     // Assert navigation id is 1 when the document is loaded first time.
-    let navigationId = await pageA.execute_script(
-      params.funcBeforeNavigation, [params.constants])
-    assert_implements_optional(
-      navigationId === 1, 'Navigation Id should be 0.');
+    let navigationIds = await pageA.execute_script(testInitial);
+    assert_true(
+      navigationIds.every(t => t === 1), 'All Navigation Ids should be 1.');
 
     for (i = 1; i <= params.navigationTimes; i++) {
+      // Navigate away to url B and back.
       await navigateAndThenBack(pageA, pageB, urlB);
 
-      let navigationIds = await pageA.execute_script(
-        params.funcAfterBFCacheLoad, [i + 1, params.constants]);
-      assert_implements_optional(
+      // Assert navigation id increments when the document is load from bfcache.
+      navigationIds = await pageA.execute_script(
+        testFunctionMap[params.testName], [i + 1]);
+      assert_true(
         navigationIds.every(t => t === (i + 1)),
-        'Navigation Id should all be ' + (i + 1) + '.');
+        params.testName + ' Navigation Id should all be ' + (i + 1) + '.');
     }
   }, description);
 }
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/resources/json_resource.json b/third_party/blink/web_tests/external/wpt/performance-timeline/resources/json_resource.json
new file mode 100644
index 0000000..68b6ac1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/resources/json_resource.json
@@ -0,0 +1,4 @@
+{
+  "name": "nav_id_test",
+  "target": "resource_timing"
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/resources/make_long_task.js b/third_party/blink/web_tests/external/wpt/performance-timeline/resources/make_long_task.js
new file mode 100644
index 0000000..a52d6d8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/resources/make_long_task.js
@@ -0,0 +1,4 @@
+(function () {
+  let now = window.performance.now();
+  while (window.performance.now() < now + 60);
+}());
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/performance-timeline/resources/navigation-id-detached-frame-page.html b/third_party/blink/web_tests/external/wpt/performance-timeline/resources/navigation-id-detached-frame-page.html
new file mode 100644
index 0000000..02aafbb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/performance-timeline/resources/navigation-id-detached-frame-page.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <title>The navigation_id Detached iframe Page.</title>
+</head>
+
+<body>
+  <script>
+      window.addEventListener("load", () => {
+        setTimeout(() => {
+          const container = window.frameElement;
+          container.remove();
+        }, 10);
+        performance.mark('mark-window-detached-frame');
+      });
+  </script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change-ref.html b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change-ref.html
index cdcad92..df8afce 100644
--- a/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change-ref.html
+++ b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change-ref.html
@@ -6,6 +6,10 @@
     <circle cx="475" cy="975" r="40" stroke="black" stroke-width="2" fill="none" />
     <text fill="red" style="font-size: 40px;" transform="matrix(1, 0, 0, 1, 468, 988)">A</text>
   </svg>
+  <svg width="2384" height="1684" style="position:absolute; left:0; top:200px;">
+    <circle cx="475" cy="975" r="40" stroke="black" stroke-width="2" fill="none" />
+    <text style="font-size: 40px;" transform="matrix(1, 0, 0, 1, 468, 988)"><tspan fill="blue">B</tspan></text>
+  </svg>
 </div>
 
 <svg width="500" height="400" style="transform-origin: 0px 0px; transform: scale(2) translate(-300px, -300px);">
diff --git a/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change.html b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change.html
index abd05cc..57b555106 100644
--- a/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change.html
+++ b/third_party/blink/web_tests/external/wpt/svg/text/reftests/transform-dynamic-change.html
@@ -10,6 +10,10 @@
     <circle cx="475" cy="975" r="40" stroke="black" stroke-width="2" fill="none" />
     <text id="txt" style="font-size: 40px;" transform="matrix(1, 0, 0, 1, 468, 988)">A</text>
   </svg>
+  <svg width="2384" height="1684" style="position:absolute; left:0; top:200px;">
+    <circle cx="475" cy="975" r="40" stroke="black" stroke-width="2" fill="none" />
+    <text style="font-size: 40px;" transform="matrix(1, 0, 0, 1, 468, 988)"><tspan id="inline">B</tspan></text>
+  </svg>
 </div>
 
 <svg id="svg2" width="500" height="400" style="transform-origin: 0px 0px; transform: translate(-300px, -300px);">
@@ -21,6 +25,7 @@
   requestAnimationFrame(() => {
     document.getElementById('moveme').style.transform = 'matrix(0.9, 0, 0, 0.9, -210, -777)';
     document.getElementById('txt').style.fill = 'red';
+    document.getElementById('inline').style.fill = 'blue';
 
     document.getElementById('svg2').style.transform = 'scale(2) translate(-300px, -300px)';
     document.getElementById('text2').style.fill = 'green';
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/svg/zoom-foreignObject-expected.png
new file mode 100644
index 0000000..4130567
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/paint/invalidation/svg/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/foreignObject/svg-document-in-html-document-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/foreignObject/svg-document-in-html-document-expected.png
new file mode 100644
index 0000000..085e2c6
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/foreignObject/svg-document-in-html-document-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/zoom/page/zoom-foreign-content-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/zoom/page/zoom-foreign-content-expected.png
new file mode 100644
index 0000000..adfcdc9
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/zoom/page/zoom-foreign-content-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/zoom/page/zoom-foreignObject-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/zoom/page/zoom-foreignObject-expected.png
new file mode 100644
index 0000000..d709ed37
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/svg/zoom/page/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-fonts-updated-event.js b/third_party/blink/web_tests/inspector-protocol/css/css-fonts-updated-event.js
index 635fcf3..2eccefb 100644
--- a/third_party/blink/web_tests/inspector-protocol/css/css-fonts-updated-event.js
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-fonts-updated-event.js
@@ -15,6 +15,7 @@
   var fontStyle = font.fontStyle;
   var fontVariant = font.fontVariant;
   var fontWeight = font.fontWeight;
+  var fontDisplay = font.fontDisplay;
   var fontStretch = font.fontStretch;
   var unicodeRange = font.unicodeRange;
   var src = font.src;
@@ -25,6 +26,7 @@
   testRunner.log(fontVariant);        // normal (default)
   testRunner.log(fontWeight);         // normal (default)
   testRunner.log(fontStretch);        // normal (default)
+  testRunner.log(fontDisplay);        // auto (default)
   testRunner.log(unicodeRange);       // U+0-10FFFE
   testRunner.log(platformFontFamily); // ಠ_ಠNoto Monoಠ_ಠ
 
diff --git a/third_party/blink/web_tests/inspector-protocol/css/css-get-media-queries.js b/third_party/blink/web_tests/inspector-protocol/css/css-get-media-queries.js
index e099877..8076d51 100644
--- a/third_party/blink/web_tests/inspector-protocol/css/css-get-media-queries.js
+++ b/third_party/blink/web_tests/inspector-protocol/css/css-get-media-queries.js
@@ -14,6 +14,12 @@
         }
     }
 }
+
+@media (10px < width < 1000px) {
+  * {
+    color: green;
+  }
+}
 </style>
 `, 'Verify that media queries are reported properly.');
 
diff --git a/third_party/blink/web_tests/inspector-protocol/emulation/set-css-media-feature-override.js b/third_party/blink/web_tests/inspector-protocol/emulation/set-css-media-feature-override.js
index eef1b0d..be6ac2f 100644
--- a/third_party/blink/web_tests/inspector-protocol/emulation/set-css-media-feature-override.js
+++ b/third_party/blink/web_tests/inspector-protocol/emulation/set-css-media-feature-override.js
@@ -4,6 +4,16 @@
 
   await session.navigate('../resources/css-media-features.html');
 
+  // For each emulated media feature, produce a list of corresponding
+  // custom properties to inspect.
+  async function formatComputedValues(features) {
+    let props = features.map(f => `--${f}`);
+    let values = [];
+    for (let prop of props)
+      values.push(await session.evaluate(`getComputedStyle(p).getPropertyValue('${prop}')`));
+    return values.join('; ');
+  }
+
   async function setEmulatedMediaFeature(feature, value) {
     await dp.Emulation.setEmulatedMedia({
       features: [
@@ -17,10 +27,8 @@
     const code = `matchMedia(${JSON.stringify(mediaQuery)}).matches`;
     const result = await session.evaluate(code);
     testRunner.log(`${code}: ${result}`);
-    const width = await session.evaluate('getComputedStyle(p).width');
-    const height = await session.evaluate('getComputedStyle(p).height');
-    const color = await session.evaluate('getComputedStyle(p).color');
-    testRunner.log(`${code} applied: ${width} x ${height}, ${color}`);
+    const applied = await formatComputedValues([feature]);
+    testRunner.log(`${code} applied: ${applied}`);
   }
 
   async function setEmulatedMediaFeatures({ features, mediaQuery }) {
@@ -30,10 +38,8 @@
     const code = `matchMedia(${JSON.stringify(mediaQuery)}).matches`;
     const result = await session.evaluate(code);
     testRunner.log(`${code}: ${result}`);
-    const width = await session.evaluate('getComputedStyle(p).width');
-    const height = await session.evaluate('getComputedStyle(p).height');
-    const color = await session.evaluate('getComputedStyle(p).color');
-    testRunner.log(`${code} applied: ${width} x ${height}, ${color}`);
+    const applied = await formatComputedValues(features.map(f => f.name));
+    testRunner.log(`${code} applied: ${applied}`);
   }
 
   // Test `prefers-color-scheme`.
@@ -51,6 +57,13 @@
   await setEmulatedMediaFeature('prefers-reduced-motion', 'reduce');
   await setEmulatedMediaFeature('prefers-reduced-motion', '__invalid__');
 
+  // Test `prefers-reduced-data`.
+  // https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-data
+  await setEmulatedMediaFeature('prefers-reduced-data', '__invalid__');
+  await setEmulatedMediaFeature('prefers-reduced-data', 'no-preference');
+  await setEmulatedMediaFeature('prefers-reduced-data', 'reduce');
+  await setEmulatedMediaFeature('prefers-reduced-data', '__invalid__');
+
   // Test `prefers-contrast`.
   // https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
   await setEmulatedMediaFeature('prefers-contrast', '__invalid__');
diff --git a/third_party/blink/web_tests/inspector-protocol/resources/css-media-features.html b/third_party/blink/web_tests/inspector-protocol/resources/css-media-features.html
index 179f38c..a61ae3c 100644
--- a/third_party/blink/web_tests/inspector-protocol/resources/css-media-features.html
+++ b/third_party/blink/web_tests/inspector-protocol/resources/css-media-features.html
@@ -1,17 +1,28 @@
 <!DOCTYPE html>
 <style>
-  p { width: 1px; height: 1px; color: red; }
-  @media (prefers-color-scheme: no-preference) { p { width: 2px; } }
-  @media (prefers-color-scheme: light) { p { width: 3px; } }
-  @media (prefers-color-scheme: dark) { p { width: 4px; } }
-  @media (prefers-reduced-motion: no-preference) { p { height: 2px; } }
-  @media (prefers-reduced-motion: reduce) { p { height: 3px; } }
-  @media (prefers-contrast: no-preference) { p { color: blue; } }
-  @media (prefers-contrast: more) { p { color: green; } }
-  @media (prefers-contrast: less) { p { color: yellow; } }
-  @media (prefers-contrast: custom) { p { color: orange; } }
-  @media (prefers-color-scheme: dark) and (prefers-reduced-motion: reduce) { p { width: 999px; height: 999px; } }
-  @media (color-gamut: p3) { p { width: 6px; height: 6px; } }
-  @media (color-gamut: rec2020) { p { width: 7px; height: 7px; } }
+  p {
+    --prefers-color-scheme:invalid;
+    --prefers-reduced-motion:invalid;
+    --prefers-reduced-data:invalid;
+    --prefers-contrast:invalid;
+    --color-gamut:invalid;
+    --forced-colors:invalid;
+  }
+  @media (prefers-color-scheme: no-preference) { p { --prefers-color-scheme:no-preference; } }
+  @media (prefers-color-scheme: light) { p { --prefers-color-scheme:light; } }
+  @media (prefers-color-scheme: dark) { p { --prefers-color-scheme:dark; } }
+  @media (prefers-reduced-motion: no-preference) { p { --prefers-reduced-motion:no-preference; } }
+  @media (prefers-reduced-motion: reduce) { p { --prefers-reduced-motion:reduce; } }
+  @media (prefers-reduced-data: no-preference) { p { --prefers-reduced-data:no-preference; } }
+  @media (prefers-reduced-data: reduce) { p { --prefers-reduced-data:reduce; } }
+  @media (prefers-contrast: no-preference) { p { --prefers-contrast:no-preference; } }
+  @media (prefers-contrast: more) { p { --prefers-contrast:more; } }
+  @media (prefers-contrast: less) { p { --prefers-contrast:less; } }
+  @media (prefers-contrast: custom) { p { --prefers-contrast:custom; } }
+  @media (prefers-color-scheme: dark) and (prefers-reduced-motion: reduce) { p { --prefers-color-scheme:dark2; --prefers-reduced-motion:reduce2; } }
+  @media (color-gamut: p3) { p { --color-gamut:p3; } }
+  @media (color-gamut: rec2020) { p { --color-gamut:rec2020; } }
+  @media (forced-colors: active) { p { --forced-colors:active; } }
+  @media (forced-colors: none) { p { --forced-colors:none; } }
 </style>
 <p id="p">
diff --git a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/svg-foreignobject-child-container-expected.txt b/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/svg-foreignobject-child-container-expected.txt
deleted file mode 100644
index 3953eec2..0000000
--- a/third_party/blink/web_tests/platform/generic/external/wpt/css/css-contain/container-queries/svg-foreignobject-child-container-expected.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-This is a testharness.js-based test.
-FAIL #inner querying #container inside foreignObject assert_equals: expected "rgb(0, 128, 0)" but got "rgb(0, 0, 0)"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-expected.txt b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-expected.txt
index 06964af..d6b77aa 100644
--- a/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-expected.txt
@@ -4,6 +4,7 @@
 normal
 normal
 normal
+auto
 U+0-10FFFE
 ಠ_ಠNoto Monoಠ_ಠ
 SUCCESS: CSS.FontsUpdated events received.
diff --git a/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-on-startup-expected.txt b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-on-startup-expected.txt
index 792be5e..a665c86 100644
--- a/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-on-startup-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-fonts-updated-event-on-startup-expected.txt
@@ -1,5 +1,6 @@
 Verifies that CSS.fontsUpdated events are sent after CSS domain is enabled
 {
+    fontDisplay : auto
     fontFamily : Amstelvar
     fontStretch : normal
     fontStyle : normal
diff --git a/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-get-media-queries-expected.txt b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-get-media-queries-expected.txt
index 92da54e..7433e7b2 100644
--- a/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-get-media-queries-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/inspector-protocol/css/css-get-media-queries-expected.txt
@@ -1,5 +1,16 @@
 Verify that media queries are reported properly.
 mediaRule #0
+    text: (10px < width < 1000px)
+    source: mediaRule
+    range: {"endColumn":30,"endLine":13,"startColumn":7,"startLine":13}
+    computedText: (10px < width < 1000px)
+    mediaQuery #0
+        mediaExpression #0
+            feature: width
+            value: 1000
+            unit: px
+            computed length: 1000
+mediaRule #1
     text: (max-width: 0px), (min-width: 1000em)
     source: mediaRule
     range: {"endColumn":52,"endLine":12,"startColumn":7,"startLine":12}
@@ -15,7 +26,7 @@
             value: 1000
             unit: em
             computed length: 16000
-mediaRule #1
+mediaRule #2
     text: (max-width: 100px), (max-width: 72em)
     source: mediaRule
     range: {"endColumn":65,"endLine":21,"startColumn":7,"startLine":21}
@@ -32,7 +43,7 @@
             value: 72
             unit: em
             computed length: 1152
-mediaRule #2
+mediaRule #3
     text: (max-width: 200px) and (min-width: 100px)
     source: mediaRule
     range: {"endColumn":52,"endLine":6,"startColumn":11,"startLine":6}
@@ -48,7 +59,7 @@
             value: 100
             unit: px
             computed length: 100
-mediaRule #3
+mediaRule #4
     text: (min-monochrome: 8)
     source: mediaRule
     range: {"endColumn":38,"endLine":22,"startColumn":11,"startLine":22}
@@ -58,17 +69,6 @@
             feature: min-monochrome
             value: 8
             unit: 
-mediaRule #4
-    text: (min-width: 100px)
-    source: mediaRule
-    range: {"endColumn":25,"endLine":4,"startColumn":7,"startLine":4}
-    computedText: (min-width: 100px)
-    mediaQuery #0
-        mediaExpression #0
-            feature: min-width
-            value: 100
-            unit: px
-            computed length: 100
 mediaRule #5
     text: (min-width: 100px)
     source: mediaRule
@@ -92,6 +92,17 @@
             unit: px
             computed length: 100
 mediaRule #7
+    text: (min-width: 100px)
+    source: mediaRule
+    range: {"endColumn":25,"endLine":4,"startColumn":7,"startLine":4}
+    computedText: (min-width: 100px)
+    mediaQuery #0
+        mediaExpression #0
+            feature: min-width
+            value: 100
+            unit: px
+            computed length: 100
+mediaRule #8
     text: (min-width: 1px), (max-width: 1000em)
     source: mediaRule
     range: {"endColumn":1,"endLine":9,"startColumn":7,"startLine":1}
@@ -108,7 +119,7 @@
             value: 1000
             unit: em
             computed length: 16000
-mediaRule #8
+mediaRule #9
     text: (min-width: 20px) and (max-width: 10px)
     source: mediaRule
     range: {"endColumn":46,"endLine":13,"startColumn":7,"startLine":13}
@@ -124,7 +135,7 @@
             value: 10
             unit: px
             computed length: 10
-mediaRule #9
+mediaRule #10
     text: (orientation: landscape), handheld and (max-resolution: 3dppx)
     source: importRule
     range: {"endColumn":99,"endLine":3,"startColumn":37,"startLine":3}
@@ -134,13 +145,13 @@
             feature: max-resolution
             value: 3
             unit: dppx
-mediaRule #10
+mediaRule #11
     text: (orientation: portrait)
     source: mediaRule
     range: {"endColumn":42,"endLine":14,"startColumn":11,"startLine":14}
     computedText: all and (orientation: portrait)
     mediaList is empty
-mediaRule #11
+mediaRule #12
     text: handheld and (min-width: 20em), screen and (min-width: 20em)
     source: mediaRule
     range: {"endColumn":71,"endLine":8,"startColumn":11,"startLine":8}
@@ -157,19 +168,19 @@
             value: 20
             unit: em
             computed length: 320
-mediaRule #12
+mediaRule #13
     text: not all
     source: mediaRule
     range: {"endColumn":43,"endLine":5,"startColumn":7,"startLine":5}
     computedText: screen and (min-width: 10px) and and
     mediaList is empty
-mediaRule #13
+mediaRule #14
     text: not all
     source: mediaRule
     range: {"endColumn":10,"endLine":29,"startColumn":7,"startLine":29}
     computedText: (])
     mediaList is empty
-mediaRule #14
+mediaRule #15
     text: print and (min-width: 8.5in)
     source: linkedSheet
     range: undefined
@@ -179,13 +190,13 @@
             value: 8.5
             unit: in
             computed length: 816
-mediaRule #15
+mediaRule #16
     text: screen and (device-aspect-ratio: 16 / 9), screen and (device-aspect-ratio: 16 / 10)
     source: mediaRule
     range: {"endColumn":86,"endLine":1,"startColumn":7,"startLine":1}
     computedText: screen and (device-aspect-ratio: 16/9), screen and (device-aspect-ratio: 16/10)
     mediaList is empty
-mediaRule #16
+mediaRule #17
     text: screen and (min-resolution: 2dppx)
     source: mediaRule
     range: {"endColumn":41,"endLine":7,"startColumn":7,"startLine":7}
@@ -195,7 +206,7 @@
             feature: min-resolution
             value: 2
             unit: dppx
-mediaRule #17
+mediaRule #18
     text: screen and (min-width: 10px) and (max-height: 4000px)
     source: importRule
     range: {"endColumn":42,"endLine":1,"startColumn":37,"startLine":0}
diff --git a/third_party/blink/web_tests/platform/generic/inspector-protocol/emulation/set-css-media-feature-override-expected.txt b/third_party/blink/web_tests/platform/generic/inspector-protocol/emulation/set-css-media-feature-override-expected.txt
index a4706f3e..ced2fcb 100644
--- a/third_party/blink/web_tests/platform/generic/inspector-protocol/emulation/set-css-media-feature-override-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/inspector-protocol/emulation/set-css-media-feature-override-expected.txt
@@ -1,52 +1,60 @@
 Tests that CSS media features can be overridden.
 matchMedia("(prefers-color-scheme: __invalid__)").matches: false
-matchMedia("(prefers-color-scheme: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-color-scheme: __invalid__)").matches applied: light
 matchMedia("(prefers-color-scheme: no-preference)").matches: false
-matchMedia("(prefers-color-scheme: no-preference)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-color-scheme: no-preference)").matches applied: light
 matchMedia("(prefers-color-scheme: light)").matches: true
-matchMedia("(prefers-color-scheme: light)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-color-scheme: light)").matches applied: light
 matchMedia("(prefers-color-scheme: dark)").matches: true
-matchMedia("(prefers-color-scheme: dark)").matches applied: 4px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-color-scheme: dark)").matches applied: dark
 matchMedia("(prefers-color-scheme: __invalid__)").matches: false
-matchMedia("(prefers-color-scheme: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-color-scheme: __invalid__)").matches applied: light
 matchMedia("(prefers-reduced-motion: __invalid__)").matches: false
-matchMedia("(prefers-reduced-motion: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-reduced-motion: __invalid__)").matches applied: no-preference
 matchMedia("(prefers-reduced-motion: no-preference)").matches: true
-matchMedia("(prefers-reduced-motion: no-preference)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-reduced-motion: no-preference)").matches applied: no-preference
 matchMedia("(prefers-reduced-motion: reduce)").matches: true
-matchMedia("(prefers-reduced-motion: reduce)").matches applied: 3px x 3px, rgb(0, 0, 255)
+matchMedia("(prefers-reduced-motion: reduce)").matches applied: reduce
 matchMedia("(prefers-reduced-motion: __invalid__)").matches: false
-matchMedia("(prefers-reduced-motion: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-reduced-motion: __invalid__)").matches applied: no-preference
+matchMedia("(prefers-reduced-data: __invalid__)").matches: false
+matchMedia("(prefers-reduced-data: __invalid__)").matches applied: no-preference
+matchMedia("(prefers-reduced-data: no-preference)").matches: true
+matchMedia("(prefers-reduced-data: no-preference)").matches applied: no-preference
+matchMedia("(prefers-reduced-data: reduce)").matches: true
+matchMedia("(prefers-reduced-data: reduce)").matches applied: reduce
+matchMedia("(prefers-reduced-data: __invalid__)").matches: false
+matchMedia("(prefers-reduced-data: __invalid__)").matches applied: no-preference
 matchMedia("(prefers-contrast: __invalid__)").matches: false
-matchMedia("(prefers-contrast: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-contrast: __invalid__)").matches applied: no-preference
 matchMedia("(prefers-contrast: no-preference)").matches: true
-matchMedia("(prefers-contrast: no-preference)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-contrast: no-preference)").matches applied: no-preference
 matchMedia("(prefers-contrast: more)").matches: true
-matchMedia("(prefers-contrast: more)").matches applied: 3px x 2px, rgb(0, 128, 0)
+matchMedia("(prefers-contrast: more)").matches applied: more
 matchMedia("(prefers-contrast: less)").matches: true
-matchMedia("(prefers-contrast: less)").matches applied: 3px x 2px, rgb(255, 255, 0)
+matchMedia("(prefers-contrast: less)").matches applied: less
 matchMedia("(prefers-contrast: custom)").matches: true
-matchMedia("(prefers-contrast: custom)").matches applied: 3px x 2px, rgb(255, 165, 0)
+matchMedia("(prefers-contrast: custom)").matches applied: custom
 matchMedia("(prefers-contrast: __invalid__)").matches: false
-matchMedia("(prefers-contrast: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-contrast: __invalid__)").matches applied: no-preference
 matchMedia("(color-gamut: __invalid__)").matches: false
-matchMedia("(color-gamut: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(color-gamut: __invalid__)").matches applied: invalid
 matchMedia("(color-gamut: p3)").matches: true
-matchMedia("(color-gamut: p3)").matches applied: 6px x 6px, rgb(0, 0, 255)
+matchMedia("(color-gamut: p3)").matches applied: p3
 matchMedia("(color-gamut: rec2020)").matches: true
-matchMedia("(color-gamut: rec2020)").matches applied: 7px x 7px, rgb(0, 0, 255)
+matchMedia("(color-gamut: rec2020)").matches applied: rec2020
 matchMedia("(color-gamut: __invalid__)").matches: false
-matchMedia("(color-gamut: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(color-gamut: __invalid__)").matches applied: invalid
 matchMedia("(forced-colors: __invalid__)").matches: false
-matchMedia("(forced-colors: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(forced-colors: __invalid__)").matches applied: none
 matchMedia("(forced-colors: active)").matches: true
-matchMedia("(forced-colors: active)").matches applied: 3px x 2px, rgb(0, 0, 0)
+matchMedia("(forced-colors: active)").matches applied: active
 matchMedia("(forced-colors: none)").matches: true
-matchMedia("(forced-colors: none)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(forced-colors: none)").matches applied: none
 matchMedia("(forced-colors: __invalid__)").matches: false
-matchMedia("(forced-colors: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(forced-colors: __invalid__)").matches applied: none
 matchMedia("(prefers-color-scheme: dark) and (prefers-reduced-motion: reduce)").matches: true
-matchMedia("(prefers-color-scheme: dark) and (prefers-reduced-motion: reduce)").matches applied: 999px x 999px, rgb(0, 0, 255)
+matchMedia("(prefers-color-scheme: dark) and (prefers-reduced-motion: reduce)").matches applied: dark2; reduce2
 matchMedia("(prefers-color-scheme: __invalid__)").matches: false
-matchMedia("(prefers-color-scheme: __invalid__)").matches applied: 3px x 2px, rgb(0, 0, 255)
+matchMedia("(prefers-color-scheme: __invalid__)").matches applied: light
 
diff --git a/third_party/blink/web_tests/platform/generic/svg/zoom/page/zoom-foreign-content-expected.png b/third_party/blink/web_tests/platform/generic/svg/zoom/page/zoom-foreign-content-expected.png
index adfcdc9..2ef70db 100644
--- a/third_party/blink/web_tests/platform/generic/svg/zoom/page/zoom-foreign-content-expected.png
+++ b/third_party/blink/web_tests/platform/generic/svg/zoom/page/zoom-foreign-content-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index f6c7a061..e06208c 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -168,6 +168,10 @@
     getter highWaterMark
     getter size
     method constructor
+interface CropTarget
+    static method fromElement
+    attribute @@toStringTag
+    method constructor
 interface Crypto
     attribute @@toStringTag
     getter subtle
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 21a67974f..0f54e0c4 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -160,6 +160,10 @@
 [Worker]     getter highWaterMark
 [Worker]     getter size
 [Worker]     method constructor
+[Worker] interface CropTarget
+[Worker]     static method fromElement
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface Crypto
 [Worker]     attribute @@toStringTag
 [Worker]     getter subtle
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt
index 048d274b..8dd54bc9 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -413,6 +413,10 @@
     method postMessage
     setter onmessage
     setter onmessageerror
+interface BrowserCaptureMediaStreamTrack : MediaStreamTrack
+    attribute @@toStringTag
+    method constructor
+    method cropTo
 interface ByteLengthQueuingStrategy
     attribute @@toStringTag
     getter highWaterMark
@@ -1009,6 +1013,10 @@
     method get
     method preventSilentAccess
     method store
+interface CropTarget
+    static method fromElement
+    attribute @@toStringTag
+    method constructor
 interface Crypto
     attribute @@toStringTag
     getter subtle
diff --git a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
index 41aab633..bc5f78316 100644
--- a/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/virtual/stable/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -125,6 +125,10 @@
 [Worker]     getter highWaterMark
 [Worker]     getter size
 [Worker]     method constructor
+[Worker] interface CropTarget
+[Worker]     static method fromElement
+[Worker]     attribute @@toStringTag
+[Worker]     method constructor
 [Worker] interface Crypto
 [Worker]     attribute @@toStringTag
 [Worker]     getter subtle
diff --git a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
index b18a501..eeae9af 100644
--- a/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/platform/generic/webexposed/global-interface-listing-expected.txt
@@ -608,10 +608,11 @@
     method postMessage
     setter onmessage
     setter onmessageerror
-interface BrowserCaptureMediaStreamTrack : FocusableMediaStreamTrack
+interface BrowserCaptureMediaStreamTrack : MediaStreamTrack
     attribute @@toStringTag
     method constructor
     method cropTo
+    method focus
 interface ByteLengthQueuingStrategy
     attribute @@toStringTag
     getter highWaterMark
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/linux/paint/invalidation/svg/zoom-foreignObject-expected.png
index 4130567..c9104a2 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/svg/zoom-foreignObject-expected.png
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/svg/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/foreignObject/svg-document-in-html-document-expected.png b/third_party/blink/web_tests/platform/linux/svg/foreignObject/svg-document-in-html-document-expected.png
index 085e2c6..b79cdec 100644
--- a/third_party/blink/web_tests/platform/linux/svg/foreignObject/svg-document-in-html-document-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/foreignObject/svg-document-in-html-document-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/svg/zoom/page/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/linux/svg/zoom/page/zoom-foreignObject-expected.png
index d709ed37..820af93 100644
--- a/third_party/blink/web_tests/platform/linux/svg/zoom/page/zoom-foreignObject-expected.png
+++ b/third_party/blink/web_tests/platform/linux/svg/zoom/page/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/svg/zoom-foreignObject-expected.png
index 4130567..c9104a2 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/svg/zoom-foreignObject-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/backface-visibility-interop/paint/invalidation/svg/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/foreignObject-text-clipping-bug-expected.png b/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/foreignObject-text-clipping-bug-expected.png
index 1732609..54322a4f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/foreignObject-text-clipping-bug-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/svg/text/foreignObject-text-clipping-bug-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/paint/invalidation/svg/zoom-foreignObject-expected.png
new file mode 100644
index 0000000..705289c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/paint/invalidation/svg/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/svg/custom/dominant-baseline-hanging-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/svg/custom/dominant-baseline-hanging-expected.png
new file mode 100644
index 0000000..b74e241
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/svg/custom/dominant-baseline-hanging-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/svg/foreignObject/svg-document-in-html-document-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/svg/foreignObject/svg-document-in-html-document-expected.png
new file mode 100644
index 0000000..51a6aee
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/svg/foreignObject/svg-document-in-html-document-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/svg/zoom/page/zoom-foreign-content-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/svg/zoom/page/zoom-foreign-content-expected.png
new file mode 100644
index 0000000..adfcdc9
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/svg/zoom/page/zoom-foreign-content-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/svg/zoom/page/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/svg/zoom/page/zoom-foreignObject-expected.png
new file mode 100644
index 0000000..64e9652
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/svg/zoom/page/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.14/virtual/backface-visibility-interop/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/backface-visibility-interop/paint/invalidation/svg/zoom-foreignObject-expected.png
new file mode 100644
index 0000000..705289c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.14/virtual/backface-visibility-interop/paint/invalidation/svg/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/zoom-foreignObject-expected.png
index 705289c..388ce2f 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/zoom-foreignObject-expected.png
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/svg/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png b/third_party/blink/web_tests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png
index b74e241..f7b0916 100644
--- a/third_party/blink/web_tests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/custom/dominant-baseline-hanging-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/foreignObject/svg-document-in-html-document-expected.png b/third_party/blink/web_tests/platform/mac/svg/foreignObject/svg-document-in-html-document-expected.png
index 51a6aee..6b57183 100644
--- a/third_party/blink/web_tests/platform/mac/svg/foreignObject/svg-document-in-html-document-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/foreignObject/svg-document-in-html-document-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/text/foreignObject-text-clipping-bug-expected.png b/third_party/blink/web_tests/platform/mac/svg/text/foreignObject-text-clipping-bug-expected.png
index 0ffcb6c..21486a6 100644
--- a/third_party/blink/web_tests/platform/mac/svg/text/foreignObject-text-clipping-bug-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/text/foreignObject-text-clipping-bug-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/svg/zoom/page/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/mac/svg/zoom/page/zoom-foreignObject-expected.png
index 64e9652..27ea8a3 100644
--- a/third_party/blink/web_tests/platform/mac/svg/zoom/page/zoom-foreignObject-expected.png
+++ b/third_party/blink/web_tests/platform/mac/svg/zoom/page/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/svg/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/win/paint/invalidation/svg/zoom-foreignObject-expected.png
index 19ef50b..2f6e159f 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/svg/zoom-foreignObject-expected.png
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/svg/zoom-foreignObject-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/foreignObject/svg-document-in-html-document-expected.png b/third_party/blink/web_tests/platform/win/svg/foreignObject/svg-document-in-html-document-expected.png
index 6114c97..8c07899f 100644
--- a/third_party/blink/web_tests/platform/win/svg/foreignObject/svg-document-in-html-document-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/foreignObject/svg-document-in-html-document-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/svg/zoom/page/zoom-foreignObject-expected.png b/third_party/blink/web_tests/platform/win/svg/zoom/page/zoom-foreignObject-expected.png
index cf3aa95..25646ad 100644
--- a/third_party/blink/web_tests/platform/win/svg/zoom/page/zoom-foreignObject-expected.png
+++ b/third_party/blink/web_tests/platform/win/svg/zoom/page/zoom-foreignObject-expected.png
Binary files differ
diff --git a/tools/grit/preprocess_if_expr.py b/tools/grit/preprocess_if_expr.py
index 77a4dd0..32acd6de 100644
--- a/tools/grit/preprocess_if_expr.py
+++ b/tools/grit/preprocess_if_expr.py
@@ -116,7 +116,12 @@
       # for overlapping directories hit the makedirs line at the same time.
       if e.errno != errno.EEXIST:
         raise
-    if os.path.exists(out_path) and os.path.samefile(out_path, in_path):
+
+    # Delete the target file before witing it, as it may be hardlinked to other
+    # files, which can break the build. This is the case in particular if the
+    # file was "copied" to different locations with GN (as GN's copy is actually
+    # a hard link under the hood). See https://crbug.com/1332497
+    if os.path.exists(out_path):
       os.remove(out_path)
 
     # Detect and delete any stale TypeScript files present in the output folder,
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index a275e6ad..31a371c 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -27971,6 +27971,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromAvatarBubbleSignin"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with an account in the
@@ -27981,6 +27982,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromBookmarkBubble"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with an account in the
@@ -27991,6 +27993,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromBookmarkManager"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with an account in the
@@ -28001,6 +28004,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromExtensionInstallBubble"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with an account in the
@@ -28023,6 +28027,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromNTPContentSuggestions"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>bsazonov@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with a default account,
@@ -28033,6 +28038,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromNTPFeedTopPromo"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>mrefaat@chromium.org</owner>
   <owner>sczs@chromium.org</owner>
   <owner>jlebel@chromium.org</owner>
@@ -28045,6 +28051,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromPasswordBubble"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with an account in the
@@ -28055,6 +28062,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromRecentTabs"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with a default account,
@@ -28076,6 +28084,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromSettings"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with a default account,
@@ -28085,6 +28094,7 @@
 
 <action name="Signin_ImpressionWithAccount_FromTabSwitcher"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with a default account,
@@ -28094,6 +28104,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromAvatarBubbleSignin"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with no account in the
@@ -28104,6 +28115,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromBookmarkBubble"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with no account in the
@@ -28114,6 +28126,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromBookmarkManager"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with no default
@@ -28124,6 +28137,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromExtensionInstallBubble"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with no account in the
@@ -28146,6 +28160,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromNTPContentSuggestions"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>bsazonov@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with no default
@@ -28156,6 +28171,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromNTPFeedTopPromo"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>mrefaat@chromium.org</owner>
   <owner>jlebel@chromium.org</owner>
   <owner>sczs@chromium.org</owner>
@@ -28168,6 +28184,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromPasswordBubble"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>msarda@chromium.org</owner>
   <description>
     Recorded when the personalized sign-in promo is shown with no account in the
@@ -28178,6 +28195,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromRecentTabs"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with no default
@@ -28200,6 +28218,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromSettings"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with no default
@@ -28209,6 +28228,7 @@
 
 <action name="Signin_ImpressionWithNoAccount_FromTabSwitcher"
     not_user_triggered="true">
+  <obsolete>Removed in M104.</obsolete>
   <owner>jlebel@chromium.org</owner>
   <description>
     Recorded when starting sign-in using the promo view, with no default
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2a8d6900..3e89b1a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -87,7 +87,8 @@
   <int value="13" label="kMissingDescriptionLang">Lang is missing.</int>
   <int value="14" label="kMissingDescriptionSource">Source is missing.</int>
   <int value="15" label="kMissingBannerInfo">Banner info is missing.</int>
-  <int value="16" label="kInvalidMoreAbout">MoreAbout is invalid.</int>
+  <int value="16" label="kInvalidMoreAbout">MoreAbout URL is invalid.</int>
+  <int value="17" label="kMissingMoreAbout">MoreAbout URL is missing.</int>
 </enum>
 
 <enum name="AcceleratedFixedRootBackground">
@@ -21041,9 +21042,11 @@
   <int value="1" label="UnsupportedPlatform"/>
   <int value="2" label="InvalidCropTargetFormat"/>
   <int value="3" label="RejectedWithErrorGeneric"/>
-  <int value="4" label="RejectedWithUnsupportedCaptureDevice"/>
+  <int value="4" label="RejectedWithUnsupportedCaptureDevice_DEPRECATED"/>
   <int value="5" label="RejectedWithErrorUnknownDeviceId"/>
   <int value="6" label="RejectedWithNotImplemented"/>
+  <int value="7" label="NonIncreasingCropVersion"/>
+  <int value="8" label="InvalidCropTarget"/>
 </enum>
 
 <enum name="CrOSActionRecorderEvent">
@@ -24627,6 +24630,7 @@
   <int value="58" label="evaluateExpressionsWithSourceMaps"/>
   <int value="59" label="cssLayers"/>
   <int value="60" label="eyedropperColorPicker"/>
+  <int value="62" label="cssAuthoringHints"/>
 </enum>
 
 <enum name="DevToolsGridOverlayOpenedFrom">
@@ -71777,6 +71781,7 @@
       label="Privacy: Enable/Disable peripheral data access protection"/>
   <int value="1114" label="Privacy: Snooping Protection"/>
   <int value="1115" label="Privacy: Lock On Leave"/>
+  <int value="1116" label="Privacy: Camera Software Switch"/>
   <int value="1200" label="Add Language"/>
   <int value="1201" label="Show Input Options In Shelf"/>
   <int value="1202" label="Show Personal Information Suggestions"/>
@@ -71958,6 +71963,7 @@
   <int value="1101" label="Security And Sign In V2"/>
   <int value="1102" label="Fingerprint V2"/>
   <int value="1103" label="Smart Privacy Protections"/>
+  <int value="1104" label="Privacy Hub"/>
   <int value="1200" label="Languages And Input Details"/>
   <int value="1201" label="Manage Input Methods"/>
   <int value="1202" label="Smart Inputs"/>
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 0d769398..37a21faf 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -275,15 +275,22 @@
       break;
     }
     case TextUnit_Line:
+      // Walk backwards to the previous line start (but don't walk backwards
+      // if we're already at the start of a line). The previous line start can
+      // occur in a different node than where `start` is currently pointing, so
+      // use kStopAtLastAnchorBoundary, which will stop at the tree boundary if
+      // no previous line start is found.
       SetStart(start()->CreateBoundaryStartPosition(
-          {AXBoundaryBehavior::kStopAtAnchorBoundary,
+          {AXBoundaryBehavior::kStopAtLastAnchorBoundary,
            AXBoundaryDetection::kCheckInitialPosition},
           ax::mojom::MoveDirection::kBackward,
           base::BindRepeating(&AtStartOfLinePredicate),
           base::BindRepeating(&AtEndOfLinePredicate)));
+      // From the start we just walked backwards to, walk forwards to the line
+      // end position.
       SetEnd(start()->CreateBoundaryEndPosition(
-          {AXBoundaryBehavior::kStopAtAnchorBoundary,
-           AXBoundaryDetection::kCheckInitialPosition},
+          {AXBoundaryBehavior::kStopAtLastAnchorBoundary,
+           AXBoundaryDetection::kDontCheckInitialPosition},
           ax::mojom::MoveDirection::kForward,
           base::BindRepeating(&AtStartOfLinePredicate),
           base::BindRepeating(&AtEndOfLinePredicate)));
@@ -291,7 +298,7 @@
     case TextUnit_Paragraph:
       SetStart(
           start()->CreatePreviousParagraphStartPositionSkippingEmptyParagraphs(
-              {AXBoundaryBehavior::kStopAtAnchorBoundary,
+              {AXBoundaryBehavior::kStopAtLastAnchorBoundary,
                AXBoundaryDetection::kCheckInitialPosition}));
       SetEnd(start()->CreateNextParagraphStartPositionSkippingEmptyParagraphs(
           {AXBoundaryBehavior::kStopAtLastAnchorBoundary,
@@ -303,7 +310,7 @@
       const AXNode* common_anchor = start()->LowestCommonAnchor(*end());
       if (common_anchor->tree()->HasPaginationSupport()) {
         SetStart(start()->CreatePreviousPageStartPosition(
-            {AXBoundaryBehavior::kStopAtAnchorBoundary,
+            {AXBoundaryBehavior::kStopAtLastAnchorBoundary,
              AXBoundaryDetection::kCheckInitialPosition}));
         SetEnd(start()->CreateNextPageEndPosition(
             {AXBoundaryBehavior::kStopAtAnchorBoundary,
diff --git a/ui/file_manager/file_manager/foreground/js/task_controller.js b/ui/file_manager/file_manager/foreground/js/task_controller.js
index f214cfb..899a84f 100644
--- a/ui/file_manager/file_manager/foreground/js/task_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/task_controller.js
@@ -533,27 +533,39 @@
     this.extractTasks_.delete(taskId);
   }
 
-  async startGetPasswordThenExtractTask(selectionEntries, params) {
+  /**
+   * Starts extraction for a single entry and stores the task details.
+   * @private
+   */
+  async startExtractTask_(entry, params) {
+    let taskId;
+    try {
+      taskId = await startIOTask(
+          chrome.fileManagerPrivate.IOTaskType.EXTRACT, [entry], params);
+      this.storeExtractTaskDetails(taskId, [entry], params);
+    } catch (e) {
+      console.warn('Error getting extract taskID', e);
+    }
+  }
+
+  /**
+   * Triggers a password dialog and starts an extract task with the
+   * password (unless cancel is clicked on the dialog).
+   * @private
+   */
+  async startGetPasswordThenExtractTask_(entry, params) {
     /** @type {?string} */ let password = null;
     // Ask for password.
     try {
       password = await this.ui_.passwordDialog.askForPassword(
-          selectionEntries[0].fullPath, password);
+          entry.fullPath, password);
     } catch (error) {
       console.warn('User cancelled password fetch ', error);
       return;
     }
 
     params['password'] = password;
-    let taskId;
-    try {
-      taskId = await startIOTask(
-          chrome.fileManagerPrivate.IOTaskType.EXTRACT, selectionEntries,
-          params);
-      this.storeExtractTaskDetails(taskId, selectionEntries, params);
-    } catch (e) {
-      console.warn('Error getting extract taskID', e);
-    }
+    await this.startExtractTask_(entry, params);
   }
 
   /**
@@ -564,8 +576,20 @@
   handleMissingPassword(taskId) {
     const existingOperation = this.extractTasks_.get(taskId);
     if (existingOperation) {
-      this.startGetPasswordThenExtractTask(
-          existingOperation['entries'], existingOperation['params']);
+      // If we have multiple entries (from a multi-select extract) then
+      // we need to start a new task for each of them individually so
+      // that the password dialog is presented once for every file
+      // that's encrypted.
+      const selectionEntries = existingOperation['entries'];
+      const params = existingOperation['params'];
+      if (selectionEntries.length == 1) {
+        this.startGetPasswordThenExtractTask_(
+            existingOperation['entries'][0], params);
+      } else {
+        for (const entry of selectionEntries) {
+          this.startExtractTask_(entry, params);
+        }
+      }
     }
     // Remove the failed operation reference since it's finished.
     this.deleteExtractTaskDetails(taskId);