diff --git a/AUTHORS b/AUTHORS
index 49ddab2..86c4f7f0 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1334,6 +1334,7 @@
 wafuwafu13 <mariobaske@i.softbank.jp>
 Wojciech Bielawski <wojciech.bielawski@gmail.com>
 Wang Chen <wangchen20@iscas.ac.cn>
+Wang Chen <unicornxw@gmail.com>
 Wang Weiwei <wangww@dingdao.com>
 Wangyang Dai <jludwy@gmail.com>
 Wanming Lin <wanming.lin@intel.com>
diff --git a/DEPS b/DEPS
index d9ff744..46fa2bc 100644
--- a/DEPS
+++ b/DEPS
@@ -300,19 +300,19 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': 'e03e8d1d7f7bd1869aff4f3d755d780810fcb21f',
+  'src_internal_revision': 'ee9a494402f066aee426dc42b75d30cc71a18dbd',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'a226cfbd6cc7179e9ed165125d6861b99550a001',
+  'skia_revision': '8a0152a423497497cc7425541947cf1bb3745a2e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '1a1dc7616f2c9fe00b8fffb056e678a428acf516',
+  'v8_revision': '6013b6402ff6de4db468323ac6b7c69de83218f2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'e7a9275be22d28c74049d397a87183d47a1d188f',
+  'angle_revision': '6e40ce0071228c189506b986b6702b95ef0c2ac0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -391,7 +391,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': '59dd87f2f45f6d8659291fe1bff52540657566a9',
+  'devtools_frontend_revision': 'bf4f2fcc886593af85cf7c2d5647d14ebccbbe51',
   # 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.
@@ -479,7 +479,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.
-  'libunwind_revision':    'cd144ced35285edaa064a91561969e5b22c219b1',
+  'libunwind_revision':    'b5a43ecdac82a248f8a700a68c722b4d98708377',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -911,7 +911,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/linux-amd64',
-          'version': 'qFXOotL3BRF2fhT8kz0P-RscghSPWuGF9_9_2b2xjUAC',
+          'version': 'pgJa4nimCNU7g921DwcTZgWvtrtwVu6rkZhDfqjVLq0C',
         },
       ],
       'dep_type': 'cipd',
@@ -922,7 +922,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/mac-amd64',
-          'version': 'OyU9564VNQX3Yh8NwBC8iAhDU4gToLpsJxAgGK94c1MC',
+          'version': 'gTuVXh_RZKUMB39uZY-Bu6PRx_fMNG_jRpiWeONhBhwC',
         },
       ],
       'dep_type': 'cipd',
@@ -933,7 +933,7 @@
       'packages': [
         {
           'package': 'chromium/rts/model/windows-amd64',
-          'version': 'ZTwDU5fQef0bjjHIFes5ZMRsn7uW89bAu7wNppEnhUoC',
+          'version': 'LwcvneReAE8h5RWfbKv-flvZ0RcYiatwjJspxUbOZUwC',
         },
       ],
       'dep_type': 'cipd',
@@ -1001,7 +1001,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'P3-Etm0wxlp5sUCCgNTJJBJWGVl8xyaQ6_Rwn7TmtHwC',
+          'version': 'iYjHXTBxKD0S9CuLGLN6QESvNkURfGdqURQSUDrnai0C',
       },
     ],
     'condition': 'checkout_android',
@@ -1207,7 +1207,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e01cbdf84fb1dccb96a35c82bbe0cad642519d7e',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1a51818385ec4ef833f9945ca5f248852f5cfaf0',
       'condition': 'checkout_chromeos',
   },
 
@@ -1242,13 +1242,13 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'df2f11113c23d65c1fd7824b7ab3c55f7cfb9604',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '37d1312fcd44558402bebc2966b6b98babca5d7b',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + 'a4b757e5eae6a111eb772290e67cad21199b0a17',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '7b9cd2ab1fce7837fc5a40b816f3cd4f5ba5235a',
     'condition': 'checkout_src_internal',
   },
 
@@ -1715,7 +1715,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'bcbe569710dbd97b0d7d9e95d87e93be7d1f8065',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '55c4abdb8736d8618e0d6c1c0c6a7df5a41559a0',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -2025,7 +2025,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': '1YpDqTSySZ2wiWMd2YtGLNjqQ7O16Kd-w2aIxGs-mxEC',
+        'version': 'U3w8ZDVd1zRC2BZDJJV71ryb9r5MHSTxIBmW4lnNyYMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2036,7 +2036,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'qFBi9r80SlowIX-MUdDoXWp0uK_A928fmBIKgR2dWJUC',
+        'version': 'LcVQfWIBP6eUziUIUD3rSzXFvnUxu2Fg0M2E2IMzmCUC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3995,7 +3995,7 @@
 
   'src/chrome/app/theme/default_100_percent/google_chrome': {
       'url': Var('chrome_git') + '/chrome/theme/default_100_percent/google_chrome.git' + '@' +
-        '534ecab0aa3978f434d3fedc5f5b298265523482',
+        '6cae6834aee1c39513f0b2e22a6249466e79d407',
       'condition': 'checkout_src_internal',
   },
 
@@ -4025,7 +4025,7 @@
 
   'src/chrome/browser/internal': {
       'url': Var('chrome_git') + '/chrome/browser_internal.git' + '@' +
-        'd3a55d714679c55a73ff96a7ea77493b326dae91',
+        '86edf3656689330827cd0bd118656e6be5ec88a4',
       'condition': 'checkout_src_internal',
   },
 
@@ -4146,7 +4146,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '15a8ac31d8c154261e8e937c18a4fcf3a76ec22f',
+        '8d544b1e6c0adba47bb66f70bd7f8b92eed6d46d',
       'condition': 'checkout_src_internal',
   },
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 0ec63376..29b4c2a 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -288,7 +288,8 @@
       r'/RecordHistogram\.getHistogram(ValueCount|TotalCount|Samples)ForTesting\(',
       (
        'Raw histogram counts are easy to misuse; for example they don\'t reset '
-       'between batched tests. Use HistogramWatcher to check histogram records instead.',
+       'between batched tests. Use HistogramWatcher to check histogram records '
+       'instead.',
       ),
       False,
       excluded_paths=(
@@ -5005,6 +5006,10 @@
         "correct_name": "Chrome",
         "incorrect_name": "Chromium",
     }, {
+        "filename_postfix": "google_chrome_strings.grd",
+        "correct_name": "Chrome",
+        "incorrect_name": "Chrome for Testing",
+    }, {
         "filename_postfix": "chromium_strings.grd",
         "correct_name": "Chromium",
         "incorrect_name": "Chrome",
@@ -5023,6 +5028,9 @@
                 if "<message" in line or "<!--" in line or "-->" in line:
                     continue
                 if test_case["incorrect_name"] in line:
+                    # Chrome for Testing is a special edge case: https://goo.gle/chrome-for-testing#bookmark=id.n1rat320av91
+                    if (test_case["correct_name"] == "Chromium" and line.count("Chrome") == line.count("Chrome for Testing")):
+                        continue
                     problems.append("Incorrect product name in %s:%d" %
                                     (f.LocalPath(), line_num))
 
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
index ea7797d..9d29a21 100755
--- a/PRESUBMIT_test.py
+++ b/PRESUBMIT_test.py
@@ -2325,6 +2325,34 @@
     self.assertTrue(
         'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
 
+  def testChromeForTestingInChromium(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/app/chromium_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chrome for Testing!',
+        '</message>',
+      ]),
+    ]
+    warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(0, len(warnings))
+
+  def testChromeForTestingInChrome(self):
+    mock_input_api = MockInputApi()
+    mock_input_api.files = [
+      MockAffectedFile('chrome/app/google_chrome_strings.grd', [
+        '<message name="Bar" desc="Welcome to Chrome">',
+        '  Welcome to Chrome for Testing!',
+        '</message>',
+      ]),
+    ]
+    warnings = PRESUBMIT.CheckCorrectProductNameInMessages(
+        mock_input_api, MockOutputApi())
+    self.assertEqual(1, len(warnings))
+    self.assertTrue(
+        'chrome/app/google_chrome_strings.grd:2' in warnings[0].items[0])
+
   def testMultipleInstances(self):
     mock_input_api = MockInputApi()
     mock_input_api.files = [
diff --git a/android_webview/browser/aw_autofill_client.cc b/android_webview/browser/aw_autofill_client.cc
index 928a207..355a946 100644
--- a/android_webview/browser/aw_autofill_client.cc
+++ b/android_webview/browser/aw_autofill_client.cc
@@ -24,6 +24,7 @@
 #include "components/autofill/core/browser/autofill_download_manager.h"
 #include "components/autofill/core/browser/payments/legal_message_line.h"
 #include "components/autofill/core/browser/ui/autofill_popup_delegate.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -358,7 +359,7 @@
            content::SSLStatus::RAN_INSECURE_CONTENT);
 }
 
-void AwAutofillClient::ExecuteCommand(autofill::Suggestion::FrontendId id) {
+void AwAutofillClient::ExecuteCommand(autofill::PopupItemId popup_item_id) {
   NOTIMPLEMENTED();
 }
 
@@ -445,7 +446,7 @@
 
     Java_AwAutofillClient_addToAutofillSuggestionArray(
         env, data_array, i, name, label,
-        base::to_underlying(suggestions[i].frontend_id.as_popup_item_id()));
+        base::to_underlying(suggestions[i].popup_item_id));
   }
   ui::ViewAndroid* view_android = GetWebContents().GetNativeView();
   if (!view_android)
diff --git a/android_webview/browser/aw_autofill_client.h b/android_webview/browser/aw_autofill_client.h
index afd7ef2..105ea55 100644
--- a/android_webview/browser/aw_autofill_client.h
+++ b/android_webview/browser/aw_autofill_client.h
@@ -16,6 +16,7 @@
 #include "components/autofill/content/browser/content_autofill_client.h"
 #include "components/autofill/core/browser/autofill_trigger_source.h"
 #include "components/autofill/core/browser/payments/legal_message_line.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "ui/android/view_android.h"
 
@@ -162,7 +163,7 @@
   void DidFillOrPreviewField(const std::u16string& autofilled_value,
                              const std::u16string& profile_full_name) override;
   bool IsContextSecure() const override;
-  void ExecuteCommand(autofill::Suggestion::FrontendId id) override;
+  void ExecuteCommand(autofill::PopupItemId popup_item_id) override;
   void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
   autofill::FormInteractionsFlowId GetCurrentFormInteractionsFlowId() override;
 
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 5788c4b..cb48303 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -59,6 +59,8 @@
 #include "base/supports_user_data.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/typed_macros.h"
 #include "components/android_autofill/browser/android_autofill_manager.h"
 #include "components/android_autofill/browser/autofill_provider_android.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
@@ -225,6 +227,8 @@
     : content::WebContentsObserver(web_contents.get()),
       browser_view_renderer_(this, content::GetUIThreadTaskRunner({})),
       web_contents_(std::move(web_contents)) {
+  TRACE_EVENT_BEGIN("android_webview.timeline", "WebView Instance",
+                    perfetto::Track::FromPointer(this));
   base::subtle::NoBarrier_AtomicIncrement(&g_instance_count, 1);
   icon_helper_ = std::make_unique<IconHelper>(web_contents_.get());
   icon_helper_->SetListener(this);
@@ -368,6 +372,9 @@
   WebContentsObserver::Observe(nullptr);
   AwBrowserProcess::GetInstance()->visibility_metrics_logger()->RemoveClient(
       this);
+  // Corresponds to "WebView Instance" in AwContents's constructor.
+  TRACE_EVENT_END("android_webview.timeline",
+                  perfetto::Track::FromPointer(this));
 }
 
 base::android::ScopedJavaLocalRef<jobject> AwContents::GetWebContents(
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index e898572..1214e35 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -130,6 +130,8 @@
                     "More robust heuristic for calling Invalidate"),
             Flag.baseFeature(
                     VizFeatures.WEBVIEW_VULKAN_INTERMEDIATE_BUFFER, "For debugging vulkan"),
+            Flag.baseFeature(VizFeatures.ALLOW_UNDAMAGED_NONROOT_RENDER_PASS_TO_SKIP,
+                    "Enable optimization for skipping undamaged nonroot render passes."),
             Flag.baseFeature(
                     GpuFeatures.USE_GLES2_FOR_OOP_R, "Force Skia context to use es2 only."),
             Flag.baseFeature(AwFeatures.WEBVIEW_CONNECTIONLESS_SAFE_BROWSING,
diff --git a/android_webview/system_webview_apk_tmpl.gni b/android_webview/system_webview_apk_tmpl.gni
index 3942af4..50bd7d02 100644
--- a/android_webview/system_webview_apk_tmpl.gni
+++ b/android_webview/system_webview_apk_tmpl.gni
@@ -162,9 +162,6 @@
     if (!_omit_dex) {
       product_config_java_packages =
           [ invoker.webview_product_config_java_package ]
-      if (allow_jni_multiplexing) {
-        enable_jni_multiplexing = true
-      }
     }
 
     if (webview_includes_weblayer) {
diff --git a/android_webview/tools/cts_config/webview_cts_gcs_path.json b/android_webview/tools/cts_config/webview_cts_gcs_path.json
index 1cba621..fd2e56e 100644
--- a/android_webview/tools/cts_config/webview_cts_gcs_path.json
+++ b/android_webview/tools/cts_config/webview_cts_gcs_path.json
@@ -86,6 +86,10 @@
           {
             "match": "android.webkit.cts.WebViewClientTest#testOnSafeBrowsingHit",
             "_bug_id": "crbug.com/1300977"
+          },
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
           }
         ]
       },
@@ -151,6 +155,10 @@
           {
             "match": "android.webkit.cts.WebSettingsTest#testAppCacheEnabled",
             "_bug_id": "crbug.com/1246334"
+          },
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
           }
         ]
       },
@@ -208,7 +216,13 @@
     },
     "test_runs": [
       {
-        "apk": "android-cts/testcases/CtsWebkitTestCases.apk"
+        "apk": "android-cts/testcases/CtsWebkitTestCases.apk",
+        "excludes": [
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
+          }
+        ]
       },
       {
         "apk": "android-cts/testcases/CtsWebViewStartupApp.apk"
@@ -276,6 +290,10 @@
           {
             "match": "android.webkit.cts.WebViewClientTest#testOnSafeBrowsingHit",
             "_bug_id": "crbug.com/1245351"
+          },
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
           }
         ]
       },
@@ -349,6 +367,10 @@
           {
             "match": "android.webkit.cts.WebViewClientTest#testOnSafeBrowsingHitBackToSafety",
             "_bug_id": "crbug.com/1245351"
+          },
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
           }
         ]
       },
@@ -437,6 +459,10 @@
           {
             "match": "android.webkit.cts.WebViewClientTest#testOnSafeBrowsingHitBackToSafety",
             "_bug_id": "crbug.com/1245351"
+          },
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
           }
         ]
       },
@@ -573,6 +599,10 @@
           {
             "match": "android.webkit.cts.WebViewClientTest#testOnSafeBrowsingHitBackToSafety",
             "_bug_id": "crbug.com/1245351"
+          },
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
           }
         ]
       },
@@ -744,6 +774,10 @@
           {
             "match": "android.webkit.cts.WebViewTest#testSetNetworkAvailable",
             "_bug_id": "crbug.com/1368663"
+          },
+          {
+            "match": "android.webkit.cts.WebViewTest#testLoadUrl",
+            "_bug_id": "crbug.com/1450170"
           }
         ]
       },
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 0efb848..da9dab4 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -606,9 +606,15 @@
       <message name="IDS_ASH_STATUS_TRAY_CAST_PAUSE" desc="Label for a button in the cast notification to pause the current cast mirroring session to a Chromecast device">
         Pause
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_CAST_PAUSE_CASTING" desc="Label for a button in the system tray to pause the current cast mirroring session to a Chromecast device">
+        Pause casting
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_CAST_RESUME" desc="Label for a button in the cast notification to resume a paused cast mirroring session to a Chromecast device">
         Resume
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_CAST_RESUME_CASTING" desc="Label for a button in the system tray to resume a paused cast mirroring session to a Chromecast device">
+        Resume casting
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_QUIET_MODE_TOOLTIP" desc="The tooltip text for the status area icon to tell do-not-disturb mode is currently on.">
         Do Not Disturb is on
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_PAUSE_CASTING.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_PAUSE_CASTING.png.sha1
new file mode 100644
index 0000000..87510852
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_PAUSE_CASTING.png.sha1
@@ -0,0 +1 @@
+c8647e60121c1b5a16205154492ba70f83d50d29
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_RESUME_CASTING.png.sha1 b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_RESUME_CASTING.png.sha1
new file mode 100644
index 0000000..fd0de7fe
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_STATUS_TRAY_CAST_RESUME_CASTING.png.sha1
@@ -0,0 +1 @@
+12ca3db87ee67fdfa25fd132901a7f491ef74f29
\ No newline at end of file
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 7a64b3d..ac21915 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -2453,7 +2453,7 @@
 // on ChromeOS.
 BASE_FEATURE(kDeviceActiveClientChurnCohortCheckIn,
              "DeviceActiveClientChurnCohortCheckIn",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables or disables PSM CheckMembership for the churn cohort device active
 // pings on ChromeOS.
@@ -2465,7 +2465,7 @@
 // pings on ChromeOS.
 BASE_FEATURE(kDeviceActiveClientChurnObservationCheckIn,
              "DeviceActiveClientChurnObservationCheckIn",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables or disables PSM CheckMembership for the churn observation
 // device active pings on ChromeOS.
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 1861d26..996d54d 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -269,6 +269,8 @@
     "quick_settings_a11y_sticky_keys.icon",
     "quick_settings_cast.icon",
     "quick_settings_cast_connected.icon",
+    "quick_settings_circle_pause.icon",
+    "quick_settings_circle_play.icon",
     "quick_settings_circle_stop.icon",
     "quick_settings_left_arrow.icon",
     "quick_settings_managed.icon",
diff --git a/ash/resources/vector_icons/quick_settings_circle_pause.icon b/ash/resources/vector_icons/quick_settings_circle_pause.icon
new file mode 100644
index 0000000..e9272e8
--- /dev/null
+++ b/ash/resources/vector_icons/quick_settings_circle_pause.icon
@@ -0,0 +1,45 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 7.5f, 13,
+H_LINE_TO, 9,
+V_LINE_TO, 7,
+H_LINE_TO, 7.5f,
+V_LINE_TO, 13,
+CLOSE,
+MOVE_TO, 11, 13,
+H_LINE_TO, 12.5f,
+V_LINE_TO, 7,
+H_LINE_TO, 11,
+V_LINE_TO, 13,
+CLOSE,
+MOVE_TO, 10, 18,
+CUBIC_TO, 8.9f, 18, 7.87f, 17.79f, 6.9f, 17.38f,
+CUBIC_TO, 5.92f, 16.96f, 5.07f, 16.39f, 4.33f, 15.67f,
+CUBIC_TO, 3.61f, 14.93f, 3.04f, 14.08f, 2.63f, 13.1f,
+CUBIC_TO, 2.21f, 12.13f, 2, 11.1f, 2, 10,
+CUBIC_TO, 2, 8.89f, 2.21f, 7.85f, 2.63f, 6.9f,
+CUBIC_TO, 3.04f, 5.92f, 3.61f, 5.08f, 4.33f, 4.35f,
+CUBIC_TO, 5.07f, 3.62f, 5.92f, 3.04f, 6.9f, 2.63f,
+CUBIC_TO, 7.87f, 2.21f, 8.9f, 2, 10, 2,
+CUBIC_TO, 11.11f, 2, 12.15f, 2.21f, 13.1f, 2.63f,
+CUBIC_TO, 14.08f, 3.04f, 14.92f, 3.62f, 15.65f, 4.35f,
+CUBIC_TO, 16.38f, 5.08f, 16.96f, 5.92f, 17.38f, 6.9f,
+CUBIC_TO, 17.79f, 7.85f, 18, 8.89f, 18, 10,
+CUBIC_TO, 18, 11.1f, 17.79f, 12.13f, 17.38f, 13.1f,
+CUBIC_TO, 16.96f, 14.08f, 16.38f, 14.93f, 15.65f, 15.67f,
+CUBIC_TO, 14.92f, 16.39f, 14.08f, 16.96f, 13.1f, 17.38f,
+CUBIC_TO, 12.15f, 17.79f, 11.11f, 18, 10, 18,
+CLOSE,
+MOVE_TO, 10, 16.5f,
+CUBIC_TO, 11.81f, 16.5f, 13.34f, 15.87f, 14.6f, 14.6f,
+CUBIC_TO, 15.87f, 13.34f, 16.5f, 11.81f, 16.5f, 10,
+CUBIC_TO, 16.5f, 8.19f, 15.87f, 6.66f, 14.6f, 5.4f,
+CUBIC_TO, 13.34f, 4.13f, 11.81f, 3.5f, 10, 3.5f,
+CUBIC_TO, 8.19f, 3.5f, 6.66f, 4.13f, 5.4f, 5.4f,
+CUBIC_TO, 4.13f, 6.66f, 3.5f, 8.19f, 3.5f, 10,
+CUBIC_TO, 3.5f, 11.81f, 4.13f, 13.34f, 5.4f, 14.6f,
+CUBIC_TO, 6.66f, 15.87f, 8.19f, 16.5f, 10, 16.5f,
+CLOSE
\ No newline at end of file
diff --git a/ash/resources/vector_icons/quick_settings_circle_play.icon b/ash/resources/vector_icons/quick_settings_circle_play.icon
new file mode 100644
index 0000000..aefbbc6
--- /dev/null
+++ b/ash/resources/vector_icons/quick_settings_circle_play.icon
@@ -0,0 +1,38 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 8, 13.5f,
+LINE_TO, 13.5f, 10,
+LINE_TO, 8, 6.5f,
+V_LINE_TO, 13.5f,
+CLOSE,
+MOVE_TO, 10, 18,
+CUBIC_TO, 8.9f, 18, 7.87f, 17.79f, 6.9f, 17.38f,
+CUBIC_TO, 5.92f, 16.96f, 5.07f, 16.39f, 4.33f, 15.67f,
+CUBIC_TO, 3.61f, 14.93f, 3.04f, 14.08f, 2.63f, 13.1f,
+CUBIC_TO, 2.21f, 12.13f, 2, 11.1f, 2, 10,
+CUBIC_TO, 2, 8.89f, 2.21f, 7.85f, 2.63f, 6.9f,
+CUBIC_TO, 3.04f, 5.92f, 3.61f, 5.08f, 4.33f, 4.35f,
+CUBIC_TO, 5.07f, 3.62f, 5.92f, 3.04f, 6.9f, 2.63f,
+CUBIC_TO, 7.87f, 2.21f, 8.9f, 2, 10, 2,
+CUBIC_TO, 11.11f, 2, 12.15f, 2.21f, 13.1f, 2.63f,
+CUBIC_TO, 14.08f, 3.04f, 14.92f, 3.62f, 15.65f, 4.35f,
+CUBIC_TO, 16.38f, 5.08f, 16.96f, 5.92f, 17.38f, 6.9f,
+CUBIC_TO, 17.79f, 7.85f, 18, 8.89f, 18, 10,
+CUBIC_TO, 18, 11.1f, 17.79f, 12.13f, 17.38f, 13.1f,
+CUBIC_TO, 16.96f, 14.08f, 16.38f, 14.93f, 15.65f, 15.67f,
+CUBIC_TO, 14.92f, 16.39f, 14.08f, 16.96f, 13.1f, 17.38f,
+CUBIC_TO, 12.15f, 17.79f, 11.11f, 18, 10, 18,
+CLOSE,
+MOVE_TO, 10, 16.5f,
+CUBIC_TO, 11.81f, 16.5f, 13.34f, 15.87f, 14.6f, 14.6f,
+CUBIC_TO, 15.87f, 13.34f, 16.5f, 11.81f, 16.5f, 10,
+CUBIC_TO, 16.5f, 8.19f, 15.87f, 6.66f, 14.6f, 5.4f,
+CUBIC_TO, 13.34f, 4.13f, 11.81f, 3.5f, 10, 3.5f,
+CUBIC_TO, 8.19f, 3.5f, 6.66f, 4.13f, 5.4f, 5.4f,
+CUBIC_TO, 4.13f, 6.66f, 3.5f, 8.19f, 3.5f, 10,
+CUBIC_TO, 3.5f, 11.81f, 4.13f, 13.34f, 5.4f, 14.6f,
+CUBIC_TO, 6.66f, 15.87f, 8.19f, 16.5f, 10, 16.5f,
+CLOSE
\ No newline at end of file
diff --git a/ash/system/cast/cast_detailed_view.cc b/ash/system/cast/cast_detailed_view.cc
index 577511e..aceda3c 100644
--- a/ash/system/cast/cast_detailed_view.cc
+++ b/ash/system/cast/cast_detailed_view.cc
@@ -35,11 +35,15 @@
 #include "ui/views/border.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/view_class_properties.h"
 
 namespace ash {
 
 namespace {
 
+// Extra spacing to add between cast stop buttons and the edge of the qs tray.
+constexpr int kStopButtonExtraMargin = 4;
+
 // Returns the correct vector icon for |icon_type|. Some types may be different
 // for branded builds.
 const gfx::VectorIcon& SinkIconTypeToIcon(SinkIconType icon_type) {
@@ -65,6 +69,21 @@
   return kSystemMenuCastGenericIcon;
 }
 
+std::unique_ptr<views::View> MakeButtonContainer() {
+  std::unique_ptr<views::View> button_container =
+      std::make_unique<views::View>();
+  views::BoxLayout* manager =
+      button_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kHorizontal));
+  manager->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);
+  manager->set_between_child_spacing(kTrayPopupLabelRightPadding);
+  button_container->SetProperty(
+      views::kMarginsKey,
+      gfx::Insets::TLBR(0, 0, 0,
+                        kStopButtonExtraMargin + kQsExtraMarginsFromRightEdge));
+  return button_container;
+}
+
 }  // namespace
 
 CastDetailedView::CastDetailedView(DetailedViewDelegate* delegate)
@@ -85,40 +104,18 @@
 
 void CastDetailedView::OnDevicesUpdated(
     const std::vector<SinkAndRoute>& sinks_routes) {
+  sinks_and_routes_.clear();
   // Add/update existing.
   for (const auto& device : sinks_routes)
     sinks_and_routes_.insert(std::make_pair(device.sink.id, device));
 
-  // Remove non-existent sinks. Removing an element invalidates all existing
-  // iterators.
-  auto iter = sinks_and_routes_.begin();
-  while (iter != sinks_and_routes_.end()) {
-    bool has_receiver = false;
-    for (auto& receiver : sinks_routes) {
-      if (iter->first == receiver.sink.id)
-        has_receiver = true;
-    }
-
-    if (has_receiver)
-      ++iter;
-    else
-      iter = sinks_and_routes_.erase(iter);
-  }
-
   // Update UI.
   UpdateReceiverListFromCachedData();
   Layout();
 }
 
 void CastDetailedView::UpdateReceiverListFromCachedData() {
-  // Remove all of the existing views.
-  view_to_sink_map_.clear();
-  scroll_content()->RemoveAllChildViews();
-  add_access_code_device_ = nullptr;
-  if (zero_state_view_) {
-    RemoveChildViewT(zero_state_view_.get());
-    zero_state_view_ = nullptr;
-  }
+  RemoveAllViews();
 
   // QsRevamp places items in a rounded container.
   const bool is_qs_revamp_enabled = features::IsQsRevampEnabled();
@@ -130,18 +127,7 @@
   // Per product requirement, access code receiver should be shown before other
   // receivers.
   if (CastConfigController::Get()->AccessCodeCastingEnabled()) {
-    add_access_code_device_ = AddScrollListItem(
-        item_container, vector_icons::kKeyboardIcon,
-        l10n_util::GetStringUTF16(
-            IDS_ASH_STATUS_TRAY_CAST_ACCESS_CODE_CAST_CONNECT));
-    if (chromeos::features::IsJellyEnabled()) {
-      // `views::ImageView` does not support changing the color, so set the
-      // image with an updated `ui::ImageModel`.
-      add_access_code_device_->icon()->SetImage(ui::ImageModel::FromVectorIcon(
-          vector_icons::kKeyboardIcon, cros_tokens::kCrosSysPrimary));
-      add_access_code_device_->text_label()->SetEnabledColorId(
-          cros_tokens::kCrosSysPrimary);
-    }
+    AddAccessCodeCastButton(item_container);
   }
 
   // Add a view for each receiver.
@@ -153,20 +139,10 @@
         base::UTF8ToUTF16(sink.name));
     view_to_sink_map_[container] = sink.id;
 
-    // Add a stop casting button if this machine ("local source") is casting to
-    // the device. See also CastNotificationController::OnDevicesUpdated().
+    // Add receiver action buttons if this machine ("local source") is casting
+    // to the device. See also CastNotificationController::OnDevicesUpdated().
     if (is_qs_revamp_enabled && !route.id.empty() && route.is_local_source) {
-      auto button = std::make_unique<PillButton>(
-          base::BindRepeating(&CastDetailedView::StopCasting,
-                              base::Unretained(this), route.id),
-          l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_STOP_CASTING),
-          PillButton::kDefaultWithIconLeading, &kQuickSettingsCircleStopIcon);
-      button->SetBackgroundColorId(cros_tokens::kCrosSysErrorContainer);
-      button->SetIconColorId(cros_tokens::kCrosSysError);
-      button->SetButtonTextColorId(cros_tokens::kCrosSysError);
-      container->AddRightView(
-          button.release(),
-          views::CreateEmptyBorder(gfx::Insets::TLBR(0, 0, 0, 4)));
+      AddReceiverActionButtons(sink, route, container, item_container);
     }
   }
 
@@ -221,6 +197,102 @@
   CloseBubble();  // Deletes `this`.
 }
 
+void CastDetailedView::FreezePressed(const std::string& route_id,
+                                     bool is_frozen) {
+  DCHECK(features::IsQsRevampEnabled());
+  if (is_frozen) {
+    CastConfigController::Get()->UnfreezeRoute(route_id);
+  } else {
+    CastConfigController::Get()->FreezeRoute(route_id);
+    CloseBubble();
+  }
+}
+
+void CastDetailedView::RemoveAllViews() {
+  view_to_sink_map_.clear();
+  sink_extra_views_map_.clear();
+  scroll_content()->RemoveAllChildViews();
+  add_access_code_device_ = nullptr;
+  if (zero_state_view_) {
+    RemoveChildViewT(zero_state_view_.get());
+    zero_state_view_ = nullptr;
+  }
+}
+
+void CastDetailedView::AddAccessCodeCastButton(
+    views::View* receiver_list_view) {
+  add_access_code_device_ =
+      AddScrollListItem(receiver_list_view, vector_icons::kKeyboardIcon,
+                        l10n_util::GetStringUTF16(
+                            IDS_ASH_STATUS_TRAY_CAST_ACCESS_CODE_CAST_CONNECT));
+  if (chromeos::features::IsJellyEnabled()) {
+    // `views::ImageView` does not support changing the color, so set the
+    // image with an updated `ui::ImageModel`.
+    add_access_code_device_->icon()->SetImage(ui::ImageModel::FromVectorIcon(
+        vector_icons::kKeyboardIcon, cros_tokens::kCrosSysPrimary));
+    add_access_code_device_->text_label()->SetEnabledColorId(
+        cros_tokens::kCrosSysPrimary);
+  }
+}
+
+void CastDetailedView::AddReceiverActionButtons(
+    const CastSink& sink,
+    const CastRoute& route,
+    HoverHighlightView* receiver_view,
+    views::View* receiver_list_view) {
+  std::unique_ptr<PillButton> stop_button = CreateStopButton(route);
+
+  // In the case that we want to show a pause/resume button, then we must
+  // put both buttons on a row below the cast sink.
+  if (route.freeze_info.can_freeze) {
+    std::unique_ptr<PillButton> freeze_button = CreateFreezeButton(route);
+    std::unique_ptr<views::View> button_container = MakeButtonContainer();
+    std::vector<views::View*> extra_views;
+    extra_views.emplace_back(
+        button_container->AddChildView(std::move(freeze_button)));
+    extra_views.emplace_back(
+        button_container->AddChildView(std::move(stop_button)));
+    sink_extra_views_map_[sink.id] = extra_views;
+
+    // Add the button container directly as a new row in the list of cast
+    // devices. Since the associated device was just added, the buttons will
+    // show up correctly below their associated device.
+    receiver_list_view->AddChildView(std::move(button_container));
+  } else {
+    receiver_view->AddRightView(stop_button.release(),
+                                views::CreateEmptyBorder(gfx::Insets::TLBR(
+                                    0, 0, 0, kStopButtonExtraMargin)));
+  }
+}
+
+std::unique_ptr<PillButton> CastDetailedView::CreateStopButton(
+    const CastRoute& route) {
+  std::unique_ptr<PillButton> stop_button = std::make_unique<PillButton>(
+      base::BindRepeating(&CastDetailedView::StopCasting,
+                          base::Unretained(this), route.id),
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_STOP_CASTING),
+      PillButton::kDefaultWithIconLeading, &kQuickSettingsCircleStopIcon);
+  stop_button->SetBackgroundColorId(cros_tokens::kCrosSysErrorContainer);
+  stop_button->SetIconColorId(cros_tokens::kCrosSysError);
+  stop_button->SetButtonTextColorId(cros_tokens::kCrosSysError);
+  return stop_button;
+}
+
+std::unique_ptr<PillButton> CastDetailedView::CreateFreezeButton(
+    const CastRoute& route) {
+  std::unique_ptr<PillButton> freeze_button = std::make_unique<PillButton>(
+      base::BindRepeating(&CastDetailedView::FreezePressed,
+                          base::Unretained(this), route.id,
+                          route.freeze_info.is_frozen),
+      route.freeze_info.is_frozen
+          ? l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_RESUME_CASTING)
+          : l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_CAST_PAUSE_CASTING),
+      PillButton::kSecondaryWithIconLeading,
+      route.freeze_info.is_frozen ? &kQuickSettingsCirclePlayIcon
+                                  : &kQuickSettingsCirclePauseIcon);
+  return freeze_button;
+}
+
 BEGIN_METADATA(CastDetailedView, TrayDetailedView)
 END_METADATA
 
diff --git a/ash/system/cast/cast_detailed_view.h b/ash/system/cast/cast_detailed_view.h
index b8e21e19..4d1c7522 100644
--- a/ash/system/cast/cast_detailed_view.h
+++ b/ash/system/cast/cast_detailed_view.h
@@ -22,6 +22,7 @@
 namespace ash {
 
 class HoverHighlightView;
+class PillButton;
 
 // This view displays a list of cast receivers that can be clicked on and casted
 // to. It is activated by clicking on the chevron inside of
@@ -61,12 +62,38 @@
   // Stops casting the route identified by `route_id`.
   void StopCasting(const std::string& route_id);
 
+  // Pauses or resumes the route given by `route_id`.
+  void FreezePressed(const std::string& route_id, bool is_frozen);
+
+  // Remove all child views from CastDetailedView.
+  void RemoveAllViews();
+
+  // Adds a button which allows for adding a device using an access code.
+  void AddAccessCodeCastButton(views::View* receiver_list_view);
+
+  // Adds buttons associated with a receiver so the user may perform route
+  // actions like stopping or pausing.
+  void AddReceiverActionButtons(const CastSink& sink,
+                                const CastRoute& route,
+                                HoverHighlightView* receiver_view,
+                                views::View* receiver_list_view);
+
+  // Creates a stop button which, when pressed, stops the associated `route`.
+  std::unique_ptr<PillButton> CreateStopButton(const CastRoute& route);
+
+  // Creates a freeze button which, when pressed, pauses / resumes the
+  // associated `route`.
+  std::unique_ptr<PillButton> CreateFreezeButton(const CastRoute& route);
+
   // A mapping from the sink id to the receiver/activity data.
   std::map<std::string, SinkAndRoute> sinks_and_routes_;
 
   // A mapping from the view pointer to the associated activity sink id.
   std::map<views::View*, std::string> view_to_sink_map_;
 
+  // A mapping of sink id to the associated extra views.
+  std::map<std::string, std::vector<views::View*>> sink_extra_views_map_;
+
   // Special list item that, if clicked, launches the access code casting dialog
   raw_ptr<HoverHighlightView, ExperimentalAsh> add_access_code_device_ =
       nullptr;
diff --git a/ash/system/cast/cast_detailed_view_unittest.cc b/ash/system/cast/cast_detailed_view_unittest.cc
index 88e821e..fb30dc4 100644
--- a/ash/system/cast/cast_detailed_view_unittest.cc
+++ b/ash/system/cast/cast_detailed_view_unittest.cc
@@ -57,6 +57,10 @@
     return views;
   }
 
+  std::vector<views::View*> GetExtraViewsForSink(const std::string& sink_id) {
+    return detailed_view_->sink_extra_views_map_[sink_id];
+  }
+
   views::View* GetZeroStateView() { return detailed_view_->zero_state_view_; }
 
   // Adds two simulated cast devices.
@@ -226,4 +230,33 @@
   EXPECT_FALSE(row->right_view());
 }
 
+TEST_F(CastDetailedViewTest, FreezeButton) {
+  // Set up a fake sink and route, as if this Chromebook is casting to the
+  // device. And, the route may be frozen.
+  std::vector<SinkAndRoute> devices;
+  SinkAndRoute device;
+  device.sink.id = "fake_sink_id_1";
+  device.sink.name = "Sink Name 1";
+  device.sink.sink_icon_type = SinkIconType::kCast;
+  device.route.id = "fake_route_id_1";
+  device.route.title = "Title 1";
+  // Simulate a local source (this Chromebook).
+  device.route.is_local_source = true;
+  device.route.freeze_info.can_freeze = true;
+  devices.push_back(device);
+  OnDevicesUpdated(devices);
+
+  std::vector<views::View*> views = GetExtraViewsForSink("fake_sink_id_1");
+  ASSERT_EQ(views.size(), 2u);
+  auto* freeze_button = views[0];
+  EXPECT_TRUE(views::IsViewClass<PillButton>(freeze_button));
+  EXPECT_EQ(freeze_button->GetTooltipText(gfx::Point()), u"Pause casting");
+
+  // Clicking on the button pauses casting.
+  LeftClickOn(freeze_button);
+  EXPECT_EQ(cast_config_.freeze_route_count(), 1u);
+  EXPECT_EQ(cast_config_.freeze_route_route_id(), "fake_route_id_1");
+  EXPECT_EQ(delegate_->close_bubble_call_count(), 1u);
+}
+
 }  // namespace ash
diff --git a/ash/system/human_presence/OWNERS b/ash/system/human_presence/OWNERS
index 40600b67..35989f4 100644
--- a/ash/system/human_presence/OWNERS
+++ b/ash/system/human_presence/OWNERS
@@ -1,5 +1,4 @@
 # Primary OWNERS
 charleszhao@chromium.org
-martis@chromium.org
 
-# Backup OWNERS
+# Backup OWNERS
\ No newline at end of file
diff --git a/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_impl.cc b/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_impl.cc
index 2597787..8b2f3e6 100644
--- a/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_impl.cc
+++ b/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_impl.cc
@@ -213,13 +213,13 @@
     ForceKeyboardSettingPersistence& force_persistence) {
   mojom::KeyboardSettingsPtr settings = mojom::KeyboardSettings::New();
 
-  const auto* top_row_are_fkeys_preference =
-      prefs->GetUserPrefValue(prefs::kSendFunctionKeys);
-  settings->top_row_are_fkeys =
-      top_row_are_fkeys_preference
-          ? top_row_are_fkeys_preference->GetBool()
-          : GetDefaultTopRowAreFKeysValue(keyboard_policies, keyboard);
-  force_persistence.top_row_are_fkeys = top_row_are_fkeys_preference != nullptr;
+  // For the transition period, since the default behavior changed for external
+  // keyboards, the value from prefs must always be used even if the user did
+  // not explicitly configure it. Users expect their settings to remain
+  // consistent even if we think they may like the new default better.
+  settings->top_row_are_fkeys = prefs->GetBoolean(prefs::kSendFunctionKeys);
+  force_persistence.top_row_are_fkeys =
+      prefs->GetUserPrefValue(prefs::kSendFunctionKeys) != nullptr;
 
   settings->suppress_meta_fkey_rewrites = kDefaultSuppressMetaFKeyRewrites;
   // Do not persist as default should not be persisted.
diff --git a/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_unittest.cc b/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_unittest.cc
index ba2d8cf..f8c7afa 100644
--- a/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_unittest.cc
+++ b/ash/system/input_device_settings/pref_handlers/keyboard_pref_handler_unittest.cc
@@ -586,6 +586,30 @@
             kDefaultSuppressMetaFKeyRewrites);
 }
 
+TEST_F(KeyboardPrefHandlerTest,
+       KeyboardSendFunctionKeysTransitionPrefAlwaysConsistent) {
+  mojom::Keyboard keyboard;
+  keyboard.is_external = true;
+  keyboard.meta_key = mojom::MetaKey::kExternalMeta;
+  keyboard.device_key = kKeyboardKey1;
+  {
+    base::test::ScopedFeatureList disable_settings_split_feature_list;
+    disable_settings_split_feature_list.InitAndDisableFeature(
+        features::kInputDeviceSettingsSplit);
+    Shell::Get()->input_device_tracker()->OnKeyboardConnected(keyboard);
+  }
+
+  // Initialize keyboard settings for the device and check that the global
+  // prefs were used as defaults even though the `kSendFunctionKeys` pref was
+  // never changed and it goes against the default since the keyboard is
+  // external.
+  mojom::KeyboardSettingsPtr settings =
+      CallInitializeKeyboardSettings(keyboard);
+  ASSERT_EQ(settings->top_row_are_fkeys, kGlobalSendFunctionKeys);
+  ASSERT_EQ(settings->suppress_meta_fkey_rewrites,
+            kDefaultSuppressMetaFKeyRewrites);
+}
+
 TEST_F(KeyboardPrefHandlerTest, ModifierRemappingsFromGlobalPrefs) {
   // Disable flag in this test since `InputDeviceTracker` only records
   // connected devices when the settings split flag is disabled.
diff --git a/ash/webui/personalization_app/README.md b/ash/webui/personalization_app/README.md
index 89ba906..b431c32d 100644
--- a/ash/webui/personalization_app/README.md
+++ b/ash/webui/personalization_app/README.md
@@ -97,10 +97,18 @@
 * mojom handling logic
   * mojom handler unit tests
 
+### Debugging tests
+* The [browser test doc](https://www.chromium.org/developers/testing/browser-tests/#debugging)
+has some useful information.
+* Inject `debugger;` as a breakpoint.
+* Run a specific test/test suite: `test("test name", () => ...) => test.only("test name"...)`.
+* Debug flaky tests: Pass flags `--gtest_repeat=1000 --gtest_break_on_failure`.
+
 ## Environment Setup
 ### VSCode
 
 - Follow [vscode setup](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/vscode.md).
+  - (Optional) Set up [code-server](go/vscode/remote_development_via_web) for remote development.
 - Create `tsconfig.json` using [helper script](https://chromium.googlesource.com/chromium/src/+/HEAD/ash/webui/personalization_app/tools/gen_tsconfig.py).
   Please follow the help doc in the header of the helper script.
 - Edit `${PATH_TO_CHROMIUM}/src/.git/info/exclude` and add these lines
diff --git a/ash/webui/print_management/resources/print_management.html b/ash/webui/print_management/resources/print_management.html
index 51dcacd..5ab84ea 100644
--- a/ash/webui/print_management/resources/print_management.html
+++ b/ash/webui/print_management/resources/print_management.html
@@ -167,11 +167,14 @@
       </iron-list>
     </template>
 
-    <div id="ongoingEmptyState" hidden="[[ongoingPrintJobs.length]]">
+    <div id="ongoingEmptyState" hidden="[[!shouldShowOngoingEmptyState(
+          ongoingPrintJobs.length, printJobs.length)]]">
       [[i18n('noPrintJobInProgress')]]
     </div>
 
-    <printer-setup-info hidden$="[[!showSetupAssistance]]"></printer-setup-info>
+    <printer-setup-info hidden="[[!shouldShowSetupAssistance(
+        ongoingPrintJobs.length, printJobs.length)]]">
+    </printer-setup-info>
 
     <template is="dom-if" if="[[printJobs.length]]" restamp>
       <div id="historyHeaderContainer" class="column-headers flex-center">
diff --git a/ash/webui/print_management/resources/print_management.ts b/ash/webui/print_management/resources/print_management.ts
index 1827f5c3..557137e 100644
--- a/ash/webui/print_management/resources/print_management.ts
+++ b/ash/webui/print_management/resources/print_management.ts
@@ -348,6 +348,18 @@
     this.$.deleteIcon.classList.toggle(
         'delete-disabled', this.shouldDisableClearAllButton);
   }
+
+  /** Determine if printer setup UI should be shown. */
+  private shouldShowSetupAssistance(): boolean {
+    return this.showSetupAssistance && this.ongoingPrintJobs.length === 0 &&
+        this.printJobs.length === 0;
+  }
+
+  /** Determine if ongoing jobs empty messaging should be shown. */
+  private shouldShowOngoingEmptyState(): boolean {
+    return !this.shouldShowSetupAssistance() &&
+        this.ongoingPrintJobs.length === 0;
+  }
 }
 
 customElements.define(PrintManagementElement.is, PrintManagementElement);
diff --git a/base/metrics/histogram_samples.h b/base/metrics/histogram_samples.h
index 7f34093..3fe25047 100644
--- a/base/metrics/histogram_samples.h
+++ b/base/metrics/histogram_samples.h
@@ -257,7 +257,7 @@
   //   external object. The callers guarantees the value will outlive this
   //   instance.
   std::unique_ptr<Metadata> meta_owned_;
-  raw_ptr<Metadata> meta_;
+  raw_ptr<Metadata, LeakedDanglingUntriaged> meta_;
 };
 
 class BASE_EXPORT SampleCountIterator {
diff --git a/base/metrics/persistent_memory_allocator.h b/base/metrics/persistent_memory_allocator.h
index 806df91c..bd736dd 100644
--- a/base/metrics/persistent_memory_allocator.h
+++ b/base/metrics/persistent_memory_allocator.h
@@ -900,7 +900,7 @@
   // The underlying object that does the actual allocation of memory. Its
   // lifetime must exceed that of all DelayedPersistentAllocation objects
   // that use it.
-  const raw_ptr<PersistentMemoryAllocator> allocator_;
+  const raw_ptr<PersistentMemoryAllocator, LeakedDanglingUntriaged> allocator_;
 
   // The desired type and size of the allocated segment plus the offset
   // within it for the defined request.
@@ -912,7 +912,8 @@
   // stored once the allocation is complete. If multiple delayed allocations
   // share the same pointer then an allocation on one will amount to an
   // allocation for all.
-  const raw_ptr<volatile std::atomic<Reference>> reference_;
+  const raw_ptr<volatile std::atomic<Reference>, LeakedDanglingUntriaged>
+      reference_;
 
   // No DISALLOW_COPY_AND_ASSIGN as it's okay to copy/move these objects.
 };
diff --git a/base/metrics/persistent_sample_map.h b/base/metrics/persistent_sample_map.h
index 21afc90..cd336a20 100644
--- a/base/metrics/persistent_sample_map.h
+++ b/base/metrics/persistent_sample_map.h
@@ -98,13 +98,14 @@
 
   // The allocator that manages histograms inside persistent memory. This is
   // owned externally and is expected to live beyond the life of this object.
-  raw_ptr<PersistentHistogramAllocator> allocator_;
+  raw_ptr<PersistentHistogramAllocator, LeakedDanglingUntriaged> allocator_;
 
   // The object that manages sample records inside persistent memory. This is
   // owned by the |allocator_| object (above) and so, like it, is expected to
   // live beyond the life of this object. This value is lazily-initialized on
   // first use via the GetRecords() accessor method.
-  raw_ptr<PersistentSampleMapRecords> records_ = nullptr;
+  raw_ptr<PersistentSampleMapRecords, LeakedDanglingUntriaged> records_ =
+      nullptr;
 };
 
 }  // namespace base
diff --git a/base/metrics/sample_vector.h b/base/metrics/sample_vector.h
index 0160f59..0f22647f 100644
--- a/base/metrics/sample_vector.h
+++ b/base/metrics/sample_vector.h
@@ -113,7 +113,7 @@
   mutable std::atomic<HistogramBase::AtomicCount*> counts_{nullptr};
 
   // Shares the same BucketRanges with Histogram object.
-  const raw_ptr<const BucketRanges> bucket_ranges_;
+  const raw_ptr<const BucketRanges, LeakedDanglingUntriaged> bucket_ranges_;
 };
 
 // A sample vector that uses local memory for the counts array.
diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn
index bff6112..7341fb7ac 100644
--- a/build/android/BUILD.gn
+++ b/build/android/BUILD.gn
@@ -71,7 +71,6 @@
     }
     robolectric_binary("build_junit_tests") {
       # Test has no JNI, so skip JNI Generator step.
-      generate_final_jni = false
       resources_package = "org.chromium.build"
       sources = [
         "junit/src/org/chromium/build/AndroidAssetsTest.java",
diff --git a/build/config/android/abi.gni b/build/config/android/abi.gni
index 1101b4a..1aafbd0f 100644
--- a/build/config/android/abi.gni
+++ b/build/config/android/abi.gni
@@ -88,7 +88,7 @@
   assert(false, "Unknown target CPU: $target_cpu")
 }
 
-# Do not define android_app_secondary_abi_cpu or android_app_secondary_abi for
+# Do not define android_secondary_abi_cpu or android_app_secondary_abi for
 # target_cpu's that are 32-bit-only or 64-bit-only, as they are not used. The
 # presence of this variable may be used in conjunction with android_64bit_target_cpu
 # to identify target_cpu's that are 32-bit-only or 64-bit-only.
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index 54e95d2..5714ed5 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -283,12 +283,6 @@
     # Reduce build time by using d8 incremental build.
     enable_incremental_d8 = true
 
-    # Use hashed symbol names to reduce JNI symbol overhead.
-    use_hashed_jni_names = !is_java_debug
-
-    # Enables JNI multiplexing to reduce JNI native methods overhead.
-    allow_jni_multiplexing = false
-
     # Enables trace event injection on Android views with bytecode rewriting.
     # This adds an additional step on android_app_bundle_module targets that
     # adds trace events to some methods in android.view.View subclasses.
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index d3427ebd..0a854267 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -4263,20 +4263,6 @@
       }
     }
   }
-
-  template("jni_sources_list") {
-    generated_file(target_name) {
-      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "deps" ])
-      outputs = [ invoker.output ]
-      data_keys = [ "jni_source_files" ]
-      rebase = root_build_dir
-      metadata = {
-        # This target is just collecting source files used - this is not a
-        # legitimate dependency.
-        shared_libraries_barrier = []
-      }
-    }
-  }
 }
 
 # Create a zip archive corresponding to an application bundle module.
diff --git a/build/config/android/jni.gni b/build/config/android/jni.gni
index 3d496ef..bcbf33e 100644
--- a/build/config/android/jni.gni
+++ b/build/config/android/jni.gni
@@ -2,9 +2,201 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/android/rules.gni")
+import("//build/config/android/config.gni")
+import("//build/config/python.gni")
 import("//build/partitioned_shared_library.gni")
 
+declare_args() {
+  # Enables JNI multiplexing to reduce JNI native methods overhead.
+  allow_jni_multiplexing = false
+
+  # Use hashed symbol names to reduce JNI symbol overhead.
+  use_hashed_jni_names = !is_java_debug
+}
+
+# Use a dedicated include dir so that files can #include headers from other
+# toolchains without affecting non-JNI #includes.
+if (target_os == "android") {
+  jni_headers_dir = "$root_build_dir/gen/jni_headers"
+} else {
+  # Chrome OS builds cannot share gen/ directories because is_android=false
+  # within default_toolchain.
+  jni_headers_dir = "$root_gen_dir/jni_headers"
+}
+
+template("jni_sources_list") {
+  generated_file(target_name) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY + [ "deps" ])
+    outputs = [ invoker.output ]
+    data_keys = [ "jni_source_files" ]
+    rebase = root_build_dir
+    metadata = {
+      # This target is just collecting source files used - this is not a
+      # legitimate dependency.
+      shared_libraries_barrier = []
+    }
+  }
+}
+
+# Declare a jni registration target.
+#
+# This target generates a srcjar containing a copy of GEN_JNI.java, which has
+# the native methods of all dependent java files. It can also create a .h file
+# for use with manual JNI registration.
+#
+# The script does not scan any generated sources (those within .srcjars, or
+# within root_build_dir). This could be fixed by adding deps & logic to scan
+# .srcjars, but isn't currently needed.
+#
+# See base/android/jni_generator/jni_registration_generator.py for more info
+# about the format of the header file.
+#
+# Variables
+#   targets: List of .build_config.json supported targets to provide java sources.
+#   manual_jni_registration: Manually do JNI registration - required for feature
+#     splits which provide their own native library. (optional)
+#   file_exclusions: List of .java files that should be skipped. (optional)
+#   namespace: Registration functions will be wrapped into this. (optional)
+#   require_native_mocks: Enforce that any native calls using
+#     org.chromium.base.annotations.NativeMethods must have a mock set
+#     (optional).
+#   enable_native_mocks: Allow native calls using
+#     org.chromium.base.annotations.NativeMethods to be mocked in tests
+#     (optional).
+#
+# Example
+#   generate_jni_registration("chrome_jni_registration") {
+#     targets = [ ":chrome_public_apk" ]
+#     manual_jni_registration = false
+#     file_exclusions = [
+#       "//path/to/Exception.java",
+#     ]
+#   }
+template("generate_jni_registration") {
+  if (defined(invoker.native_deps)) {
+    _jni_sources_list = "$target_gen_dir/$target_name.jnisources"
+    _jni_sources_target = "${target_name}__jni_sources"
+    jni_sources_list(_jni_sources_target) {
+      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+      deps = invoker.native_deps
+      output = _jni_sources_list
+    }
+  }
+
+  action_with_pydeps(target_name) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+
+    script = "//base/android/jni_generator/jni_registration_generator.py"
+    inputs = []
+    deps = []
+    _srcjar_output = "$target_gen_dir/$target_name.srcjar"
+    outputs = [ _srcjar_output ]
+    depfile = "$target_gen_dir/$target_name.d"
+
+    args = [
+      "--srcjar-path",
+      rebase_path(_srcjar_output, root_build_dir),
+      "--depfile",
+      rebase_path(depfile, root_build_dir),
+    ]
+
+    if (defined(_jni_sources_target)) {
+      inputs += [ _jni_sources_list ]
+      deps += [ ":$_jni_sources_target" ]
+      args += [
+        "--native-sources-file",
+        rebase_path(_jni_sources_list, root_build_dir),
+      ]
+    }
+    foreach(_target, invoker.java_targets) {
+      # Due to robolectric, we can't import internal_rules.gni unconditionally,
+      # which means that build_config_target_suffix isn't always available, so
+      # we hardcode the value of it here.
+      deps += [ "${_target}__build_config_crbug_908819($default_toolchain)" ]
+      _build_config =
+          get_label_info("${_target}($default_toolchain)", "target_gen_dir") +
+          "/" + get_label_info("${_target}($default_toolchain)", "name") +
+          ".build_config.json"
+      _rebased_build_config = rebase_path(_build_config, root_build_dir)
+      inputs += [ _build_config ]
+
+      args += [
+        # This is a list of .sources files.
+        "--java-sources-files=@FileArg($_rebased_build_config:deps_info:jni_all_source)",
+      ]
+    }
+
+    if (defined(invoker.include_testonly)) {
+      _include_testonly = invoker.include_testonly
+    } else {
+      _include_testonly = defined(testonly) && testonly
+    }
+    if (_include_testonly) {
+      args += [ "--include-test-only" ]
+    }
+
+    if (use_hashed_jni_names) {
+      args += [ "--use-proxy-hash" ]
+    }
+
+    if (defined(invoker.enable_native_mocks) && invoker.enable_native_mocks) {
+      args += [ "--enable-proxy-mocks" ]
+
+      if (defined(invoker.require_native_mocks) &&
+          invoker.require_native_mocks) {
+        args += [ "--require-mocks" ]
+      }
+    }
+
+    if (defined(invoker.remove_uncalled_jni) && invoker.remove_uncalled_jni) {
+      args += [ "--remove-uncalled-methods" ]
+    }
+    if (defined(invoker.add_stubs_for_missing_jni) &&
+        invoker.add_stubs_for_missing_jni) {
+      args += [ "--add-stubs-for-missing-native" ]
+    }
+
+    _manual_jni_registration = defined(invoker.manual_jni_registration) &&
+                               invoker.manual_jni_registration
+    _enable_jni_multiplexing = defined(invoker.enable_jni_multiplexing) &&
+                               invoker.enable_jni_multiplexing
+    if (_manual_jni_registration) {
+      args += [ "--manual-jni-registration" ]
+    }
+    if (_enable_jni_multiplexing) {
+      args += [ "--enable-jni-multiplexing" ]
+    }
+
+    if (_manual_jni_registration || _enable_jni_multiplexing) {
+      _subdir = rebase_path(target_gen_dir, root_gen_dir)
+      _jni_header_output =
+          "$jni_headers_dir/$_subdir/${target_name}_generated.h"
+      outputs += [ _jni_header_output ]
+      args += [
+        "--header-path",
+        rebase_path(_jni_header_output, root_build_dir),
+      ]
+
+      # This gives targets depending on this registration access to our generated header.
+      public_configs = [ "//build/config/android:jni_include_dir" ]
+    }
+
+    if (defined(invoker.file_exclusions)) {
+      _rebase_file_exclusions =
+          rebase_path(invoker.file_exclusions, root_build_dir)
+      args += [ "--file-exclusions=$_rebase_file_exclusions" ]
+    }
+
+    if (defined(invoker.namespace)) {
+      args += [ "--namespace=${invoker.namespace}" ]
+    }
+
+    if (defined(invoker.module_name)) {
+      args += [ "--module-name=${invoker.module_name}" ]
+    }
+  }
+}
+
 # This is a wrapper around an underlying native target which inserts JNI
 # registration.
 #
diff --git a/build/config/android/linker_version_script.gni b/build/config/android/linker_version_script.gni
index 864233c8..77d9983 100644
--- a/build/config/android/linker_version_script.gni
+++ b/build/config/android/linker_version_script.gni
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//build/config/android/config.gni")
+import("//build/config/android/jni.gni")
 import("//build/config/python.gni")
 
 # Generate a custom linker version script that can later be used with
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 0c6f5a3..05bc8ad 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -6,6 +6,7 @@
 # Some projects (e.g. V8) do not have non-build directories DEPS'ed in.
 import("//build/config/android/config.gni")
 import("//build/config/android/copy_ex.gni")
+import("//build/config/android/jni.gni")
 import("//build/config/clang/clang.gni")
 import("//build/config/compiler/compiler.gni")
 import("//build/config/coverage/coverage.gni")
@@ -16,16 +17,6 @@
 import("//build/toolchain/toolchain.gni")
 assert(is_android || is_robolectric)
 
-# Use a dedicated include dir so that files can #include headers from other
-# toolchains without affecting non-JNI #includes.
-if (target_os == "android") {
-  jni_headers_dir = "$root_build_dir/gen/jni_headers"
-} else {
-  # Chrome OS builds cannot share gen/ directories because is_android=false
-  # within default_toolchain.
-  jni_headers_dir = "$root_gen_dir/jni_headers"
-}
-
 if (target_cpu == "arm") {
   _sanitizer_arch = "arm"
 } else if (target_cpu == "arm64") {
@@ -217,11 +208,11 @@
         inputs += invoker.sources
         _input_args = rebase_path(invoker.sources, root_build_dir)
         _input_names = invoker.sources
-        if (!is_robolectric && use_hashed_jni_names) {
+        if (use_hashed_jni_names) {
           args += [ "--use_proxy_hash" ]
         }
 
-        if (!is_robolectric && defined(invoker.enable_jni_multiplexing) &&
+        if (defined(invoker.enable_jni_multiplexing) &&
             invoker.enable_jni_multiplexing) {
           args += [ "--enable_jni_multiplexing" ]
         }
@@ -351,162 +342,6 @@
 
 # non-robolectric things
 if (enable_java_templates && is_android) {
-  # Declare a jni registration target.
-  #
-  # This target generates a srcjar containing a copy of GEN_JNI.java, which has
-  # the native methods of all dependent java files. It can also create a .h file
-  # for use with manual JNI registration.
-  #
-  # The script does not scan any generated sources (those within .srcjars, or
-  # within root_build_dir). This could be fixed by adding deps & logic to scan
-  # .srcjars, but isn't currently needed.
-  #
-  # See base/android/jni_generator/jni_registration_generator.py for more info
-  # about the format of the header file.
-  #
-  # Variables
-  #   targets: List of .build_config.json supported targets to provide java sources.
-  #   manual_jni_registration: Manually do JNI registration - required for feature
-  #     splits which provide their own native library. (optional)
-  #   file_exclusions: List of .java files that should be skipped. (optional)
-  #   namespace: Registration functions will be wrapped into this. (optional)
-  #   require_native_mocks: Enforce that any native calls using
-  #     org.chromium.base.annotations.NativeMethods must have a mock set
-  #     (optional).
-  #   enable_native_mocks: Allow native calls using
-  #     org.chromium.base.annotations.NativeMethods to be mocked in tests
-  #     (optional).
-  #
-  # Example
-  #   generate_jni_registration("chrome_jni_registration") {
-  #     targets = [ ":chrome_public_apk" ]
-  #     manual_jni_registration = false
-  #     file_exclusions = [
-  #       "//path/to/Exception.java",
-  #     ]
-  #   }
-  template("generate_jni_registration") {
-    if (defined(invoker.native_deps)) {
-      _jni_sources_list = "$target_gen_dir/$target_name.jnisources"
-      _jni_sources_target = "${target_name}__jni_sources"
-      jni_sources_list(_jni_sources_target) {
-        forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
-        deps = invoker.native_deps
-        output = _jni_sources_list
-      }
-    }
-
-    action_with_pydeps(target_name) {
-      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
-
-      script = "//base/android/jni_generator/jni_registration_generator.py"
-      inputs = []
-      deps = []
-      _srcjar_output = "$target_gen_dir/$target_name.srcjar"
-      outputs = [ _srcjar_output ]
-      depfile = "$target_gen_dir/$target_name.d"
-
-      args = [
-        "--srcjar-path",
-        rebase_path(_srcjar_output, root_build_dir),
-        "--depfile",
-        rebase_path(depfile, root_build_dir),
-      ]
-
-      if (defined(_jni_sources_target)) {
-        inputs += [ _jni_sources_list ]
-        deps += [ ":$_jni_sources_target" ]
-        args += [
-          "--native-sources-file",
-          rebase_path(_jni_sources_list, root_build_dir),
-        ]
-      }
-      foreach(_target, invoker.java_targets) {
-        deps += [ "${_target}$build_config_target_suffix($default_toolchain)" ]
-        _build_config =
-            get_label_info("${_target}($default_toolchain)", "target_gen_dir") +
-            "/" + get_label_info("${_target}($default_toolchain)", "name") +
-            ".build_config.json"
-        _rebased_build_config = rebase_path(_build_config, root_build_dir)
-        inputs += [ _build_config ]
-
-        args += [
-          # This is a list of .sources files.
-          "--java-sources-files=@FileArg($_rebased_build_config:deps_info:jni_all_source)",
-        ]
-      }
-
-      if (defined(invoker.include_testonly)) {
-        _include_testonly = invoker.include_testonly
-      } else {
-        _include_testonly = defined(testonly) && testonly
-      }
-      if (_include_testonly) {
-        args += [ "--include-test-only" ]
-      }
-
-      if (use_hashed_jni_names) {
-        args += [ "--use-proxy-hash" ]
-      }
-
-      if (defined(invoker.enable_native_mocks) && invoker.enable_native_mocks) {
-        args += [ "--enable-proxy-mocks" ]
-
-        if (defined(invoker.require_native_mocks) &&
-            invoker.require_native_mocks) {
-          args += [ "--require-mocks" ]
-        }
-      }
-
-      if (defined(invoker.remove_uncalled_jni) && invoker.remove_uncalled_jni) {
-        args += [ "--remove-uncalled-methods" ]
-      }
-      if (defined(invoker.add_stubs_for_missing_jni) &&
-          invoker.add_stubs_for_missing_jni) {
-        args += [ "--add-stubs-for-missing-native" ]
-      }
-
-      _manual_jni_registration = defined(invoker.manual_jni_registration) &&
-                                 invoker.manual_jni_registration
-      _enable_jni_multiplexing = defined(invoker.enable_jni_multiplexing) &&
-                                 invoker.enable_jni_multiplexing
-      if (_manual_jni_registration) {
-        args += [ "--manual-jni-registration" ]
-      }
-      if (_enable_jni_multiplexing) {
-        args += [ "--enable-jni-multiplexing" ]
-      }
-
-      if (_manual_jni_registration || _enable_jni_multiplexing) {
-        _subdir = rebase_path(target_gen_dir, root_gen_dir)
-        _jni_header_output =
-            "$jni_headers_dir/$_subdir/${target_name}_generated.h"
-        outputs += [ _jni_header_output ]
-        args += [
-          "--header-path",
-          rebase_path(_jni_header_output, root_build_dir),
-        ]
-
-        # This gives targets depending on this registration access to our generated header.
-        public_configs = [ "//build/config/android:jni_include_dir" ]
-      }
-
-      if (defined(invoker.file_exclusions)) {
-        _rebase_file_exclusions =
-            rebase_path(invoker.file_exclusions, root_build_dir)
-        args += [ "--file-exclusions=$_rebase_file_exclusions" ]
-      }
-
-      if (defined(invoker.namespace)) {
-        args += [ "--namespace=${invoker.namespace}" ]
-      }
-
-      if (defined(invoker.module_name)) {
-        args += [ "--module-name=${invoker.module_name}" ]
-      }
-    }
-  }
-
   # Declare a target for c-preprocessor-generated java files
   #
   # NOTE: For generating Java conterparts to enums prefer using the java_cpp_enum
@@ -1577,21 +1412,17 @@
     _generate_final_jni =
         !defined(invoker.generate_final_jni) || invoker.generate_final_jni
     if (_generate_final_jni) {
-      _jni_srcjar_target_name = "${target_name}__final_jni"
-      _outer_target_name = target_name
-
-      generate_jni_registration(_jni_srcjar_target_name) {
-        enable_native_mocks = true
-        require_native_mocks = !defined(invoker.shared_libraries)
-        java_targets = [ ":$_outer_target_name" ]
-      }
-
+      _jni_srcjar_deps = []
       if (defined(invoker.shared_libraries)) {
         foreach(_dep, invoker.shared_libraries) {
+          _dep_no_toolchain = get_label_info(_dep, "label_no_toolchain")
+          _dep_toolchain = get_label_info(_dep, "toolchain")
           assert(
-              string_replace(_dep, robolectric_toolchain, "") != _dep,
+              _dep_toolchain == robolectric_toolchain,
               "$target_name has shared_libraries with incorrect toolchain. " +
                   "Should contain (\$robolectric_toolchain) suffix: $_dep")
+          _jni_srcjar_deps +=
+              [ "${_dep_no_toolchain}__jni_registration($_dep_toolchain)" ]
         }
 
         # Write shared library output files of all dependencies to a file. Those
@@ -1604,6 +1435,16 @@
           walk_keys = [ "shared_libraries_barrier" ]
           rebase = root_build_dir
         }
+      } else {
+        _outer_target_name = target_name
+        _jni_srcjar_target_name = "${target_name}__final_jni"
+        generate_jni_registration(_jni_srcjar_target_name) {
+          enable_native_mocks = true
+          require_native_mocks = true
+          java_targets = [ ":$_outer_target_name" ]
+          add_stubs_for_missing_jni = true
+        }
+        _jni_srcjar_deps = [ ":$_jni_srcjar_target_name" ]
       }
       _native_libraries_target_name = "${target_name}__native_libraries"
       write_native_libraries_java(_native_libraries_target_name) {
@@ -1648,10 +1489,7 @@
         "//build/android:build_config_for_testing_gen",
       ]
       if (_generate_final_jni) {
-        srcjar_deps += [
-          ":$_jni_srcjar_target_name",
-          ":$_native_libraries_target_name",
-        ]
+        srcjar_deps += [ ":$_native_libraries_target_name" ] + _jni_srcjar_deps
       }
     }
 
diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn
index ed39cc6..ae371093f 100644
--- a/build/config/clang/BUILD.gn
+++ b/build/config/clang/BUILD.gn
@@ -56,6 +56,15 @@
         "raw-ptr-exclude-path=base/containers/span.h",
       ]
     }
+
+    if (enable_check_raw_ref_fields) {
+      cflags += [
+        "-Xclang",
+        "-plugin-arg-find-bad-constructs",
+        "-Xclang",
+        "check-raw-ref-fields",
+      ]
+    }
   }
 }
 
diff --git a/build/config/clang/clang.gni b/build/config/clang/clang.gni
index 0de9792..0ff60e9 100644
--- a/build/config/clang/clang.gni
+++ b/build/config/clang/clang.gni
@@ -18,6 +18,11 @@
       build_with_chromium && !is_official_build &&
       ((is_linux && !is_castos) || (is_android && !is_cast_android))
 
+  # TODO(crbug.com/1446146): Merge with enable_check_raw_ptr_fields once both
+  # checks are activated on the same set of platforms.
+  enable_check_raw_ref_fields =
+      build_with_chromium && !is_official_build && is_linux && !is_castos
+
   clang_base_path = default_clang_base_path
 
   # Specifies whether or not bitcode should be embedded during compilation.
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index eef0c6f..a6e3523 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-12.20230520.1.1
+12.20230605.1.1
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 9eadf0a..6280b9a9 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -754,6 +754,7 @@
       "//chrome/browser/commerce/merchant_viewer/android:java",
       "//chrome/browser/content_creation/notes/internal/android:java",
       "//chrome/browser/download/internal/android:java",
+      "//chrome/browser/mandatory_reauth/android/internal:java",
       "//chrome/browser/password_check:internal_java",
       "//chrome/browser/password_edit_dialog/android:java",
       "//chrome/browser/password_entry_edit/android/internal:java",
@@ -2710,6 +2711,7 @@
       "//chrome/browser/password_manager/android/pwd_migration:javatests",
       "//chrome/browser/subresource_filter:subresource_filter_javatests",
       "//chrome/browser/touch_to_fill/android:test_java",
+      "//chrome/browser/touch_to_fill/password_generation/android/internal:javatests",
       "//chrome/browser/touch_to_fill/payments/android/internal:javatests",
       "//chrome/browser/ui/android/fast_checkout/internal:javatests",
       "//chrome/browser/ui/android/omnibox:javatests",
@@ -3271,6 +3273,7 @@
     "java/src/org/chromium/chrome/browser/autofill/SaveUpdateAddressProfilePromptController.java",
     "java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfileBridge.java",
+    "java/src/org/chromium/chrome/browser/autofill/settings/SettingsLauncherHelper.java",
     "java/src/org/chromium/chrome/browser/autofill/settings/VirtualCardEnrollmentFields.java",
     "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java",
     "java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTask.java",
@@ -3485,6 +3488,7 @@
     "//chrome/browser/image_descriptions:jni_headers",
     "//chrome/browser/incognito:jni_headers",
     "//chrome/browser/locale:jni_headers",
+    "//chrome/browser/mandatory_reauth/android:jni_headers",
     "//chrome/browser/preferences:jni_headers",
     "//chrome/browser/privacy:jni_headers",
     "//chrome/browser/profiles/android:jni_headers",
@@ -3515,9 +3519,6 @@
   chrome_common_shared_library(target_name) {
     sources = [ "../browser/android/chrome_entry_point.cc" ]
     deps = []
-    if (allow_jni_multiplexing) {
-      enable_jni_multiplexing = true
-    }
     if (defined(invoker.deps)) {
       deps += invoker.deps
     }
@@ -3617,9 +3618,6 @@
       deps += [ "//weblayer:weblayer_lib" ]
     }
 
-    if (allow_jni_multiplexing) {
-      enable_jni_multiplexing = true
-    }
     if (android_64bit_target_cpu) {
       java_targets = [ "//chrome/android:monochrome_64_public_bundle" ]
     } else {
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index d10f2f7..918f57f 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -143,6 +143,7 @@
   "java/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardEnrollmentDialog.java",
   "java/src/org/chromium/chrome/browser/autofill/settings/AutofillVirtualCardUnenrollmentDialog.java",
   "java/src/org/chromium/chrome/browser/autofill/settings/CreditCardNumberFormattingTextWatcher.java",
+  "java/src/org/chromium/chrome/browser/autofill/settings/SettingsLauncherHelper.java",
   "java/src/org/chromium/chrome/browser/autofill/settings/VirtualCardEnrollmentFields.java",
   "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchBridge.java",
   "java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchProvider.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index 3f2270f1..06ea16b 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -302,10 +302,6 @@
       custom_assertion_handler = crash_reporting_assertion_handler
     }
 
-    if (allow_jni_multiplexing) {
-      enable_jni_multiplexing = true
-    }
-
     # Include resource strings files only for supported locales.
     aapt_locale_allowlist = platform_pak_locales
 
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index 4df8f95d..9a93c0af 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -3704,17 +3704,6 @@
 
     <issue
         id="WrongConstant"
-        message="Must be one of: DiscoverabilityAction.PERMISSION_ICON_SHOWN, DiscoverabilityAction.PAGE_INFO_OPENED, DiscoverabilityAction.PERMISSIONS_OPENED, DiscoverabilityAction.PERMISSION_CHANGED, DiscoverabilityAction.STORE_ICON_SHOWN, DiscoverabilityAction.PAGE_INFO_OPENED_FROM_STORE_ICON, DiscoverabilityAction.STORE_INFO_OPENED"
-        errorLine1="                DiscoverabilityAction.NUM_ENTRIES);"
-        errorLine2="                                      ~~~~~~~~~~~">
-        <location
-            file="../../components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDiscoverabilityMetrics.java"
-            line="48"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="WrongConstant"
         message="Must be one of: PasswordCheckUserAction.START_CHECK_AUTOMATICALLY, PasswordCheckUserAction.START_CHECK_MANUALLY, PasswordCheckUserAction.CANCEL_CHECK, PasswordCheckUserAction.CHANGE_PASSWORD, PasswordCheckUserAction.CHANGE_PASSWORD_MANUALLY, PasswordCheckUserAction.VIEW_PASSWORD_CLICK, PasswordCheckUserAction.VIEWED_PASSWORD, PasswordCheckUserAction.COPIED_PASSWORD, PasswordCheckUserAction.EDIT_PASSWORD_CLICK, PasswordCheckUserAction.EDITED_PASSWORD, PasswordCheckUserAction.DELETE_PASSWORD_CLICK, PasswordCheckUserAction.DELETED_PASSWORD"
         errorLine1="                userAction, PasswordCheckUserAction.COUNT);"
         errorLine2="                                                    ~~~~~">
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 524d87f..6096e8f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -67,8 +67,6 @@
 import org.chromium.chrome.browser.app.tabmodel.TabModelOrchestrator;
 import org.chromium.chrome.browser.app.tabmodel.TabWindowManagerSingleton;
 import org.chromium.chrome.browser.app.tabmodel.TabbedModeTabModelOrchestrator;
-import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchController;
-import org.chromium.chrome.browser.auxiliary_search.AuxiliarySearchControllerImpl;
 import org.chromium.chrome.browser.back_press.BackPressManager;
 import org.chromium.chrome.browser.back_press.MinimizeAppAndCloseTabBackPressHandler;
 import org.chromium.chrome.browser.back_press.MinimizeAppAndCloseTabBackPressHandler.MinimizeAppAndCloseTabType;
@@ -329,10 +327,6 @@
      *  Keeps track of the pref for the last time since this activity was stopped.
      */
     private ChromeInactivityTracker mInactivityTracker;
-    /**
-     *  The controller for the auxiliary search.
-     */
-    private AuxiliarySearchController mAuxiliarySearchController;
 
     // This is the cached value of mIntentHandler#shouldIgnoreIntent and shouldn't be read directly.
     // Use #shouldIgnoreIntent instead.
@@ -1350,9 +1344,6 @@
                 mTabModelOrchestrator.setSkipSavingNonActiveNtps(skipSavingNonActiveNtps);
             }
 
-            mAuxiliarySearchController = new AuxiliarySearchControllerImpl(
-                    Profile.getLastUsedRegularProfile(), mTabModelSelector);
-            mAuxiliarySearchController.register(this.getLifecycleDispatcher());
             mInactivityTracker.register(this.getLifecycleDispatcher());
             boolean isIntentWithEffect = false;
             boolean isMainIntentFromLauncher = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfileBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfileBridge.java
index 6f25cb8..9de4971 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfileBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfileBridge.java
@@ -4,24 +4,16 @@
 
 package org.chromium.chrome.browser.autofill.settings;
 
-import android.app.Activity;
-
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
-import androidx.fragment.app.Fragment;
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.autofill.editors.EditorProperties.DropdownKeyValue;
-import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
-import org.chromium.components.browser_ui.settings.SettingsLauncher;
-import org.chromium.content_public.browser.WebContents;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -209,26 +201,6 @@
         }
     }
 
-    @CalledByNative
-    private static void showAutofillProfileSettings(WebContents webContents) {
-        RecordUserAction.record("AutofillAddressesViewed");
-        showSettingSubpage(webContents, AutofillProfilesFragment.class);
-    }
-
-    @CalledByNative
-    private static void showAutofillCreditCardSettings(WebContents webContents) {
-        RecordUserAction.record("AutofillCreditCardsViewed");
-        showSettingSubpage(webContents, AutofillPaymentMethodsFragment.class);
-    }
-
-    private static void showSettingSubpage(
-            WebContents webContents, Class<? extends Fragment> fragment) {
-        WeakReference<Activity> currentActivity =
-                webContents.getTopLevelNativeWindow().getActivity();
-        SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
-        settingsLauncher.launchSettingsActivity(currentActivity.get(), fragment);
-    }
-
     @NativeMethods
     @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
     public interface Natives {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/SettingsLauncherHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/SettingsLauncherHelper.java
new file mode 100644
index 0000000..0f39d29
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/SettingsLauncherHelper.java
@@ -0,0 +1,39 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill.settings;
+
+import android.content.Context;
+
+import androidx.fragment.app.Fragment;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
+import org.chromium.components.browser_ui.settings.SettingsLauncher;
+import org.chromium.content_public.browser.WebContents;
+
+/** Launches autofill settings subpages. */
+public class SettingsLauncherHelper {
+    @CalledByNative
+    private static void showAutofillProfileSettings(WebContents webContents) {
+        RecordUserAction.record("AutofillAddressesViewed");
+        showSettingSubpage(webContents, AutofillProfilesFragment.class);
+    }
+
+    @CalledByNative
+    private static void showAutofillCreditCardSettings(WebContents webContents) {
+        RecordUserAction.record("AutofillCreditCardsViewed");
+        showSettingSubpage(webContents, AutofillPaymentMethodsFragment.class);
+    }
+
+    private static void showSettingSubpage(
+            WebContents webContents, Class<? extends Fragment> fragment) {
+        Context context = webContents.getTopLevelNativeWindow().getActivity().get();
+        if (context != null) {
+            SettingsLauncher settingsLauncher = new SettingsLauncherImpl();
+            settingsLauncher.launchSettingsActivity(context, fragment);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java
index 0ff490e..1acd0a9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonController.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.bookmarks;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.view.View;
 
@@ -15,6 +16,8 @@
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
+import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
 import org.chromium.chrome.browser.tab.CurrentTabObserver;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -25,20 +28,25 @@
 import org.chromium.components.feature_engagement.EventConstants;
 import org.chromium.components.feature_engagement.FeatureConstants;
 import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.ui.base.DeviceFormFactor;
 
 import java.util.Objects;
 
 /**
  *  Defines a toolbar button to add the current web page to bookmarks.
  */
-public class AddToBookmarksToolbarButtonController extends BaseButtonDataProvider {
+public class AddToBookmarksToolbarButtonController
+        extends BaseButtonDataProvider implements ConfigurationChangedObserver {
+    private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
     private final Supplier<TabBookmarker> mTabBookmarkerSupplier;
     private final Supplier<Tracker> mTrackerSupplier;
     private final ObservableSupplier<BookmarkModel> mBookmarkModelSupplier;
     private final ButtonSpec mFilledButtonSpec;
     private final ButtonSpec mEmptyButtonSpec;
+    private final Context mContext;
     private CurrentTabObserver mCurrentTabObserver;
     private BookmarkModel mObservedBookmarkModel;
+    private boolean mIsTablet;
 
     private final Callback<BookmarkModel> mBookmarkModelSupplierObserver =
             new Callback<BookmarkModel>() {
@@ -72,8 +80,8 @@
      *         changes and checking if the current tab is bookmarked.
      */
     public AddToBookmarksToolbarButtonController(ObservableSupplier<Tab> activeTabSupplier,
-            Context context, Supplier<TabBookmarker> tabBookmarkerSupplier,
-            Supplier<Tracker> trackerSupplier,
+            Context context, ActivityLifecycleDispatcher activityLifecycleDispatcher,
+            Supplier<TabBookmarker> tabBookmarkerSupplier, Supplier<Tracker> trackerSupplier,
             ObservableSupplier<BookmarkModel> bookmarkModelSupplier) {
         // By default use the empty star drawable with an "Add to bookmarks" description.
         super(activeTabSupplier, /* modalDialogManager = */ null,
@@ -81,10 +89,13 @@
                 context.getString(R.string.accessibility_menu_bookmark),
                 /* actionChipLabelResId = */ Resources.ID_NULL, /* supportsTinting = */ true,
                 /* iphCommandBuilder = */ null, AdaptiveToolbarButtonVariant.ADD_TO_BOOKMARKS);
+        mActivityLifecycleDispatcher = activityLifecycleDispatcher;
         mTabBookmarkerSupplier = tabBookmarkerSupplier;
         mTrackerSupplier = trackerSupplier;
+        mContext = context;
 
         mBookmarkModelSupplier = bookmarkModelSupplier;
+        mActivityLifecycleDispatcher.register(this);
         mBookmarkModelSupplier.addObserver(mBookmarkModelSupplierObserver);
         mCurrentTabObserver = new CurrentTabObserver(activeTabSupplier, new EmptyTabObserver() {
             @Override
@@ -100,6 +111,8 @@
                 context.getString(R.string.menu_edit_bookmark), true,
                 /* iphCommandBuilder= */ null, AdaptiveToolbarButtonVariant.ADD_TO_BOOKMARKS,
                 /* actionChipLabelResId = */ Resources.ID_NULL);
+
+        mIsTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
     }
 
     private void refreshBookmarkIcon() {
@@ -119,6 +132,13 @@
     }
 
     @Override
+    protected boolean shouldShowButton(Tab tab) {
+        if (mIsTablet) return false;
+
+        return super.shouldShowButton(tab);
+    }
+
+    @Override
     protected IPHCommandBuilder getIphCommandBuilder(Tab tab) {
         return new IPHCommandBuilder(tab.getContext().getResources(),
                 FeatureConstants
@@ -129,6 +149,17 @@
     }
 
     @Override
+    public void onConfigurationChanged(Configuration configuration) {
+        boolean isTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
+        if (mIsTablet == isTablet) {
+            return;
+        }
+        mIsTablet = isTablet;
+
+        mButtonData.setCanShow(shouldShowButton(mActiveTabSupplier.get()));
+    }
+
+    @Override
     public void onClick(View view) {
         if (!mTabBookmarkerSupplier.hasValue() || !mActiveTabSupplier.hasValue()) return;
 
@@ -157,6 +188,10 @@
             mCurrentTabObserver = null;
         }
 
+        if (mActivityLifecycleDispatcher != null) {
+            mActivityLifecycleDispatcher.unregister(this);
+        }
+
         super.destroy();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index 62b097b4..5670e8f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -1156,7 +1156,8 @@
                             mActivityTabProvider, trackerSupplier);
             AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
                     new AddToBookmarksToolbarButtonController(mActivityTabProvider, mActivity,
-                            mTabBookmarkerSupplier, trackerSupplier, mBookmarkModelSupplier);
+                            mActivityLifecycleDispatcher, mTabBookmarkerSupplier, trackerSupplier,
+                            mBookmarkModelSupplier);
             AdaptiveToolbarButtonController adaptiveToolbarButtonController =
                     new AdaptiveToolbarButtonController(mActivity, new SettingsLauncherImpl(),
                             mActivityLifecycleDispatcher, new AdaptiveButtonActionMenuCoordinator(),
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonControllerUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonControllerUnitTest.java
index fdb527c..5555826 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonControllerUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/AddToBookmarksToolbarButtonControllerUnitTest.java
@@ -8,6 +8,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Looper;
 
@@ -21,8 +24,10 @@
 import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 
@@ -32,6 +37,8 @@
 import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.UserActionTester;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.init.ActivityLifecycleDispatcherImpl;
+import org.chromium.chrome.browser.lifecycle.ConfigurationChangedObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
 import org.chromium.chrome.browser.toolbar.ButtonData;
@@ -59,6 +66,11 @@
     private TabBookmarker mTabBookmarker;
     @Mock
     private BookmarkModel mBookmarkModel;
+    @Mock
+    private ActivityLifecycleDispatcherImpl mActivityLifecycleDispatcher;
+    @Captor
+    private ArgumentCaptor<ConfigurationChangedObserver> mConfigurationChangedObserver;
+
     private ObservableSupplierImpl<Tab> mTabSupplier;
     private ObservableSupplierImpl<BookmarkModel> mBookmarkModelSupplier;
 
@@ -82,18 +94,73 @@
         mActionTester.tearDown();
     }
 
+    /** Sets device qualifiers and notifies the activity about configuration change. */
+    private void applyQualifiers(Activity activity, String qualifiers) {
+        RuntimeEnvironment.setQualifiers(qualifiers);
+        Configuration configuration = Resources.getSystem().getConfiguration();
+        // ChromeTabbedActivity takes care of calling onConfigurationChanged on
+        // ActivityLifecycleDispatcher, but this is a TestActivity so we do it manually.
+        activity.onConfigurationChanged(configuration);
+        mConfigurationChangedObserver.getValue().onConfigurationChanged(configuration);
+    }
+
+    private AddToBookmarksToolbarButtonController createController(Activity activity) {
+        AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
+                new AddToBookmarksToolbarButtonController(mTabSupplier, activity,
+                        mActivityLifecycleDispatcher,
+                        () -> mTabBookmarker, () -> mTracker, mBookmarkModelSupplier);
+        verify(mActivityLifecycleDispatcher).register(mConfigurationChangedObserver.capture());
+        return addToBookmarksToolbarButtonController;
+    }
+
     @Test
-    public void testButtonData() {
+    @Config(qualifiers = "w390dp-h820dp-land")
+    public void testButtonData_shownOnPhone() {
         mActivityScenarioRule.getScenario().onActivity(activity -> {
             when(mTab.getContext()).thenReturn(activity);
             AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
-                    new AddToBookmarksToolbarButtonController(mTabSupplier, activity,
-                            () -> mTabBookmarker, () -> mTracker, mBookmarkModelSupplier);
+                    createController(activity);
             ButtonData buttonData = addToBookmarksToolbarButtonController.get(mTab);
 
             Assert.assertTrue(buttonData.canShow());
             Assert.assertTrue(buttonData.isEnabled());
             Assert.assertNotNull(buttonData.getButtonSpec());
+
+            applyQualifiers(activity, "+port");
+
+            Assert.assertTrue(addToBookmarksToolbarButtonController.get(mTab).canShow());
+        });
+    }
+
+    @Test
+    @Config(qualifiers = "w600dp-h820dp")
+    public void testButtonData_notShownOnTablet() {
+        mActivityScenarioRule.getScenario().onActivity(activity -> {
+            when(mTab.getContext()).thenReturn(activity);
+            AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
+                    createController(activity);
+            ButtonData buttonData = addToBookmarksToolbarButtonController.get(mTab);
+
+            Assert.assertFalse(buttonData.canShow());
+            applyQualifiers(activity, "+land");
+
+            Assert.assertFalse(buttonData.canShow());
+        });
+    }
+
+    @Test
+    @Config(qualifiers = "w600dp-h820dp")
+    public void testButtonData_visibilityChangesOnScreenSizeUpdate() {
+        mActivityScenarioRule.getScenario().onActivity(activity -> {
+            when(mTab.getContext()).thenReturn(activity);
+            AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
+                    createController(activity);
+            ButtonData buttonData = addToBookmarksToolbarButtonController.get(mTab);
+
+            Assert.assertFalse(buttonData.canShow());
+            applyQualifiers(activity, "w390dp-h820dp");
+
+            Assert.assertTrue(buttonData.canShow());
         });
     }
 
@@ -102,8 +169,7 @@
         mActivityScenarioRule.getScenario().onActivity(activity -> {
             when(mTab.getContext()).thenReturn(activity);
             AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
-                    new AddToBookmarksToolbarButtonController(mTabSupplier, activity,
-                            () -> mTabBookmarker, () -> mTracker, mBookmarkModelSupplier);
+                    createController(activity);
             addToBookmarksToolbarButtonController.onClick(null);
 
             Assert.assertEquals(
@@ -124,8 +190,7 @@
             when(mBookmarkModel.hasBookmarkIdForTab(mTab)).thenReturn(false);
             when(mBookmarkModel.hasBookmarkIdForTab(anotherTab)).thenReturn(true);
             AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
-                    new AddToBookmarksToolbarButtonController(mTabSupplier, activity,
-                            () -> mTabBookmarker, () -> mTracker, mBookmarkModelSupplier);
+                    createController(activity);
 
             ButtonDataObserver mockButtonObserver = mock(ButtonDataObserver.class);
             addToBookmarksToolbarButtonController.addObserver(mockButtonObserver);
@@ -158,8 +223,7 @@
             // Set the current tab as not bookmarked.
             when(mBookmarkModel.hasBookmarkIdForTab(mTab)).thenReturn(false);
             AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
-                    new AddToBookmarksToolbarButtonController(mTabSupplier, activity,
-                            () -> mTabBookmarker, () -> mTracker, mBookmarkModelSupplier);
+                    createController(activity);
 
             // If an ObservableSupplier already has a value then its change callback will be called
             // immediately as a separate task, idle the main looper to ensure all Observable
@@ -201,8 +265,7 @@
             // Set the current tab as not bookmarked.
             when(mBookmarkModel.hasBookmarkIdForTab(mTab)).thenReturn(false);
             AddToBookmarksToolbarButtonController addToBookmarksToolbarButtonController =
-                    new AddToBookmarksToolbarButtonController(mTabSupplier, activity,
-                            () -> mTabBookmarker, () -> mTracker, mBookmarkModelSupplier);
+                    createController(activity);
 
             // If an ObservableSupplier already has a value then its change callback will be called
             // immediately as a separate task, idle the main looper to ensure all Observable
diff --git a/chrome/app/PRESUBMIT.py b/chrome/app/PRESUBMIT.py
index 6bf4324..df98e69 100644
--- a/chrome/app/PRESUBMIT.py
+++ b/chrome/app/PRESUBMIT.py
@@ -24,14 +24,15 @@
   filename_filter = lambda x: x.LocalPath().endswith(('.grd', '.grdp'))
 
   for f, line_num, line in input_api.RightHandSideLines(filename_filter):
-    if 'PRODUCT_NAME' in line:
+    if ('PRODUCT_NAME' in line and 'name="IDS_PRODUCT_NAME"' not in line and
+       'name="IDS_SHORT_PRODUCT_NAME"' not in line):
       problems.append('%s:%d' % (f.LocalPath(), line_num))
 
   if problems:
     return [output_api.PresubmitPromptWarning(
-        "Don't use PRODUCT_NAME placeholders in string resources. Instead, add "
-        "separate strings to google_chrome_strings.grd and "
-        "chromium_strings.grd. See http://goo.gl/6614MQ for more information."
+        "Don't use PRODUCT_NAME placeholders in string resources. Instead, "
+        "add separate strings to google_chrome_strings.grd and "
+        "chromium_strings.grd. See http://goo.gl/6614MQ for more information. "
         "Problems with this check? Contact dubroy@chromium.org.",
         items=problems)]
   return []
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index d5cc68c..1fbc396 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -276,12 +276,31 @@
         <part file="settings_chromium_strings.grdp" />
       </if>
 
-      <message name="IDS_PRODUCT_NAME" desc="The Chrome application name">
-        Chromium
-      </message>
-      <message name="IDS_SHORT_PRODUCT_NAME" desc="The Chrome application short name.">
-        Chromium
-      </message>
+      <!--
+        Even though Google Chrome for Testing is a separate brand, there is
+        no separate file for it. Instead, we use the same file as Chromium, with
+        a few select overrides for the most prominent end-user strings.
+
+        Rationale: https://goo.gle/chrome-for-testing#bookmark=id.n1rat320av91
+      -->
+      <if expr="_is_chrome_for_testing_branded">
+        <then>
+          <message name="IDS_PRODUCT_NAME" desc="The Chrome for Testing application name" translateable="false">
+            Google Chrome for Testing
+          </message>
+          <message name="IDS_SHORT_PRODUCT_NAME" desc="The Chrome for Testing application short name." translateable="false">
+            Chrome for Testing
+          </message>
+        </then>
+        <else>
+          <message name="IDS_PRODUCT_NAME" desc="The Chromium application name">
+            Chromium
+          </message>
+          <message name="IDS_SHORT_PRODUCT_NAME" desc="The Chromium application short name.">
+            Chromium
+          </message>
+        </else>
+      </if>
       <if expr="is_win">
         <message name="IDS_SXS_SHORTCUT_NAME" desc="Unused in Chromium builds" translateable="false">
         </message>
@@ -799,17 +818,35 @@
       </message>
 
       <if expr="use_titlecase and not chromeos_ash">
-        <message name="IDS_ABOUT" desc="In Title Case: The text label of the About Chrome menu item">
-          About &amp;Chromium
-        </message>
+        <if expr="_is_chrome_for_testing_branded">
+          <then>
+            <message name="IDS_ABOUT" desc="In Title Case: The text label of the About Chrome for Testing menu item">
+              About &amp;Google Chrome for Testing
+            </message>
+          </then>
+          <else>
+            <message name="IDS_ABOUT" desc="In Title Case: The text label of the About Chrome menu item">
+              About &amp;Chromium
+            </message>
+          </else>
+        </if>
         <message name="IDS_RELAUNCH_TO_UPDATE" desc="In Title Case: The text label of the relaunch to update Chrome menu item">
           Relaunch to Update &amp;Chromium
         </message>
       </if>
       <if expr="not use_titlecase and not chromeos_ash">
-        <message name="IDS_ABOUT" desc="The text label of the About Chrome menu item">
-          About &amp;Chromium
-        </message>
+        <if expr="_is_chrome_for_testing_branded">
+          <then>
+            <message name="IDS_ABOUT" desc="The text label of the About Chrome for Testing menu item">
+              About &amp;Google Chrome for Testing
+            </message>
+          </then>
+          <else>
+            <message name="IDS_ABOUT" desc="The text label of the About Chrome menu item">
+              About &amp;Chromium
+            </message>
+          </else>
+        </if>
         <message name="IDS_RELAUNCH_TO_UPDATE" desc="The text label of the relaunch to update Chrome menu item">
           Relaunch to update &amp;Chromium
         </message>
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 115b0d2..4fe3b8c7e 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -8,12 +8,24 @@
     </message>
   </if>
   <!-- About Page -->
-  <message name="IDS_SETTINGS_ABOUT_PROGRAM" desc="Menu title for the About Chromium page.">
-    About Chromium
-  </message>
-  <message name="IDS_SETTINGS_GET_HELP_USING_CHROME" desc="Text of the button which takes the user to the Chrome help page.">
-    Get help with Chromium
-  </message>
+  <if expr="_is_chrome_for_testing_branded">
+    <then>
+      <message name="IDS_SETTINGS_ABOUT_PROGRAM" desc="Menu title for the About Chrome for Testing page.">
+        About Chrome for Testing
+      </message>
+      <message name="IDS_SETTINGS_GET_HELP_USING_CHROME" desc="Text of the button which takes the user to the Chrome for Testing help page.">
+        Get help with Chrome for Testing
+      </message>
+    </then>
+    <else>
+      <message name="IDS_SETTINGS_ABOUT_PROGRAM" desc="Menu title for the About Chromium page.">
+        About Chromium
+      </message>
+      <message name="IDS_SETTINGS_GET_HELP_USING_CHROME" desc="Text of the button which takes the user to the Chrome help page.">
+        Get help with Chromium
+      </message>
+    </else>
+  </if>
   <if expr="not chromeos_ash">
     <message name="IDS_SETTINGS_UPGRADE_UPDATING" desc="Status label: Updating Chromium">
       Updating Chromium
@@ -88,9 +100,18 @@
     <message name="IDS_SETTINGS_DEFAULT_BROWSER_ERROR" desc="The text displayed when Chrome cannot determine or set the default browser">
       Chromium cannot determine or set the default browser
     </message>
-    <message name="IDS_SETTINGS_DEFAULT_BROWSER_SECONDARY" desc="The text displayed when Chromium is installed in side-by-side mode, which does not support setting as the default browser.">
-      This is a secondary installation of Chromium, and cannot be made your default browser.
-    </message>
+    <if expr="_is_chrome_for_testing_branded">
+      <then>
+        <message name="IDS_SETTINGS_DEFAULT_BROWSER_SECONDARY" desc="The text displayed when Chrome for Testing is installed in side-by-side mode, which does not support setting as the default browser.">
+          Google Chrome for Testing cannot be made your default browser.
+        </message>
+      </then>
+      <else>
+        <message name="IDS_SETTINGS_DEFAULT_BROWSER_SECONDARY" desc="The text displayed when Chromium is installed in side-by-side mode, which does not support setting as the default browser.">
+          This is a secondary installation of Chromium, and cannot be made your default browser.
+        </message>
+      </else>
+    </if>
   </if>
 
   <!-- Privacy Page -->
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index ad806e2..5c981e6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1034,6 +1034,8 @@
     "page_load_metrics/observers/service_worker_page_load_metrics_observer.h",
     "page_load_metrics/observers/signed_exchange_page_load_metrics_observer.cc",
     "page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h",
+    "page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc",
+    "page_load_metrics/observers/tab_strip_page_load_metrics_observer.h",
     "page_load_metrics/observers/third_party_metrics_observer.cc",
     "page_load_metrics/observers/third_party_metrics_observer.h",
     "page_load_metrics/observers/translate_page_load_metrics_observer.cc",
@@ -2761,6 +2763,8 @@
       "android/preferences/autofill/autofill_payment_methods_delegate.h",
       "android/preferences/autofill/autofill_profile_bridge.cc",
       "android/preferences/autofill/autofill_profile_bridge.h",
+      "android/preferences/autofill/settings_launcher_helper.cc",
+      "android/preferences/autofill/settings_launcher_helper.h",
       "android/preferences/browser_prefs_android.cc",
       "android/preferences/browser_prefs_android.h",
       "android/preferences/clipboard_android.cc",
@@ -3013,6 +3017,8 @@
       "lookalikes/safety_tip_infobar_delegate_android.h",
       "lookalikes/safety_tip_message_delegate_android.cc",
       "lookalikes/safety_tip_message_delegate_android.h",
+      "mandatory_reauth/android/mandatory_reauth_opt_in_view_android.cc",
+      "mandatory_reauth/android/mandatory_reauth_opt_in_view_android.h",
       "media/android/cdm/media_drm_origin_id_manager.cc",
       "media/android/cdm/media_drm_origin_id_manager.h",
       "media/android/cdm/media_drm_origin_id_manager_factory.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 60f1554..e1f2fa7 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4791,16 +4791,6 @@
      flag_descriptions::kDesktopPWAsDetailedInstallDialogName,
      flag_descriptions::kDesktopPWAsDetailedInstallDialogDescription, kOsAll,
      FEATURE_VALUE_TYPE(webapps::features::kDesktopPWAsDetailedInstallDialog)},
-    {"enable-skip-service-worker-check-install-only",
-     flag_descriptions::kSkipServiceWorkerCheckInstallOnlyName,
-     flag_descriptions::kSkipServiceWorkerCheckInstallOnlyDescription,
-     kOsDesktop,
-     FEATURE_VALUE_TYPE(webapps::features::kSkipServiceWorkerCheckInstallOnly)},
-    {"enable-skip-service-worker-check-install-for-promotion",
-     flag_descriptions::kSkipServiceWorkerForInstallPromptName,
-     flag_descriptions::kSkipServiceWorkerForInstallPromptDescription,
-     kOsDesktop,
-     FEATURE_VALUE_TYPE(webapps::features::kSkipServiceWorkerForInstallPrompt)},
     {"record-web-app-debug-info", flag_descriptions::kRecordWebAppDebugInfoName,
      flag_descriptions::kRecordWebAppDebugInfoDescription, kOsDesktop,
      FEATURE_VALUE_TYPE(features::kRecordWebAppDebugInfo)},
@@ -10391,6 +10381,10 @@
      FEATURE_VALUE_TYPE(features::kCrosWebAppShortcutUiUpdate)},
 #endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
 
+    {"use-gpu-scheduler-dfs", flag_descriptions::kUseGpuSchedulerDfsName,
+     flag_descriptions::kUseGpuSchedulerDfsDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kUseGpuSchedulerDfs)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
index 7373ba0..9ff26201 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.cc
@@ -25,6 +25,7 @@
 #include "media/base/media_switches.h"
 #include "media/mojo/mojom/speech_recognition_result.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace captions {
 
@@ -53,6 +54,8 @@
   prefs_ = Profile::FromBrowserContext(GetWebContents()->GetBrowserContext())
                ->GetPrefs();
   context_ = CaptionBubbleContextBrowser::Create(web_contents);
+
+  source_language_ = prefs_->GetString(prefs::kLiveCaptionLanguageCode);
 }
 
 LiveCaptionSpeechRecognitionHost::~LiveCaptionSpeechRecognitionHost() {
@@ -71,13 +74,13 @@
   }
 
   if (base::FeatureList::IsEnabled(media::kLiveTranslate) &&
-      prefs_->GetBoolean(prefs::kLiveTranslateEnabled)) {
-    std::string source_language =
-        prefs_->GetString(prefs::kLiveCaptionLanguageCode);
-    std::string target_language =
-        prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode);
+      prefs_->GetBoolean(prefs::kLiveTranslateEnabled) &&
+      l10n_util::GetLanguage(
+          prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode)) !=
+          l10n_util::GetLanguage(source_language_)) {
     GetLiveTranslateController()->GetTranslation(
-        result, source_language, target_language,
+        result, source_language_,
+        prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode),
         base::BindOnce(&LiveCaptionSpeechRecognitionHost::OnTranslationCallback,
                        weak_factory_.GetWeakPtr()));
     std::move(reply).Run(!stop_transcriptions_);
@@ -93,7 +96,13 @@
   if (!live_caption_controller)
     return;
 
-  live_caption_controller->OnLanguageIdentificationEvent(std::move(event));
+  if (event->asr_switch_result ==
+      media::mojom::AsrSwitchResult::kSwitchSucceeded) {
+    source_language_ = event->language;
+  }
+
+  live_caption_controller->OnLanguageIdentificationEvent(context_.get(),
+                                                         std::move(event));
 }
 
 void LiveCaptionSpeechRecognitionHost::OnSpeechRecognitionError() {
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
index 63bcb204..47549dac 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host.h
@@ -93,6 +93,9 @@
   // should stop.
   bool stop_transcriptions_ = false;
 
+  // The source language code of the audio stream.
+  std::string source_language_;
+
   // The user preferences containing the target and source language codes.
   raw_ptr<PrefService> prefs_;
 
diff --git a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
index c1d8e3b..69f52c59 100644
--- a/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
+++ b/chrome/browser/accessibility/live_caption/live_caption_speech_recognition_host_browsertest.cc
@@ -100,10 +100,11 @@
   void OnLanguageIdentificationEvent(
       content::RenderFrameHost* frame_host,
       const std::string& language,
-      const media::mojom::ConfidenceLevel confidence_level) {
+      const media::mojom::ConfidenceLevel confidence_level,
+      const media::mojom::AsrSwitchResult asr_switch_result) {
     remotes_[frame_host]->OnLanguageIdentificationEvent(
-        media::mojom::LanguageIdentificationEvent::New(language,
-                                                       confidence_level));
+        media::mojom::LanguageIdentificationEvent::New(
+            language, confidence_level, asr_switch_result));
   }
 
   void OnSpeechRecognitionError(content::RenderFrameHost* frame_host) {
@@ -204,7 +205,8 @@
 
   SetLiveCaptionEnabled(true);
   OnLanguageIdentificationEvent(
-      frame_host, "en-US", media::mojom::ConfidenceLevel::kHighlyConfident);
+      frame_host, "en-US", media::mojom::ConfidenceLevel::kHighlyConfident,
+      media::mojom::AsrSwitchResult::kSwitchSucceeded);
 }
 
 IN_PROC_BROWSER_TEST_F(LiveCaptionSpeechRecognitionHostTest,
@@ -253,14 +255,11 @@
   SetLiveCaptionEnabled(true);
   SetLiveTranslateEnabled(true);
 
-  // Dispatching the speech recognition should be successful, but the
-  // widget should remain hidden because the test does not fetch real
-  // translations.
   OnSpeechRecognitionRecognitionEvent(
       frame_host, "Elephants can live over 90 years in captivity.",
       /* expected_success= */ true);
   base::RunLoop().RunUntilIdle();
-  ExpectIsWidgetVisible(false);
+  ExpectIsWidgetVisible(true);
 }
 
 }  // namespace captions
diff --git a/chrome/browser/android/preferences/autofill/autofill_profile_bridge.cc b/chrome/browser/android/preferences/autofill/autofill_profile_bridge.cc
index 20418bb5..fb9c52e7 100644
--- a/chrome/browser/android/preferences/autofill/autofill_profile_bridge.cc
+++ b/chrome/browser/android/preferences/autofill/autofill_profile_bridge.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/android/preferences/autofill/autofill_profile_bridge.h"
 
-#include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
@@ -14,7 +13,6 @@
 #include "chrome/browser/browser_process.h"
 #include "components/autofill/core/browser/autofill_address_util.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
-#include "content/public/browser/web_contents.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_field.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_metadata.h"
 #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui.h"
@@ -158,14 +156,4 @@
   return ConvertUTF8ToJavaString(env, best_language_tag);
 }
 
-void ShowAutofillProfileSettings(content::WebContents* web_contents) {
-  Java_AutofillProfileBridge_showAutofillProfileSettings(
-      base::android::AttachCurrentThread(), web_contents->GetJavaWebContents());
-}
-
-void ShowAutofillCreditCardSettings(content::WebContents* web_contents) {
-  Java_AutofillProfileBridge_showAutofillCreditCardSettings(
-      base::android::AttachCurrentThread(), web_contents->GetJavaWebContents());
-}
-
 }  // namespace autofill
diff --git a/chrome/browser/android/preferences/autofill/autofill_profile_bridge.h b/chrome/browser/android/preferences/autofill/autofill_profile_bridge.h
index 8cc5c8b..1303930 100644
--- a/chrome/browser/android/preferences/autofill/autofill_profile_bridge.h
+++ b/chrome/browser/android/preferences/autofill/autofill_profile_bridge.h
@@ -5,10 +5,6 @@
 #ifndef CHROME_BROWSER_ANDROID_PREFERENCES_AUTOFILL_AUTOFILL_PROFILE_BRIDGE_H_
 #define CHROME_BROWSER_ANDROID_PREFERENCES_AUTOFILL_AUTOFILL_PROFILE_BRIDGE_H_
 
-namespace content {
-class WebContents;
-}
-
 namespace autofill {
 
 // Specifies which rules are to be used for address validation.
@@ -21,12 +17,6 @@
   kAccount = 1
 };
 
-// Opens the autofill settings page for profiles.
-void ShowAutofillProfileSettings(content::WebContents* web_contents);
-
-// Opens the autofill settings page for credit cards.
-void ShowAutofillCreditCardSettings(content::WebContents* web_contents);
-
 }  // namespace autofill
 
 #endif  // CHROME_BROWSER_ANDROID_PREFERENCES_AUTOFILL_AUTOFILL_PROFILE_BRIDGE_H_
diff --git a/chrome/browser/android/preferences/autofill/settings_launcher_helper.cc b/chrome/browser/android/preferences/autofill/settings_launcher_helper.cc
new file mode 100644
index 0000000..61cfebb
--- /dev/null
+++ b/chrome/browser/android/preferences/autofill/settings_launcher_helper.cc
@@ -0,0 +1,23 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/android/preferences/autofill/settings_launcher_helper.h"
+
+#include "base/android/jni_android.h"
+#include "chrome/android/chrome_jni_headers/SettingsLauncherHelper_jni.h"
+#include "content/public/browser/web_contents.h"
+
+namespace autofill {
+
+void ShowAutofillProfileSettings(content::WebContents* web_contents) {
+  Java_SettingsLauncherHelper_showAutofillProfileSettings(
+      base::android::AttachCurrentThread(), web_contents->GetJavaWebContents());
+}
+
+void ShowAutofillCreditCardSettings(content::WebContents* web_contents) {
+  Java_SettingsLauncherHelper_showAutofillCreditCardSettings(
+      base::android::AttachCurrentThread(), web_contents->GetJavaWebContents());
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/android/preferences/autofill/settings_launcher_helper.h b/chrome/browser/android/preferences/autofill/settings_launcher_helper.h
new file mode 100644
index 0000000..ef04fbd
--- /dev/null
+++ b/chrome/browser/android/preferences/autofill/settings_launcher_helper.h
@@ -0,0 +1,22 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ANDROID_PREFERENCES_AUTOFILL_SETTINGS_LAUNCHER_HELPER_H_
+#define CHROME_BROWSER_ANDROID_PREFERENCES_AUTOFILL_SETTINGS_LAUNCHER_HELPER_H_
+
+namespace content {
+class WebContents;
+}
+
+namespace autofill {
+
+// Opens the autofill settings page for profiles.
+void ShowAutofillProfileSettings(content::WebContents* web_contents);
+
+// Opens the autofill settings page for credit cards.
+void ShowAutofillCreditCardSettings(content::WebContents* web_contents);
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_ANDROID_PREFERENCES_AUTOFILL_SETTINGS_LAUNCHER_HELPER_H_
diff --git a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
index 973de80..b4b5ddd 100644
--- a/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
+++ b/chrome/browser/android/webapk/webapk_update_data_fetcher.cc
@@ -146,8 +146,8 @@
   params.valid_manifest = true;
   params.prefer_maskable_icon =
       webapps::WebappsIconUtils::DoesAndroidSupportMaskableIcons();
-  params.has_worker = !webapps::features::SkipInstallServiceWorkerCheck();
-  params.wait_for_worker = !webapps::features::SkipInstallServiceWorkerCheck();
+  params.has_worker = false;
+  params.wait_for_worker = false;
   params.valid_primary_icon = true;
   params.valid_splash_icon = true;
   webapps::InstallableManager* installable_manager =
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 0e6b5de..097dacc1 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -2817,6 +2817,22 @@
   EXPECT_TRUE(content::NavigateToURLFromRenderer(guest2, coop_url));
 }
 
+// This test creates a situation where we have two unattached webviews which
+// have an opener relationship, and ensures that we can shutdown safely. See
+// https://crbug.com/1450397.
+IN_PROC_BROWSER_TEST_P(WebViewNewWindowTest, DestroyOpenerBeforeAttachment) {
+  TestHelper("testDestroyOpenerBeforeAttachment", "web_view/newwindow",
+             NEEDS_TEST_SERVER);
+  GetGuestViewManager()->WaitForNumGuestsCreated(2);
+
+  content::RenderProcessHost* embedder_rph =
+      GetEmbedderWebContents()->GetPrimaryMainFrame()->GetProcess();
+  content::RenderProcessHostWatcher kill_observer(
+      embedder_rph, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  EXPECT_TRUE(embedder_rph->Shutdown(content::RESULT_CODE_KILLED));
+  kill_observer.Wait();
+}
+
 IN_PROC_BROWSER_TEST_F(WebViewTest, ContextMenuInspectElement) {
   LoadAppWithGuest("web_view/context_menus/basic");
   content::RenderFrameHost* guest_rfh = GetGuestRenderFrameHost();
diff --git a/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc
index 9892129..b2e2c47f 100644
--- a/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ash/app_list/arc/arc_app_list_prefs.cc
@@ -2250,7 +2250,7 @@
     const std::string* app_package_name =
         app.second.GetDict().FindString(kPackageName);
     if (!app_package_name) {
-      NOTREACHED();
+      LOG(ERROR) << "App is malformed: " << app.first;
       continue;
     }
     if (*app_package_name != package_name) {
diff --git a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
index 9154ee14..9d67b64 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_embedded_policy_server_browsertest.cc
@@ -495,7 +495,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -513,7 +513,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR_MEETS,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -529,7 +529,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_AUTH_ACCOUNT_ERROR, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -546,7 +546,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_ACCOUNT_ERROR_MEETS, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -564,7 +564,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_POLICY_DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -580,7 +580,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_DOMAIN_MISMATCH_ERROR, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -597,7 +597,7 @@
   // TODO (antrim, rsorokin): find out why it makes sense to retry here?
   enrollment_ui_.ExpectErrorMessage(
       IDS_POLICY_DM_STATUS_SERVICE_DEVICE_ID_CONFLICT, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -613,7 +613,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_POLICY_DM_STATUS_SERVICE_ACTIVATION_PENDING, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -630,7 +630,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -646,7 +646,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(IDS_POLICY_DM_STATUS_TEMPORARY_UNAVAILABLE,
                                     /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -663,7 +663,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -679,7 +679,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -697,7 +697,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED_MEETS,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -713,7 +713,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_ILLEGAL_ACCOUNT_FOR_PACKAGED_EDU_LICENSE,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -728,7 +728,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(IDS_POLICY_DM_STATUS_HTTP_STATUS_ERROR,
                                     /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -769,7 +769,7 @@
                                     /*can_retry=*/true);
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
 }
 
 // Error during enrollment : Error 418: PACKAGED_DEVICE_KIOSK_DISALLOWED.
@@ -784,7 +784,7 @@
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_INVALID_PACKAGED_DEVICE_FOR_KIOSK,
       /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -803,7 +803,7 @@
       /*can_retry=*/true);
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
 }
 
 // Error during enrollment : Error fetching policy : 903 - deprovisioned.
@@ -819,7 +819,7 @@
                                     /*can_retry=*/true);
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
 }
 
 // No state keys on the server. Auto enrollment check should proceed to login.
@@ -1069,7 +1069,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_STATUS_LOCK_WRONG_USER, true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
 }
 
 IN_PROC_BROWSER_TEST_F(InitialEnrollmentTest, EnrollmentForced) {
@@ -1304,7 +1304,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
@@ -1335,7 +1335,7 @@
   enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
   enrollment_ui_.ExpectErrorMessage(
       IDS_ENTERPRISE_ENROLLMENT_MISSING_LICENSES_ERROR, /*can_retry=*/true);
-  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.RetryAndWaitForSigninStep();
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
   EXPECT_FALSE(InstallAttributes::Get()->IsEnterpriseManaged());
 }
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen.cc b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
index dc9fc16..68e59cd 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
@@ -323,8 +323,14 @@
     scoped_network_observation_.Observe(network_state_informer_.get());
   }
   is_rollback_flow_ = IsRollbackFlow(*context());
-  if (view_)
+  if (view_) {
+    // Reset the view when the screen is shown for the first time or after a
+    // retry. Notably, the ShowImpl is not invoked after network error overlay
+    // is dismissed, which prevents the view from resetting when enrollment has
+    // already been completed.
+    view_->ResetEnrollmentScreen();
     view_->SetEnrollmentController(this);
+  }
   // Block enrollment on liveboot (OS isn't installed yet and this is trial
   // flow).
   if (switches::IsOsInstallAllowed()) {
@@ -815,6 +821,11 @@
   UpdateStateInternal(reason, false);
 }
 
+void EnrollmentScreen::SetNetworkStateForTesting(const NetworkState* state) {
+  CHECK_IS_TEST();
+  network_state_informer_->DefaultNetworkChanged(state);
+}
+
 // TODO(rsorokin): This function is mostly copied from SigninScreenHandler and
 // should be refactored in the future.
 void EnrollmentScreen::UpdateStateInternal(NetworkError::ErrorReason reason,
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen.h b/chrome/browser/ash/login/enrollment/enrollment_screen.h
index 99eb95f..cb94bee0 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen.h
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen.h
@@ -124,6 +124,9 @@
                           weak_ptr_factory_.GetWeakPtr());
   }
 
+  // Changes network state. Useful for simulating network issues in tests.
+  void SetNetworkStateForTesting(const NetworkState* state);
+
  protected:
   // BaseScreen:
   bool MaybeSkip(WizardContext& context) override;
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc b/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
index 425c4a9..66df70c7 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_browsertest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ash/login/wizard_controller.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_config.h"
 #include "chrome/browser/ash/policy/enrollment/enrollment_status.h"
+#include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/tpm_error_screen_handler.h"
 #include "chrome/common/chrome_paths.h"
 #include "chromeos/dbus/tpm_manager/fake_tpm_manager_client.h"
@@ -102,6 +103,15 @@
     return enrollment_screen;
   }
 
+  policy::EnrollmentConfig CreateConfig(
+      policy::EnrollmentConfig::Mode mode,
+      policy::EnrollmentConfig::AuthMechanism auth_mechanism) {
+    policy::EnrollmentConfig config;
+    config.mode = mode;
+    config.auth_mechanism = auth_mechanism;
+    return config;
+  }
+
   test::EnrollmentUIMixin enrollment_ui_{&mixin_host_};
   test::EnrollmentHelperMixin enrollment_helper_{&mixin_host_};
 };
@@ -466,6 +476,55 @@
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
 }
 
+IN_PROC_BROWSER_TEST_F(EnrollmentScreenTest,
+                       SuccessStepPreservedAfterNetworkErrorScreen) {
+  WizardContext context;
+  enrollment_helper_.ExpectAttestationEnrollmentSuccess();
+  enrollment_helper_.DisableAttributePromptUpdate();
+  enrollment_screen()->SetEnrollmentConfig(
+      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
+                   policy::EnrollmentConfig::AUTH_MECHANISM_BEST_AVAILABLE));
+  enrollment_screen()->Show(&context);
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
+
+  enrollment_screen()->SetNetworkStateForTesting(nullptr);
+  OobeScreenWaiter(ErrorScreenView::kScreenId).Wait();
+  enrollment_screen()->SetNetworkStateForTesting(
+      NetworkHandler::Get()->network_state_handler()->DefaultNetwork());
+  OobeScreenWaiter(EnrollmentScreenView::kScreenId).Wait();
+
+  enrollment_ui_.ExpectStepVisibility(true, test::ui::kEnrollmentStepSuccess);
+}
+
+IN_PROC_BROWSER_TEST_F(EnrollmentScreenTest,
+                       ShowsWorkingStepOnAttestationFlow) {
+  WizardContext context;
+  enrollment_screen()->SetEnrollmentConfig(
+      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
+                   policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION));
+
+  enrollment_screen()->Show(&context);
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepWorking);
+}
+
+IN_PROC_BROWSER_TEST_F(EnrollmentScreenTest,
+                       ShowsWorkingStepAfterAttestationRetry) {
+  WizardContext context;
+  enrollment_helper_.ExpectAttestationEnrollmentError(
+      policy::EnrollmentStatus::ForRegistrationError(
+          policy::DeviceManagementStatus::DM_STATUS_SERVICE_DEVICE_NOT_FOUND));
+  enrollment_helper_.SetupClearAuth();
+  enrollment_screen()->SetEnrollmentConfig(
+      CreateConfig(policy::EnrollmentConfig::MODE_ATTESTATION_SERVER_FORCED,
+                   policy::EnrollmentConfig::AUTH_MECHANISM_ATTESTATION));
+  enrollment_screen()->Show(&context);
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepError);
+
+  enrollment_helper_.VerifyAndClear();
+  enrollment_ui_.RetryAfterError();
+  enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepWorking);
+}
+
 struct EnrollmentErrorScreenTestParams {
   policy::EnrollmentConfig::Mode enrollment_mode;
   policy::EnrollmentConfig::AuthMechanism enrollment_auth_mechanism;
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen_view.h b/chrome/browser/ash/login/enrollment/enrollment_screen_view.h
index 932cf5b..63414866 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen_view.h
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen_view.h
@@ -89,6 +89,9 @@
   // Reloads the signin screen.
   virtual void ReloadSigninScreen() = 0;
 
+  // Resets shown enrollment screen.
+  virtual void ResetEnrollmentScreen() = 0;
+
   // Shows error related to user account eligibility.
   virtual void ShowUserError(const std::string& email) = 0;
 
diff --git a/chrome/browser/ash/login/enrollment/mock_enrollment_screen.h b/chrome/browser/ash/login/enrollment/mock_enrollment_screen.h
index 3bd3dabd..3e93a54a 100644
--- a/chrome/browser/ash/login/enrollment/mock_enrollment_screen.h
+++ b/chrome/browser/ash/login/enrollment/mock_enrollment_screen.h
@@ -48,6 +48,7 @@
   MOCK_METHOD(void, MockUnbind, ());
   MOCK_METHOD(void, ShowSigninScreen, ());
   MOCK_METHOD(void, ReloadSigninScreen, ());
+  MOCK_METHOD(void, ResetEnrollmentScreen, ());
   MOCK_METHOD(void, ShowUserError, (const std::string& email));
   MOCK_METHOD(void, ShowEnrollmentDuringTrialNotAllowedError, ());
   MOCK_METHOD(void, ShowSkipConfirmationDialog, ());
diff --git a/chrome/browser/ash/login/test/enrollment_helper_mixin.cc b/chrome/browser/ash/login/test/enrollment_helper_mixin.cc
index 729a1da7..65cf7529 100644
--- a/chrome/browser/ash/login/test/enrollment_helper_mixin.cc
+++ b/chrome/browser/ash/login/test/enrollment_helper_mixin.cc
@@ -62,6 +62,10 @@
   EnrollmentLauncher::SetEnrollmentHelperMock(std::move(mock));
 }
 
+void EnrollmentHelperMixin::VerifyAndClear() {
+  testing::Mock::VerifyAndClear(mock_);
+}
+
 void EnrollmentHelperMixin::ExpectNoEnrollment() {
   EXPECT_CALL(*mock_, Setup(_, _, _)).Times(0);
 }
diff --git a/chrome/browser/ash/login/test/enrollment_helper_mixin.h b/chrome/browser/ash/login/test/enrollment_helper_mixin.h
index b649224b..4fdd73d 100644
--- a/chrome/browser/ash/login/test/enrollment_helper_mixin.h
+++ b/chrome/browser/ash/login/test/enrollment_helper_mixin.h
@@ -36,9 +36,14 @@
 
   ~EnrollmentHelperMixin() override;
 
-  // Resets mock (to be used in tests that retry enrollment.
+  // Re-creates mock. Useful in tests that retry enrollment with different auth
+  // mechanism, which causes original mock to be destroyed by EnrollmentScreen.
   void ResetMock();
 
+  // Verifies mock expectations and clears them. Useful in tests that retry
+  // enrollment with the same auth mechanism.
+  void VerifyAndClear();
+
   // Sets up expectation of no enrollment attempt.
   void ExpectNoEnrollment();
 
diff --git a/chrome/browser/ash/login/test/enrollment_ui_mixin.cc b/chrome/browser/ash/login/test/enrollment_ui_mixin.cc
index f74d31c..4576d0a 100644
--- a/chrome/browser/ash/login/test/enrollment_ui_mixin.cc
+++ b/chrome/browser/ash/login/test/enrollment_ui_mixin.cc
@@ -103,6 +103,10 @@
 
 void EnrollmentUIMixin::RetryAfterError() {
   OobeJS().ClickOnPath(kEnrollmentErrorRetryButtonPath);
+}
+
+void EnrollmentUIMixin::RetryAndWaitForSigninStep() {
+  RetryAfterError();
   WaitForStep(ui::kEnrollmentStepSignin);
 }
 
diff --git a/chrome/browser/ash/login/test/enrollment_ui_mixin.h b/chrome/browser/ash/login/test/enrollment_ui_mixin.h
index c427385..fb21151a 100644
--- a/chrome/browser/ash/login/test/enrollment_ui_mixin.h
+++ b/chrome/browser/ash/login/test/enrollment_ui_mixin.h
@@ -63,6 +63,7 @@
 
   void ExpectErrorMessage(int error_message_id, bool can_retry);
   void RetryAfterError();
+  void RetryAndWaitForSigninStep();
   void CancelAfterError();
 
   // Fills out the UI with device attribute information and submits it.
diff --git a/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc b/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc
index 44b0707..61fc7d0 100644
--- a/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc
@@ -13,7 +13,9 @@
 #include "chrome/browser/ui/webui/ash/diagnostics_dialog.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chromeos/strings/grit/chromeos_strings.h"
 #include "third_party/blink/public/mojom/manifest/display_mode.mojom.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "url/gurl.h"
 
 std::unique_ptr<WebAppInstallInfo>
@@ -23,8 +25,7 @@
   info->start_url = GURL(ash::kChromeUIDiagnosticsAppUrl);
   info->scope = GURL(ash::kChromeUIDiagnosticsAppUrl);
 
-  // TODO(jimmyxgong): Update the title with finalized i18n copy.
-  info->title = u"Diagnostics";
+  info->title = l10n_util::GetStringUTF16(IDS_DIAGNOSTICS_TITLE);
   web_app::CreateIconInfoForSystemWebApp(
       info->start_url,
       {{"app_icon_192.png", 192, IDR_ASH_DIAGNOSTICS_APP_APP_ICON_192_PNG}},
diff --git a/chrome/browser/autofill/address_accessory_controller_impl.cc b/chrome/browser/autofill/address_accessory_controller_impl.cc
index 8dc18d22..bd3c0de 100644
--- a/chrome/browser/autofill/address_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/address_accessory_controller_impl.cc
@@ -10,7 +10,7 @@
 #include "base/ranges/algorithm.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
-#include "chrome/browser/android/preferences/autofill/autofill_profile_bridge.h"
+#include "chrome/browser/android/preferences/autofill/settings_launcher_helper.h"
 #include "chrome/browser/autofill/manual_filling_controller.h"
 #include "chrome/browser/autofill/manual_filling_utils.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
diff --git a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
index 745a587..d184fb9 100644
--- a/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
+++ b/chrome/browser/autofill/credit_card_accessory_controller_impl.cc
@@ -15,7 +15,7 @@
 #include "base/ranges/algorithm.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
-#include "chrome/browser/android/preferences/autofill/autofill_profile_bridge.h"
+#include "chrome/browser/android/preferences/autofill/settings_launcher_helper.h"
 #include "chrome/browser/autofill/manual_filling_controller.h"
 #include "chrome/browser/autofill/manual_filling_utils.h"
 #include "chrome/browser/autofill/personal_data_manager_factory.h"
diff --git a/chrome/browser/autofill/mock_autofill_popup_controller.h b/chrome/browser/autofill/mock_autofill_popup_controller.h
index 74c6bb85..c735d66 100644
--- a/chrome/browser/autofill/mock_autofill_popup_controller.h
+++ b/chrome/browser/autofill/mock_autofill_popup_controller.h
@@ -14,6 +14,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -85,7 +86,7 @@
   MOCK_METHOD(void, SelectSuggestion, (absl::optional<size_t>), (override));
   MOCK_METHOD(PopupType, GetPopupType, (), (const override));
 
-  void set_suggestions(const std::vector<Suggestion::FrontendId>& ids) {
+  void set_suggestions(const std::vector<PopupItemId>& ids) {
     for (const auto& id : ids) {
       // Accessibility requires all focusable AutofillPopupItemView to have
       // ui::AXNodeData with non-empty names. We specify dummy values and labels
diff --git a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
index 178e7e1..ef700c1 100644
--- a/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
+++ b/chrome/browser/banners/android/java/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java
@@ -1210,29 +1210,6 @@
     @Test
     @MediumTest
     @Feature({"AppBanners"})
-    @Features.EnableFeatures({ChromeFeatureList.INSTALLABLE_AMBIENT_BADGE_INFOBAR,
-            ChromeFeatureList.SKIP_SERVICE_WORKER_FOR_INSTALL_PROMPT})
-    public void
-    testAmbientBadgeDoesNotAppearWhenNoServiceWorker() throws Exception {
-        String webBannerUrl = WebappTestPage.getNonServiceWorkerUrlWithAction(
-                mTestServer, "call_stashed_prompt_on_click");
-        resetEngagementForUrl(webBannerUrl, 10);
-        navigateToUrlAndWaitForBannerManager(mTabbedActivityTestRule, webBannerUrl);
-
-        // As the page doesn't have service worker, we do not expect to
-        // see an ambient badge.
-        Tab tab = mTabbedActivityTestRule.getActivity().getActivityTab();
-        waitForBadgeStatus(tab, AmbientBadgeState.PENDING_WORKER);
-        checkAmbientBadgePromptNotExist(mTabbedActivityTestRule);
-
-        // Tap to trigger beforeinstallprompt.prompt, we expect to see the modal banner.
-        tapAndWaitForModalBanner(tab);
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"AppBanners"})
-    @Features.EnableFeatures({ChromeFeatureList.SKIP_SERVICE_WORKER_FOR_INSTALL_PROMPT})
     public void testAmbientBadgeAppearWithServiceWorkerPage() throws Exception {
         String webBannerUrl = WebappTestPage.getServiceWorkerUrlWithAction(
                 mTestServer, "call_stashed_prompt_on_click");
diff --git a/chrome/browser/banners/app_banner_manager_browsertest.cc b/chrome/browser/banners/app_banner_manager_browsertest.cc
index c97dfee6..be26474b 100644
--- a/chrome/browser/banners/app_banner_manager_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_browsertest.cc
@@ -101,10 +101,6 @@
     on_banner_prompt_reply_ = std::move(on_banner_prompt_reply);
   }
 
-  void SetWaitForServiceWorker(bool wait_for_worker) {
-    wait_for_worker_ = wait_for_worker;
-  }
-
   bool IsAppFullyInstalledForSiteUrl(const GURL& site_url) const override {
     return false;
   }
@@ -172,19 +168,6 @@
     }
   }
 
-  InstallableParams ParamsToPerformInstallableWebAppCheck() override {
-    InstallableParams params =
-        AppBannerManager::ParamsToPerformInstallableWebAppCheck();
-    params.wait_for_worker = wait_for_worker_;
-    return params;
-  }
-
-  InstallableParams ParamsToPerformWorkerCheck() override {
-    InstallableParams params = AppBannerManager::ParamsToPerformWorkerCheck();
-    params.wait_for_worker = wait_for_worker_;
-    return params;
-  }
-
   void OnBannerPromptReply(
       mojo::Remote<blink::mojom::AppBannerController> controller,
       blink::mojom::AppBannerPromptReply reply) override {
@@ -227,8 +210,6 @@
   std::unique_ptr<bool> banner_shown_;
   std::unique_ptr<WebappInstallSource> install_source_;
 
-  bool wait_for_worker_ = true;
-
   base::WeakPtrFactory<AppBannerManagerTest> weak_factory_{this};
 };
 
@@ -287,7 +268,6 @@
     EXPECT_TRUE(manager->state() == State::COMPLETE ||
                 manager->state() == State::PENDING_PROMPT_CANCELED ||
                 manager->state() == State::PENDING_PROMPT_NOT_CANCELED ||
-                manager->state() == State::PENDING_WORKER ||
                 manager->state() == State::INACTIVE);
 
     // If in incognito, ensure that nothing is recorded.
@@ -871,73 +851,6 @@
   std::unique_ptr<InstallableData> failure_data_;
 };
 
-class AppBannerManagerBrowserTestWithFailableInstallableManager
-    : public AppBannerManagerBrowserTest {
- public:
-  AppBannerManagerBrowserTestWithFailableInstallableManager() = default;
-  ~AppBannerManagerBrowserTestWithFailableInstallableManager() override =
-      default;
-
-  void SetUpOnMainThread() override {
-    // Manually inject the FailingInstallableManager as a "InstallableManager"
-    // WebContentsUserData. We can't directly call ::CreateForWebContents due to
-    // typing issues since FailingInstallableManager doesn't directly inherit
-    // from WebContentsUserData.
-    browser()->tab_strip_model()->GetActiveWebContents()->SetUserData(
-        FailingInstallableManager::UserDataKey(),
-        base::WrapUnique(new FailingInstallableManager(
-            browser()->tab_strip_model()->GetActiveWebContents())));
-    installable_manager_ = static_cast<FailingInstallableManager*>(
-        browser()->tab_strip_model()->GetActiveWebContents()->GetUserData(
-            FailingInstallableManager::UserDataKey()));
-
-    AppBannerManagerBrowserTest::SetUpOnMainThread();
-  }
-
- protected:
-  raw_ptr<FailingInstallableManager, DanglingUntriaged> installable_manager_ =
-      nullptr;
-};
-
-IN_PROC_BROWSER_TEST_F(
-    AppBannerManagerBrowserTestWithFailableInstallableManager,
-    AppBannerManagerRetriesPipeline) {
-  std::unique_ptr<AppBannerManagerTest> manager(
-      CreateAppBannerManager(browser()));
-
-  site_engagement::SiteEngagementService* service =
-      site_engagement::SiteEngagementService::Get(browser()->profile());
-  GURL test_url = GetBannerURLWithAction("stash_event");
-  service->ResetBaseScoreForURL(test_url, 10);
-
-  blink::mojom::Manifest manifest;
-  std::vector<Screenshot> screenshots;
-  installable_manager_->FailNext(base::WrapUnique(
-      new InstallableData({MANIFEST_URL_CHANGED}, GURL::EmptyGURL(), manifest,
-                          GURL::EmptyGURL(), nullptr, false, GURL::EmptyGURL(),
-                          nullptr, false, screenshots, false, false)));
-
-  // The page should record one failure of MANIFEST_URL_CHANGED, but it should
-  // still successfully get to the PENDING_PROMPT state of the pipeline, as it
-  // should retry the call to GetData on the InstallableManager.
-  RunBannerTest(browser(), manager.get(), test_url, MANIFEST_URL_CHANGED);
-  EXPECT_EQ(manager->state(),
-            AppBannerManager::State::PENDING_PROMPT_NOT_CANCELED);
-
-  {
-    base::HistogramTester histograms;
-    // Now let the page call prompt with a gesture. The banner should be shown.
-    TriggerBannerFlow(
-        browser(), manager.get(),
-        base::BindOnce(&AppBannerManagerBrowserTest::ExecuteScript, browser(),
-                       "callStashedPrompt();", true /* with_gesture */),
-        true /* expected_will_show */, State::COMPLETE);
-
-    histograms.ExpectUniqueSample(kInstallableStatusCodeHistogram,
-                                  SHOWING_WEB_APP_BANNER, 1);
-  }
-}
-
 class AppBannerManagerMPArchBrowserTest : public AppBannerManagerBrowserTest {
  public:
   AppBannerManagerMPArchBrowserTest() = default;
@@ -1063,36 +976,9 @@
   EXPECT_EQ(manager->state(), AppBannerManager::State::INACTIVE);
 }
 
-enum class ServiceWorkerCriteriaType {
-  kDisabled,
-  kSkipForInstalls,
-  kNoForBeforeInstalls
-};
-
-class AppBannerServiceWorkerCriteriaTest
-    : public AppBannerManagerBrowserTest,
-      public testing::WithParamInterface<ServiceWorkerCriteriaType> {
+class AppBannerServiceWorkerCriteriaTest : public AppBannerManagerBrowserTest {
  public:
-  AppBannerServiceWorkerCriteriaTest() {
-    switch (GetParam()) {
-      case ServiceWorkerCriteriaType::kDisabled:
-        scoped_feature_list_.InitWithFeatures(
-            {}, {features::kSkipServiceWorkerCheckInstallOnly,
-                 features::kSkipServiceWorkerForInstallPrompt});
-        break;
-      case ServiceWorkerCriteriaType::kSkipForInstalls:
-        scoped_feature_list_.InitWithFeatures(
-            {features::kSkipServiceWorkerCheckInstallOnly},
-            {features::kSkipServiceWorkerForInstallPrompt});
-        break;
-      case ServiceWorkerCriteriaType::kNoForBeforeInstalls:
-        scoped_feature_list_.InitWithFeatures(
-            {features::kSkipServiceWorkerCheckInstallOnly,
-             features::kSkipServiceWorkerForInstallPrompt},
-            {});
-        break;
-    }
-  }
+  AppBannerServiceWorkerCriteriaTest() = default;
   ~AppBannerServiceWorkerCriteriaTest() override = default;
 
   AppBannerServiceWorkerCriteriaTest(
@@ -1107,28 +993,12 @@
   void CheckInstallableResult(
       AppBannerManager::InstallableWebAppCheckResult result,
       AppBannerManager::InstallableWebAppCheckResult expected_control_result) {
-    switch (GetParam()) {
-      case ServiceWorkerCriteriaType::kDisabled:
-        EXPECT_EQ(result, expected_control_result);
-        break;
-      case ServiceWorkerCriteriaType::kSkipForInstalls:
-        EXPECT_EQ(
-            result,
-            AppBannerManager::InstallableWebAppCheckResult::kYes_ByUserRequest);
-        break;
-      case ServiceWorkerCriteriaType::kNoForBeforeInstalls:
-        EXPECT_EQ(
-            result,
-            AppBannerManager::InstallableWebAppCheckResult::kYes_Promotable);
-        break;
-    }
+    EXPECT_EQ(result,
+              AppBannerManager::InstallableWebAppCheckResult::kYes_Promotable);
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_P(AppBannerServiceWorkerCriteriaTest, ShowBanner) {
+IN_PROC_BROWSER_TEST_F(AppBannerServiceWorkerCriteriaTest, ShowBanner) {
   std::unique_ptr<AppBannerManagerTest> manager(
       CreateAppBannerManager(browser()));
   RunBannerTest(
@@ -1141,96 +1011,51 @@
             AppBannerManager::InstallableWebAppCheckResult::kYes_Promotable);
 }
 
-IN_PROC_BROWSER_TEST_P(AppBannerServiceWorkerCriteriaTest, NoServiceWorker) {
+IN_PROC_BROWSER_TEST_F(AppBannerServiceWorkerCriteriaTest, NoServiceWorker) {
   std::unique_ptr<AppBannerManagerTest> manager(
       CreateAppBannerManager(browser()));
-  // Set not wait for service worker so it will not timeout.
-  manager->SetWaitForServiceWorker(false);
-
-  absl::optional<InstallableStatusCode> expected_code;
-  if (GetParam() != ServiceWorkerCriteriaType::kNoForBeforeInstalls)
-    expected_code = NO_MATCHING_SERVICE_WORKER;
 
   RunBannerTest(browser(), manager.get(),
                 embedded_test_server()->GetURL(
                     "/banners/manifest_no_service_worker.html"),
-                expected_code);
+                /*expected_code_for_histogram=*/absl::nullopt);
 
-  if (GetParam() == ServiceWorkerCriteriaType::kNoForBeforeInstalls) {
-    EXPECT_EQ(manager->state(),
-              AppBannerManager::State::PENDING_PROMPT_NOT_CANCELED);
-  } else {
-    EXPECT_EQ(manager->state(), AppBannerManager::State::COMPLETE);
-  }
-
+  EXPECT_EQ(manager->state(),
+            AppBannerManager::State::PENDING_PROMPT_NOT_CANCELED);
   CheckInstallableResult(manager->GetInstallableWebAppCheckResultForTesting(),
                          AppBannerManager::InstallableWebAppCheckResult::kNo);
 }
 
-IN_PROC_BROWSER_TEST_P(AppBannerServiceWorkerCriteriaTest, NoFetchHandler) {
+IN_PROC_BROWSER_TEST_F(AppBannerServiceWorkerCriteriaTest, NoFetchHandler) {
   std::unique_ptr<AppBannerManagerTest> manager(
       CreateAppBannerManager(browser()));
 
-  absl::optional<InstallableStatusCode> expected_code;
-  if (GetParam() != ServiceWorkerCriteriaType::kNoForBeforeInstalls)
-    expected_code = NOT_OFFLINE_CAPABLE;
-
   RunBannerTest(browser(), manager.get(),
                 embedded_test_server()->GetURL(
                     "/banners/no_sw_fetch_handler_test_page.html"),
-                expected_code);
+                /*expected_code_for_histogram=*/absl::nullopt);
 
-  if (GetParam() == ServiceWorkerCriteriaType::kNoForBeforeInstalls) {
-    EXPECT_EQ(manager->state(),
-              AppBannerManager::State::PENDING_PROMPT_NOT_CANCELED);
-  } else {
-    EXPECT_EQ(manager->state(), AppBannerManager::State::COMPLETE);
-  }
+  EXPECT_EQ(manager->state(),
+            AppBannerManager::State::PENDING_PROMPT_NOT_CANCELED);
 
   CheckInstallableResult(manager->GetInstallableWebAppCheckResultForTesting(),
                          AppBannerManager::InstallableWebAppCheckResult::kNo);
 }
 
-class PendingWorkerAppBannerManager : public AppBannerManagerTest {
- public:
-  explicit PendingWorkerAppBannerManager(content::WebContents* web_contents)
-      : AppBannerManagerTest(web_contents) {}
-
-  PendingWorkerAppBannerManager(const PendingWorkerAppBannerManager&) = delete;
-  PendingWorkerAppBannerManager& operator=(
-      const PendingWorkerAppBannerManager&) = delete;
-
-  ~PendingWorkerAppBannerManager() override = default;
-
- protected:
-  void UpdateState(AppBannerManager::State state) override {
-    AppBannerManagerTest::UpdateState(state);
-    if (state == AppBannerManager::State::PENDING_WORKER) {
-      if (on_done_)
-        base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
-            FROM_HERE, std::move(on_done_));
-    }
-  }
-};
-
-IN_PROC_BROWSER_TEST_P(AppBannerServiceWorkerCriteriaTest,
+IN_PROC_BROWSER_TEST_F(AppBannerServiceWorkerCriteriaTest,
                        PendingServiceWorker) {
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  std::unique_ptr<PendingWorkerAppBannerManager> manager =
-      std::make_unique<PendingWorkerAppBannerManager>(web_contents);
+  std::unique_ptr<AppBannerManagerTest> manager =
+      std::make_unique<AppBannerManagerTest>(web_contents);
 
   RunBannerTest(browser(), manager.get(),
                 embedded_test_server()->GetURL(
                     "/banners/manifest_no_service_worker.html"),
                 absl::nullopt);
 
-  if (GetParam() == ServiceWorkerCriteriaType::kNoForBeforeInstalls) {
-    EXPECT_EQ(manager->state(),
-              AppBannerManager::State::PENDING_PROMPT_NOT_CANCELED);
-  } else {
-    EXPECT_EQ(manager->state(), AppBannerManager::State::PENDING_WORKER);
-  }
+  EXPECT_EQ(manager->state(),
+            AppBannerManager::State::PENDING_PROMPT_NOT_CANCELED);
 
   CheckInstallableResult(
       manager->GetInstallableWebAppCheckResultForTesting(),
@@ -1239,12 +1064,5 @@
   EXPECT_EQ(manager->GetAppName(), u"Manifest test app");
 }
 
-INSTANTIATE_TEST_SUITE_P(
-    All,
-    AppBannerServiceWorkerCriteriaTest,
-    testing::Values(ServiceWorkerCriteriaType::kDisabled,
-                    ServiceWorkerCriteriaType::kSkipForInstalls,
-                    ServiceWorkerCriteriaType::kNoForBeforeInstalls));
-
 }  // namespace
 }  // namespace webapps
diff --git a/chrome/browser/banners/test_app_banner_manager_desktop.cc b/chrome/browser/banners/test_app_banner_manager_desktop.cc
index de33e9d..f26e695 100644
--- a/chrome/browser/banners/test_app_banner_manager_desktop.cc
+++ b/chrome/browser/banners/test_app_banner_manager_desktop.cc
@@ -110,23 +110,6 @@
   SetInstallable(result.NoBlockingErrors());
 }
 
-void TestAppBannerManagerDesktop::PerformServiceWorkerCheck() {
-  waiting_for_worker_ = true;
-  AppBannerManagerDesktop::PerformServiceWorkerCheck();
-}
-
-void TestAppBannerManagerDesktop::OnDidPerformWorkerCheck(
-    const InstallableData& result) {
-  debug_log_.Append("OnDidPerformWorkerCheck");
-  AppBannerManagerDesktop::OnDidPerformWorkerCheck(result);
-
-  DCHECK(waiting_for_worker_);
-  waiting_for_worker_ = false;
-  if (promotable_quit_closure_) {
-    std::move(promotable_quit_closure_).Run();
-  }
-}
-
 void TestAppBannerManagerDesktop::ResetCurrentPageData() {
   debug_log_.Append("ResetCurrentPageData");
   AppBannerManagerDesktop::ResetCurrentPageData();
diff --git a/chrome/browser/banners/test_app_banner_manager_desktop.h b/chrome/browser/banners/test_app_banner_manager_desktop.h
index 79a487c..3e570911 100644
--- a/chrome/browser/banners/test_app_banner_manager_desktop.h
+++ b/chrome/browser/banners/test_app_banner_manager_desktop.h
@@ -55,8 +55,6 @@
   void OnDidGetManifest(const InstallableData& result) override;
   void OnDidPerformInstallableWebAppCheck(
       const InstallableData& result) override;
-  void PerformServiceWorkerCheck() override;
-  void OnDidPerformWorkerCheck(const InstallableData& result) override;
   void ResetCurrentPageData() override;
 
   // AppBannerManagerDesktop:
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoController.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoController.java
index c9f1181..25102194 100644
--- a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoController.java
+++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/PageInfoStoreInfoController.java
@@ -17,8 +17,6 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.commerce.core.ShoppingService.MerchantInfo;
 import org.chromium.components.page_info.PageInfoAction;
-import org.chromium.components.page_info.PageInfoDiscoverabilityMetrics;
-import org.chromium.components.page_info.PageInfoDiscoverabilityMetrics.DiscoverabilityAction;
 import org.chromium.components.page_info.PageInfoMainController;
 import org.chromium.components.page_info.PageInfoRowView;
 import org.chromium.components.page_info.PageInfoSubpageController;
@@ -42,8 +40,6 @@
     private final Context mContext;
     private final boolean mPageInfoOpenedFromStoreIcon;
     private final WebContents mWebContents;
-    private final PageInfoDiscoverabilityMetrics mDiscoverabilityMetrics =
-            new PageInfoDiscoverabilityMetrics();
     private final MerchantTrustMetrics mMetrics = new MerchantTrustMetrics();
 
     public PageInfoStoreInfoController(PageInfoMainController mainController,
@@ -86,10 +82,6 @@
                 rowParams.rowTint = R.color.iph_highlight_blue;
             }
             rowParams.clickCallback = () -> {
-                if (mPageInfoOpenedFromStoreIcon) {
-                    mDiscoverabilityMetrics.recordDiscoverabilityAction(
-                            DiscoverabilityAction.STORE_INFO_OPENED);
-                }
                 mMainController.recordAction(PageInfoAction.PAGE_INFO_STORE_INFO_CLICKED);
                 mMainController.dismiss();
                 mMetrics.recordUkmOnRowClicked(mWebContents);
@@ -133,4 +125,4 @@
 
     @Override
     public void updateRowIfNeeded() {}
-}
\ No newline at end of file
+}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 9c405cb..b57e8ba 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4737,7 +4737,7 @@
   {
       "name": "iph-price-notifications-while-browsing",
       "owners": [ "danieltwhite", "ajuma" ],
-      "expiry_milestone": 114
+      "expiry_milestone": 118
   },
   {
     "name": "isolate-origins",
@@ -7464,6 +7464,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "use-gpu-scheduler-dfs",
+    "owners": [ "vikassoni", "sunnyps" ],
+    "expiry_milestone": 125
+  },
+  {
     "name": "use-hdr-transfer-function",
     "owners": [ "mcasas", "chromeos-gfx@google.com" ],
     "expiry_milestone": 125
@@ -7832,6 +7837,11 @@
     "expiry_milestone": 111
   },
   {
+    "name": "whats-new-ios-m116",
+    "owners": [ "cheickcisse@google.com", "bling-flags@google.com" ],
+    "expiry_milestone": 122
+  },
+  {
     "name": "wifi-connect-mac-address-randomization",
     "owners": [ "jsiuda", "chromeos-wifi-team" ],
     "expiry_milestone": 130
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 00c7743..596bcff 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -986,18 +986,6 @@
     "Enable PWAs with screenshots to show a detailed install dialog during "
     "installation";
 
-const char kSkipServiceWorkerCheckInstallOnlyName[] =
-    "Skip service worker check for PWA installs";
-const char kSkipServiceWorkerCheckInstallOnlyDescription[] =
-    "Allows PWAs to be installed without a service worker.";
-
-const char kSkipServiceWorkerForInstallPromptName[] =
-    "Promote PWA installation without a service worker";
-const char kSkipServiceWorkerForInstallPromptDescription[] =
-    "Allows PWAs that can be installed without a service worker to be "
-    "promoted. Requires #enable-skip-service-worker-check-install-only flag to "
-    "also be enabled.";
-
 const char kEnablePreinstalledWebAppDuplicationFixerName[] =
     "Enable the app deduplication fix for migrated preinstalled web apps";
 const char kEnablePreinstalledWebAppDuplicationFixerDescription[] =
@@ -3534,6 +3522,11 @@
 const char kWebSQLAccessDescription[] =
     "The WebSQL API is enabled by default, but can be disabled here.";
 
+const char kUseGpuSchedulerDfsName[] = "Use new gpu scheduler.";
+const char kUseGpuSchedulerDfsDescription[] =
+    "Enables using the new gpu "
+    "scheduler called GpuSchedulerDfs.";
+
 const char kUseIDNA2008NonTransitionalName[] =
     "Enable IDNA 2008 Non-Transitional Mode";
 const char kUseIDNA2008NonTransitionalDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 7f160329..82a6ef66 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -564,12 +564,6 @@
 extern const char kDesktopPWAsDetailedInstallDialogName[];
 extern const char kDesktopPWAsDetailedInstallDialogDescription[];
 
-extern const char kSkipServiceWorkerCheckInstallOnlyName[];
-extern const char kSkipServiceWorkerCheckInstallOnlyDescription[];
-
-extern const char kSkipServiceWorkerForInstallPromptName[];
-extern const char kSkipServiceWorkerForInstallPromptDescription[];
-
 extern const char kDeviceForceScheduledRebootName[];
 extern const char kDeviceForceScheduledRebootDescription[];
 
@@ -2036,6 +2030,9 @@
 extern const char kWebSQLAccessName[];
 extern const char kWebSQLAccessDescription[];
 
+extern const char kUseGpuSchedulerDfsName[];
+extern const char kUseGpuSchedulerDfsDescription[];
+
 extern const char kUseIDNA2008NonTransitionalName[];
 extern const char kUseIDNA2008NonTransitionalDescription[];
 
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index 4ace63e0..e856e1e 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -401,8 +401,6 @@
     public static final String SHARED_HIGHLIGHTING_AMP = "SharedHighlightingAmp";
     public static final String SHOPPING_LIST = "ShoppingList";
     public static final String SHOW_SCROLLABLE_MVT_ON_NTP_ANDROID = "ShowScrollableMVTOnNTPAndroid";
-    public static final String SKIP_SERVICE_WORKER_FOR_INSTALL_PROMPT =
-            "SkipServiceWorkerForInstallPromot";
     public static final String SMART_SUGGESTION_FOR_LARGE_DOWNLOADS =
             "SmartSuggestionForLargeDownloads";
     public static final String SPARE_TAB = "SpareTab";
diff --git a/chrome/browser/mandatory_reauth/android/BUILD.gn b/chrome/browser/mandatory_reauth/android/BUILD.gn
new file mode 100644
index 0000000..ff1a46a
--- /dev/null
+++ b/chrome/browser/mandatory_reauth/android/BUILD.gn
@@ -0,0 +1,9 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+generate_jni("jni_headers") {
+  sources = [ "internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetViewBridge.java" ]
+}
diff --git a/chrome/browser/mandatory_reauth/android/internal/BUILD.gn b/chrome/browser/mandatory_reauth/android/internal/BUILD.gn
new file mode 100644
index 0000000..5aafefca
--- /dev/null
+++ b/chrome/browser/mandatory_reauth/android/internal/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2023 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/android/rules.gni")
+
+android_library("java") {
+  deps = [ "//base:jni_java" ]
+  sources = [ "java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetViewBridge.java" ]
+  resources_package = "org.chromium.chrome.browser.mandatory_reauth"
+}
diff --git a/chrome/browser/mandatory_reauth/android/internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetViewBridge.java b/chrome/browser/mandatory_reauth/android/internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetViewBridge.java
new file mode 100644
index 0000000..e20931f
--- /dev/null
+++ b/chrome/browser/mandatory_reauth/android/internal/java/src/org/chromium/chrome/browser/mandatory_reauth/MandatoryReauthOptInBottomSheetViewBridge.java
@@ -0,0 +1,37 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.mandatory_reauth;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Java bridge to delegate calls from native MandatoryReauthOptInViewAndroid. Facilitates creating
+ * the Mandatory Reauth opt-in bottom sheet.
+ */
+@JNINamespace("autofill")
+public class MandatoryReauthOptInBottomSheetViewBridge {
+    private MandatoryReauthOptInBottomSheetViewBridge() {}
+
+    /**
+     * Creates an instance of a {@link MandatoryReauthOptInBottomSheetViewBridge}.
+     */
+    @CalledByNative
+    private static MandatoryReauthOptInBottomSheetViewBridge create() {
+        return new MandatoryReauthOptInBottomSheetViewBridge();
+    }
+
+    /**
+     * Create and show the view.
+     */
+    @CalledByNative
+    private void show() {}
+
+    /**
+     * Lets the native controller dismiss the view.
+     */
+    @CalledByNative
+    private void close() {}
+}
diff --git a/chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.cc b/chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.cc
new file mode 100644
index 0000000..61d97c4
--- /dev/null
+++ b/chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.cc
@@ -0,0 +1,40 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.h"
+
+#include <jni.h>
+
+#include "chrome/browser/mandatory_reauth/android/jni_headers/MandatoryReauthOptInBottomSheetViewBridge_jni.h"
+
+namespace autofill {
+
+std::unique_ptr<MandatoryReauthOptInViewAndroid>
+MandatoryReauthOptInViewAndroid::CreateAndShow() {
+  std::unique_ptr<MandatoryReauthOptInViewAndroid> view =
+      std::make_unique<MandatoryReauthOptInViewAndroid>();
+  view->Show();
+  return view;
+}
+
+MandatoryReauthOptInViewAndroid::MandatoryReauthOptInViewAndroid() = default;
+
+MandatoryReauthOptInViewAndroid::~MandatoryReauthOptInViewAndroid() = default;
+
+void MandatoryReauthOptInViewAndroid::Show() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  java_bridge_.Reset(
+      Java_MandatoryReauthOptInBottomSheetViewBridge_create(env));
+  CHECK(java_bridge_);
+  Java_MandatoryReauthOptInBottomSheetViewBridge_show(env, java_bridge_);
+}
+
+void MandatoryReauthOptInViewAndroid::Hide() {
+  if (java_bridge_) {
+    Java_MandatoryReauthOptInBottomSheetViewBridge_close(
+        base::android::AttachCurrentThread(), java_bridge_);
+  }
+}
+
+}  // namespace autofill
diff --git a/chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.h b/chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.h
new file mode 100644
index 0000000..3cb4dcd
--- /dev/null
+++ b/chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.h
@@ -0,0 +1,39 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_MANDATORY_REAUTH_ANDROID_MANDATORY_REAUTH_OPT_IN_VIEW_ANDROID_H_
+#define CHROME_BROWSER_MANDATORY_REAUTH_ANDROID_MANDATORY_REAUTH_OPT_IN_VIEW_ANDROID_H_
+
+#include "base/android/scoped_java_ref.h"
+#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
+
+namespace autofill {
+
+// The native class responsible for managing the Android view to show the
+// Mandatory Reauth opt-in prompt.
+class MandatoryReauthOptInViewAndroid final : public AutofillBubbleBase {
+ public:
+  // Factory function for creating and showing the view.
+  static std::unique_ptr<MandatoryReauthOptInViewAndroid> CreateAndShow();
+
+  MandatoryReauthOptInViewAndroid();
+  ~MandatoryReauthOptInViewAndroid();
+  MandatoryReauthOptInViewAndroid(const MandatoryReauthOptInViewAndroid&) =
+      delete;
+  MandatoryReauthOptInViewAndroid& operator=(
+      const MandatoryReauthOptInViewAndroid&) = delete;
+
+  // AutofillBubbleBase:
+  void Hide() override;
+
+ private:
+  void Show();
+
+  // This class's corresponding java object.
+  base::android::ScopedJavaGlobalRef<jobject> java_bridge_;
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_MANDATORY_REAUTH_ANDROID_MANDATORY_REAUTH_OPT_IN_VIEW_ANDROID_H_
diff --git a/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc b/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc
index 00d6e7e..3093106 100644
--- a/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc
+++ b/chrome/browser/page_load_metrics/integration_tests/layout_instability_browsertest.cc
@@ -73,14 +73,14 @@
       expectations.Append(std::move(d));
   }
 
-  // It compares the trace data of layout shift events with |expectations| and
-  // computes a score that's used to check the UKM and UMA values below.
-  double final_score = CheckTraceData(expectations, *StopTracingAndAnalyze());
-
   waiter->Wait();
   // Finish session.
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("about:blank")));
 
+  // It compares the trace data of layout shift events with |expectations| and
+  // computes a score that's used to check the UKM and UMA values below.
+  double final_score = CheckTraceData(expectations, *StopTracingAndAnalyze());
+
   // We can only verify the layout shift metrics here in UKM and UMA if layout
   // shift only happens in the main frame. For layout shift happens in the
   // sub-frame, it needs to apply a sub-frame weighting factor.
@@ -280,24 +280,12 @@
   CheckUKMAndUMAMetricsWithValues(totalCls, cls);
 }
 
-// TODO(crbug.com/1407011): Flaky on linux.
-#if BUILDFLAG(IS_LINUX)
-#define MAYBE_Sources_Enclosure DISABLED_Sources_Enclosure
-#else
-#define MAYBE_Sources_Enclosure Sources_Enclosure
-#endif
-IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, MAYBE_Sources_Enclosure) {
+IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_Enclosure) {
   RunWPT("sources-enclosure.html", ShiftFrame::LayoutShiftOnlyInMainFrame,
          /*num_layout_shifts=*/2);
 }
 
-// TODO(crbug.com/1407011): Flaky on linux.
-#if BUILDFLAG(IS_LINUX)
-#define MAYBE_Sources_MaxImpact DISABLED_Sources_MaxImpact
-#else
-#define MAYBE_Sources_MaxImpact Sources_MaxImpact
-#endif
-IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, MAYBE_Sources_MaxImpact) {
+IN_PROC_BROWSER_TEST_F(LayoutInstabilityTest, Sources_MaxImpact) {
   RunWPT("sources-maximpact.html");
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc
new file mode 100644
index 0000000..18205882
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.cc
@@ -0,0 +1,133 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/time/time.h"
+#include "base/time/time_delta_from_string.h"
+
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/android/tab_model/tab_model.h"
+#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
+#else  // BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#endif  // BUILDFLAG(IS_ANDROID)
+
+#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+
+namespace internal {
+
+const char kTabsActiveAbsolutePosition[] = "Tabs.Active.AbsolutePosition";
+const char kTabsActiveRelativePosition[] = "Tabs.Active.RelativePosition";
+const char kTabsPageLoadTimeSinceActive[] = "Tabs.PageLoad.TimeSinceActive";
+const char kTabsPageLoadTimeSinceCreated[] = "Tabs.PageLoad.TimeSinceCreated";
+
+}  // namespace internal
+
+namespace {
+
+// TODO(crbug/1450633): Create an iterator abstraction that could be reused
+// in other places we need to iterate across tabs for both Android and desktop.
+std::vector<std::vector<content::WebContents*>> GetAllWebContents() {
+  std::vector<std::vector<content::WebContents*>> all_web_contents = {};
+#if BUILDFLAG(IS_ANDROID)
+  for (const TabModel* model : TabModelList::models()) {
+    std::vector<content::WebContents*> web_contents_for_tab_strip = {};
+    for (int i = 0; i < model->GetTabCount(); ++i) {
+#else   // BUILDFLAG(IS_ANDROID)
+  for (auto* browser : *BrowserList::GetInstance()) {
+    std::vector<content::WebContents*> web_contents_for_tab_strip = {};
+    TabStripModel* model = browser->tab_strip_model();
+    for (int i = 0; i < model->count(); ++i) {
+#endif  // BUILDFLAG(IS_ANDROID)
+      web_contents_for_tab_strip.push_back(model->GetWebContentsAt(i));
+    }
+    all_web_contents.push_back(web_contents_for_tab_strip);
+  }
+  return all_web_contents;
+}
+
+void RecordTimeDeltaHistogram(const char histogram_name[],
+                              base::TimeDelta value) {
+  base::UmaHistogramCustomTimes(histogram_name, value, base::TimeDelta(),
+                                base::Days(14), 50);
+}
+
+}  // namespace
+
+TabStripPageLoadMetricsObserver::TabStripPageLoadMetricsObserver(
+    content::WebContents* web_contents) {
+  web_contents_ = web_contents;
+}
+
+TabStripPageLoadMetricsObserver::~TabStripPageLoadMetricsObserver() = default;
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+TabStripPageLoadMetricsObserver::OnStart(
+    content::NavigationHandle* navigation_handle,
+    const GURL& currently_committed_url,
+    bool started_in_foreground) {
+  if (!started_in_foreground) {
+    return CONTINUE_OBSERVING;
+  }
+  base::TimeTicks now = base::TimeTicks::Now();
+  std::vector<std::vector<content::WebContents*>> all_web_contents =
+      GetAllWebContents();
+  for (std::vector<content::WebContents*> tab_strip_web_contents :
+       all_web_contents) {
+    for (content::WebContents* web_contents : tab_strip_web_contents) {
+      if (web_contents) {
+        base::TimeTicks last_active = web_contents->GetLastActiveTime();
+        page_load_metrics::MetricsWebContentsObserver*
+            metrics_web_contents_observer =
+                page_load_metrics::MetricsWebContentsObserver::FromWebContents(
+                    web_contents);
+        base::TimeTicks created = metrics_web_contents_observer->GetCreated();
+        RecordTimeDeltaHistogram(internal::kTabsPageLoadTimeSinceActive,
+                                 now - last_active);
+        RecordTimeDeltaHistogram(internal::kTabsPageLoadTimeSinceCreated,
+                                 now - created);
+      }
+    }
+  }
+  return CONTINUE_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+TabStripPageLoadMetricsObserver::OnPrerenderStart(
+    content::NavigationHandle* navigation_handle,
+    const GURL& currently_committed_url) {
+  return CONTINUE_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+TabStripPageLoadMetricsObserver::OnFencedFramesStart(
+    content::NavigationHandle* navigation_handle,
+    const GURL& currently_committed_url) {
+  return STOP_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+TabStripPageLoadMetricsObserver::OnShown() {
+  std::vector<std::vector<content::WebContents*>> all_web_contents =
+      GetAllWebContents();
+  for (std::vector<content::WebContents*> tab_strip_web_contents :
+       all_web_contents) {
+    const int count = tab_strip_web_contents.size();
+    for (int i = 0; i < count; i++) {
+      if (tab_strip_web_contents.at(i) == web_contents_.get()) {
+        const int absolute_sequence = i + 1;
+        base::UmaHistogramCounts1000(internal::kTabsActiveAbsolutePosition,
+                                     absolute_sequence);
+        base::UmaHistogramPercentage(internal::kTabsActiveRelativePosition,
+                                     absolute_sequence * 100 / count);
+        break;
+      }
+    }
+  }
+  return CONTINUE_OBSERVING;
+}
diff --git a/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.h
new file mode 100644
index 0000000..23ef02d6
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.h
@@ -0,0 +1,49 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_TAB_STRIP_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_TAB_STRIP_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+
+namespace internal {
+
+extern const char kTabsActiveAbsolutePosition[];
+extern const char kTabsActiveRelativePosition[];
+extern const char kTabsPageLoadTimeSinceActive[];
+extern const char kTabsPageLoadTimeSinceCreated[];
+
+}  // namespace internal
+
+// Observer responsible for notifying the tab strip of the status of a page
+// load. This information is used to log UMA metrics at a page load level.
+class TabStripPageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver {
+ public:
+  explicit TabStripPageLoadMetricsObserver(content::WebContents* web_contents);
+  ~TabStripPageLoadMetricsObserver() override;
+
+  TabStripPageLoadMetricsObserver(const TabStripPageLoadMetricsObserver&) =
+      delete;
+  TabStripPageLoadMetricsObserver& operator=(
+      const TabStripPageLoadMetricsObserver&) = delete;
+
+  // page_load_metrics::PageLoadMetricsObserver
+  ObservePolicy OnStart(content::NavigationHandle* navigation_handle,
+                        const GURL& currently_committed_url,
+                        bool started_in_foreground) override;
+  ObservePolicy OnPrerenderStart(content::NavigationHandle* navigation_handle,
+                                 const GURL& currently_committed_url) override;
+  ObservePolicy OnFencedFramesStart(
+      content::NavigationHandle* navigation_handle,
+      const GURL& currently_committed_url) override;
+  ObservePolicy OnShown() override;
+
+ private:
+  raw_ptr<content::WebContents> web_contents_ = nullptr;
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_TAB_STRIP_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer_browsertest.cc
new file mode 100644
index 0000000..df76c5e
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer_browsertest.cc
@@ -0,0 +1,79 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.h"
+
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "content/public/test/browser_test.h"
+#include "ui/base/window_open_disposition.h"
+
+class TabStripPageLoadMetricsObserverTest : public InProcessBrowserTest {
+ protected:
+  void StartHttpsServer(net::EmbeddedTestServer::ServerCertificate cert) {
+    https_test_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    https_test_server_->SetSSLConfig(cert);
+    https_test_server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
+    ASSERT_TRUE(https_test_server_->Start());
+  }
+
+  net::EmbeddedTestServer* https_test_server() {
+    return https_test_server_.get();
+  }
+
+ private:
+  std::unique_ptr<net::EmbeddedTestServer> https_test_server_;
+};
+
+IN_PROC_BROWSER_TEST_F(TabStripPageLoadMetricsObserverTest,
+                       RecordPageLoadMetrics) {
+  base::HistogramTester histogram_tester;
+  StartHttpsServer(net::EmbeddedTestServer::CERT_OK);
+  GURL url = https_test_server()->GetURL("/simple.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  histogram_tester.ExpectTotalCount(internal::kTabsPageLoadTimeSinceActive, 2);
+  histogram_tester.ExpectTotalCount(internal::kTabsPageLoadTimeSinceCreated, 2);
+}
+
+IN_PROC_BROWSER_TEST_F(TabStripPageLoadMetricsObserverTest,
+                       RecordActiveMetrics) {
+  base::HistogramTester histogram_tester;
+  StartHttpsServer(net::EmbeddedTestServer::CERT_OK);
+  GURL url = https_test_server()->GetURL("/simple.html");
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), url, WindowOpenDisposition::NEW_BACKGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+
+  browser()->tab_strip_model()->ActivateTabAt(
+      2, TabStripUserGestureDetails(
+             TabStripUserGestureDetails::GestureType::kOther));
+
+  histogram_tester.ExpectUniqueSample(internal::kTabsActiveAbsolutePosition, 3,
+                                      1);
+  histogram_tester.ExpectUniqueSample(internal::kTabsActiveRelativePosition, 60,
+                                      1);
+}
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index acba34ae..7a189cb 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/page_load_metrics/observers/security_state_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/translate_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_memory_tracker_factory.h"
@@ -172,6 +173,8 @@
       tracker->AddObserver(std::move(loading_predictor_observer));
     tracker->AddObserver(
         std::make_unique<LocalNetworkRequestsPageLoadMetricsObserver>());
+    tracker->AddObserver(
+        std::make_unique<TabStripPageLoadMetricsObserver>(web_contents()));
   }
   tracker->AddObserver(
       std::make_unique<OmniboxSuggestionUsedMetricsObserver>());
diff --git a/chrome/browser/password_manager/android/password_generation_controller_impl.cc b/chrome/browser/password_manager/android/password_generation_controller_impl.cc
index 26143f9b..589bd12 100644
--- a/chrome/browser/password_manager/android/password_generation_controller_impl.cc
+++ b/chrome/browser/password_manager/android/password_generation_controller_impl.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/autofill/manual_filling_controller.h"
 #include "chrome/browser/password_manager/android/password_accessory_controller.h"
 #include "chrome/browser/password_manager/android/password_generation_dialog_view_interface.h"
+#include "chrome/browser/password_manager/android/password_infobar_utils.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.h"
 #include "chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.h"
@@ -305,7 +306,16 @@
 
   touch_to_fill_generation_controller_ =
       create_touch_to_fill_generation_controller_.Run();
-  if (!touch_to_fill_generation_controller_->ShowTouchToFill()) {
+  std::u16string generated_password =
+      active_frame_driver_->GetPasswordGenerationHelper()->GeneratePassword(
+          GetWebContents().GetLastCommittedURL().DeprecatedGetOriginAsURL(),
+          generation_element_data_->form_signature,
+          generation_element_data_->field_signature,
+          generation_element_data_->max_password_length);
+  std::string account =
+      password_manager::GetDisplayableAccountName(&GetWebContents());
+  if (!touch_to_fill_generation_controller_->ShowTouchToFill(
+          std::move(generated_password), std::move(account))) {
     return false;
   }
 
diff --git a/chrome/browser/password_manager/android/password_infobar_utils.cc b/chrome/browser/password_manager/android/password_infobar_utils.cc
index e3245b8..b0e3c8a 100644
--- a/chrome/browser/password_manager/android/password_infobar_utils.cc
+++ b/chrome/browser/password_manager/android/password_infobar_utils.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/password_manager/android/password_infobar_utils.h"
 
+#include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
@@ -11,6 +12,7 @@
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "content/public/browser/web_contents.h"
 
 namespace password_manager {
 
@@ -28,4 +30,21 @@
   return identity_manager->FindExtendedAccountInfoByAccountId(account_id);
 }
 
+std::string GetDisplayableAccountName(content::WebContents* web_contents) {
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  absl::optional<AccountInfo> account_info =
+      password_manager::GetAccountInfoForPasswordMessages(profile);
+  if (!account_info.has_value()) {
+    return "";
+  }
+  if (!base::FeatureList::IsEnabled(
+          chrome::android::kHideNonDisplayableAccountEmail)) {
+    return account_info.value().email;
+  }
+  return account_info->CanHaveEmailAddressDisplayed()
+             ? account_info.value().email
+             : account_info.value().full_name;
+}
+
 }  // namespace password_manager
diff --git a/chrome/browser/password_manager/android/password_infobar_utils.h b/chrome/browser/password_manager/android/password_infobar_utils.h
index 0b5a182c..6b723444 100644
--- a/chrome/browser/password_manager/android/password_infobar_utils.h
+++ b/chrome/browser/password_manager/android/password_infobar_utils.h
@@ -6,13 +6,19 @@
 #define CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_PASSWORD_INFOBAR_UTILS_H_
 
 #include "components/signin/public/identity_manager/account_info.h"
+#include "content/public/browser/web_contents.h"
 
 class Profile;
+namespace content {
+class WebContents;
+}
 
 namespace password_manager {
 
 AccountInfo GetAccountInfoForPasswordMessages(Profile* profile);
 
+std::string GetDisplayableAccountName(content::WebContents* web_contents);
+
 }  // namespace password_manager
 
 #endif  // CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_PASSWORD_INFOBAR_UTILS_H_
diff --git a/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediator.java b/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediator.java
index b524ad4..c730b13e 100644
--- a/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediator.java
+++ b/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediator.java
@@ -37,7 +37,7 @@
 
     @Override
     public void onAcknowledge(BottomSheetController bottomSheetController) {
-        bottomSheetController.collapseSheet(true);
+        mModel.set(VISIBLE, false);
     }
 
     @Override
@@ -53,6 +53,6 @@
 
     @Override
     public void onCancel(BottomSheetController bottomSheetController) {
-        bottomSheetController.collapseSheet(true);
+        mModel.set(VISIBLE, false);
     }
 }
diff --git a/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediatorTest.java b/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediatorTest.java
index e3aaefd..97d409d 100644
--- a/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediatorTest.java
+++ b/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningMediatorTest.java
@@ -8,7 +8,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verify;
 
 import static org.chromium.chrome.browser.pwd_migration.PasswordMigrationWarningProperties.CURRENT_SCREEN;
 import static org.chromium.chrome.browser.pwd_migration.PasswordMigrationWarningProperties.DISMISS_HANDLER;
@@ -90,14 +89,14 @@
     }
 
     @Test
-    public void testOnAcknowledgeCollapsesTheSheet() {
+    public void testOnAcknowledgeHidesTheSheet() {
         mMediator.onAcknowledge(mBottomSheetController);
-        verify(mBottomSheetController).collapseSheet(true);
+        assertFalse(mModel.get(VISIBLE));
     }
 
     @Test
-    public void testOnCancelCollapsesTheSheet() {
+    public void testOnCancelHidesTheSheet() {
         mMediator.onCancel(mBottomSheetController);
-        verify(mBottomSheetController).collapseSheet(true);
+        assertFalse(mModel.get(VISIBLE));
     }
 }
diff --git a/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningViewTest.java b/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningViewTest.java
index 3b1f1179d5..3b171cf 100644
--- a/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningViewTest.java
+++ b/chrome/browser/password_manager/android/pwd_migration/java/src/org/chromium/chrome/browser/pwd_migration/PasswordMigrationWarningViewTest.java
@@ -22,6 +22,7 @@
 import androidx.test.filters.MediumTest;
 
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,12 +45,17 @@
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.test.util.DisableAnimationsTestRule;
 
 /** Tests for {@link PasswordMigrationWarningView} */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @Batch(Batch.PER_CLASS)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class PasswordMigrationWarningViewTest {
+    @ClassRule
+    public static DisableAnimationsTestRule sDisableAnimationsRule =
+            new DisableAnimationsTestRule();
+
     @Rule
     public final MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
     @Rule
diff --git a/chrome/browser/password_manager/android/save_update_password_message_delegate.cc b/chrome/browser/password_manager/android/save_update_password_message_delegate.cc
index c739ba5..88a399a3 100644
--- a/chrome/browser/password_manager/android/save_update_password_message_delegate.cc
+++ b/chrome/browser/password_manager/android/save_update_password_message_delegate.cc
@@ -93,6 +93,18 @@
   return string_version == 2 || string_version == 3;
 }
 
+void TryToShowPasswordMigrationWarning(
+    base::RepeatingCallback<void(gfx::NativeWindow)> callback,
+    raw_ptr<content::WebContents> web_contents) {
+  if (base::FeatureList::IsEnabled(
+          password_manager::features::
+              kUnifiedPasswordManagerLocalPasswordsMigrationWarning)) {
+    // TODO(crbug.com/1439853): Check if the bottom sheet was shown a month ago
+    // or more.
+    callback.Run(web_contents->GetTopLevelNativeWindow());
+  }
+}
+
 }  // namespace
 
 SaveUpdatePasswordMessageDelegate::SaveUpdatePasswordMessageDelegate()
@@ -415,15 +427,6 @@
 
 void SaveUpdatePasswordMessageDelegate::HandleSaveButtonClicked() {
   passwords_state_.form_manager()->Save();
-
-  if (base::FeatureList::IsEnabled(
-          password_manager::features::
-              kUnifiedPasswordManagerLocalPasswordsMigrationWarning)) {
-    // TODO(crbug.com/439853): Check if the bottom sheet was shown a month ago
-    // or more.
-    create_migration_warning_callback_.Run(
-        web_contents_->GetTopLevelNativeWindow());
-  }
 }
 
 void SaveUpdatePasswordMessageDelegate::HandleNeverSaveClicked() {
@@ -482,6 +485,9 @@
     RecordSaveUpdateUIDismissalReason(
         GetSaveUpdatePasswordMessageDismissReason(dismiss_reason));
   }
+
+  TryToShowPasswordMigrationWarning(create_migration_warning_callback_,
+                                    web_contents_);
   ClearState();
 }
 
@@ -521,6 +527,9 @@
         GetPasswordEditDialogDismissReason(dialog_accepted));
   }
 
+  TryToShowPasswordMigrationWarning(create_migration_warning_callback_,
+                                    web_contents_);
+
   password_edit_dialog_.reset();
   ClearState();
 }
diff --git a/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc b/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc
index bbe044f..9f9ddcf 100644
--- a/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc
+++ b/chrome/browser/password_manager/android/save_update_password_message_delegate_unittest.cc
@@ -51,6 +51,7 @@
 using password_manager::PasswordFormMetricsRecorder;
 using ::testing::_;
 using ::testing::ElementsAre;
+using ::testing::Eq;
 using ::testing::Return;
 using ::testing::ReturnRef;
 
@@ -609,8 +610,8 @@
       password_manager::metrics_util::CLICKED_ACCEPT, 1);
 }
 
-// Tests that local password migration warning will show when the user clicks
-// "Save" button.
+// Tests that the local password migration warning will show when the user
+// clicks the "Save" button.
 TEST_F(SaveUpdatePasswordMessageDelegateTest,
        TriggerLocalPasswordMigrationWarning_OnSaveClicked) {
   base::test::ScopedFeatureList scoped_feature_state;
@@ -628,6 +629,146 @@
   EXPECT_EQ(nullptr, GetMessageWrapper());
 }
 
+TEST_F(SaveUpdatePasswordMessageDelegateTest,
+       TriggerLocalPasswordMigrationWarning_OnSavePasswordDialogAccepted) {
+  base::test::ScopedFeatureList scoped_feature_state;
+  scoped_feature_state.InitAndEnableFeature(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  MockPasswordFormManagerForUI* form_manager_pointer = form_manager.get();
+  MockPasswordEditDialog* mock_dialog = PreparePasswordEditDialog();
+
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/false,
+                 /*update_password=*/false);
+  EXPECT_NE(nullptr, GetMessageWrapper());
+  EXPECT_CALL(*mock_dialog, ShowPasswordEditDialog);
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_)).Times(0);
+  TriggerPasswordEditDialog(/*update_password=*/false);
+
+  EXPECT_EQ(nullptr, GetMessageWrapper());
+  EXPECT_CALL(*form_manager_pointer, Save());
+  TriggerDialogAcceptedCallback(/*username=*/kUsername,
+                                /*password=*/kPassword);
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_));
+  TriggerDialogDismissedCallback(/*dialog_accepted=*/true);
+}
+
+// Tests that the local password migration warning will show when the user
+// dismisses the save password message.
+TEST_F(SaveUpdatePasswordMessageDelegateTest,
+       TriggerLocalPasswordMigrationWarning_OnSaveMessageDismissed) {
+  base::test::ScopedFeatureList scoped_feature_state;
+  scoped_feature_state.InitAndEnableFeature(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), empty_best_matches());
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/false);
+  EXPECT_NE(nullptr, GetMessageWrapper());
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_));
+  DismissMessage(messages::DismissReason::GESTURE);
+  EXPECT_EQ(nullptr, GetMessageWrapper());
+}
+
+// Tests that the local password migration warning will show when the user
+// accepts the update password message in case when there is no confirmation
+// dialog.
+TEST_F(SaveUpdatePasswordMessageDelegateTest,
+       TriggerLocalPasswordMigrationWarning_OnUpdatePasswordWithSingleForm) {
+  base::test::ScopedFeatureList scoped_feature_state;
+  scoped_feature_state.InitAndEnableFeature(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  SetPendingCredentials(kUsername, kPassword);
+  PasswordForm password_form = CreatePasswordForm(kUsername, kPassword);
+  std::vector<const PasswordForm*> single_form_best_matches = {&password_form};
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), &single_form_best_matches);
+  EXPECT_CALL(*form_manager, Save());
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/true);
+  EXPECT_NE(nullptr, GetMessageWrapper());
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_));
+  TriggerActionClick();
+  EXPECT_EQ(nullptr, GetMessageWrapper());
+}
+
+// Tests that the local password migration warning will show when the user
+// dismisses the update password message.
+TEST_F(SaveUpdatePasswordMessageDelegateTest,
+       TriggerLocalPasswordMigrationWarning_OnUpdatePasswordMessageDismissed) {
+  base::test::ScopedFeatureList scoped_feature_state;
+  scoped_feature_state.InitAndEnableFeature(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  SetPendingCredentials(kUsername, kPassword);
+  PasswordForm password_form = CreatePasswordForm(kUsername, kPassword);
+  std::vector<const PasswordForm*> single_form_best_matches = {&password_form};
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), &single_form_best_matches);
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/true);
+  EXPECT_NE(nullptr, GetMessageWrapper());
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_));
+  DismissMessage(messages::DismissReason::GESTURE);
+  EXPECT_EQ(nullptr, GetMessageWrapper());
+}
+
+// Tests that the local password migration warning will show when the user
+// accepts the update password message and the confirmation dialog.
+TEST_F(SaveUpdatePasswordMessageDelegateTest,
+       TriggerLocalPasswordMigrationWarning_OnUpdatePasswordDialogAccepted) {
+  base::test::ScopedFeatureList scoped_feature_state;
+  scoped_feature_state.InitAndEnableFeature(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  SetPendingCredentials(kUsername, kPassword);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), two_forms_best_matches());
+  MockPasswordEditDialog* mock_dialog = PreparePasswordEditDialog();
+  EXPECT_CALL(
+      *mock_dialog,
+      ShowPasswordEditDialog(
+          ElementsAre(std::u16string(kUsername), std::u16string(kUsername2)),
+          Eq(kUsername), Eq(kPassword), Eq(kAccountEmail)));
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/true);
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_)).Times(0);
+  TriggerActionClick();
+  TriggerDialogAcceptedCallback(/*username=*/kUsername,
+                                /*password=*/kPassword);
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_));
+  TriggerDialogDismissedCallback(/*dialog_accepted=*/true);
+}
+
+// Tests that the local password migration warning will show when the user
+// accepts the update password message and cancels the confirmation dialog.
+TEST_F(SaveUpdatePasswordMessageDelegateTest,
+       TriggerLocalPasswordMigrationWarning_OnUpdatePasswordDialogCanceled) {
+  base::test::ScopedFeatureList scoped_feature_state;
+  scoped_feature_state.InitAndEnableFeature(
+      password_manager::features::
+          kUnifiedPasswordManagerLocalPasswordsMigrationWarning);
+  SetPendingCredentials(kUsername, kPassword);
+  auto form_manager =
+      CreateFormManager(GURL(kDefaultUrl), two_forms_best_matches());
+  MockPasswordEditDialog* mock_dialog = PreparePasswordEditDialog();
+  EXPECT_CALL(
+      *mock_dialog,
+      ShowPasswordEditDialog(
+          ElementsAre(std::u16string(kUsername), std::u16string(kUsername2)),
+          Eq(kUsername), Eq(kPassword), Eq(kAccountEmail)));
+  EnqueueMessage(std::move(form_manager), /*user_signed_in=*/true,
+                 /*update_password=*/true);
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_)).Times(0);
+  TriggerActionClick();
+  EXPECT_CALL(GetMigrationWarningCallback(), Run(_));
+  TriggerDialogDismissedCallback(/*dialog_accepted=*/false);
+}
+
 // Tests that password form is not saved and metrics recorded correctly when the
 // user dismisses the message.
 TEST_F(SaveUpdatePasswordMessageDelegateTest, DontSaveOnDismiss) {
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index ef3a17f..801df69 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -417,8 +417,10 @@
   }
   auto* webauthn_delegate = GetWebAuthnCredentialsDelegateForDriver(driver);
   std::vector<password_manager::PasskeyCredential> passkeys;
+  bool should_show_hybrid_option = false;
   if (webauthn_delegate && webauthn_delegate->GetPasskeys().has_value()) {
     passkeys = *webauthn_delegate->GetPasskeys();
+    should_show_hybrid_option = webauthn_delegate->IsAndroidHybridAvailable();
   }
   GetOrCreateTouchToFillController()->Show(
       credential_cache_
@@ -428,7 +430,9 @@
       passkeys,
       std::make_unique<TouchToFillControllerAutofillDelegate>(
           this, GetDeviceAuthenticator(), driver->AsWeakPtr(),
-          submission_readiness));
+          submission_readiness,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(
+              should_show_hybrid_option)));
 }
 #endif
 
diff --git a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc
index 58efe9f..5a50cb0 100644
--- a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc
+++ b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.cc
@@ -98,3 +98,21 @@
     std::move(retrieve_passkeys_callback_).Run();
   }
 }
+
+#if BUILDFLAG(IS_ANDROID)
+void ChromeWebAuthnCredentialsDelegate::ShowAndroidHybridSignIn() {
+  if (WebAuthnRequestDelegateAndroid* delegate =
+          WebAuthnRequestDelegateAndroid::GetRequestDelegate(web_contents_)) {
+    delegate->ShowHybridSignIn();
+  }
+}
+
+bool ChromeWebAuthnCredentialsDelegate::IsAndroidHybridAvailable() const {
+  return android_hybrid_available_.value();
+}
+
+void ChromeWebAuthnCredentialsDelegate::SetAndroidHybridAvailable(
+    AndroidHybridAvailable available) {
+  android_hybrid_available_ = available;
+}
+#endif
diff --git a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h
index fad1650..a100a46 100644
--- a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h
+++ b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate.h
@@ -8,6 +8,8 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/types/strong_alias.h"
+#include "build/build_config.h"
 #include "components/password_manager/core/browser/passkey_credential.h"
 #include "components/password_manager/core/browser/webauthn_credentials_delegate.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -20,6 +22,9 @@
 class ChromeWebAuthnCredentialsDelegate
     : public password_manager::WebAuthnCredentialsDelegate {
  public:
+  using AndroidHybridAvailable =
+      base::StrongAlias<struct AndroidHybridAvailableTag, bool>;
+
   explicit ChromeWebAuthnCredentialsDelegate(
       content::WebContents* web_contents);
   ~ChromeWebAuthnCredentialsDelegate() override;
@@ -45,6 +50,16 @@
   // WebAuthn options should no longer show up on the autofill popup.
   void NotifyWebAuthnRequestAborted();
 
+#if BUILDFLAG(IS_ANDROID)
+  // password_manager::WebAuthnCredentialsDelegate:
+  void ShowAndroidHybridSignIn() override;
+  bool IsAndroidHybridAvailable() const override;
+
+  // Sets the hybrid availability flag, which can be queried through
+  // `IsAndroidHybridAvailable()`.
+  void SetAndroidHybridAvailable(AndroidHybridAvailable available);
+#endif
+
  protected:
   const raw_ptr<content::WebContents> web_contents_;
 
@@ -57,6 +72,11 @@
 
   base::OnceClosure retrieve_passkeys_callback_;
 
+#if BUILDFLAG(IS_ANDROID)
+  AndroidHybridAvailable android_hybrid_available_ =
+      AndroidHybridAvailable(false);
+#endif
+
   base::WeakPtrFactory<ChromeWebAuthnCredentialsDelegate> weak_ptr_factory_{
       this};
 };
diff --git a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate_unittest.cc b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate_unittest.cc
index 76448c7..d72a9635 100644
--- a/chrome/browser/password_manager/chrome_webauthn_credentials_delegate_unittest.cc
+++ b/chrome/browser/password_manager/chrome_webauthn_credentials_delegate_unittest.cc
@@ -239,3 +239,12 @@
   EXPECT_TRUE(callback.was_called());
   EXPECT_FALSE(credentials_delegate_->GetPasskeys());
 }
+
+#if BUILDFLAG(IS_ANDROID)
+TEST_F(ChromeWebAuthnCredentialsDelegateTest, AndroidHybridAvailability) {
+  EXPECT_FALSE(credentials_delegate_->IsAndroidHybridAvailable());
+  credentials_delegate_->SetAndroidHybridAvailable(
+      ChromeWebAuthnCredentialsDelegate::AndroidHybridAvailable(true));
+  EXPECT_TRUE(credentials_delegate_->IsAndroidHybridAvailable());
+}
+#endif  // BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeRestrictedV4.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeRestrictedV4.java
index 0d3ca427..6ae54df 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeRestrictedV4.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeRestrictedV4.java
@@ -64,7 +64,7 @@
 
     @Override
     public void show() {
-        PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_SHOWN);
+        PrivacySandboxBridge.promptActionOccurred(PromptAction.RESTRICTED_NOTICE_SHOWN);
         super.show();
     }
 
@@ -80,7 +80,8 @@
             dismiss();
             mSettingsLauncher.launchSettingsActivity(getContext(), AdMeasurementFragmentV4.class);
         } else if (id == R.id.more_button) {
-            PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_MORE_BUTTON_CLICKED);
+            PrivacySandboxBridge.promptActionOccurred(
+                    PromptAction.RESTRICTED_NOTICE_MORE_BUTTON_CLICKED);
             if (mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
                 mScrollView.post(() -> { mScrollView.pageScroll(ScrollView.FOCUS_DOWN); });
             } else {
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
index 54e3b78..09d2db8 100644
--- a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
+++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
@@ -159,10 +159,13 @@
                     assertEquals("Last dialog action", PromptAction.CONSENT_MORE_BUTTON_CLICKED,
                             (int) mFakePrivacySandboxBridge.getLastPromptAction());
                 } else if (promptType == PromptType.M1_NOTICE_EEA
-                        || promptType == PromptType.M1_NOTICE_ROW
-                        || promptType == PromptType.M1_NOTICE_RESTRICTED) {
+                        || promptType == PromptType.M1_NOTICE_ROW) {
                     assertEquals("Last dialog action", PromptAction.NOTICE_MORE_BUTTON_CLICKED,
                             (int) mFakePrivacySandboxBridge.getLastPromptAction());
+                } else if (promptType == PromptType.M1_NOTICE_RESTRICTED) {
+                    assertEquals("Last dialog action",
+                            PromptAction.RESTRICTED_NOTICE_MORE_BUTTON_CLICKED,
+                            (int) mFakePrivacySandboxBridge.getLastPromptAction());
                 }
             } catch (PerformException e) {
                 return;
@@ -530,7 +533,7 @@
         launchDialog();
         // Verify that the restricted notice is shown
         onViewWaiting(withId(R.id.privacy_sandbox_notice_title));
-        assertEquals("Last dialog action", PromptAction.NOTICE_SHOWN,
+        assertEquals("Last dialog action", PromptAction.RESTRICTED_NOTICE_SHOWN,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
         // Ack the notice and verify it worked correctly.
         tryClickOn(withId(R.id.ack_button));
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
index 70f278a..7f4d7138 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
@@ -1745,6 +1745,21 @@
           "Settings.PrivacySandbox.RestrictedNotice.OpenedSettings"));
       break;
     }
+    case (PromptAction::kRestrictedNoticeShown): {
+      base::RecordAction(base::UserMetricsAction(
+          "Settings.PrivacySandbox.RestrictedNotice.Shown"));
+      break;
+    }
+    case (PromptAction::kRestrictedNoticeClosedNoInteraction): {
+      base::RecordAction(base::UserMetricsAction(
+          "Settings.PrivacySandbox.RestrictedNotice.ClosedNoInteraction"));
+      break;
+    }
+    case (PromptAction::kRestrictedNoticeMoreButtonClicked): {
+      base::RecordAction(base::UserMetricsAction(
+          "Settings.PrivacySandbox.RestrictedNotice.MoreButtonClicked"));
+      break;
+    }
   }
 }
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
index c72ab5c..46665fb7 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service.h
@@ -97,13 +97,14 @@
     kConsentMoreButtonClicked = 14,
     kNoticeMoreButtonClicked = 15,
 
-    // Restricted notice interactions, including only the interactions that
-    // complete
-    // the notice, using the `kNoticeXxx` for all other interactions.
+    // Restricted notice interactions
     kRestrictedNoticeAcknowledge = 16,
     kRestrictedNoticeOpenSettings = 17,
+    kRestrictedNoticeShown = 18,
+    kRestrictedNoticeClosedNoInteraction = 19,
+    kRestrictedNoticeMoreButtonClicked = 20,
 
-    kMaxValue = kRestrictedNoticeOpenSettings,
+    kMaxValue = kRestrictedNoticeMoreButtonClicked,
   };
 
   // TODO(crbug.com/1378703): Integrate this when handling Notice and Consent
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
index 1d27f96..49c3808 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_unittest.cc
@@ -1199,11 +1199,6 @@
                    "Settings.PrivacySandbox.Notice.OpenedSettings"));
 
   privacy_sandbox_service()->PromptActionOccurred(
-      PrivacySandboxService::PromptAction::kRestrictedNoticeOpenSettings);
-  EXPECT_EQ(1, user_action_tester.GetActionCount(
-                   "Settings.PrivacySandbox.RestrictedNotice.OpenedSettings"));
-
-  privacy_sandbox_service()->PromptActionOccurred(
       PrivacySandboxService::PromptAction::kNoticeAcknowledge);
   EXPECT_EQ(1, user_action_tester.GetActionCount(
                    "Settings.PrivacySandbox.Notice.Acknowledged"));
@@ -1272,6 +1267,34 @@
       PrivacySandboxService::PromptAction::kNoticeMoreButtonClicked);
   EXPECT_EQ(1, user_action_tester.GetActionCount(
                    "Settings.PrivacySandbox.Notice.MoreButtonClicked"));
+
+  privacy_sandbox_service()->PromptActionOccurred(
+      PrivacySandboxService::PromptAction::kRestrictedNoticeOpenSettings);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "Settings.PrivacySandbox.RestrictedNotice.OpenedSettings"));
+
+  privacy_sandbox_service()->PromptActionOccurred(
+      PrivacySandboxService::PromptAction::kRestrictedNoticeAcknowledge);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "Settings.PrivacySandbox.RestrictedNotice.Acknowledged"));
+
+  privacy_sandbox_service()->PromptActionOccurred(
+      PrivacySandboxService::PromptAction::kRestrictedNoticeShown);
+  EXPECT_EQ(1, user_action_tester.GetActionCount(
+                   "Settings.PrivacySandbox.RestrictedNotice.Shown"));
+
+  privacy_sandbox_service()->PromptActionOccurred(
+      PrivacySandboxService::PromptAction::
+          kRestrictedNoticeClosedNoInteraction);
+  EXPECT_EQ(
+      1, user_action_tester.GetActionCount(
+             "Settings.PrivacySandbox.RestrictedNotice.ClosedNoInteraction"));
+
+  privacy_sandbox_service()->PromptActionOccurred(
+      PrivacySandboxService::PromptAction::kRestrictedNoticeMoreButtonClicked);
+  EXPECT_EQ(1,
+            user_action_tester.GetActionCount(
+                "Settings.PrivacySandbox.RestrictedNotice.MoreButtonClicked"));
 }
 
 #if !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 80acbb14..92dec1c2 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -933,9 +933,12 @@
   // adding an entry to ProfileAttributesStorage. Creating a new
   // ProfileAttributesEntry consistently before writing the profile folder to
   // disk would resolve this.
+  // The TaskPriority should be `USER_BLOCKING` because `CreateProfileAsync`
+  // will eventually open a new browser window or navigates to the sign-in page,
+  // either of which will block the user's interaction.
   base::ThreadPool::PostTask(
       FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+      {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
       base::BindOnce(&NukeProfileFromDisk, new_path,
                      base::BindOnce(&ProfileManager::CreateProfileAsync,
diff --git a/chrome/browser/resources/chromeos/login/screens/oobe/enterprise_enrollment.js b/chrome/browser/resources/chromeos/login/screens/oobe/enterprise_enrollment.js
index c5e5930..e440c9f 100644
--- a/chrome/browser/resources/chromeos/login/screens/oobe/enterprise_enrollment.js
+++ b/chrome/browser/resources/chromeos/login/screens/oobe/enterprise_enrollment.js
@@ -348,9 +348,11 @@
     }
 
     invokePolymerMethod(this.$['step-ad-join'], 'onBeforeShow');
-    this.showStep(
-        this.isAutoEnroll_ ? OobeTypes.EnrollmentStep.WORKING :
-                             OobeTypes.EnrollmentStep.LOADING);
+    if (!this.uiStep) {
+      this.showStep(
+          this.isAutoEnroll_ ? OobeTypes.EnrollmentStep.WORKING :
+                               OobeTypes.EnrollmentStep.LOADING);
+    }
   }
 
   /**
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts
index 6eab420..81c2b30 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_browser_proxy.ts
@@ -29,6 +29,9 @@
   NOTICE_MORE_BUTTON_CLICKED = 15,
   RESTRICTED_NOTICE_ACKNOWLEDGE = 16,
   RESTRICTED_NOTICE_OPEN_SETTINGS = 17,
+  RESTRICTED_NOTICE_SHOWN = 18,
+  RESTRICTED_NOTICE_CLOSED_NO_INTERACTION = 19,
+  RESTRICTED_NOTICE_MORE_BUTTON_CLICKED = 20,
 }
 
 export class PrivacySandboxDialogBrowserProxy {
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts
index deecd51..d3a8828a 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_dialog_mixin.ts
@@ -83,6 +83,12 @@
               PrivacySandboxPromptAction.NOTICE_MORE_BUTTON_CLICKED);
         }
 
+        onRestrictedNoticeMoreClicked() {
+          this.onMoreClicked_();
+          this.promptActionOccurred(
+              PrivacySandboxPromptAction.RESTRICTED_NOTICE_MORE_BUTTON_CLICKED);
+        }
+
         promptActionOccurred(action: PrivacySandboxPromptAction) {
           PrivacySandboxDialogBrowserProxy.getInstance().promptActionOccurred(
               action);
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.html b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.html
index 53aa564..0c28653 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.html
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.html
@@ -29,7 +29,7 @@
     </div>
   </div>
   <div id="showMoreOverlay" hidden="[[wasScrolledToBottom]]">
-    <cr-button id="moreButton" on-click="onNoticeMoreClicked"
+    <cr-button id="moreButton" on-click="onRestrictedNoticeMoreClicked"
         class="action-button" aria-hidden="true" tabindex="-1">
       $i18n{m1DialogMoreButton}
       <iron-icon icon="cr:expand-more"></iron-icon>
diff --git a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.ts b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.ts
index acff955..64f8638 100644
--- a/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.ts
+++ b/chrome/browser/resources/privacy_sandbox/privacy_sandbox_notice_restricted_dialog_app.ts
@@ -36,7 +36,7 @@
       this.updateScrollableContents();
       this.maybeShowMoreButton().then(
           () => this.promptActionOccurred(
-              PrivacySandboxPromptAction.NOTICE_SHOWN));
+              PrivacySandboxPromptAction.RESTRICTED_NOTICE_SHOWN));
     });
   }
 
diff --git a/chrome/browser/resources/settings/chromeos/main_page_mixin.ts b/chrome/browser/resources/settings/chromeos/main_page_mixin.ts
index 23d7d89..0ca4898 100644
--- a/chrome/browser/resources/settings/chromeos/main_page_mixin.ts
+++ b/chrome/browser/resources/settings/chromeos/main_page_mixin.ts
@@ -74,6 +74,11 @@
   [RouteState.ROOT, ALL_STATES],
 ]);
 
+/**
+ * The route for the first page listed in the Settings menu.
+ */
+const FIRST_PAGE_ROUTE: Route = Router.getInstance().routes.INTERNET;
+
 export interface MainPageMixinInterface extends RouteObserverMixinInterface {
   containsRoute(route: Route|undefined): boolean;
   querySection(section: string): HTMLElement|null;
@@ -99,6 +104,10 @@
           return castExists(hostEl ? hostEl.parentElement : document.body);
         }
 
+        private get isMainPageContainer(): boolean {
+          return this.tagName === 'MAIN-PAGE-CONTAINER';
+        }
+
         /**
          * Method to be overridden by users of MainPageMixin.
          * @return Whether the given route is part of |this| page.
@@ -130,8 +139,7 @@
           const waitFn = beforeNextRender.bind(null, this);
 
           return new Promise(resolve => {
-            if (this.tagName === 'MAIN-PAGE-CONTAINER' &&
-                isAdvancedRoute(route)) {
+            if (this.isMainPageContainer && isAdvancedRoute(route)) {
               this.dispatchCustomEvent_('hide-container');
               waitFn(async () => {
                 await this.loadAdvancedPage();
@@ -280,7 +288,13 @@
                 return;
 
               case RouteState.ROOT:
-                // TODO(b/282961146) Activate first top-level page (Network)
+                // Do not activate the Network page if the host element is
+                // the About page since it does not contain that page.
+                // TODO(b/282961146) Investigate removing MainPageMixin from
+                // the About page so this check can be removed.
+                if (isRevampWayfindingEnabled() && this.isMainPageContainer) {
+                  this.activatePage(FIRST_PAGE_ROUTE);
+                }
                 return;
 
               // Nothing to do here for the DIALOG case.
diff --git a/chrome/browser/resources/waffle/app.html b/chrome/browser/resources/waffle/app.html
index 259b9d8..6fb92b8 100644
--- a/chrome/browser/resources/waffle/app.html
+++ b/chrome/browser/resources/waffle/app.html
@@ -5,4 +5,3 @@
 </style>
 
 <h1>$i18n{title}</h1>
-<button on-click="onCloseClick_">$i18n{closeButtonTitle}</button>
diff --git a/chrome/browser/resources/waffle/app.ts b/chrome/browser/resources/waffle/app.ts
index 19d25f8..98da622 100644
--- a/chrome/browser/resources/waffle/app.ts
+++ b/chrome/browser/resources/waffle/app.ts
@@ -5,7 +5,7 @@
 import './strings.m.js';
 import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
 
-import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {getTemplate} from './app.html.js';
 import {WaffleBrowserProxy} from './browser_proxy.js';
@@ -19,8 +19,12 @@
     return getTemplate();
   }
 
-  private onCloseClick_() {
-    WaffleBrowserProxy.getInstance().handler.closeClicked();
+  override connectedCallback() {
+    super.connectedCallback();
+
+    afterNextRender(this, () => {
+      WaffleBrowserProxy.getInstance().handler.displayDialog();
+    });
   }
 }
 
diff --git a/chrome/browser/resources/waffle/waffle.html b/chrome/browser/resources/waffle/waffle.html
index 2e244ea..0fd0106 100644
--- a/chrome/browser/resources/waffle/waffle.html
+++ b/chrome/browser/resources/waffle/waffle.html
@@ -6,9 +6,9 @@
     <link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css">
     <style>
       body {
-        height: 100vh;
+        height: 100%;
         margin: 0;
-        width: 100vw;
+        width: 100%;
       }
       @media (prefers-color-scheme: dark) {
         body {
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc
index 53d062b7..1903a642 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc
@@ -96,12 +96,16 @@
             "Connector for scanning."
           trigger:
             "If the OnFileAttachedEnterpriseConnector, "
-            "OnFileDownloadedEnterpriseConnector or "
-            "OnBulkDataEntryEnterpriseConnector policy is set, a request is made to "
-            "scan a file attached to Chrome, a file downloaded by Chrome or "
-            "data pasted in Chrome respectively."
+            "OnFileDownloadedEnterpriseConnector, "
+            "OnFileTransferEnterpriseConnector, "
+            "OnBulkDataEntryEnterpriseConnector or OnPrintEnterpriseConnector "
+            "policy is set, a request is made to scan a file attached to "
+            "Chrome, a file downloaded by Chrome, a file transfered from a "
+            "ChromeOS file system, data pasted in "
+            "Chrome or data printed from Chrome respectively."
           data:
-            "The uploaded or downloaded file, or pasted data."
+            "The uploaded/downloaded/transfered file, pasted data or printed "
+            "data."
           destination: GOOGLE_OWNED_SERVICE
         }
         policy {
@@ -119,6 +123,12 @@
             OnBulkDataEntryEnterpriseConnector {
               OnBulkDataEntryEnterpriseConnector: "[]"
             }
+            OnFileTransferEnterpriseConnector {
+              OnFileTransferEnterpriseConnector: "[]"
+            }
+            OnPrintEnterpriseConnector {
+              OnPrintEnterpriseConnector: "[]"
+            }
           }
         }
         )");
diff --git a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_footer_item.xml b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_footer_item.xml
index 1ebd654..6dcb25e7 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_footer_item.xml
+++ b/chrome/browser/touch_to_fill/android/internal/java/res/layout/touch_to_fill_footer_item.xml
@@ -22,6 +22,19 @@
         android:layout_marginBottom="@dimen/ttf_buttons_vertical_margin"/>
 
     <TextView
+        android:id="@+id/touch_to_fill_sheet_use_passkeys_other_device"
+        android:text="@string/touch_to_fill_use_device_passkey"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/ttf_buttons_height"
+        android:paddingHorizontal="@dimen/ttf_sheet_padding"
+        android:minHeight="48dp"
+        android:gravity="center_vertical|start"
+        android:textAppearance="@style/TextAppearance.TextLarge.Primary"
+        android:background="?android:attr/selectableItemBackground"
+        android:textDirection="locale"
+        android:textAlignment="viewStart"/>
+
+    <TextView
         android:id="@+id/touch_to_fill_sheet_manage_passwords"
         android:layout_width="match_parent"
         android:layout_height="@dimen/ttf_buttons_height"
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillBridge.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillBridge.java
index d4cf335b..050cc36 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillBridge.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillBridge.java
@@ -76,10 +76,11 @@
     @CalledByNative
     private void showCredentials(GURL url, boolean isOriginSecure,
             WebAuthnCredential[] webAuthnCredentials, Credential[] credentials,
-            boolean submitCredential, boolean managePasskeysHidesPasswords) {
+            boolean submitCredential, boolean managePasskeysHidesPasswords,
+            boolean showHybridPasskeyOption) {
         mTouchToFillComponent.showCredentials(url, isOriginSecure,
                 Arrays.asList(webAuthnCredentials), Arrays.asList(credentials), submitCredential,
-                managePasskeysHidesPasswords);
+                managePasskeysHidesPasswords, showHybridPasskeyOption);
     }
 
     @Override
@@ -95,6 +96,13 @@
     }
 
     @Override
+    public void onHybridSignInSelected() {
+        if (mNativeView != 0) {
+            TouchToFillBridgeJni.get().onHybridSignInSelected(mNativeView);
+        }
+    }
+
+    @Override
     public void onCredentialSelected(Credential credential) {
         if (mNativeView != 0) {
             TouchToFillBridgeJni.get().onCredentialSelected(mNativeView, credential);
@@ -114,6 +122,7 @@
         void onWebAuthnCredentialSelected(
                 long nativeTouchToFillViewImpl, WebAuthnCredential credential);
         void onManagePasswordsSelected(long nativeTouchToFillViewImpl, boolean passkeysShown);
+        void onHybridSignInSelected(long nativeTouchToFillViewImpl);
         void onDismiss(long nativeTouchToFillViewImpl);
     }
 }
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
index 98f33ca..c5a5710 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java
@@ -46,9 +46,10 @@
     @Override
     public void showCredentials(GURL url, boolean isOriginSecure,
             List<WebAuthnCredential> webAuthnCredentials, List<Credential> credentials,
-            boolean triggerSubmission, boolean managePasskeysHidesPasswords) {
+            boolean triggerSubmission, boolean managePasskeysHidesPasswords,
+            boolean showHybridPasskeyOption) {
         mMediator.showCredentials(url, isOriginSecure, webAuthnCredentials, credentials,
-                triggerSubmission, managePasskeysHidesPasswords);
+                triggerSubmission, managePasskeysHidesPasswords, showHybridPasskeyOption);
     }
 
     /**
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
index bff8343..80c4006 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java
@@ -10,7 +10,9 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.MANAGE_BUTTON_TEXT;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_MANAGE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.SHOW_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
@@ -87,7 +89,8 @@
 
     void showCredentials(GURL url, boolean isOriginSecure,
             List<WebAuthnCredential> webAuthnCredentials, List<Credential> credentials,
-            boolean triggerSubmission, boolean managePasskeysHidesPasswords) {
+            boolean triggerSubmission, boolean managePasskeysHidesPasswords,
+            boolean showHybridPasskeyOption) {
         assert credentials != null;
 
         mManagePasskeysHidesPasswords = managePasskeysHidesPasswords;
@@ -133,6 +136,8 @@
                         .with(ON_CLICK_MANAGE, this::onManagePasswordSelected)
                         .with(MANAGE_BUTTON_TEXT,
                                 getManageButtonText(credentials, webAuthnCredentials))
+                        .with(ON_CLICK_HYBRID, this::onHybridSignInSelected)
+                        .with(SHOW_HYBRID, showHybridPasskeyOption)
                         .build()));
 
         mBottomSheetFocusHelper.registerForOneTimeUse();
@@ -254,6 +259,13 @@
         mDelegate.onManagePasswordsSelected(passkeysShown);
     }
 
+    private void onHybridSignInSelected() {
+        mModel.set(VISIBLE, false);
+        RecordHistogram.recordEnumeratedHistogram(
+                UMA_TOUCH_TO_FILL_USER_ACTION, UserAction.SELECT_HYBRID, UserAction.MAX_VALUE + 1);
+        mDelegate.onHybridSignInSelected();
+    }
+
     /**
      * @param credentials The available credentials. Show the confirmation for a lone credential.
      * @return True if a confirmation button should be shown at the end of the bottom sheet.
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
index c1ab90f3..afaf1ee 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java
@@ -134,8 +134,13 @@
                 new PropertyModel.WritableObjectPropertyKey<>("on_click_manage");
         static final PropertyModel.WritableObjectPropertyKey<String> MANAGE_BUTTON_TEXT =
                 new PropertyModel.WritableObjectPropertyKey<>("manage_button_text");
+        static final PropertyModel.WritableObjectPropertyKey<Runnable> ON_CLICK_HYBRID =
+                new PropertyModel.WritableObjectPropertyKey<>("on_click_hybrid");
+        static final PropertyModel.WritableBooleanPropertyKey SHOW_HYBRID =
+                new PropertyModel.WritableBooleanPropertyKey("show_hybrid");
 
-        static final PropertyKey[] ALL_KEYS = {ON_CLICK_MANAGE, MANAGE_BUTTON_TEXT};
+        static final PropertyKey[] ALL_KEYS = {
+                ON_CLICK_MANAGE, MANAGE_BUTTON_TEXT, ON_CLICK_HYBRID, SHOW_HYBRID};
 
         private FooterProperties() {}
     }
diff --git a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
index 34e1372..4fd36f3 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
+++ b/chrome/browser/touch_to_fill/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java
@@ -12,7 +12,9 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.MANAGE_BUTTON_TEXT;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_MANAGE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.SHOW_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
@@ -310,6 +312,12 @@
         if (key == ON_CLICK_MANAGE) {
             view.findViewById(R.id.touch_to_fill_sheet_manage_passwords)
                     .setOnClickListener((v) -> model.get(ON_CLICK_MANAGE).run());
+        } else if (key == ON_CLICK_HYBRID) {
+            view.findViewById(R.id.touch_to_fill_sheet_use_passkeys_other_device)
+                    .setOnClickListener((v) -> model.get(ON_CLICK_HYBRID).run());
+        } else if (key == SHOW_HYBRID) {
+            view.findViewById(R.id.touch_to_fill_sheet_use_passkeys_other_device)
+                    .setVisibility(model.get(SHOW_HYBRID) ? View.VISIBLE : View.GONE);
         } else if (key == MANAGE_BUTTON_TEXT) {
             TextView managePasswordsView =
                     view.findViewById(R.id.touch_to_fill_sheet_manage_passwords);
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd b/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd
index d5eb346..79f59d0e 100644
--- a/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings.grd
@@ -232,6 +232,9 @@
       <message name="IDS_TOUCH_TO_FILL_PASSKEY_CREDENTIAL_ACCESSIBILITY_DESCRIPTION" desc="Content description for a passkey credential item on the sheet list. This is not visually displayed, but is audibly read by screen readers for accessibility purposes.">
         Passkey for <ph name="USERNAME">%1$s<ex>John.Doe@example.com</ex></ph>, use your screen lock.
       </message>
+      <message name="IDS_TOUCH_TO_FILL_USE_DEVICE_PASSKEY" desc="Title of a button at the end of a touch to fill sheet that will use a different device’s passkey. This string also exists for desktop password manager and translations should match. See IDS_PASSWORD_MANAGER_USE_DEVICE_PASSKEY in components/password_manager_strings.grdp.">
+        Use a passkey on a different device
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings_grd/IDS_TOUCH_TO_FILL_USE_DEVICE_PASSKEY.png.sha1 b/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings_grd/IDS_TOUCH_TO_FILL_USE_DEVICE_PASSKEY.png.sha1
new file mode 100644
index 0000000..09e21af
--- /dev/null
+++ b/chrome/browser/touch_to_fill/android/internal/java/strings/android_touch_to_fill_strings_grd/IDS_TOUCH_TO_FILL_USE_DEVICE_PASSKEY.png.sha1
@@ -0,0 +1 @@
+452dbb6ffe46f729da35bf3f5c97aa3e7a9852aa
\ No newline at end of file
diff --git a/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillComponent.java b/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillComponent.java
index a77aeeb..1e661e8 100644
--- a/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillComponent.java
+++ b/chrome/browser/touch_to_fill/android/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillComponent.java
@@ -32,14 +32,15 @@
      * TODO(crbug.com/1013134): Deduplicate the Java and C++ enum.
      */
     @IntDef({UserAction.SELECT_CREDENTIAL, UserAction.DISMISS, UserAction.SELECT_MANAGE_PASSWORDS,
-            UserAction.SELECT_WEBAUTHN_CREDENTIAL, UserAction.MAX_VALUE})
+            UserAction.SELECT_WEBAUTHN_CREDENTIAL, UserAction.SELECT_HYBRID, UserAction.MAX_VALUE})
     @Retention(RetentionPolicy.SOURCE)
     @interface UserAction {
         int SELECT_CREDENTIAL = 0;
         int DISMISS = 1;
         int SELECT_MANAGE_PASSWORDS = 2;
         int SELECT_WEBAUTHN_CREDENTIAL = 3;
-        int MAX_VALUE = SELECT_WEBAUTHN_CREDENTIAL;
+        int SELECT_HYBRID = 4;
+        int MAX_VALUE = SELECT_HYBRID;
     }
 
     /**
@@ -71,6 +72,11 @@
          * @param passkeysShown True when the sheet contained passkey credentials.
          */
         void onManagePasswordsSelected(boolean passkeysShown);
+
+        /**
+         * Called when the user selects 'Use a Passkey on a Different Device'.
+         */
+        void onHybridSignInSelected();
     }
 
     /**
@@ -94,8 +100,11 @@
      *         after filling.
      * @param managePasskeysHidesPasswords A {@link boolean} that indicates whether managing
      *         passkeys will show a screen that does not provide password management.
+     * @param showHybridPasskeyOption A {@link boolean} that indicates whether the footer should
+     *         display an option to initiate hybrid sign-in.
      */
     void showCredentials(GURL url, boolean isOriginSecure,
             List<WebAuthnCredential> webauthnCredentials, List<Credential> credentials,
-            boolean triggerSubmission, boolean managePasskeysHidesPasswords);
+            boolean triggerSubmission, boolean managePasskeysHidesPasswords,
+            boolean showHybridPasskeyOption);
 }
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
index 92ffb62e..a6d58d4 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillIntegrationTest.java
@@ -111,7 +111,7 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(sExampleUrl, true, Collections.emptyList(),
                     Collections.singletonList(sAna), /*submitCredential=*/false,
-                    /*managePasskeysHidesPasswords=*/false);
+                    /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
 
@@ -128,7 +128,7 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(sExampleUrl, true, Collections.singletonList(sCam),
                     Collections.singletonList(sAna), /*submitCredential=*/false,
-                    /*managePasskeysHidesPasswords=*/false);
+                    /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
 
@@ -145,7 +145,7 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(sExampleUrl, true, Collections.emptyList(),
                     Collections.singletonList(sAna), /*submitCredential=*/false,
-                    /*managePasskeysHidesPasswords=*/false);
+                    /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
 
@@ -162,7 +162,7 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(sExampleUrl, true, Collections.emptyList(),
                     Arrays.asList(sAna, sBob), /*submitCredential=*/false,
-                    /*managePasskeysHidesPasswords=*/false);
+                    /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
 
@@ -178,13 +178,13 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(sExampleUrl, true, Collections.emptyList(),
                     Collections.singletonList(sAna), /*submitCredential=*/false,
-                    /*managePasskeysHidesPasswords=*/false);
+                    /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
 
         BottomSheetTestSupport sheetSupport = new BottomSheetTestSupport(mBottomSheetController);
 
-        // Swipe the sheet up to it's full state in order to see the 'Manage Passwords' button.
+        // Swipe the sheet up to its full state in order to see the 'Manage Passwords' button.
         runOnUiThreadBlocking(() -> { sheetSupport.setSheetState(SheetState.FULL, false); });
 
         pollUiThread(() -> getManagePasswordsButton() != null);
@@ -196,6 +196,29 @@
 
     @Test
     @MediumTest
+    public void testClickingHybridButtonTriggersCallback() {
+        runOnUiThreadBlocking(() -> {
+            mTouchToFill.showCredentials(sExampleUrl, true, Collections.emptyList(),
+                    Collections.singletonList(sAna), /*submitCredential=*/false,
+                    /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/true);
+        });
+        BottomSheetTestSupport.waitForOpen(mBottomSheetController);
+
+        BottomSheetTestSupport sheetSupport = new BottomSheetTestSupport(mBottomSheetController);
+
+        // Swipe the sheet up to its full state in order to see the 'Use a Passkey on a Different
+        // Device' button.
+        runOnUiThreadBlocking(() -> { sheetSupport.setSheetState(SheetState.FULL, false); });
+
+        pollUiThread(() -> getHybridSignInButton() != null);
+        TouchCommon.singleClickView(getHybridSignInButton());
+        waitForEvent(mMockBridge).onHybridSignInSelected();
+        verify(mMockBridge, never()).onDismissed();
+        verify(mMockBridge, never()).onCredentialSelected(any());
+    }
+
+    @Test
+    @MediumTest
     @SuppressLint("SetTextI18n")
     public void testDismissedIfUnableToShow() throws Exception {
         BottomSheetContent otherBottomSheetContent = runOnUiThreadBlocking(() -> {
@@ -261,7 +284,7 @@
         runOnUiThreadBlocking(() -> {
             mTouchToFill.showCredentials(sExampleUrl, true, Collections.emptyList(),
                     Arrays.asList(sAna, sBob), /*submitCredential=*/false,
-                    /*managePasskeysHidesPasswords=*/false);
+                    /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         });
         waitForEvent(mMockBridge).onDismissed();
         verify(mMockBridge, never()).onCredentialSelected(any());
@@ -282,6 +305,11 @@
                 R.id.touch_to_fill_sheet_manage_passwords);
     }
 
+    private TextView getHybridSignInButton() {
+        return mActivityTestRule.getActivity().findViewById(
+                R.id.touch_to_fill_sheet_use_passkeys_other_device);
+    }
+
     public static <T> T waitForEvent(T mock) {
         return verify(mock,
                 timeout(ScalableTimeout.scaleTimeout(CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL)));
diff --git a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
index 0300fae..6793ae6f 100644
--- a/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
+++ b/chrome/browser/touch_to_fill/android/javatests/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewTest.java
@@ -22,7 +22,9 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.ON_CLICK_LISTENER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.MANAGE_BUTTON_TEXT;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_MANAGE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.SHOW_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
@@ -94,6 +96,7 @@
     private static final Credential NIK =
             new Credential("Nik", "***", "Nik", "group.xyz", GetLoginMatchType.AFFILIATED, 0);
     private final AtomicBoolean mManageButtonClicked = new AtomicBoolean(false);
+    private final AtomicBoolean mHybridButtonClicked = new AtomicBoolean(false);
 
     @Mock
     private Callback<Integer> mDismissHandler;
@@ -132,7 +135,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, true),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -161,7 +164,7 @@
                                             .with(IMAGE_DRAWABLE_ID,
                                                     mResourceProvider.getHeaderImageDrawableId())
                                             .build()),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -189,7 +192,7 @@
                                             .with(IMAGE_DRAWABLE_ID,
                                                     mResourceProvider.getHeaderImageDrawableId())
                                             .build()),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -213,7 +216,7 @@
                                             .with(IMAGE_DRAWABLE_ID,
                                                     mResourceProvider.getHeaderImageDrawableId())
                                             .build()),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -236,7 +239,7 @@
                                             .with(IMAGE_DRAWABLE_ID,
                                                     mResourceProvider.getHeaderImageDrawableId())
                                             .build()),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -260,7 +263,7 @@
                                             .with(IMAGE_DRAWABLE_ID,
                                                     mResourceProvider.getHeaderImageDrawableId())
                                             .build()),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -284,7 +287,7 @@
                                             .with(IMAGE_DRAWABLE_ID,
                                                     mResourceProvider.getHeaderImageDrawableId())
                                             .build()),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -336,7 +339,8 @@
     @MediumTest
     public void testCredentialsAreClickable() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
-            mModel.get(SHEET_ITEMS).addAll(asList(buildCredentialItem(ANA), buildFooterItem()));
+            mModel.get(SHEET_ITEMS)
+                    .addAll(asList(buildCredentialItem(ANA), buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -354,7 +358,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, false),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -374,7 +378,8 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA),
-                            buildConfirmationButton(ANA, showSubmitButton), buildFooterItem()));
+                            buildConfirmationButton(ANA, showSubmitButton),
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -391,7 +396,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, true),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -408,7 +413,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, true),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -430,7 +435,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, true),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
         BottomSheetTestSupport.waitForOpen(mBottomSheetController);
@@ -445,7 +450,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, true),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
 
@@ -463,7 +468,7 @@
     public void testPasskeyCredentialAccessibilityDescription() {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
-                    .addAll(asList(buildWebAuthnCredentialItem(CAM), buildFooterItem()));
+                    .addAll(asList(buildWebAuthnCredentialItem(CAM), buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
 
@@ -504,7 +509,7 @@
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             mModel.get(SHEET_ITEMS)
                     .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, true),
-                            buildFooterItem()));
+                            buildFooterItem(false)));
             mModel.set(VISIBLE, true);
         });
 
@@ -538,6 +543,55 @@
         assertFalse(recyclerView.isLayoutSuppressed());
     }
 
+    @Test
+    @MediumTest
+    @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
+    public void testHybridPropertyShowsHybridButton() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mModel.get(SHEET_ITEMS)
+                    .addAll(asList(
+                            new MVCListAdapter.ListItem(TouchToFillProperties.ItemType.HEADER,
+                                    new PropertyModel.Builder(HeaderProperties.ALL_KEYS)
+                                            .with(TITLE,
+                                                    getActivity().getString(
+                                                            R.string.touch_to_fill_sheet_uniform_title))
+                                            .with(FORMATTED_URL, "www.example.org")
+                                            .with(ORIGIN_SECURE, true)
+                                            .with(IMAGE_DRAWABLE_ID,
+                                                    mResourceProvider.getHeaderImageDrawableId())
+                                            .build()),
+                            buildFooterItem(true)));
+            mModel.set(VISIBLE, true);
+        });
+        BottomSheetTestSupport.waitForOpen(mBottomSheetController);
+        TextView hybridButtonText = mTouchToFillView.getContentView().findViewById(
+                R.id.touch_to_fill_sheet_use_passkeys_other_device);
+
+        assertThat(hybridButtonText.getText(),
+                is(getActivity().getString(R.string.touch_to_fill_use_device_passkey)));
+    }
+
+    @Test
+    @MediumTest
+    public void testHybridButtonIsClickable() {
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            mModel.get(SHEET_ITEMS)
+                    .addAll(asList(buildCredentialItem(ANA), buildConfirmationButton(ANA, true),
+                            buildFooterItem(true)));
+            mModel.set(VISIBLE, true);
+        });
+        BottomSheetTestSupport.waitForOpen(mBottomSheetController);
+
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> mSheetTestSupport.setSheetState(SheetState.FULL, false));
+
+        TextView hybridButton = mTouchToFillView.getContentView().findViewById(
+                R.id.touch_to_fill_sheet_use_passkeys_other_device);
+        TouchCommon.singleClickView(hybridButton);
+
+        pollUiThread(mHybridButtonClicked::get);
+    }
+
     private ChromeActivity getActivity() {
         return mActivityTestRule.getActivity();
     }
@@ -598,13 +652,15 @@
                         .build());
     }
 
-    private MVCListAdapter.ListItem buildFooterItem() {
+    private MVCListAdapter.ListItem buildFooterItem(boolean showHybrid) {
         return new MVCListAdapter.ListItem(TouchToFillProperties.ItemType.FOOTER,
                 new PropertyModel.Builder(FooterProperties.ALL_KEYS)
                         .with(MANAGE_BUTTON_TEXT,
                                 mActivityTestRule.getActivity().getString(
                                         R.string.manage_passwords_and_passkeys))
                         .with(ON_CLICK_MANAGE, () -> mManageButtonClicked.set(true))
+                        .with(SHOW_HYBRID, showHybrid)
+                        .with(ON_CLICK_HYBRID, () -> mHybridButtonClicked.set(true))
                         .build());
     }
 }
diff --git a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
index e1c469dc..fdfee2be6 100644
--- a/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
+++ b/chrome/browser/touch_to_fill/android/junit/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillControllerTest.java
@@ -23,7 +23,9 @@
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.CredentialProperties.SHOW_SUBMIT_BUTTON;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.DISMISS_HANDLER;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.MANAGE_BUTTON_TEXT;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.ON_CLICK_MANAGE;
+import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.FooterProperties.SHOW_HYBRID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.FORMATTED_URL;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.TouchToFillProperties.HeaderProperties.ORIGIN_SECURE;
@@ -151,7 +153,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testShowCredentialsWithMultipleEntries() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(), Arrays.asList(ANA, CARL),
-                /*submitCredential=*/true, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/true, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(4)); // Header + 2 credentials + footer.
         assertThat(itemList.get(itemList.size() - 1).model.get(MANAGE_BUTTON_TEXT),
@@ -182,7 +185,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testShowCredentialsWithSingleEntry() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(), Arrays.asList(ANA),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(4)); // Header + 1 credential + Button + Footer.
         assertThat(itemList.get(itemList.size() - 1).model.get(MANAGE_BUTTON_TEXT),
@@ -208,7 +212,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testShowCredentialsWithSingleWebAuthnEntry() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(DINO), Collections.emptyList(),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(4)); // Header + 1 credential + Button + Footer.
         assertThat(itemList.get(itemList.size() - 1).model.get(MANAGE_BUTTON_TEXT),
@@ -230,7 +235,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testShowCredentialsWithWebAuthnAndPasswordEntries() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(DINO), Arrays.asList(ANA),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         // Header + 1 webauthn credential + 1 password credential + Footer.
         assertThat(itemList.size(), is(4));
@@ -255,7 +261,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testShowCredentialsToSubmit() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(), Arrays.asList(ANA),
-                /*submitCredential=*/true, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/true, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(4)); // Header + 1 credential + Button + Footer.
         assertThat(itemList.get(itemList.size() - 1).model.get(MANAGE_BUTTON_TEXT),
@@ -273,7 +280,7 @@
     public void testShowCredentialsSetsCredentialListAndRequestsFavicons() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
                 Arrays.asList(ANA, CARL, BOB), /*submitCredential=*/false,
-                /*managePasskeysHidesPasswords=*/false);
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(5)); // Header + 3 Credentials + Footer.
         assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
@@ -299,7 +306,7 @@
     public void testFetchFaviconUpdatesModel() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
                 Collections.singletonList(CARL), /*submitCredential=*/false,
-                /*managePasskeysHidesPasswords=*/false);
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(4)); // Header + Credential + Continue Button + Footer.
         assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
@@ -327,7 +334,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testShowCredentialsFormatPslOrigins() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(), Arrays.asList(ANA, BOB),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         assertThat(mModel.get(SHEET_ITEMS).size(), is(4)); // Header + 2 Credentials + Footer.
         assertThat(mModel.get(SHEET_ITEMS).get(1).type, is(ItemType.CREDENTIAL));
         assertThat(mModel.get(SHEET_ITEMS).get(1).model.get(FORMATTED_ORIGIN),
@@ -342,7 +350,7 @@
     public void testClearsCredentialListWhenShowingAgain() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
                 Collections.singletonList(ANA), /*submitCredential=*/false,
-                /*managePasskeysHidesPasswords=*/false);
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(4)); // Header + Credential + Continue Button + Footer.
         assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
@@ -352,7 +360,7 @@
         // Showing the sheet a second time should replace all changed credentials.
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
                 Collections.singletonList(BOB), /*submitCredential=*/false,
-                /*managePasskeysHidesPasswords=*/false);
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         itemList = mModel.get(SHEET_ITEMS);
         assertThat(itemList.size(), is(4)); // Header + Credential + Continue Button + Footer.
         assertThat(itemList.get(1).type, is(ItemType.CREDENTIAL));
@@ -365,7 +373,7 @@
     public void testShowCredentialsSetsVisibile() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
                 Arrays.asList(ANA, CARL, BOB), /*submitCredential=*/false,
-                /*managePasskeysHidesPasswords=*/false);
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         assertThat(mModel.get(VISIBLE), is(true));
     }
 
@@ -373,7 +381,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testCallsCallbackAndHidesOnSelectingItemDoesNotRecordIndexForSingleCredential() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(), Arrays.asList(ANA),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         assertThat(mModel.get(VISIBLE), is(true));
         assertNotNull(mModel.get(SHEET_ITEMS).get(1).model.get(ON_CLICK_LISTENER));
 
@@ -393,7 +402,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testCallsCallbackAndHidesOnSelectingItem() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(), Arrays.asList(ANA, CARL),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         assertThat(mModel.get(VISIBLE), is(true));
         assertNotNull(mModel.get(SHEET_ITEMS).get(1).model.get(ON_CLICK_LISTENER));
 
@@ -413,7 +423,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testCallsDelegateAndHidesOnDismiss() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(), Arrays.asList(ANA, CARL),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/false,
+                /*showHybridPasskeyOption=*/false);
         mMediator.onDismissed(BottomSheetController.StateChangeReason.BACK_PRESS);
         verify(mMockDelegate).onDismissed();
         assertThat(mModel.get(VISIBLE), is(false));
@@ -431,7 +442,7 @@
     public void testHidesWhenSelectingManagePasswords() {
         mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
                 Arrays.asList(ANA, CARL, BOB), /*submitCredential=*/false,
-                /*managePasskeysHidesPasswords=*/false);
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(
                 itemList.get(itemList.size() - 1).model.get(ON_CLICK_MANAGE), is(notNullValue()));
@@ -448,7 +459,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testManagePasswordsWithPasskeysShown() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(DINO), Collections.emptyList(),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/true);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/true,
+                /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         assertThat(
                 itemList.get(itemList.size() - 1).model.get(ON_CLICK_MANAGE), is(notNullValue()));
@@ -460,7 +472,8 @@
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
     public void testManagePasskeysButtonTitleWhenPasswordsHidden() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(DINO), Arrays.asList(ANA),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/true);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/true,
+                /*showHybridPasskeyOption=*/false);
         ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
         // Header + 1 webauthn credential + 1 password credential + Footer.
         assertThat(itemList.size(), is(4));
@@ -470,13 +483,42 @@
 
     @Test
     @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
-    public void testAddsTheBottomSheetHeperToObserveTheSheet() {
+    public void testAddsTheBottomSheetHelperToObserveTheSheet() {
         mMediator.showCredentials(TEST_URL, true, Arrays.asList(DINO), Arrays.asList(ANA),
-                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/true);
+                /*submitCredential=*/false, /*managePasskeysHidesPasswords=*/true,
+                /*showHybridPasskeyOption=*/false);
 
         verify(mMockFocusHelper).registerForOneTimeUse();
     }
 
+    @Test
+    @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
+    public void testHybridPasskeysShown() {
+        mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
+                Arrays.asList(ANA, CARL, BOB), /*submitCredential=*/false,
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/true);
+        ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
+        assertThat(itemList.get(itemList.size() - 1).model.get(SHOW_HYBRID), is(true));
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_ANDROID})
+    public void testHidesWhenSelectingHybridSignin() {
+        mMediator.showCredentials(TEST_URL, true, Collections.emptyList(),
+                Arrays.asList(ANA, CARL, BOB), /*submitCredential=*/false,
+                /*managePasskeysHidesPasswords=*/false, /*showHybridPasskeyOption=*/true);
+        ListModel<MVCListAdapter.ListItem> itemList = mModel.get(SHEET_ITEMS);
+        assertThat(
+                itemList.get(itemList.size() - 1).model.get(ON_CLICK_HYBRID), is(notNullValue()));
+        itemList.get(itemList.size() - 1).model.get(ON_CLICK_HYBRID).run();
+        verify(mMockDelegate).onHybridSignInSelected();
+        assertThat(mModel.get(VISIBLE), is(false));
+        assertThat(RecordHistogram.getHistogramValueCountForTesting(
+                           TouchToFillMediator.UMA_TOUCH_TO_FILL_USER_ACTION,
+                           UserAction.SELECT_HYBRID),
+                is(1));
+    }
+
     /**
      * Helper to verify formatted URLs. The real implementation calls {@link UrlFormatter}. It's not
      * useful to actually reimplement the formatter, so just modify the string in a trivial way.
diff --git a/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.cc b/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.cc
index 8df7d30..de9547c 100644
--- a/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.cc
+++ b/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.cc
@@ -94,8 +94,7 @@
     IsOriginSecure is_origin_secure,
     base::span<const password_manager::UiCredential> credentials,
     base::span<const PasskeyCredential> passkey_credentials,
-    bool trigger_submission,
-    bool can_manage_passwords_when_passkeys_present) {
+    int flags) {
   if (!RecreateJavaObject()) {
     // It's possible that the constructor cannot access the bottom sheet clank
     // component. That case may be temporary but we can't let users in a waiting
@@ -137,7 +136,9 @@
   Java_TouchToFillBridge_showCredentials(
       env, java_object_internal_, url::GURLAndroid::FromNativeGURL(env, url),
       is_origin_secure.value(), passkey_array, credential_array,
-      trigger_submission, !can_manage_passwords_when_passkeys_present);
+      !!(flags & TouchToFillView::kTriggerSubmission),
+      !(flags & TouchToFillView::kCanManagePasswordsWhenPasskeysPresent),
+      !!(flags & TouchToFillView::kShouldShowHybridOption));
 }
 
 void TouchToFillViewImpl::OnCredentialSelected(const UiCredential& credential) {
@@ -166,6 +167,10 @@
   controller_->OnManagePasswordsSelected(passkeys_shown);
 }
 
+void TouchToFillViewImpl::OnHybridSignInSelected(JNIEnv* env) {
+  controller_->OnHybridSignInSelected();
+}
+
 void TouchToFillViewImpl::OnDismiss(JNIEnv* env) {
   OnDismiss();
 }
diff --git a/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.h b/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.h
index 725a900..62629e2 100644
--- a/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.h
+++ b/chrome/browser/touch_to_fill/android/touch_to_fill_view_impl.h
@@ -34,8 +34,7 @@
       IsOriginSecure is_origin_secure,
       base::span<const password_manager::UiCredential> credentials,
       base::span<const password_manager::PasskeyCredential> passkey_credentials,
-      bool trigger_submission,
-      bool can_manage_passwords_when_passkeys_present) override;
+      int flags) override;
   void OnCredentialSelected(
       const password_manager::UiCredential& credential) override;
   void OnDismiss() override;
@@ -47,6 +46,7 @@
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& credential);
   void OnManagePasswordsSelected(JNIEnv* env, jboolean passkeys_shown);
+  void OnHybridSignInSelected(JNIEnv* env);
   void OnDismiss(JNIEnv* env);
 
  private:
diff --git a/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.cc b/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.cc
index bf747b6..dcd2f7f 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.cc
+++ b/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.cc
@@ -13,7 +13,9 @@
 
 bool FakeTouchToFillPasswordGenerationBridge::Show(
     content::WebContents* web_contents,
-    base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate) {
+    base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate,
+    std::u16string password,
+    std::string account) {
   delegate_ = delegate;
   return true;
 }
diff --git a/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.h b/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.h
index 16a3451..4cfe6e6 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.h
+++ b/chrome/browser/touch_to_fill/password_generation/android/fake_touch_to_fill_password_generation_bridge.h
@@ -15,9 +15,10 @@
   FakeTouchToFillPasswordGenerationBridge();
   ~FakeTouchToFillPasswordGenerationBridge() override;
 
-  bool Show(
-      content::WebContents* web_contents,
-      base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate) override;
+  bool Show(content::WebContents* web_contents,
+            base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate,
+            std::u16string password,
+            std::string account) override;
   void Hide() override;
   void OnDismissed(JNIEnv* env) override;
 
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn
index 3d31eb47..23af4bb 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/BUILD.gn
@@ -11,10 +11,12 @@
     "//base:jni_java",
     "//chrome/browser/autofill/android:java",
     "//chrome/browser/password_manager/android:password_manager_resource_provider_java",
+    "//chrome/browser/sync/android:java",
     "//chrome/browser/touch_to_fill/common/android:java",
     "//chrome/browser/touch_to_fill/common/android:java_resources",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//components/browser_ui/bottomsheet/android:java",
+    "//components/signin/public/android:java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_appcompat_appcompat_resources_java",
     "//third_party/androidx:androidx_recyclerview_recyclerview_java",
@@ -24,7 +26,9 @@
   sources = [
     "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridge.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationCoordinator.java",
+    "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationProperties.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java",
+    "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationViewBinder.java",
   ]
 
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
@@ -49,9 +53,41 @@
     "//base:base_java",
     "//base:base_java_test_support",
     "//base:base_junit_test_support",
+    "//chrome/browser/sync/android:java",
     "//chrome/test/android:chrome_java_unit_test_support",
     "//components/browser_ui/bottomsheet/android:java",
+    "//components/signin/public/android:java",
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
   ]
 }
+
+android_library("javatests") {
+  testonly = true
+  resources_package =
+      "org.chromium.chrome.browser.touch_to_fill.password_generation"
+
+  sources = [ "java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationRenderTest.java" ]
+
+  deps = [
+    ":java",
+    ":java_resources",
+    "//base:base_java",
+    "//base:base_java_test_support",
+    "//chrome/android:chrome_java",
+    "//chrome/browser/flags:java",
+    "//chrome/browser/sync/android:java",
+    "//chrome/browser/ui/android/night_mode:night_mode_java_test_support",
+    "//chrome/test/android:chrome_java_integration_test_support",
+    "//components/browser_ui/bottomsheet/android:java",
+    "//components/browser_ui/bottomsheet/android:java_resources",
+    "//components/browser_ui/bottomsheet/android/test:java",
+    "//components/signin/public/android:java",
+    "//content/public/test/android:content_java_test_support",
+    "//third_party/androidx:androidx_test_runner_java",
+    "//third_party/junit:junit",
+    "//third_party/mockito:mockito_java",
+    "//ui/android:ui_java_test_support",
+    "//ui/android:ui_no_recycler_view_java",
+  ]
+}
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml b/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml
index 6d740c38..940c695b 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/res/layout/touch_to_fill_password_generation.xml
@@ -44,6 +44,7 @@
       android:layout_height="52dp"
       android:layout_marginHorizontal="@dimen/ttf_sheet_padding"
       android:layout_marginBottom="24dp"
+      android:gravity="center"
       android:background="@drawable/touch_to_fill_credential_background_modern_rounded_all"
       android:singleLine="true"
       android:textAppearance="@style/TextAppearance.TextMedium.Primary" />
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridge.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridge.java
index 59b980a..c62fb384 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridge.java
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationBridge.java
@@ -38,8 +38,8 @@
     }
 
     @CalledByNative
-    public boolean show() {
-        return mCoordinator.show();
+    public boolean show(String generatedPassword, String account) {
+        return mCoordinator.show(generatedPassword, account);
     }
 
     @CalledByNative
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationCoordinator.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationCoordinator.java
index b5c3a36..6ba194de 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationCoordinator.java
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationCoordinator.java
@@ -4,14 +4,17 @@
 
 package org.chromium.chrome.browser.touch_to_fill.password_generation;
 
-import android.content.Context;
+import static org.chromium.chrome.browser.touch_to_fill.password_generation.TouchToFillPasswordGenerationProperties.ACCOUNT_EMAIL;
+import static org.chromium.chrome.browser.touch_to_fill.password_generation.TouchToFillPasswordGenerationProperties.GENERATED_PASSWORD;
 
-import androidx.annotation.VisibleForTesting;
+import android.content.Context;
 
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
 import org.chromium.components.browser_ui.bottomsheet.EmptyBottomSheetObserver;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
 /**
  * Coordinates the password generation bottom sheet functionality. It shows the bottom sheet, fills
@@ -44,7 +47,14 @@
     /**
      *  Displays the bottom sheet.
      */
-    public boolean show() {
+    public boolean show(String generatedPassword, String account) {
+        PropertyModel model =
+                new PropertyModel.Builder(TouchToFillPasswordGenerationProperties.ALL_KEYS)
+                        .with(ACCOUNT_EMAIL, account)
+                        .with(GENERATED_PASSWORD, generatedPassword)
+                        .build();
+        setUpModelChangeProcessors(model, mTouchToFillPasswordGenerationView);
+
         mBottomSheetController.addObserver(mBottomSheetObserver);
         if (mBottomSheetController.requestShowContent(mTouchToFillPasswordGenerationView, true)) {
             return true;
@@ -63,8 +73,15 @@
         mTouchToFillPasswordGenerationDelegate.onDismissed();
     }
 
-    @VisibleForTesting
-    TouchToFillPasswordGenerationView getViewForTesting() {
-        return mTouchToFillPasswordGenerationView;
+    /**
+     * Connects the given model with the given view using Model Change Processors.
+     * @param model A {@link PropertyModel} built with {@link
+     *         TouchToFillPasswordGenerationProperties}.
+     * @param view A {@link TouchToFillPasswordGenerationView}.
+     */
+    private static void setUpModelChangeProcessors(
+            PropertyModel model, TouchToFillPasswordGenerationView view) {
+        PropertyModelChangeProcessor.create(
+                model, view, TouchToFillPasswordGenerationViewBinder::bindView);
     }
 }
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java
index ed6f3b7..4834fc6f 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationModuleTest.java
@@ -50,6 +50,8 @@
     private TouchToFillPasswordGenerationBridge.Natives mBridgeJniMock;
 
     private static final long sDummyNativePointer = 1;
+    private static final String sTestEmailAddress = "test@email.com";
+    private static final String sGeneratedPassword = "Strong generated password";
 
     @Before
     public void setUp() {
@@ -67,7 +69,7 @@
 
     @Test
     public void showsAndHidesBottomSheet() {
-        mBridge.show();
+        mBridge.show(sGeneratedPassword, sTestEmailAddress);
         verify(mBottomSheetController).requestShowContent(any(), anyBoolean());
         verify(mBottomSheetController).addObserver(any());
 
@@ -78,7 +80,7 @@
 
     @Test
     public void testBottomSheetForceHide() {
-        mBridge.show();
+        mBridge.show(sGeneratedPassword, sTestEmailAddress);
         verify(mBottomSheetController).requestShowContent(any(), anyBoolean());
 
         mBridge.hide();
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationProperties.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationProperties.java
new file mode 100644
index 0000000..0443c574
--- /dev/null
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationProperties.java
@@ -0,0 +1,22 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.touch_to_fill.password_generation;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
+
+/**
+ *  Properties defined here reflect the visible state of the TouchToFillPasswordGeneration
+ * component.
+ */
+class TouchToFillPasswordGenerationProperties {
+    public static final ReadableObjectPropertyKey<String> ACCOUNT_EMAIL =
+            new ReadableObjectPropertyKey<>();
+    public static final ReadableObjectPropertyKey<String> GENERATED_PASSWORD =
+            new ReadableObjectPropertyKey<>();
+
+    public static final PropertyKey[] ALL_KEYS =
+            new PropertyKey[] {ACCOUNT_EMAIL, GENERATED_PASSWORD};
+}
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationRenderTest.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationRenderTest.java
new file mode 100644
index 0000000..3c6a019
--- /dev/null
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationRenderTest.java
@@ -0,0 +1,121 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.touch_to_fill.password_generation;
+
+import static org.chromium.base.test.util.ApplicationTestUtils.finishActivity;
+import static org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils.tearDownNightModeAfterChromeActivityDestroyed;
+import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
+import static org.chromium.ui.base.LocalizationUtils.setRtlForTesting;
+
+import android.view.View;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+
+import org.chromium.base.test.params.ParameterAnnotations;
+import org.chromium.base.test.params.ParameterSet;
+import org.chromium.base.test.params.ParameterizedRunner;
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
+import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
+import org.chromium.chrome.test.util.ChromeRenderTestRule;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
+import org.chromium.components.browser_ui.bottomsheet.BottomSheetTestSupport;
+import org.chromium.ui.test.util.RenderTestRule.Component;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * These tests render screenshots of touch to fill for credit cards sheet and compare them to a gold
+ * standard.
+ */
+@RunWith(ParameterizedRunner.class)
+@Batch(Batch.PER_CLASS)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class TouchToFillPasswordGenerationRenderTest {
+    @ParameterAnnotations.ClassParameter
+    private static List<ParameterSet> sClassParams =
+            Arrays.asList(new ParameterSet().value(false, false).name("Default"),
+                    new ParameterSet().value(false, true).name("RTL"),
+                    new ParameterSet().value(true, false).name("NightMode"));
+
+    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+
+    @Rule
+    public final ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setRevision(0)
+                    .setBugComponent(Component.UI_BROWSER_AUTOFILL)
+                    .build();
+
+    @Mock
+    private TouchToFillPasswordGenerationCoordinator.Delegate mDelegateMock;
+
+    private BottomSheetController mBottomSheetController;
+    private TouchToFillPasswordGenerationCoordinator mCoordinator;
+    private static final String sGeneratedPassword = "Strong generated password";
+    private static final String sTestEmailAddress = "test@email.com";
+
+    public TouchToFillPasswordGenerationRenderTest(boolean nightModeEnabled, boolean useRtlLayout) {
+        setRtlForTesting(useRtlLayout);
+        ChromeNightModeTestUtils.setUpNightModeForChromeActivity(nightModeEnabled);
+        mRenderTestRule.setNightModeEnabled(nightModeEnabled);
+        mRenderTestRule.setVariantPrefix(useRtlLayout ? "RTL" : "LTR");
+    }
+
+    @Before
+    public void setUp() throws InterruptedException {
+        MockitoAnnotations.openMocks(this);
+        mActivityTestRule.startMainActivityOnBlankPage();
+        mActivityTestRule.waitForActivityCompletelyLoaded();
+        mBottomSheetController = mActivityTestRule.getActivity()
+                                         .getRootUiCoordinatorForTesting()
+                                         .getBottomSheetController();
+        runOnUiThreadBlocking(() -> {
+            mCoordinator = new TouchToFillPasswordGenerationCoordinator(
+                    mActivityTestRule.getActivity(), mBottomSheetController, mDelegateMock);
+        });
+    }
+
+    @After
+    public void tearDown() {
+        setRtlForTesting(false);
+        try {
+            finishActivity(mActivityTestRule.getActivity());
+        } catch (Exception e) {
+            // Activity was already closed (e.g. due to last test tearing down the suite).
+        }
+        runOnUiThreadBlocking(() -> tearDownNightModeAfterChromeActivityDestroyed());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"RenderTest"})
+    public void testShowsOneCard() throws IOException {
+        runOnUiThreadBlocking(() -> { mCoordinator.show(sGeneratedPassword, sTestEmailAddress); });
+        BottomSheetTestSupport.waitForOpen(mBottomSheetController);
+
+        View bottomSheetView = mActivityTestRule.getActivity().findViewById(R.id.bottom_sheet);
+        mRenderTestRule.render(bottomSheetView, "touch_to_fill_password_generation_bottom_sheet");
+    }
+}
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java
index c5506192..fcd49f8 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationView.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.touch_to_fill.password_generation;
 
 import android.content.Context;
+import android.graphics.Typeface;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
@@ -22,8 +23,10 @@
  */
 class TouchToFillPasswordGenerationView implements BottomSheetContent {
     private final View mContent;
+    private final Context mContext;
 
     TouchToFillPasswordGenerationView(Context context) {
+        mContext = context;
         mContent = LayoutInflater.from(context).inflate(
                 R.layout.touch_to_fill_password_generation, null);
         ImageView sheetHeaderImage = mContent.findViewById(R.id.touch_to_fill_sheet_header_image);
@@ -36,6 +39,22 @@
                         "elisa.becket@gmail.com"));
     }
 
+    void setSheetSubtitle(String accountEmail) {
+        TextView sheetSubtitleView = mContent.findViewById(R.id.touch_to_fill_sheet_subtitle);
+        String sheetSubtitle = accountEmail.isEmpty()
+                ? mContext.getString(R.string.password_generation_bottom_sheet_subtitle_no_account)
+                : String.format(
+                        mContext.getString(R.string.password_generation_bottom_sheet_subtitle),
+                        accountEmail);
+        sheetSubtitleView.setText(sheetSubtitle);
+    }
+
+    void setGeneratedPassword(String generatedPassword) {
+        TextView passwordView = mContent.findViewById(R.id.password);
+        passwordView.setTypeface(Typeface.MONOSPACE);
+        passwordView.setText(generatedPassword);
+    }
+
     @Override
     public View getContentView() {
         return mContent;
diff --git a/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationViewBinder.java b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationViewBinder.java
new file mode 100644
index 0000000..59dd4a2
--- /dev/null
+++ b/chrome/browser/touch_to_fill/password_generation/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/password_generation/TouchToFillPasswordGenerationViewBinder.java
@@ -0,0 +1,32 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.touch_to_fill.password_generation;
+
+import static org.chromium.chrome.browser.touch_to_fill.password_generation.TouchToFillPasswordGenerationProperties.ACCOUNT_EMAIL;
+import static org.chromium.chrome.browser.touch_to_fill.password_generation.TouchToFillPasswordGenerationProperties.GENERATED_PASSWORD;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Maps the {@link TouchToFillPasswordGenerationProperties} model properties to the {@link
+ * TouchToFillPasswordGenerationView}.
+ */
+class TouchToFillPasswordGenerationViewBinder {
+    /**
+     * Called whenever a property in the given model changes. It updates the given view accordingly.
+     * @param model The observed {@link PropertyModel}. Its data need to be reflected in the view.
+     * @param view The {@link TouchToFillPasswordGenerationView} to update.
+     * @param propertyKey The {@link PropertyKey} which changed.
+     */
+    static void bindView(
+            PropertyModel model, TouchToFillPasswordGenerationView view, PropertyKey propertyKey) {
+        if (propertyKey == ACCOUNT_EMAIL) {
+            view.setSheetSubtitle(model.get(ACCOUNT_EMAIL));
+        } else if (propertyKey == GENERATED_PASSWORD) {
+            view.setGeneratedPassword(model.get(GENERATED_PASSWORD));
+        }
+    }
+}
diff --git a/chrome/browser/touch_to_fill/password_generation/android/mock_touch_to_fill_password_generation_bridge.h b/chrome/browser/touch_to_fill/password_generation/android/mock_touch_to_fill_password_generation_bridge.h
index 4828727..af70568 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/mock_touch_to_fill_password_generation_bridge.h
+++ b/chrome/browser/touch_to_fill/password_generation/android/mock_touch_to_fill_password_generation_bridge.h
@@ -19,7 +19,9 @@
   MOCK_METHOD(bool,
               Show,
               (content::WebContents*,
-               base::WeakPtr<TouchToFillPasswordGenerationDelegate>),
+               base::WeakPtr<TouchToFillPasswordGenerationDelegate>,
+               std::u16string,
+               std::string),
               (override));
   MOCK_METHOD(void, Hide, (), (override));
   MOCK_METHOD(void, OnDismissed, (JNIEnv * env), (override));
diff --git a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge.h b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge.h
index 31b5c3da..4c3e114 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge.h
+++ b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_TOUCH_TO_FILL_PASSWORD_GENERATION_ANDROID_TOUCH_TO_FILL_PASSWORD_GENERATION_BRIDGE_H_
 
 #include <jni.h>
+#include <string>
 
 #include "content/public/browser/web_contents.h"
 
@@ -17,7 +18,9 @@
 
   virtual bool Show(
       content::WebContents* web_contents,
-      base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate_) = 0;
+      base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate_,
+      std::u16string password,
+      std::string account) = 0;
   virtual void Hide() = 0;
   virtual void OnDismissed(JNIEnv* env) = 0;
 };
diff --git a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.cc b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.cc
index b356f89..2e908c1b 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.cc
+++ b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.h"
 
+#include "base/android/jni_string.h"
+#include "base/android/scoped_java_ref.h"
 #include "base/check.h"
 #include "chrome/browser/touch_to_fill/password_generation/android/jni_headers/TouchToFillPasswordGenerationBridge_jni.h"
 #include "components/password_manager/core/common/password_manager_features.h"
@@ -22,7 +24,9 @@
 
 bool TouchToFillPasswordGenerationBridgeImpl::Show(
     content::WebContents* web_contents,
-    base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate) {
+    base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate,
+    std::u16string password,
+    std::string account) {
   if (!web_contents->GetNativeView() ||
       !web_contents->GetNativeView()->GetWindowAndroid()) {
     return false;
@@ -35,8 +39,14 @@
       web_contents->GetNativeView()->GetWindowAndroid()->GetJavaObject(),
       reinterpret_cast<intptr_t>(this)));
 
-  return Java_TouchToFillPasswordGenerationBridge_show(
-      base::android::AttachCurrentThread(), java_object_);
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jstring> j_password =
+      base::android::ConvertUTF16ToJavaString(env, password);
+  base::android::ScopedJavaLocalRef<jstring> j_account =
+      base::android::ConvertUTF8ToJavaString(env, account);
+
+  return Java_TouchToFillPasswordGenerationBridge_show(env, java_object_,
+                                                       j_password, j_account);
 }
 
 void TouchToFillPasswordGenerationBridgeImpl::Hide() {
diff --git a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.h b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.h
index 86f38810..4595283c 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.h
+++ b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge_impl.h
@@ -21,9 +21,10 @@
       const TouchToFillPasswordGenerationBridgeImpl&) = delete;
   ~TouchToFillPasswordGenerationBridgeImpl() override;
 
-  bool Show(
-      content::WebContents* web_contents,
-      base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate) override;
+  bool Show(content::WebContents* web_contents,
+            base::WeakPtr<TouchToFillPasswordGenerationDelegate> delegate,
+            std::u16string password,
+            std::string account) override;
 
   void Hide() override;
 
diff --git a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.cc b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.cc
index ffd9203..852078e 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.cc
+++ b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.cc
@@ -6,10 +6,12 @@
 
 #include <algorithm>
 #include <memory>
+#include <string>
 #include "base/check.h"
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge.h"
 #include "chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
@@ -44,8 +46,12 @@
   });
 }
 
-bool TouchToFillPasswordGenerationController::ShowTouchToFill() {
-  if (!bridge_->Show(web_contents_, base::AsWeakPtr(this))) {
+bool TouchToFillPasswordGenerationController::ShowTouchToFill(
+    std::u16string generated_password,
+    std::string account_display_name) {
+  if (!bridge_->Show(web_contents_, base::AsWeakPtr(this),
+                     std::move(generated_password),
+                     std::move(account_display_name))) {
     return false;
   }
 
diff --git a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.h b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.h
index e2ebe082..61839cd 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.h
+++ b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_TOUCH_TO_FILL_PASSWORD_GENERATION_ANDROID_TOUCH_TO_FILL_PASSWORD_GENERATION_CONTROLLER_H_
 #define CHROME_BROWSER_TOUCH_TO_FILL_PASSWORD_GENERATION_ANDROID_TOUCH_TO_FILL_PASSWORD_GENERATION_CONTROLLER_H_
 
+#include <string>
 #include "base/allocator/partition_allocator/pointers/raw_ptr.h"
 #include "base/functional/callback_forward.h"
 #include "chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_bridge.h"
@@ -38,7 +39,8 @@
   ~TouchToFillPasswordGenerationController() override;
 
   // Shows the password generation bottom sheet.
-  bool ShowTouchToFill();
+  bool ShowTouchToFill(std::u16string generated_password,
+                       std::string account_display_name);
 
   void OnDismissed() override;
 
diff --git a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller_unittest.cc b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller_unittest.cc
index e0ca2de..12b7ebe 100644
--- a/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller_unittest.cc
+++ b/chrome/browser/touch_to_fill/password_generation/android/touch_to_fill_password_generation_controller_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <memory>
+#include <string>
 
 #include "base/functional/callback_forward.h"
 #include "base/memory/weak_ptr.h"
@@ -19,6 +20,9 @@
 #include "ui/base/ime/mojom/text_input_state.mojom.h"
 #include "ui/base/ime/text_input_type.h"
 
+using testing::_;
+using testing::Eq;
+
 class TouchToFillPasswordGenerationControllerTest
     : public ChromeRenderViewHostTestHarness {
  public:
@@ -35,6 +39,8 @@
   }
 
   base::MockCallback<base::OnceCallback<void()>> on_dismissed_callback_;
+  const std::u16string test_generated_password_ = u"Strong generated password";
+  const std::string test_user_account_ = "test@email.com";
 
  private:
   std::unique_ptr<password_manager::ContentPasswordManagerDriver>
@@ -51,7 +57,7 @@
       password_mananger_driver(), web_contents(), std::move(bridge),
       on_dismissed_callback_.Get());
   EXPECT_CALL(*bridge_ptr, Show);
-  controller->ShowTouchToFill();
+  controller->ShowTouchToFill(test_generated_password_, test_user_account_);
 
   ui::mojom::TextInputStatePtr initial_state = ui::mojom::TextInputState::New();
   initial_state->type = ui::TEXT_INPUT_TYPE_PASSWORD;
@@ -81,7 +87,7 @@
       std::make_unique<MockTouchToFillPasswordGenerationBridge>(),
       on_dismissed_callback_.Get());
 
-  controller->ShowTouchToFill();
+  controller->ShowTouchToFill(test_generated_password_, test_user_account_);
 
   EXPECT_CALL(on_dismissed_callback_, Run);
   controller->OnDismissed();
@@ -95,8 +101,9 @@
       password_mananger_driver(), web_contents(), std::move(bridge),
       on_dismissed_callback_.Get());
 
-  EXPECT_CALL(*bridge_ptr, Show);
-  controller->ShowTouchToFill();
+  EXPECT_CALL(*bridge_ptr,
+              Show(_, _, Eq(test_generated_password_), Eq(test_user_account_)));
+  controller->ShowTouchToFill(test_generated_password_, test_user_account_);
 
   EXPECT_CALL(*bridge_ptr, Hide);
   controller.reset();
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller.cc
index 25665ed..d66ff22 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller.cc
@@ -69,14 +69,23 @@
   if (!view_)
     view_ = TouchToFillViewFactory::Create(this);
 
+  int flags = TouchToFillView::kNone;
+  if (delegate_->ShouldTriggerSubmission()) {
+    flags |= TouchToFillView::kTriggerSubmission;
+  }
+  if (password_manager_launcher::CanManagePasswordsWhenPasskeysPresent()) {
+    flags |= TouchToFillView::kCanManagePasswordsWhenPasskeysPresent;
+  }
+  if (delegate_->ShouldShowHybridOption()) {
+    flags |= TouchToFillView::kShouldShowHybridOption;
+  }
+
   GURL url = delegate_->GetFrameUrl();
   view_->Show(
       url,
       TouchToFillView::IsOriginSecure(
           network::IsOriginPotentiallyTrustworthy(url::Origin::Create(url))),
-      SortCredentials(credentials), passkey_credentials,
-      delegate_->ShouldTriggerSubmission(),
-      password_manager_launcher::CanManagePasswordsWhenPasskeysPresent());
+      SortCredentials(credentials), passkey_credentials, flags);
   touch_to_fill_state_ = TouchToFillState::kIsShowing;
 }
 
@@ -106,6 +115,12 @@
                                      base::Unretained(this)));
 }
 
+void TouchToFillController::OnHybridSignInSelected() {
+  view_.reset();
+  delegate_->OnHybridSignInSelected(base::BindOnce(
+      &TouchToFillController::ActionCompleted, base::Unretained(this)));
+}
+
 void TouchToFillController::OnDismiss() {
   view_.reset();
   // Unretained is safe here because TouchToFillController owns the delegate.
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller.h b/chrome/browser/touch_to_fill/touch_to_fill_controller.h
index 6752d90..8604421 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller.h
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller.h
@@ -50,6 +50,10 @@
   // password management screen is displayed.
   void OnManagePasswordsSelected(bool passkeys_shown);
 
+  // Informs the controller that the user has tapped the "Use Passkey on a
+  // Different Device" option, which initiates hybrid passkey sign-in.
+  void OnHybridSignInSelected();
+
   // Informs the controller that the user has dismissed the sheet. No-op if
   // invoked repeatedly.
   void OnDismiss();
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
index d323335..7811d8b 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.cc
@@ -66,21 +66,25 @@
     password_manager::PasswordManagerClient* password_client,
     scoped_refptr<device_reauth::DeviceAuthenticator> authenticator,
     base::WeakPtr<password_manager::PasswordManagerDriver> driver,
-    autofill::mojom::SubmissionReadinessState submission_readiness)
+    autofill::mojom::SubmissionReadinessState submission_readiness,
+    ShowHybridOption should_show_hybrid_option)
     : password_client_(password_client),
       authenticator_(std::move(authenticator)),
       driver_(std::move(driver)),
-      submission_readiness_(submission_readiness) {}
+      submission_readiness_(submission_readiness),
+      should_show_hybrid_option_(should_show_hybrid_option) {}
 
 TouchToFillControllerAutofillDelegate::TouchToFillControllerAutofillDelegate(
     ChromePasswordManagerClient* password_client,
     scoped_refptr<device_reauth::DeviceAuthenticator> authenticator,
     base::WeakPtr<password_manager::PasswordManagerDriver> driver,
-    autofill::mojom::SubmissionReadinessState submission_readiness)
+    autofill::mojom::SubmissionReadinessState submission_readiness,
+    ShowHybridOption should_show_hybrid_option)
     : password_client_(password_client),
       authenticator_(std::move(authenticator)),
       driver_(driver),
       submission_readiness_(submission_readiness),
+      should_show_hybrid_option_(should_show_hybrid_option),
       source_id_(password_client->web_contents()
                      ->GetPrimaryMainFrame()
                      ->GetPageUkmSourceId()) {}
@@ -177,6 +181,21 @@
   std::move(action_complete).Run();
 }
 
+void TouchToFillControllerAutofillDelegate::OnHybridSignInSelected(
+    base::OnceClosure action_complete) {
+  if (!driver_) {
+    return;
+  }
+
+  password_client_->GetWebAuthnCredentialsDelegateForDriver(driver_.get())
+      ->ShowAndroidHybridSignIn();
+
+  CleanUpDriverAndReportOutcome(TouchToFillOutcome::kHybridSignInSelected,
+                                /*show_virtual_keyboard=*/false);
+
+  std::move(action_complete).Run();
+}
+
 void TouchToFillControllerAutofillDelegate::OnDismiss(
     base::OnceClosure action_complete) {
   if (!driver_)
@@ -198,6 +217,10 @@
   return trigger_submission_;
 }
 
+bool TouchToFillControllerAutofillDelegate::ShouldShowHybridOption() {
+  return should_show_hybrid_option_.value();
+}
+
 gfx::NativeView TouchToFillControllerAutofillDelegate::GetNativeView() {
   // It is not a |ChromePasswordManagerClient| only in
   // TouchToFillControllerTest.
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.h b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.h
index 1fcdb8b..2befee0 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.h
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.h
@@ -11,6 +11,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/types/pass_key.h"
+#include "base/types/strong_alias.h"
 #include "chrome/browser/touch_to_fill/touch_to_fill_controller_delegate.h"
 #include "components/autofill/core/common/mojom/autofill_types.mojom.h"
 #include "components/device_reauth/device_authenticator.h"
@@ -32,6 +33,8 @@
 class TouchToFillControllerAutofillDelegate
     : public TouchToFillControllerDelegate {
  public:
+  using ShowHybridOption = base::StrongAlias<struct ShowHybridOptionTag, bool>;
+
   // The action a user took when interacting with the Touch To Fill sheet.
   //
   // These values are persisted to logs. Entries should not be renumbered and
@@ -45,6 +48,7 @@
     kDismissed = 1,
     kSelectedManagePasswords = 2,
     kSelectedPasskeyCredential = 3,
+    kSelectedHybrid = 4,
   };
 
   // The final outcome that closes the Touch To Fill sheet.
@@ -58,7 +62,8 @@
     kReauthenticationFailed = 2,
     kManagePasswordsSelected = 3,
     kPasskeyCredentialSelected = 4,
-    kMaxValue = kPasskeyCredentialSelected,
+    kHybridSignInSelected = 5,
+    kMaxValue = kHybridSignInSelected,
   };
 
   // No-op constructor for tests.
@@ -67,13 +72,15 @@
       password_manager::PasswordManagerClient* password_client,
       scoped_refptr<device_reauth::DeviceAuthenticator> authenticator,
       base::WeakPtr<password_manager::PasswordManagerDriver> driver,
-      autofill::mojom::SubmissionReadinessState submission_readiness);
+      autofill::mojom::SubmissionReadinessState submission_readiness,
+      ShowHybridOption should_show_hybrid_option);
 
   TouchToFillControllerAutofillDelegate(
       ChromePasswordManagerClient* password_client,
       scoped_refptr<device_reauth::DeviceAuthenticator> authenticator,
       base::WeakPtr<password_manager::PasswordManagerDriver> driver,
-      autofill::mojom::SubmissionReadinessState submission_readiness);
+      autofill::mojom::SubmissionReadinessState submission_readiness,
+      ShowHybridOption should_show_hybrid_option);
   TouchToFillControllerAutofillDelegate(
       const TouchToFillControllerAutofillDelegate&) = delete;
   TouchToFillControllerAutofillDelegate& operator=(
@@ -91,9 +98,11 @@
       base::OnceClosure action_completed) override;
   void OnManagePasswordsSelected(bool passkeys_shown,
                                  base::OnceClosure action_completed) override;
+  void OnHybridSignInSelected(base::OnceClosure action_completed) override;
   void OnDismiss(base::OnceClosure action_completed) override;
   const GURL& GetFrameUrl() override;
   bool ShouldTriggerSubmission() override;
+  bool ShouldShowHybridOption() override;
   gfx::NativeView GetNativeView() override;
 
  private:
@@ -132,6 +141,9 @@
   // filled in.
   bool trigger_submission_ = false;
 
+  // Whether the controller should show an option for passkey hybrid sign-in.
+  ShowHybridOption should_show_hybrid_option_ = ShowHybridOption(false);
+
   ukm::SourceId source_id_ = ukm::kInvalidSourceId;
 };
 
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
index 979cfc0..4f39d88 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate_unittest.cc
@@ -94,8 +94,7 @@
                IsOriginSecure,
                base::span<const UiCredential>,
                base::span<const PasskeyCredential>,
-               bool,
-               bool),
+               int),
               (override));
   MOCK_METHOD(void, OnCredentialSelected, (const UiCredential&));
   MOCK_METHOD(void, OnDismiss, ());
@@ -167,10 +166,13 @@
 
   std::unique_ptr<TouchToFillControllerAutofillDelegate>
   MakeTouchToFillControllerDelegate(
-      autofill::mojom::SubmissionReadinessState submission_readiness) {
+      autofill::mojom::SubmissionReadinessState submission_readiness,
+      TouchToFillControllerAutofillDelegate::ShowHybridOption
+          should_show_hybrid_option) {
     return std::make_unique<TouchToFillControllerAutofillDelegate>(
         base::PassKey<TouchToFillControllerAutofillTest>(), &client_,
-        authenticator_, driver().AsWeakPtr(), submission_readiness);
+        authenticator_, driver().AsWeakPtr(), submission_readiness,
+        should_show_hybrid_option);
   }
 
   password_manager::MockWebAuthnCredentialsDelegate*
@@ -211,12 +213,12 @@
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(credentials),
                    ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+                   TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   // Test that we correctly log the absence of an Android credential.
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
@@ -255,12 +257,12 @@
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(credentials),
                    ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/true,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+                   TouchToFillView::kTriggerSubmission));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kTwoFields));
+          autofill::mojom::SubmissionReadinessState::kTwoFields,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
@@ -285,12 +287,12 @@
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(credentials),
                    ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+                   TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
@@ -319,12 +321,12 @@
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(credentials),
                    ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/true,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+                   TouchToFillView::kTriggerSubmission));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kTwoFields));
+          autofill::mojom::SubmissionReadinessState::kTwoFields,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   // The user picks the credential with an empty username, submission should not
   // be triggered.
@@ -352,12 +354,12 @@
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(credentials),
                    ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+                   TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kTwoFields));
+          autofill::mojom::SubmissionReadinessState::kTwoFields,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   // Filling doesn't trigger submission.
   EXPECT_CALL(driver(), TriggerFormSubmission()).Times(0);
@@ -373,16 +375,15 @@
   UiCredential credentials[] = {
       MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   // Test that we correctly log the absence of an Android credential.
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
@@ -412,16 +413,15 @@
   UiCredential credentials[] = {
       MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/true,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kTriggerSubmission));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kTwoFields));
+          autofill::mojom::SubmissionReadinessState::kTwoFields,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"alice"),
                                        std::u16string(u"p4ssw0rd")));
@@ -445,16 +445,15 @@
   UiCredential credentials[] = {
       MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(), FillSuggestion(_, _)).Times(0);
   EXPECT_CALL(driver(),
@@ -480,7 +479,8 @@
   touch_to_fill_controller().Show(
       {}, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
   histogram_tester().ExpectUniqueSample(
       "PasswordManager.TouchToFill.NumCredentialsShown", 0, 1);
 }
@@ -492,16 +492,15 @@
   UiCredential credentials[] = {
       MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL("http://example.com")), IsOriginSecure(false),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL("http://example.com")),
+                           IsOriginSecure(false), ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 }
 
 TEST_F(TouchToFillControllerAutofillTest, Show_And_Fill_Android_Credential) {
@@ -521,16 +520,15 @@
       }),
   };
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   // Test that we correctly log the presence of an Android credential.
   EXPECT_CALL(driver(), FillSuggestion(std::u16string(u"bob"),
@@ -580,32 +578,30 @@
   });
 
   UiCredential credentials[] = {alice, bob, charlie, david};
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   testing::ElementsAre(charlie, alice, bob, david),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           testing::ElementsAre(charlie, alice, bob, david),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 }
 
 TEST_F(TouchToFillControllerAutofillTest, Dismiss) {
   UiCredential credentials[] = {
       MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(),
               KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(true)));
@@ -636,12 +632,12 @@
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(credentials),
                    ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+                   TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(),
               KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
@@ -670,16 +666,15 @@
   UiCredential credentials[] = {
       MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       credentials, {},
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(*authenticator(), CanAuthenticateWithBiometrics)
       .WillOnce(Return(true));
@@ -708,13 +703,12 @@
   EXPECT_CALL(*weak_view,
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(std::vector<UiCredential>()),
-                   ElementsAreArray(credentials),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+                   ElementsAreArray(credentials), TouchToFillView::kNone));
   touch_to_fill_controller().Show(
       {}, credentials,
       MakeTouchToFillControllerDelegate(
-          autofill::mojom::SubmissionReadinessState::kNoInformation));
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(*webauthn_credentials_delegate(),
               SelectPasskey(base::Base64Encode(credential.credential_id())));
@@ -730,6 +724,29 @@
       1);
 }
 
+TEST_F(TouchToFillControllerAutofillTest, ShowAndSelectHybrid) {
+  UiCredential credentials[] = {
+      MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
+
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           TouchToFillView::kShouldShowHybridOption));
+  touch_to_fill_controller().Show(
+      credentials, {},
+      MakeTouchToFillControllerDelegate(
+          autofill::mojom::SubmissionReadinessState::kNoInformation,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(true)));
+
+  EXPECT_CALL(*webauthn_credentials_delegate(), ShowAndroidHybridSignIn());
+  touch_to_fill_controller().OnHybridSignInSelected();
+  histogram_tester().ExpectUniqueSample(
+      "PasswordManager.TouchToFill.Outcome",
+      TouchToFillControllerAutofillDelegate::TouchToFillOutcome::
+          kHybridSignInSelected,
+      1);
+}
+
 class TouchToFillControllerAutofillTestWithSubmissionReadinessVariationTest
     : public TouchToFillControllerAutofillTest,
       public testing::WithParamInterface<SubmissionReadinessState> {};
@@ -744,25 +761,28 @@
 
   // If there is no field after the password and both username and password
   // fields are there, then submit the form.
-  bool submission_expected =
-      submission_readiness == SubmissionReadinessState::kEmptyFields ||
+  int show_flags = TouchToFillView::kNone;
+  if (submission_readiness == SubmissionReadinessState::kEmptyFields ||
       submission_readiness == SubmissionReadinessState::kMoreThanTwoFields ||
-      submission_readiness == SubmissionReadinessState::kTwoFields;
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/submission_expected,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+      submission_readiness == SubmissionReadinessState::kTwoFields) {
+    show_flags |= TouchToFillView::kTriggerSubmission;
+  }
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           show_flags));
   touch_to_fill_controller().Show(
-      credentials, {}, MakeTouchToFillControllerDelegate(submission_readiness));
+      credentials, {},
+      MakeTouchToFillControllerDelegate(
+          submission_readiness,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(),
               KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(false)));
   EXPECT_CALL(driver(),
               FillSuggestion(credential.username(), credential.password()));
   EXPECT_CALL(driver(), TriggerFormSubmission())
-      .Times(submission_expected ? 1 : 0);
+      .Times((show_flags & TouchToFillView::kTriggerSubmission) ? 1 : 0);
 
   touch_to_fill_controller().OnCredentialSelected(credential);
 }
@@ -776,14 +796,15 @@
   UiCredential credentials[] = {
       MakeUiCredential({.username = "alice", .password = "p4ssw0rd"})};
 
-  EXPECT_CALL(view(),
-              Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
-                   ElementsAreArray(credentials),
-                   ElementsAreArray(std::vector<PasskeyCredential>()),
-                   /*trigger_submission=*/_,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(credentials),
+                           ElementsAreArray(std::vector<PasskeyCredential>()),
+                           /*flags=*/_));
   touch_to_fill_controller().Show(
-      credentials, {}, MakeTouchToFillControllerDelegate(submission_readiness));
+      credentials, {},
+      MakeTouchToFillControllerDelegate(
+          submission_readiness,
+          TouchToFillControllerAutofillDelegate::ShowHybridOption(false)));
 
   EXPECT_CALL(driver(),
               KeyboardReplacingSurfaceClosed(ToShowVirtualKeyboard(true)));
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_delegate.h b/chrome/browser/touch_to_fill/touch_to_fill_controller_delegate.h
index ed6c076..ec56cd3 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_delegate.h
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_delegate.h
@@ -47,6 +47,8 @@
       bool passkeys_shown,
       base::OnceClosure action_completed) = 0;
 
+  virtual void OnHybridSignInSelected(base::OnceClosure action_completed) = 0;
+
   // Informs the controller that the user has dismissed the sheet. No-op if
   // invoked repeatedly.
   virtual void OnDismiss(base::OnceClosure action_completed) = 0;
@@ -59,6 +61,10 @@
   // a password credential.
   virtual bool ShouldTriggerSubmission() = 0;
 
+  // Indicates whether the view should display an option to activate hybrid
+  // sign-in for passkeys.
+  virtual bool ShouldShowHybridOption() = 0;
+
   // The web page view containing the focused field.
   virtual gfx::NativeView GetNativeView() = 0;
 };
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.cc
index 48722f3..a8f5e07 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.cc
@@ -19,8 +19,10 @@
 #include "url/gurl.h"
 
 TouchToFillControllerWebAuthnDelegate::TouchToFillControllerWebAuthnDelegate(
-    WebAuthnRequestDelegateAndroid* request_delegate)
-    : request_delegate_(request_delegate) {}
+    WebAuthnRequestDelegateAndroid* request_delegate,
+    bool should_show_hybrid_option)
+    : request_delegate_(request_delegate),
+      should_show_hybrid_option_(should_show_hybrid_option) {}
 
 TouchToFillControllerWebAuthnDelegate::
     ~TouchToFillControllerWebAuthnDelegate() = default;
@@ -52,6 +54,12 @@
   OnDismiss(std::move(action_complete));
 }
 
+void TouchToFillControllerWebAuthnDelegate::OnHybridSignInSelected(
+    base::OnceClosure action_complete) {
+  request_delegate_->ShowHybridSignIn();
+  std::move(action_complete).Run();
+}
+
 void TouchToFillControllerWebAuthnDelegate::OnDismiss(
     base::OnceClosure action_complete) {
   request_delegate_->OnWebAuthnAccountSelected(std::vector<uint8_t>());
@@ -66,6 +74,10 @@
   return false;
 }
 
+bool TouchToFillControllerWebAuthnDelegate::ShouldShowHybridOption() {
+  return should_show_hybrid_option_;
+}
+
 gfx::NativeView TouchToFillControllerWebAuthnDelegate::GetNativeView() {
   return request_delegate_->web_contents()->GetNativeView();
 }
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.h b/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.h
index e0854e2..221a8b36 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.h
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate.h
@@ -27,7 +27,8 @@
     : public TouchToFillControllerDelegate {
  public:
   explicit TouchToFillControllerWebAuthnDelegate(
-      WebAuthnRequestDelegateAndroid* delegate);
+      WebAuthnRequestDelegateAndroid* delegate,
+      bool should_show_hybrid_option);
 
   TouchToFillControllerWebAuthnDelegate(
       const TouchToFillControllerWebAuthnDelegate&) = delete;
@@ -47,14 +48,18 @@
       base::OnceClosure action_completed) override;
   void OnManagePasswordsSelected(bool passkeys_shown,
                                  base::OnceClosure action_completed) override;
+  void OnHybridSignInSelected(base::OnceClosure action_completed) override;
   void OnDismiss(base::OnceClosure action_completed) override;
   const GURL& GetFrameUrl() override;
   bool ShouldTriggerSubmission() override;
+  bool ShouldShowHybridOption() override;
   gfx::NativeView GetNativeView() override;
 
  private:
   // Raw pointer to the request delegate that owns this.
   raw_ptr<WebAuthnRequestDelegateAndroid> request_delegate_ = nullptr;
+
+  bool should_show_hybrid_option_;
 };
 
 #endif  // CHROME_BROWSER_TOUCH_TO_FILL_TOUCH_TO_FILL_CONTROLLER_WEBAUTHN_DELEGATE_H_
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate_unittest.cc b/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate_unittest.cc
index 7e3f440b..7863854 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate_unittest.cc
+++ b/chrome/browser/touch_to_fill/touch_to_fill_controller_webauthn_delegate_unittest.cc
@@ -61,6 +61,7 @@
               OnWebAuthnAccountSelected,
               (const std::vector<uint8_t>& id),
               (override));
+  MOCK_METHOD(void, ShowHybridSignIn, (), (override));
 };
 
 struct MockTouchToFillView : public TouchToFillView {
@@ -70,8 +71,7 @@
                IsOriginSecure,
                base::span<const UiCredential>,
                base::span<const PasskeyCredential>,
-               bool,
-               bool),
+               int),
               (override));
   MOCK_METHOD(void, OnCredentialSelected, (const UiCredential&));
   MOCK_METHOD(void, OnDismiss, ());
@@ -122,9 +122,9 @@
   }
 
   std::unique_ptr<TouchToFillControllerWebAuthnDelegate>
-  MakeTouchToFillControllerDelegate() {
+  MakeTouchToFillControllerDelegate(bool should_show_hybrid_option) {
     return std::make_unique<TouchToFillControllerWebAuthnDelegate>(
-        request_delegate_.get());
+        request_delegate_.get(), should_show_hybrid_option);
   }
 
  private:
@@ -140,11 +140,10 @@
   EXPECT_CALL(view(),
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(std::vector<UiCredential>()),
-                   ElementsAreArray(credentials),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
-  touch_to_fill_controller().Show({}, credentials,
-                                  MakeTouchToFillControllerDelegate());
+                   ElementsAreArray(credentials), TouchToFillView::kNone));
+  touch_to_fill_controller().Show(
+      {}, credentials,
+      MakeTouchToFillControllerDelegate(/*should_show_hybrid_option=*/false));
 
   EXPECT_CALL(request_delegate(), OnWebAuthnAccountSelected(kCredentialId1));
   touch_to_fill_controller().OnPasskeyCredentialSelected(credentials[0]);
@@ -157,11 +156,10 @@
   EXPECT_CALL(view(),
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(std::vector<UiCredential>()),
-                   ElementsAreArray(credentials),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
-  touch_to_fill_controller().Show({}, credentials,
-                                  MakeTouchToFillControllerDelegate());
+                   ElementsAreArray(credentials), TouchToFillView::kNone));
+  touch_to_fill_controller().Show(
+      {}, credentials,
+      MakeTouchToFillControllerDelegate(/*should_show_hybrid_option=*/false));
 
   EXPECT_CALL(request_delegate(), OnWebAuthnAccountSelected(kCredentialId2));
   touch_to_fill_controller().OnPasskeyCredentialSelected(credentials[1]);
@@ -173,15 +171,28 @@
   EXPECT_CALL(view(),
               Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
                    ElementsAreArray(std::vector<UiCredential>()),
-                   ElementsAreArray(credentials),
-                   /*trigger_submission=*/false,
-                   /*can_manage_passwords_when_passkeys_present*/ false));
-  touch_to_fill_controller().Show({}, credentials,
-                                  MakeTouchToFillControllerDelegate());
+                   ElementsAreArray(credentials), TouchToFillView::kNone));
+  touch_to_fill_controller().Show(
+      {}, credentials,
+      MakeTouchToFillControllerDelegate(/*should_show_hybrid_option=*/false));
 
   EXPECT_CALL(request_delegate(),
               OnWebAuthnAccountSelected(std::vector<uint8_t>()));
   touch_to_fill_controller().Close();
 }
 
+TEST_F(TouchToFillControllerWebAuthnTest, ShowAndSelectHybrid) {
+  std::vector<PasskeyCredential> credentials({CreatePasskey()});
+
+  EXPECT_CALL(view(), Show(Eq(GURL(kExampleCom)), IsOriginSecure(true),
+                           ElementsAreArray(std::vector<UiCredential>()),
+                           ElementsAreArray(credentials),
+                           TouchToFillView::kShouldShowHybridOption));
+  touch_to_fill_controller().Show(
+      {}, credentials,
+      MakeTouchToFillControllerDelegate(/*should_show_hybrid_option=*/true));
+  EXPECT_CALL(request_delegate(), ShowHybridSignIn());
+  touch_to_fill_controller().OnHybridSignInSelected();
+}
+
 }  // namespace
diff --git a/chrome/browser/touch_to_fill/touch_to_fill_view.h b/chrome/browser/touch_to_fill/touch_to_fill_view.h
index a47176e..edab15c9 100644
--- a/chrome/browser/touch_to_fill/touch_to_fill_view.h
+++ b/chrome/browser/touch_to_fill/touch_to_fill_view.h
@@ -21,6 +21,21 @@
  public:
   using IsOriginSecure = base::StrongAlias<class IsOriginSecureTag, bool>;
 
+  enum ShowFlags {
+    kNone = 0,
+
+    // Indicates whether Touch To Fill will submit a form after filling.
+    kTriggerSubmission = 1 << 0,
+
+    // Indicates whether selecting the 'manage' button with passkeys available
+    // will show a screen that also allows management of passwords.
+    kCanManagePasswordsWhenPasskeysPresent = 1 << 1,
+
+    // Indicates whether the footer should contain a button that invokes hybrid
+    // passkey sign-in.
+    kShouldShowHybridOption = 1 << 2,
+  };
+
   TouchToFillView() = default;
   TouchToFillView(const TouchToFillView&) = delete;
   TouchToFillView& operator=(const TouchToFillView&) = delete;
@@ -29,18 +44,15 @@
   // Instructs Touch To Fill to show the provided `credentials` to the user.
   // `formatted_url` contains a human friendly version of the current origin.
   // `is_origin_secure` indicates whether the current frame origin is secure.
-  // `trigger_submission` indicates whether Touch To Fill will submit a form
-  // after filling. `can_manage_passwords_when_passkeys_present` indicates
-  // whether selecting the 'manage' button with passkeys available will show a
-  // screen that also allows management of passwords. After user interaction
-  // either OnCredentialSelected() or OnDismiss() gets invoked.
+  // `flags` is a combination of bits that affect the behaviors listed in the
+  // `ShowFlags` enum. After user interaction either OnCredentialSelected() or
+  // OnDismiss() gets invoked.
   virtual void Show(
       const GURL& url,
       IsOriginSecure is_origin_secure,
       base::span<const password_manager::UiCredential> credentials,
       base::span<const password_manager::PasskeyCredential> passkey_credentials,
-      bool trigger_submission,
-      bool can_manage_passwords_when_passkeys_present) = 0;
+      int flags) = 0;
 
   // Invoked in case the user chooses an entry from the credential list
   // presented to them.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f4515b8..0eb77add 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -825,6 +825,10 @@
   if (enable_waffle_desktop) {
     deps += [ "//chrome/browser/ui/webui/waffle:mojo_bindings" ]
     sources += [
+      "views/waffle/waffle_dialog_view.cc",
+      "views/waffle/waffle_dialog_view.h",
+      "waffle/waffle_tab_helper.cc",
+      "waffle/waffle_tab_helper.h",
       "webui/waffle/waffle_handler.cc",
       "webui/waffle/waffle_handler.h",
       "webui/waffle/waffle_ui.cc",
@@ -4749,6 +4753,8 @@
       "views/device_chooser_content_view.h",
       "views/devtools_process_observer.cc",
       "views/devtools_process_observer.h",
+      "views/download/bubble/download_bubble_contents_view.cc",
+      "views/download/bubble/download_bubble_contents_view.h",
       "views/download/bubble/download_bubble_partial_view.cc",
       "views/download/bubble/download_bubble_partial_view.h",
       "views/download/bubble/download_bubble_row_list_view.cc",
diff --git a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
index b10dca36..0c74148 100644
--- a/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
+++ b/chrome/browser/ui/android/autofill/autofill_keyboard_accessory_view.cc
@@ -103,7 +103,7 @@
     Java_AutofillKeyboardAccessoryViewBridge_addToAutofillSuggestionArray(
         env, data_array, position++, ConvertUTF16ToJavaString(env, label),
         ConvertUTF16ToJavaString(env, sublabel), android_icon_id,
-        suggestion.frontend_id.as_popup_item_id(),
+        base::to_underlying(suggestion.popup_item_id),
         controller_->GetRemovalConfirmationText(i, nullptr, nullptr),
         ConvertUTF8ToJavaString(env, suggestion.feature_for_iph),
         url::GURLAndroid::FromNativeGURL(env, suggestion.custom_icon_url));
diff --git a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
index 70d36cb..fc48312 100644
--- a/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
+++ b/chrome/browser/ui/android/autofill/autofill_popup_view_android.cc
@@ -133,10 +133,10 @@
     bool is_deletable =
         controller_->GetRemovalConfirmationText(i, nullptr, nullptr);
     bool is_label_multiline =
-        suggestion.frontend_id ==
+        suggestion.popup_item_id ==
             PopupItemId::kInsecureContextPaymentDisabledMessage ||
-        suggestion.frontend_id == PopupItemId::kCreditCardSigninPromo ||
-        suggestion.frontend_id == PopupItemId::kMixedFormMessage;
+        suggestion.popup_item_id == PopupItemId::kCreditCardSigninPromo ||
+        suggestion.popup_item_id == PopupItemId::kMixedFormMessage;
 
     Java_AutofillPopupBridge_addToAutofillSuggestionArray(
         env, java_object_, data_array, i,
@@ -145,8 +145,9 @@
         base::android::ConvertUTF16ToJavaString(env, sublabel),
         base::android::ConvertUTF16ToJavaString(env, secondary_sublabel),
         base::android::ConvertUTF16ToJavaString(env, item_tag), android_icon_id,
-        suggestion.is_icon_at_start, suggestion.frontend_id.as_popup_item_id(),
-        is_deletable, is_label_multiline, /*isLabelBold*/ false,
+        suggestion.is_icon_at_start,
+        base::to_underlying(suggestion.popup_item_id), is_deletable,
+        is_label_multiline, /*isLabelBold*/ false,
         url::GURLAndroid::FromNativeGURL(env, suggestion.custom_icon_url));
   }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 79b36bef..ef62880 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -39,8 +39,6 @@
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.page_info.PageInfoController;
-import org.chromium.components.page_info.PageInfoDiscoverabilityMetrics;
-import org.chromium.components.page_info.PageInfoDiscoverabilityMetrics.DiscoverabilityAction;
 import org.chromium.components.permissions.PermissionDialogController;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.components.search_engines.TemplateUrlService.TemplateUrlServiceObserver;
@@ -95,8 +93,6 @@
     @ContentSettingsType
     private int mLastPermission = ContentSettingsType.DEFAULT;
     private final PageInfoIPHController mPageInfoIPHController;
-    private final PageInfoDiscoverabilityMetrics mDiscoverabilityMetrics =
-            new PageInfoDiscoverabilityMetrics();
     private final WindowAndroid mWindowAndroid;
 
     private boolean mUrlBarTextIsSearch = true;
@@ -637,9 +633,6 @@
         mModel.set(StatusProperties.STATUS_ICON_RESOURCE, permissionIconResource);
         Runnable finishIconAnimation = () -> updateLocationBarIcon(IconTransitionType.ROTATE);
         mPermissionTaskHandler.postDelayed(finishIconAnimation, mPermissionIconDisplayTimeoutMs);
-
-        mDiscoverabilityMetrics.recordDiscoverabilityAction(
-                DiscoverabilityAction.PERMISSION_ICON_SHOWN);
     }
 
     private void startIPH() {
@@ -676,7 +669,6 @@
             updateLocationBarIcon(IconTransitionType.ROTATE);
         }, mPermissionIconDisplayTimeoutMs);
         mIsStoreIconShowing = true;
-        mDiscoverabilityMetrics.recordDiscoverabilityAction(DiscoverabilityAction.STORE_ICON_SHOWN);
     }
 
     // Reset all customized icons' status to avoid different icons' conflicts.
@@ -698,13 +690,6 @@
 
     /** Notifies that the page info was opened. */
     void onPageInfoOpened() {
-        if (mLastPermission != ContentSettingsType.DEFAULT) {
-            mDiscoverabilityMetrics.recordDiscoverabilityAction(
-                    DiscoverabilityAction.PAGE_INFO_OPENED);
-        } else if (mIsStoreIconShowing) {
-            mDiscoverabilityMetrics.recordDiscoverabilityAction(
-                    DiscoverabilityAction.PAGE_INFO_OPENED_FROM_STORE_ICON);
-        }
         resetCustomIconsStatus();
         updateLocationBarIcon(IconTransitionType.CROSSFADE);
     }
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 9a16b3f..9ee5c30 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -5782,6 +5782,9 @@
       <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE" desc="The subtitle for the password generation bottom sheet.">
         You won’t need to remember this password. It will be saved to Google Password Manager for <ph name="USERNAME">%1$s<ex>elisa.becket@gmail.com</ex></ph>.
       </message>
+      <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE_NO_ACCOUNT" desc="The subtitle for the password generation bottom sheet.">
+        You won’t need to remember this password. It will be saved to Google Password Manager.
+      </message>
       <message name="IDS_PASSWORD_GENERATION_BOTTOM_SHEET_USE_PASSWORD_BUTTON" desc="Appears on the button, which reflects the user agreement to user the generated strong password.">
         Use password
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE_NO_ACCOUNT.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE_NO_ACCOUNT.png.sha1
new file mode 100644
index 0000000..fb2a73f
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_PASSWORD_GENERATION_BOTTOM_SHEET_SUBTITLE_NO_ACCOUNT.png.sha1
@@ -0,0 +1 @@
+39e3d78d8976187fb813d0f47b38ba7c342ec4a4
\ No newline at end of file
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc
index b11a932..fcd7f38c 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.cc
@@ -78,7 +78,7 @@
   front_element_ = absl::nullopt;
   for (int i = 0; i < GetLineCount(); ++i) {
     const Suggestion& suggestion = controller_->GetSuggestionAt(i);
-    if (suggestion.frontend_id != PopupItemId::kClearForm) {
+    if (suggestion.popup_item_id != PopupItemId::kClearForm) {
       labels_.push_back(CreateLabel(suggestion));
       continue;
     }
diff --git a/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter_unittest.cc b/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter_unittest.cc
index fbcf7fd..d1442ea 100644
--- a/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/memory/raw_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/types/cxx23_to_underlying.h"
 #include "build/build_config.h"
 #include "chrome/browser/autofill/mock_autofill_popup_controller.h"
 #include "chrome/browser/ui/autofill/autofill_keyboard_accessory_adapter.h"
@@ -99,8 +100,9 @@
 
 // Matcher returning true if suggestions have equal members.
 MATCHER_P(equalsSuggestion, other, "") {
-  if (arg.frontend_id != other.frontend_id) {
-    *result_listener << "has frontend_id " << arg.frontend_id;
+  if (arg.popup_item_id != other.popup_item_id) {
+    *result_listener << "has popup_item_id "
+                     << base::to_underlying(arg.popup_item_id);
     return false;
   }
   if (arg.main_text != other.main_text) {
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
index 66b0843..1489094a 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_impl.cc
@@ -173,7 +173,7 @@
   // Remove all the old data list values, which should always be at the top of
   // the list if they are present.
   while (!suggestions_.empty() &&
-         suggestions_[0].frontend_id == PopupItemId::kDatalistEntry) {
+         suggestions_[0].popup_item_id == PopupItemId::kDatalistEntry) {
     suggestions_.erase(suggestions_.begin());
   }
 
@@ -181,7 +181,7 @@
   // is one).
   if (values.empty()) {
     if (!suggestions_.empty() &&
-        suggestions_[0].frontend_id == PopupItemId::kSeparator) {
+        suggestions_[0].popup_item_id == PopupItemId::kSeparator) {
       suggestions_.erase(suggestions_.begin());
     }
 
@@ -196,7 +196,7 @@
 
   // Add a separator if there are any other values.
   if (!suggestions_.empty() &&
-      suggestions_[0].frontend_id != PopupItemId::kSeparator) {
+      suggestions_[0].popup_item_id != PopupItemId::kSeparator) {
     suggestions_.insert(suggestions_.begin(),
                         Suggestion(PopupItemId::kSeparator));
   }
@@ -207,7 +207,7 @@
     suggestions_[i].main_text =
         Suggestion::Text(values[i], Suggestion::Text::IsPrimary(true));
     suggestions_[i].labels = {{Suggestion::Text(labels[i])}};
-    suggestions_[i].frontend_id = PopupItemId::kDatalistEntry;
+    suggestions_[i].popup_item_id = PopupItemId::kDatalistEntry;
   }
 
   OnSuggestionsChanged();
@@ -318,7 +318,7 @@
 #endif
 
   if (web_contents_ &&
-      suggestion.frontend_id == PopupItemId::kVirtualCreditCardEntry) {
+      suggestion.popup_item_id == PopupItemId::kVirtualCreditCardEntry) {
     feature_engagement::TrackerFactory::GetForBrowserContext(
         web_contents_->GetBrowserContext())
         ->NotifyEvent("autofill_virtual_card_suggestion_accepted");
@@ -397,7 +397,7 @@
     std::u16string* body) {
   return delegate_->GetDeletionConfirmationText(
       suggestions_[list_index].main_text.value,
-      suggestions_[list_index].frontend_id,
+      suggestions_[list_index].popup_item_id,
       suggestions_[list_index].GetPayload<Suggestion::BackendId>(), title,
       body);
 }
@@ -415,7 +415,7 @@
     return false;
   if (!delegate_->RemoveSuggestion(
           suggestions_[list_index].main_text.value,
-          suggestions_[list_index].frontend_id,
+          suggestions_[list_index].popup_item_id,
           suggestions_[list_index].GetPayload<Suggestion::BackendId>())) {
     return false;
   }
@@ -442,7 +442,7 @@
 
   if (index) {
     DCHECK_LT(*index, suggestions_.size());
-    if (!CanAccept(GetSuggestionAt(*index).frontend_id.as_popup_item_id())) {
+    if (!CanAccept(GetSuggestionAt(*index).popup_item_id)) {
       index = absl::nullopt;
     }
   }
@@ -462,9 +462,9 @@
   if (suggestions_.empty()) {
     return false;
   }
-  Suggestion::FrontendId id = suggestions_[0].frontend_id;
-  return base::Contains(kItemsTriggeringFieldFilling, id) ||
-         id == PopupItemId::kScanCreditCard;
+  PopupItemId popup_item_id = suggestions_[0].popup_item_id;
+  return base::Contains(kItemsTriggeringFieldFilling, popup_item_id) ||
+         popup_item_id == PopupItemId::kScanCreditCard;
 }
 
 void AutofillPopupControllerImpl::SetSuggestions(
diff --git a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
index 1160deb..759252d8 100644
--- a/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
+++ b/chrome/browser/ui/autofill/autofill_popup_controller_unittest.cc
@@ -108,7 +108,7 @@
 
   void DidSelectSuggestion(const Suggestion& suggestion) override {}
   bool RemoveSuggestion(const std::u16string& value,
-                        Suggestion::FrontendId frontend_id,
+                        PopupItemId popup_item_id,
                         Suggestion::BackendId backend_id) override {
     return true;
   }
@@ -225,12 +225,13 @@
         autofill_manager(), autofill_driver());
   }
 
-  // Shows empty suggestions with the frontend ids passed as `ids`.
-  void ShowSuggestions(const std::vector<Suggestion::FrontendId>& ids) {
+  // Shows empty suggestions with the popup_item_id ids passed as
+  // `popup_item_ids`.
+  void ShowSuggestions(const std::vector<PopupItemId>& popup_item_ids) {
     std::vector<Suggestion> suggestions;
-    suggestions.reserve(ids.size());
-    for (Suggestion::FrontendId id : ids) {
-      suggestions.emplace_back("", "", "", id);
+    suggestions.reserve(popup_item_ids.size());
+    for (PopupItemId popup_item_id : popup_item_ids) {
+      suggestions.emplace_back("", "", "", popup_item_id);
     }
     popup_controller().Show(std::move(suggestions),
                             AutoselectFirstSuggestion(false));
@@ -292,8 +293,7 @@
 };
 
 TEST_F(AutofillPopupControllerUnitTest, RemoveSuggestion) {
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                   Suggestion::FrontendId(PopupItemId::kAddressEntry),
+  ShowSuggestions({PopupItemId::kAddressEntry, PopupItemId::kAddressEntry,
                    PopupItemId::kAutofillOptions});
 
   // Generate a popup, so it can be hidden later. It doesn't matter what the
@@ -314,7 +314,7 @@
 }
 
 TEST_F(AutofillPopupControllerUnitTest, UpdateDataListValues) {
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
 
   // Add one data list entry.
   std::u16string value1 = u"data list value 1";
@@ -334,19 +334,19 @@
   EXPECT_EQ(label1, result0.labels[0][0].value);
   EXPECT_EQ(std::u16string(), result0.additional_label);
   EXPECT_EQ(label1, popup_controller().GetSuggestionLabelsAt(0)[0][0].value);
-  EXPECT_EQ(PopupItemId::kDatalistEntry, result0.frontend_id);
+  EXPECT_EQ(PopupItemId::kDatalistEntry, result0.popup_item_id);
 
   Suggestion result1 = popup_controller().GetSuggestionAt(1);
   EXPECT_EQ(std::u16string(), result1.main_text.value);
   EXPECT_TRUE(result1.labels.empty());
   EXPECT_EQ(std::u16string(), result1.additional_label);
-  EXPECT_EQ(PopupItemId::kSeparator, result1.frontend_id);
+  EXPECT_EQ(PopupItemId::kSeparator, result1.popup_item_id);
 
   Suggestion result2 = popup_controller().GetSuggestionAt(2);
   EXPECT_EQ(std::u16string(), result2.main_text.value);
   EXPECT_TRUE(result2.labels.empty());
   EXPECT_EQ(std::u16string(), result2.additional_label);
-  EXPECT_EQ(PopupItemId::kAddressEntry, result2.frontend_id.as_popup_item_id());
+  EXPECT_EQ(PopupItemId::kAddressEntry, result2.popup_item_id);
 
   // Add two data list entries (which should replace the current one).
   std::u16string value2 = u"data list value 2";
@@ -373,16 +373,15 @@
   EXPECT_EQ(std::u16string(),
             popup_controller().GetSuggestionAt(1).additional_label);
   EXPECT_EQ(PopupItemId::kSeparator,
-            popup_controller().GetSuggestionAt(2).frontend_id);
+            popup_controller().GetSuggestionAt(2).popup_item_id);
 
   // Clear all data list values.
   data_list_values.clear();
   popup_controller().UpdateDataListValues(data_list_values, data_list_labels);
 
   ASSERT_EQ(1, popup_controller().GetLineCount());
-  EXPECT_EQ(
-      PopupItemId::kAddressEntry,
-      popup_controller().GetSuggestionAt(0).frontend_id.as_popup_item_id());
+  EXPECT_EQ(PopupItemId::kAddressEntry,
+            popup_controller().GetSuggestionAt(0).popup_item_id);
 }
 
 TEST_F(AutofillPopupControllerUnitTest, PopupsWithOnlyDataLists) {
@@ -405,7 +404,7 @@
   EXPECT_EQ(std::u16string(),
             popup_controller().GetSuggestionAt(0).additional_label);
   EXPECT_EQ(PopupItemId::kDatalistEntry,
-            popup_controller().GetSuggestionAt(0).frontend_id);
+            popup_controller().GetSuggestionAt(0).popup_item_id);
 
   // Clear datalist values and check that the popup becomes hidden.
   EXPECT_CALL(popup_controller(), Hide(PopupHidingReason::kNoSuggestions));
@@ -527,7 +526,7 @@
 // This is a regression test for crbug.com/521133 to ensure that we don't crash
 // when suggestions updates race with user selections.
 TEST_F(AutofillPopupControllerUnitTest, SelectInvalidSuggestion) {
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
 
   EXPECT_CALL(*delegate(), DidAcceptSuggestion).Times(0);
 
@@ -536,7 +535,7 @@
 }
 
 TEST_F(AutofillPopupControllerUnitTest, AcceptSuggestionRespectsTimeout) {
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
 
   // Calls before the threshold are ignored.
   EXPECT_CALL(*delegate(), DidAcceptSuggestion).Times(0);
@@ -550,7 +549,7 @@
 }
 
 TEST_F(AutofillPopupControllerUnitTest, AcceptSuggestionWithoutThreshold) {
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
 
   // Calls are accepted immediately.
   EXPECT_CALL(*delegate(), DidAcceptSuggestion).Times(1);
@@ -559,7 +558,7 @@
 
 TEST_F(AutofillPopupControllerUnitTest,
        AcceptSuggestionTimeoutIsUpdatedOnPopupMove) {
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
 
   // Calls before the threshold are ignored.
   EXPECT_CALL(*delegate(), DidAcceptSuggestion).Times(0);
@@ -570,7 +569,7 @@
   task_environment()->FastForwardBy(base::Milliseconds(400));
   // Show the suggestions again (simulating, e.g., a click somewhere slightly
   // different).
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
 
   EXPECT_CALL(*delegate(), DidAcceptSuggestion).Times(0);
   popup_controller().AcceptSuggestion(0);
@@ -677,7 +676,7 @@
 // Test for successfully firing controls changed event for popup show/hide.
 TEST_F(AutofillPopupControllerAccessibilityUnitTest,
        FireControlsChangedEventDuringShowAndHide) {
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
   // Manually fire the event for popup show since setting the test view results
   // in the fire controls changed event not being sent.
   popup_controller().FireControlsChangedEvent(true);
@@ -695,7 +694,7 @@
   EXPECT_CALL(mock_ax_platform_node_delegate_, GetFromTreeIDAndNodeID)
       .WillOnce(Return(nullptr));
 
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
   // Manually fire the event for popup show since setting the test view results
   // in the fire controls changed event not being sent.
   popup_controller().FireControlsChangedEvent(true);
@@ -710,7 +709,7 @@
   EXPECT_CALL(*autofill_popup_view_, GetAxUniqueId)
       .WillOnce(testing::Return(absl::nullopt));
 
-  ShowSuggestions({Suggestion::FrontendId(PopupItemId::kAddressEntry)});
+  ShowSuggestions({PopupItemId::kAddressEntry});
   // Manually fire the event for popup show since setting the test view results
   // in the fire controls changed event not being sent.
   popup_controller().FireControlsChangedEvent(true);
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 36ebcfa..a448113 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -102,7 +102,7 @@
 #include "url/origin.h"
 
 #if BUILDFLAG(IS_ANDROID)
-#include "chrome/browser/android/preferences/autofill/autofill_profile_bridge.h"
+#include "chrome/browser/android/preferences/autofill/settings_launcher_helper.h"
 #include "chrome/browser/android/signin/signin_bridge.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
@@ -1079,9 +1079,9 @@
          security_level != security_state::DANGEROUS;
 }
 
-void ChromeAutofillClient::ExecuteCommand(Suggestion::FrontendId id) {
+void ChromeAutofillClient::ExecuteCommand(PopupItemId popup_item_id) {
 #if BUILDFLAG(IS_ANDROID)
-  if (id.as_popup_item_id() == PopupItemId::kCreditCardSigninPromo) {
+  if (popup_item_id == PopupItemId::kCreditCardSigninPromo) {
     auto* window = web_contents()->GetNativeView()->GetWindowAndroid();
     if (window) {
       SigninBridge::LaunchSigninActivity(
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index d0261f7..65a0777 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -25,6 +25,7 @@
 #include "components/autofill/core/browser/payments/legal_message_line.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "content/public/browser/visibility.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -251,7 +252,7 @@
   void DidFillOrPreviewField(const std::u16string& autofilled_value,
                              const std::u16string& profile_full_name) override;
   bool IsContextSecure() const override;
-  void ExecuteCommand(Suggestion::FrontendId id) override;
+  void ExecuteCommand(PopupItemId popup_item_id) override;
   void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
   LogManager* GetLogManager() const override;
   FormInteractionsFlowId GetCurrentFormInteractionsFlowId() override;
diff --git a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc
index 42fe3c1e..800d0077 100644
--- a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.cc
@@ -6,7 +6,7 @@
 #include <string>
 #include "base/metrics/histogram_functions.h"
 #include "build/build_config.h"
-#include "chrome/browser/android/preferences/autofill/autofill_profile_bridge.h"
+#include "chrome/browser/android/preferences/autofill/settings_launcher_helper.h"
 #include "chrome/browser/autofill/manual_filling_controller.h"
 #include "chrome/browser/autofill/manual_filling_controller_impl.h"
 #include "chrome/browser/ui/android/autofill/snackbar/autofill_snackbar_view_android.h"
@@ -27,20 +27,17 @@
 void AutofillSnackbarControllerImpl::Show(
     AutofillSnackbarType autofill_snackbar_type) {
   CHECK_NE(autofill_snackbar_type, AutofillSnackbarType::kUnspecified);
-  autofill_snackbar_type_ = autofill_snackbar_type;
-  if (!autofill_snackbar_view_) {
-    autofill_snackbar_view_ = AutofillSnackbarView::Create(this);
+  if (autofill_snackbar_view_) {
+    // A snackbar is already showing. Ignore the new request.
+    return;
   }
+  autofill_snackbar_type_ = autofill_snackbar_type;
+  autofill_snackbar_view_ = AutofillSnackbarView::Create(this);
   autofill_snackbar_view_->Show();
   base::UmaHistogramBoolean(
       "Autofill.Snackbar." + GetSnackbarTypeForLogging() + ".Shown", true);
 }
 
-void AutofillSnackbarControllerImpl::SetViewForTesting(
-    AutofillSnackbarView* view) {
-  autofill_snackbar_view_ = view;
-}
-
 void AutofillSnackbarControllerImpl::OnActionClicked() {
   switch (autofill_snackbar_type_) {
     case AutofillSnackbarType::kVirtualCard:
diff --git a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.h b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.h
index b51db632..e886ef0 100644
--- a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl.h
@@ -27,7 +27,6 @@
 
   // Show the snackbar.
   void Show(AutofillSnackbarType autofill_snackbar_type);
-  void SetViewForTesting(AutofillSnackbarView* view);
 
   // AutofillSnackbarController:
   void OnActionClicked() override;
diff --git a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl_unittest.cc b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl_unittest.cc
index 71c052dd..b7284388 100644
--- a/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl_unittest.cc
+++ b/chrome/browser/ui/autofill/payments/autofill_snackbar_controller_impl_unittest.cc
@@ -20,13 +20,6 @@
 
 namespace autofill {
 
-class MockAutofillSnackbarView : public AutofillSnackbarView {
- public:
-  MockAutofillSnackbarView() = default;
-  void Show() override {}
-  void Dismiss() override {}
-};
-
 class AutofillSnackbarControllerImplTest
     : public ChromeRenderViewHostTestHarness {
  public:
@@ -34,7 +27,6 @@
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-    controller()->SetViewForTesting(new MockAutofillSnackbarView());
     ManualFillingControllerImpl::CreateForWebContentsForTesting(
         web_contents(), mock_pwd_controller_.AsWeakPtr(),
         mock_address_controller_.AsWeakPtr(), mock_cc_controller_.AsWeakPtr(),
@@ -65,8 +57,6 @@
       "Autofill.Snackbar.VirtualCard.ActionClicked", 1, 0);
   controller()->OnDismissed();
 
-  // Reset the mock view.
-  controller()->SetViewForTesting(new MockAutofillSnackbarView());
   controller()->Show(AutofillSnackbarType::kVirtualCard);
   controller()->OnActionClicked();
   // Verify that the count for both Shown and ActionClicked is incremented.
@@ -76,6 +66,25 @@
       "Autofill.Snackbar.VirtualCard.ActionClicked", 1, 1);
 }
 
+TEST_F(AutofillSnackbarControllerImplTest,
+       AttemptToShowDialogWhileAlreadyShowing) {
+  base::HistogramTester histogram_tester;
+  controller()->Show(AutofillSnackbarType::kVirtualCard);
+  // Verify that the count for Shown is incremented and ActionClicked hasn't
+  // changed.
+  histogram_tester.ExpectUniqueSample("Autofill.Snackbar.VirtualCard.Shown", 1,
+                                      1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.Snackbar.VirtualCard.ActionClicked", 1, 0);
+
+  // Attempt to show another dialog without dismissing the previous one.
+  controller()->Show(AutofillSnackbarType::kVirtualCard);
+
+  // Verify that the count for both Shown is not incremented.
+  histogram_tester.ExpectUniqueSample("Autofill.Snackbar.VirtualCard.Shown", 1,
+                                      1);
+}
+
 TEST_F(AutofillSnackbarControllerImplTest, MandatoryReauthTypeMetricsTest) {
   base::HistogramTester histogram_tester;
   controller()->Show(AutofillSnackbarType::kMandatoryReauth);
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
index f1dcaae5..601cfb90 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.cc
@@ -108,6 +108,12 @@
     PaymentsBubbleClosedReason closed_reason) {
   set_bubble_view(nullptr);
 
+// After resetting the raw pointer to the view in the base class, the Android
+// view has to be deleted.
+#if BUILDFLAG(IS_ANDROID)
+  view_android_.reset();
+#endif
+
   if (current_bubble_type_ == MandatoryReauthBubbleType::kOptIn) {
     if (closed_reason == PaymentsBubbleClosedReason::kAccepted) {
       std::move(accept_mandatory_reauth_callback_).Run();
@@ -144,13 +150,19 @@
 }
 
 void MandatoryReauthBubbleControllerImpl::DoShowBubble() {
-#if !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(IS_ANDROID)
+  // The Android view's lifecycle is managed by this controller. We also
+  // register it as a raw pointer in the base class to use its closing logic
+  // when this controller wants to close it.
+  view_android_ = MandatoryReauthOptInViewAndroid::CreateAndShow();
+  set_bubble_view(view_android_.get());
+#else
   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
   AutofillBubbleHandler* autofill_bubble_handler =
       browser->window()->GetAutofillBubbleHandler();
   set_bubble_view(autofill_bubble_handler->ShowMandatoryReauthBubble(
       web_contents(), this, /*is_user_gesture=*/false, current_bubble_type_));
-#endif  // !BUILDFLAG(IS_ANDROID)
+#endif  // BUILDFLAG(IS_ANDROID)
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(MandatoryReauthBubbleControllerImpl);
diff --git a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h
index dd6340c..7c25431 100644
--- a/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller_impl.h
@@ -12,6 +12,10 @@
 #include "chrome/browser/ui/autofill/payments/mandatory_reauth_bubble_controller.h"
 #include "content/public/browser/web_contents_user_data.h"
 
+#if BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/mandatory_reauth/android/mandatory_reauth_opt_in_view_android.h"
+#endif
+
 namespace autofill {
 
 class MandatoryReauthBubbleControllerImpl
@@ -60,6 +64,12 @@
   MandatoryReauthBubbleType current_bubble_type_ =
       MandatoryReauthBubbleType::kInactive;
 
+#if BUILDFLAG(IS_ANDROID)
+  // Handles Android view's lifecycle. The Desktop view is handled by the base
+  // class `AutofillBubbleControllerBase`.
+  std::unique_ptr<MandatoryReauthOptInViewAndroid> view_android_;
+#endif
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
 
diff --git a/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm
index d70a057..0f91cab 100644
--- a/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm
+++ b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller.mm
@@ -97,9 +97,9 @@
   for (int i = 0; i < _controller->GetLineCount() && i < maxTouchBarItems;
        i++) {
     const autofill::Suggestion& suggestion = _controller->GetSuggestionAt(i);
-    if (suggestion.frontend_id.as_popup_item_id() !=
-            autofill::PopupItemId::kAutocompleteEntry &&
-        !suggestion.frontend_id.is_an_address_or_card_popup_item_id()) {
+    if (suggestion.popup_item_id != autofill::PopupItemId::kAutocompleteEntry &&
+        suggestion.popup_item_id != autofill::PopupItemId::kAddressEntry &&
+        suggestion.popup_item_id != autofill::PopupItemId::kCreditCardEntry) {
       continue;
     }
 
diff --git a/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
index d7cb5b0b2..749393fc 100644
--- a/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
+++ b/chrome/browser/ui/cocoa/touchbar/credit_card_autofill_touch_bar_controller_unittest.mm
@@ -52,11 +52,11 @@
   }
 
   void SetSuggestions(
-      const std::vector<Suggestion::FrontendId>& frontends_ids) {
+      const std::vector<autofill::PopupItemId>& popup_item_ids) {
     std::vector<Suggestion> suggestions;
-    suggestions.reserve(frontends_ids.size());
-    for (Suggestion::FrontendId frontend_id : frontends_ids) {
-      suggestions.emplace_back("", "", "", frontend_id);
+    suggestions.reserve(popup_item_ids.size());
+    for (autofill::PopupItemId popup_item_id : popup_item_ids) {
+      suggestions.emplace_back("", "", "", popup_item_id);
     }
     SetSuggestions(std::move(suggestions));
   }
@@ -78,8 +78,8 @@
   EXPECT_FALSE([touch_bar_controller_ makeTouchBar]);
 
   [touch_bar_controller_ setIsCreditCardPopup:true];
-  SetSuggestions({Suggestion::FrontendId(PopupItemId::kCreditCardEntry),
-                  Suggestion::FrontendId(PopupItemId::kCreditCardEntry)});
+  SetSuggestions(
+      {PopupItemId::kCreditCardEntry, PopupItemId::kCreditCardEntry});
   NSTouchBar* touch_bar = [touch_bar_controller_ makeTouchBar];
   EXPECT_TRUE(touch_bar);
   EXPECT_TRUE([[touch_bar customizationIdentifier]
@@ -90,10 +90,9 @@
 // Tests to check that the touch bar doesn't show more than 3 items
 TEST_F(CreditCardAutofillTouchBarControllerUnitTest, TouchBarCardLimit) {
   [touch_bar_controller_ setIsCreditCardPopup:true];
-  SetSuggestions({Suggestion::FrontendId(PopupItemId::kCreditCardEntry),
-                  Suggestion::FrontendId(PopupItemId::kCreditCardEntry),
-                  Suggestion::FrontendId(PopupItemId::kCreditCardEntry),
-                  Suggestion::FrontendId(PopupItemId::kCreditCardEntry)});
+  SetSuggestions({PopupItemId::kCreditCardEntry, PopupItemId::kCreditCardEntry,
+                  PopupItemId::kCreditCardEntry,
+                  PopupItemId::kCreditCardEntry});
   NSTouchBar* touch_bar = [touch_bar_controller_ makeTouchBar];
   EXPECT_TRUE(touch_bar);
   EXPECT_TRUE([[touch_bar customizationIdentifier]
@@ -111,9 +110,8 @@
 // Tests for for the credit card button.
 TEST_F(CreditCardAutofillTouchBarControllerUnitTest, CreditCardButtonCheck) {
   [touch_bar_controller_ setIsCreditCardPopup:true];
-  SetSuggestions(
-      {Suggestion("bufflehead", "canvasback", "goldeneye",
-                  Suggestion::FrontendId(PopupItemId::kCreditCardEntry))});
+  SetSuggestions({Suggestion("bufflehead", "canvasback", "goldeneye",
+                             PopupItemId::kCreditCardEntry)});
   NSButton* button = [touch_bar_controller_ createCreditCardButtonAtRow:0];
   EXPECT_TRUE(button);
   EXPECT_EQ(0, [button tag]);
diff --git a/chrome/browser/ui/fast_checkout/fast_checkout_controller_impl.cc b/chrome/browser/ui/fast_checkout/fast_checkout_controller_impl.cc
index 9af061b..03afd54 100644
--- a/chrome/browser/ui/fast_checkout/fast_checkout_controller_impl.cc
+++ b/chrome/browser/ui/fast_checkout/fast_checkout_controller_impl.cc
@@ -4,7 +4,7 @@
 
 #include "chrome/browser/ui/fast_checkout/fast_checkout_controller_impl.h"
 
-#include "chrome/browser/android/preferences/autofill/autofill_profile_bridge.h"
+#include "chrome/browser/android/preferences/autofill/settings_launcher_helper.h"
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index f895075..b688e30 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -73,6 +73,7 @@
 #include "chrome/browser/safe_browsing/trigger_creator.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/sessions/session_tab_helper_factory.h"
+#include "chrome/browser/signin/signin_features.h"
 #include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
 #include "chrome/browser/ssl/connection_help_tab_helper.h"
 #include "chrome/browser/ssl/https_only_mode_tab_helper.h"
@@ -97,6 +98,7 @@
 #include "chrome/browser/ui/tab_dialogs.h"
 #include "chrome/browser/ui/tab_ui_helper.h"
 #include "chrome/browser/ui/thumbnails/thumbnail_tab_helper.h"
+#include "chrome/browser/ui/waffle/waffle_tab_helper.h"
 #include "chrome/browser/user_notes/user_notes_tab_helper.h"
 #include "chrome/browser/vr/vr_tab_helper.h"
 #include "chrome/common/buildflags.h"
@@ -498,6 +500,13 @@
   ManagePasswordsUIController::CreateForWebContents(web_contents);
   if (PrivacySandboxPromptHelper::ProfileRequiresPrompt(profile))
     PrivacySandboxPromptHelper::CreateForWebContents(web_contents);
+
+#if BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
+  if (base::FeatureList::IsEnabled(kWaffle)) {
+    WaffleTabHelper::CreateForWebContents(web_contents);
+  }
+#endif
+
   SadTabHelper::CreateForWebContents(web_contents);
   SearchTabHelper::CreateForWebContents(web_contents);
   TabDialogs::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
index af8c467..29253ed 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
@@ -86,9 +86,9 @@
                        : nullptr;
   }
 
-  views::StyledLabel* GetLiveTranslateLabel() {
+  views::StyledLabel* GetLanguageLabel() {
     return controller_
-               ? controller_->caption_bubble_->GetLiveTranslateLabelForTesting()
+               ? controller_->caption_bubble_->GetLanguageLabelForTesting()
                : nullptr;
   }
 
@@ -223,6 +223,15 @@
         caption_bubble_context, media::SpeechRecognitionResult(text, true));
   }
 
+  void OnLanguageIdentificationEvent(std::string language) {
+    media::mojom::LanguageIdentificationEventPtr event =
+        media::mojom::LanguageIdentificationEvent::New();
+    event->language = language;
+    event->asr_switch_result = media::mojom::AsrSwitchResult::kSwitchSucceeded;
+    GetController()->OnLanguageIdentificationEvent(GetCaptionBubbleContext(),
+                                                   event);
+  }
+
   void OnError() { OnError(GetCaptionBubbleContext()); }
 
   void OnError(CaptionBubbleContext* caption_bubble_context) {
@@ -1275,29 +1284,28 @@
 
   OnPartialTranscription("Penguins' feet change colors as they get older.");
   EXPECT_TRUE(IsWidgetVisible());
-  ASSERT_FALSE(GetLiveTranslateLabel()->GetVisible());
+  ASSERT_TRUE(GetLanguageLabel()->GetVisible());
 
   browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveTranslateEnabled,
                                                true);
   OnPartialTranscription(
       "Sea otters can hold their breath for over 5 minutes.");
-  ASSERT_TRUE(GetLiveTranslateLabel()->GetVisible());
+  ASSERT_TRUE(GetLanguageLabel()->GetVisible());
   EXPECT_EQ("Translating French to English",
-            base::UTF16ToUTF8(GetLiveTranslateLabel()->GetText()));
-  EXPECT_EQ(line_height, GetLiveTranslateLabel()->GetLineHeight());
+            base::UTF16ToUTF8(GetLanguageLabel()->GetText()));
+  EXPECT_EQ(line_height, GetLanguageLabel()->GetLineHeight());
 
   ui::CaptionStyle caption_style;
   caption_style.text_size = "200%";
   GetController()->UpdateCaptionStyle(caption_style);
-  EXPECT_EQ(line_height * 2, GetLiveTranslateLabel()->GetLineHeight());
+  EXPECT_EQ(line_height * 2, GetLanguageLabel()->GetLineHeight());
   caption_style.text_size = "50%";
   GetController()->UpdateCaptionStyle(caption_style);
-  EXPECT_EQ(line_height / 2, GetLiveTranslateLabel()->GetLineHeight());
+  EXPECT_EQ(line_height / 2, GetLanguageLabel()->GetLineHeight());
 
-  // Disabling Live Translate should hide the Live Translate label.
   browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveTranslateEnabled,
                                                false);
-  ASSERT_FALSE(GetLiveTranslateLabel()->GetVisible());
+  ASSERT_TRUE(GetLanguageLabel()->GetVisible());
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, HeaderView) {
@@ -1309,22 +1317,31 @@
   EXPECT_EQ(2u, GetHeader()->children().size());
   views::View* left_header_container = GetHeader()->children()[0];
 
-  // The left header container should contain the live translate label and the
+  // The left header container should contain the language label and the
   // caption settings icon.
   EXPECT_EQ(2u, left_header_container->children().size());
 
-  // With Live Translate disabled, only the caption settings icon should be
-  // visible in the left header container.
-  auto* live_translate_label = left_header_container->children()[0];
+  auto* language_label = left_header_container->children()[0];
   auto* caption_settings_icon = left_header_container->children()[1];
-  ASSERT_FALSE(live_translate_label->GetVisible());
+  ASSERT_TRUE(language_label->GetVisible());
   ASSERT_TRUE(caption_settings_icon->GetVisible());
-  ASSERT_EQ(0, static_cast<views::BoxLayout*>(
+  ASSERT_EQ(4, static_cast<views::BoxLayout*>(
                    left_header_container->GetLayoutManager())
                    ->inside_border_insets()
                    .left());
   EXPECT_EQ(464, left_header_container->GetPreferredSize().width());
 
+  EXPECT_EQ(u"Captioning English",
+            static_cast<views::StyledLabel*>(language_label)->GetText());
+
+  OnLanguageIdentificationEvent("fr-FR");
+  EXPECT_EQ(u"Captioning French (auto-detected)",
+            static_cast<views::StyledLabel*>(language_label)->GetText());
+
+  OnLanguageIdentificationEvent("en-GB");
+  EXPECT_EQ(u"Captioning English",
+            static_cast<views::StyledLabel*>(language_label)->GetText());
+
   // Enable Live Translate.
   browser()->profile()->GetPrefs()->SetString(
       prefs::kLiveTranslateTargetLanguageCode, "en");
@@ -1333,12 +1350,22 @@
   browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveTranslateEnabled,
                                                true);
 
-  ASSERT_TRUE(live_translate_label->GetVisible());
+  ASSERT_TRUE(language_label->GetVisible());
   ASSERT_TRUE(caption_settings_icon->GetVisible());
   ASSERT_EQ(4, static_cast<views::BoxLayout*>(
                    left_header_container->GetLayoutManager())
                    ->inside_border_insets()
                    .left());
+  EXPECT_EQ(u"Translating French to English",
+            static_cast<views::StyledLabel*>(language_label)->GetText());
+
+  OnLanguageIdentificationEvent("it-IT");
+  EXPECT_EQ(u"Translating Italian (auto-detected) to English",
+            static_cast<views::StyledLabel*>(language_label)->GetText());
+
+  OnLanguageIdentificationEvent("en-US");
+  EXPECT_EQ(u"Captioning English (auto-detected)",
+            static_cast<views::StyledLabel*>(language_label)->GetText());
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_strategy.cc b/chrome/browser/ui/views/autofill/popup/popup_row_strategy.cc
index d8722f0..f7d9181f 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_strategy.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_strategy.cc
@@ -373,7 +373,7 @@
     std::vector<std::unique_ptr<views::View>> subtext_views,
     PopupCellView& content_view) {
   bool has_control_element =
-      suggestion.frontend_id == PopupItemId::kAutocompleteEntry &&
+      suggestion.popup_item_id == PopupItemId::kAutocompleteEntry &&
       base::FeatureList::IsEnabled(
           features::kAutofillShowAutocompleteDeleteButton);
   views::BoxLayout& layout =
@@ -544,7 +544,8 @@
   set_size_ = 0;
   set_index_ = line_number + 1;
   for (int i = 0; i < controller->GetLineCount(); ++i) {
-    if (controller->GetSuggestionAt(i).frontend_id == PopupItemId::kSeparator) {
+    if (controller->GetSuggestionAt(i).popup_item_id ==
+        PopupItemId::kSeparator) {
       if (i < line_number) {
         --set_index_;
       }
@@ -719,7 +720,7 @@
   }
 
   // If the feature is enabled, autocomplete entries have a delete button.
-  if (GetController()->GetSuggestionAt(GetLineNumber()).frontend_id ==
+  if (GetController()->GetSuggestionAt(GetLineNumber()).popup_item_id ==
           PopupItemId::kAutocompleteEntry &&
       base::FeatureList::IsEnabled(
           features::kAutofillShowAutocompleteDeleteButton)) {
@@ -869,7 +870,7 @@
   std::unique_ptr<views::ImageView> icon = GetIconImageView(kSuggestion);
 
   const bool kUseLeadingIcon =
-      base::Contains(kItemTypesUsingLeadingIcons, kSuggestion.frontend_id);
+      base::Contains(kItemTypesUsingLeadingIcons, kSuggestion.popup_item_id);
 
   if (kSuggestion.is_loading) {
     view->AddChildView(std::make_unique<views::Throbber>())->Start();
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_strategy_unittest.cc b/chrome/browser/ui/views/autofill/popup/popup_row_strategy_unittest.cc
index 201528b9..92c752c 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_strategy_unittest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_strategy_unittest.cc
@@ -41,8 +41,8 @@
 };
 
 struct RowStrategyTestdata {
-  // The frontend ids of the suggestions to be shown.
-  std::vector<Suggestion::FrontendId> frontend_ids;
+  // The popup item ids of the suggestions to be shown.
+  std::vector<PopupItemId> popup_item_ids;
   // The index of the suggestion to be tested.
   int line_number;
   // The type of strategy to be tested.
@@ -55,39 +55,37 @@
 
 const RowStrategyTestdata kTestcases[] = {
     RowStrategyTestdata{
-        .frontend_ids = {Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                         Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                         PopupItemId::kSeparator,
-                         PopupItemId::kAutofillOptions},
+        .popup_item_ids = {PopupItemId::kAddressEntry,
+                           PopupItemId::kAddressEntry, PopupItemId::kSeparator,
+                           PopupItemId::kAutofillOptions},
         .line_number = 1,
         .strategy_type = StrategyType::kSuggestion,
         .set_size = 3,
         .set_index = 2,
     },
     RowStrategyTestdata{
-        .frontend_ids = {PopupItemId::kPasswordEntry,
-                         PopupItemId::kAccountStoragePasswordEntry,
-                         PopupItemId::kSeparator,
-                         PopupItemId::kAllSavedPasswordsEntry},
+        .popup_item_ids = {PopupItemId::kPasswordEntry,
+                           PopupItemId::kAccountStoragePasswordEntry,
+                           PopupItemId::kSeparator,
+                           PopupItemId::kAllSavedPasswordsEntry},
         .line_number = 0,
         .strategy_type = StrategyType::kPasswordSuggestion,
         .set_size = 3,
         .set_index = 1,
     },
     RowStrategyTestdata{
-        .frontend_ids = {Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                         Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                         PopupItemId::kSeparator,
-                         PopupItemId::kAutofillOptions},
+        .popup_item_ids = {PopupItemId::kAddressEntry,
+                           PopupItemId::kAddressEntry, PopupItemId::kSeparator,
+                           PopupItemId::kAutofillOptions},
         .line_number = 3,
         .strategy_type = StrategyType::kFooter,
         .set_size = 3,
         .set_index = 3,
     },
     RowStrategyTestdata{
-        .frontend_ids = {PopupItemId::kAutocompleteEntry,
-                         PopupItemId::kAutocompleteEntry,
-                         PopupItemId::kAutocompleteEntry},
+        .popup_item_ids = {PopupItemId::kAutocompleteEntry,
+                           PopupItemId::kAutocompleteEntry,
+                           PopupItemId::kAutocompleteEntry},
         .line_number = 1,
         .strategy_type = StrategyType::kSuggestion,
         .set_size = 3,
@@ -101,12 +99,12 @@
 class PopupRowStrategyTest : public ChromeViewsTestBase {
  public:
   // Sets suggestions in the mocked popup controller.
-  void SetSuggestions(const std::vector<Suggestion::FrontendId>& frontend_ids) {
+  void SetSuggestions(const std::vector<PopupItemId>& popup_item_ids) {
     std::vector<Suggestion> suggestions;
-    suggestions.reserve(frontend_ids.size());
-    for (Suggestion::FrontendId frontend_id : frontend_ids) {
+    suggestions.reserve(popup_item_ids.size());
+    for (PopupItemId popup_item_id : popup_item_ids) {
       // Create a suggestion with empty labels.
-      suggestions.emplace_back("Main text", "", "", frontend_id);
+      suggestions.emplace_back("Main text", "", "", popup_item_id);
     }
     controller().set_suggestions(std::move(suggestions));
   }
@@ -285,7 +283,7 @@
 TEST_P(PopupRowStrategyParametrizedTest, HasContentArea) {
   const RowStrategyTestdata kTestdata = GetParam();
 
-  SetSuggestions(kTestdata.frontend_ids);
+  SetSuggestions(kTestdata.popup_item_ids);
   std::unique_ptr<PopupRowStrategy> strategy =
       CreateStrategy(kTestdata.strategy_type, kTestdata.line_number);
 
@@ -296,7 +294,7 @@
 TEST_P(PopupRowStrategyParametrizedTest, ContentAreaCallbacksWork) {
   const RowStrategyTestdata kTestdata = GetParam();
 
-  SetSuggestions(kTestdata.frontend_ids);
+  SetSuggestions(kTestdata.popup_item_ids);
   std::unique_ptr<PopupRowStrategy> strategy =
       CreateStrategy(kTestdata.strategy_type, kTestdata.line_number);
 
@@ -308,7 +306,7 @@
 TEST_P(PopupRowStrategyParametrizedTest, DeletedControllerIsHandledGracefully) {
   const RowStrategyTestdata kTestdata = GetParam();
 
-  SetSuggestions(kTestdata.frontend_ids);
+  SetSuggestions(kTestdata.popup_item_ids);
   std::unique_ptr<PopupRowStrategy> strategy =
       CreateStrategy(kTestdata.strategy_type, kTestdata.line_number);
 
@@ -327,7 +325,7 @@
        SetsAccessibilityAttributesForContentArea) {
   const RowStrategyTestdata kTestdata = GetParam();
 
-  SetSuggestions(kTestdata.frontend_ids);
+  SetSuggestions(kTestdata.popup_item_ids);
   std::unique_ptr<PopupRowStrategy> strategy =
       CreateStrategy(kTestdata.strategy_type, kTestdata.line_number);
 
@@ -349,7 +347,7 @@
 TEST_P(PopupRowStrategyParametrizedTest, HasControlArea) {
   const RowStrategyTestdata kTestdata = GetParam();
 
-  SetSuggestions(kTestdata.frontend_ids);
+  SetSuggestions(kTestdata.popup_item_ids);
   std::unique_ptr<PopupRowStrategy> strategy =
       CreateStrategy(kTestdata.strategy_type, kTestdata.line_number);
 
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_view.cc b/chrome/browser/ui/views/autofill/popup/popup_row_view.cc
index 9d0b09c..a303526 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_row_view.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_row_view.cc
@@ -36,11 +36,11 @@
   base::WeakPtr<AutofillPopupController> controller = popup_view.controller();
   DCHECK(controller);
 
-  Suggestion::FrontendId frontend_id =
-      controller->GetSuggestionAt(line_number).frontend_id;
+  PopupItemId popup_item_id =
+      controller->GetSuggestionAt(line_number).popup_item_id;
   std::unique_ptr<PopupRowStrategy> strategy;
-  switch (frontend_id.as_popup_item_id()) {
-    // These frontend ids should never be displayed in a `PopupRowView`.
+  switch (popup_item_id) {
+    // These `popup_item_id` should never be displayed in a `PopupRowView`.
     case PopupItemId::kSeparator:
     case PopupItemId::kMixedFormMessage:
     case PopupItemId::kInsecureContextPaymentDisabledMessage:
@@ -53,7 +53,7 @@
                                                                    line_number);
       break;
     default:
-      if (IsFooterFrontendId(frontend_id.as_popup_item_id())) {
+      if (IsFooterFrontendId(popup_item_id)) {
         strategy =
             std::make_unique<PopupFooterStrategy>(controller, line_number);
       } else {
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views.cc
index 932d20f0..dda694f 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_views.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_views.cc
@@ -90,11 +90,10 @@
 
   // Separators are a special case: They belong into the footer iff the next
   // item exists and is a footer item.
-  PopupItemId frontend_id =
-      suggestions[line_number].frontend_id.as_popup_item_id();
-  return frontend_id == PopupItemId::kSeparator
+  PopupItemId popup_item_id = suggestions[line_number].popup_item_id;
+  return popup_item_id == PopupItemId::kSeparator
              ? IsFooterItem(suggestions, line_number + 1)
-             : IsFooterFrontendId(frontend_id);
+             : IsFooterFrontendId(popup_item_id);
 }
 
 }  // namespace
@@ -278,10 +277,10 @@
     if (index->second != PopupRowView::CellType::kContent) {
       return false;
     }
-    Suggestion::FrontendId frontend_id =
-        controller_->GetSuggestionAt(index->first).frontend_id;
-    if (!base::Contains(kItemsTriggeringFieldFilling, frontend_id) &&
-        frontend_id != PopupItemId::kScanCreditCard) {
+    PopupItemId popup_item_id =
+        controller_->GetSuggestionAt(index->first).popup_item_id;
+    if (!base::Contains(kItemsTriggeringFieldFilling, popup_item_id) &&
+        popup_item_id != PopupItemId::kScanCreditCard) {
       return false;
     }
   }
@@ -305,7 +304,7 @@
   }
 
   bool was_autocomplete =
-      controller_->GetSuggestionAt(index->first).frontend_id ==
+      controller_->GetSuggestionAt(index->first).popup_item_id ==
       PopupItemId::kAutocompleteEntry;
   if (!controller_->RemoveSuggestion(index->first)) {
     return false;
@@ -404,9 +403,7 @@
     for (; current_line_number < kSuggestions.size() &&
            !IsFooterItem(kSuggestions, current_line_number);
          ++current_line_number) {
-      Suggestion::FrontendId frontend_id =
-          kSuggestions[current_line_number].frontend_id;
-      switch (frontend_id.as_popup_item_id()) {
+      switch (kSuggestions[current_line_number].popup_item_id) {
         case PopupItemId::kSeparator:
           rows_.push_back(body_container->AddChildView(
               std::make_unique<PopupSeparatorView>()));
@@ -477,7 +474,7 @@
   for (; current_line_number < kSuggestions.size(); ++current_line_number) {
     DCHECK(IsFooterItem(kSuggestions, current_line_number));
     // The footer can contain either footer views or separator lines.
-    if (kSuggestions[current_line_number].frontend_id ==
+    if (kSuggestions[current_line_number].popup_item_id ==
         PopupItemId::kSeparator) {
       rows_.push_back(footer_container->AddChildView(
           std::make_unique<PopupSeparatorView>()));
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc
index cfcb966..529e3b9 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_views_browsertest.cc
@@ -35,14 +35,14 @@
 std::vector<Suggestion> CreateAutofillProfileSuggestions() {
   std::vector<Suggestion> suggestions;
   suggestions.emplace_back("123 Apple St.", "Charles", "accountIcon",
-                           Suggestion::FrontendId(PopupItemId::kAddressEntry));
+                           PopupItemId::kAddressEntry);
   suggestions.emplace_back("3734 Elvis Presley Blvd.", "Elvis", "accountIcon",
-                           Suggestion::FrontendId(PopupItemId::kAddressEntry));
+                           PopupItemId::kAddressEntry);
 
   suggestions.emplace_back(PopupItemId::kSeparator);
 
   Suggestion settings(l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_ADDRESSES));
-  settings.frontend_id = PopupItemId::kAutofillOptions;
+  settings.popup_item_id = PopupItemId::kAutofillOptions;
   settings.icon = "settingsIcon";
   suggestions.push_back(std::move(settings));
 
@@ -51,9 +51,9 @@
 
 std::vector<Suggestion> CreateAutocompleteSuggestions() {
   return {Suggestion("Autocomplete entry 1", "", "",
-                     Suggestion::FrontendId(PopupItemId::kAutocompleteEntry)),
+                     PopupItemId::kAutocompleteEntry),
           Suggestion("Autocomplete entry 2", "", "",
-                     Suggestion::FrontendId(PopupItemId::kAutocompleteEntry))};
+                     PopupItemId::kAutocompleteEntry)};
 }
 
 }  // namespace
@@ -127,7 +127,7 @@
   entry1.main_text.is_primary = Suggestion::Text::IsPrimary(true);
   entry1.additional_label =
       std::u16string(10, gfx::RenderText::kPasswordReplacementChar);
-  entry1.frontend_id = PopupItemId::kAccountStoragePasswordEntry;
+  entry1.popup_item_id = PopupItemId::kAccountStoragePasswordEntry;
   entry1.icon = "globeIcon";
   entry1.trailing_icon = "google";
   suggestions.push_back(std::move(entry1));
@@ -137,7 +137,7 @@
   entry2.main_text.is_primary = Suggestion::Text::IsPrimary(true);
   entry2.additional_label =
       std::u16string(6, gfx::RenderText::kPasswordReplacementChar);
-  entry2.frontend_id = PopupItemId::kPasswordEntry;
+  entry2.popup_item_id = PopupItemId::kPasswordEntry;
   entry2.icon = "globeIcon";
   entry2.trailing_icon = "";
   suggestions.push_back(std::move(entry2));
@@ -147,7 +147,7 @@
   // The entry to open settings.
   Suggestion settings(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS));
-  settings.frontend_id = PopupItemId::kAllSavedPasswordsEntry;
+  settings.popup_item_id = PopupItemId::kAllSavedPasswordsEntry;
   settings.icon = "settingsIcon";
   settings.trailing_icon = "googlePasswordManager";
   suggestions.push_back(std::move(settings));
@@ -160,7 +160,7 @@
                        InvokeUi_InsecureContext_PaymentDisabled) {
   Suggestion warning(
       l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION));
-  warning.frontend_id = PopupItemId::kInsecureContextPaymentDisabledMessage;
+  warning.popup_item_id = PopupItemId::kInsecureContextPaymentDisabledMessage;
   PrepareSuggestions({std::move(warning)});
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc b/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc
index 624b6d31..d03aafb 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_views_unittest.cc
@@ -109,7 +109,7 @@
     view().SchedulePaint();
   }
 
-  void CreateAndShowView(const std::vector<Suggestion::FrontendId>& ids) {
+  void CreateAndShowView(const std::vector<PopupItemId>& ids) {
     controller().set_suggestions(ids);
     CreateAndShowView();
   }
@@ -208,7 +208,7 @@
 };
 
 TEST_F(PopupViewViewsTest, ShowHideTest) {
-  CreateAndShowView({Suggestion::FrontendId(kAutocompleteEntry)});
+  CreateAndShowView({PopupItemId::kAutocompleteEntry});
   EXPECT_CALL(controller(), AcceptSuggestion).Times(0);
   EXPECT_CALL(controller(), AcceptSuggestionWithoutThreshold).Times(0);
   view().Hide();
@@ -217,8 +217,7 @@
 // This is a regression test for crbug.com/1113255.
 TEST_F(PopupViewViewsTest, ShowViewWithOnlyFooterItemsShouldNotCrash) {
   // Set suggestions to have only a footer item.
-  std::vector<Suggestion::FrontendId> suggestion_ids = {
-      PopupItemId::kClearForm};
+  std::vector<PopupItemId> suggestion_ids = {PopupItemId::kClearForm};
   controller().set_suggestions(suggestion_ids);
   CreateAndShowView();
 }
@@ -420,8 +419,8 @@
 }
 
 TEST_F(PopupViewViewsTest, MovingSelectionSkipsSeparator) {
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kSeparator, PopupItemId::kAutofillOptions});
+  CreateAndShowView({PopupItemId::kAddressEntry, PopupItemId::kSeparator,
+                     PopupItemId::kAutofillOptions});
   view().SetSelectedCell(CellIndex{0u, CellType::kContent});
 
   // Going one down skips the separator.
@@ -436,8 +435,7 @@
 }
 
 TEST_F(PopupViewViewsTest, MovingSelectionSkipsInsecureFormWarning) {
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kSeparator,
+  CreateAndShowView({PopupItemId::kAddressEntry, PopupItemId::kSeparator,
                      PopupItemId::kInsecureContextPaymentDisabledMessage});
   view().SetSelectedCell(CellIndex{0u, CellType::kContent});
 
@@ -460,8 +458,8 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(
       features::kAutofillPopupUseThresholdForKeyboardAndMobileAccept);
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kAutofillOptions});
+  CreateAndShowView(
+      {PopupItemId::kAddressEntry, PopupItemId::kAutofillOptions});
 
   // Select the first item.
   view().SetSelectedCell(CellIndex{0u, CellType::kContent});
@@ -477,8 +475,8 @@
 TEST_F(PopupViewViewsTest, FillContentOnEnterUsesThresholdIfFeatureEnabled) {
   base::test::ScopedFeatureList feature_list{
       features::kAutofillPopupUseThresholdForKeyboardAndMobileAccept};
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kAutofillOptions});
+  CreateAndShowView(
+      {PopupItemId::kAddressEntry, PopupItemId::kAutofillOptions});
 
   // Select the first item.
   view().SetSelectedCell(CellIndex{0u, CellType::kContent});
@@ -498,8 +496,8 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(
       features::kAutofillPopupUseThresholdForKeyboardAndMobileAccept);
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kAutofillOptions});
+  CreateAndShowView(
+      {PopupItemId::kAddressEntry, PopupItemId::kAutofillOptions});
 
   // Select the first item.
   view().SetSelectedCell(CellIndex{0u, CellType::kContent});
@@ -515,8 +513,8 @@
 TEST_F(PopupViewViewsTest, FillOnTabPressedUsesThresholdIfFeatureEnabled) {
   base::test::ScopedFeatureList feature_list{
       features::kAutofillPopupUseThresholdForKeyboardAndMobileAccept};
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kAutofillOptions});
+  CreateAndShowView(
+      {PopupItemId::kAddressEntry, PopupItemId::kAutofillOptions});
 
   // Select the first item.
   view().SetSelectedCell(CellIndex{0u, CellType::kContent});
@@ -530,8 +528,8 @@
 }
 
 TEST_F(PopupViewViewsTest, NoFillOnTabPressedWithModifiers) {
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kAutofillOptions});
+  CreateAndShowView(
+      {PopupItemId::kAddressEntry, PopupItemId::kAutofillOptions});
 
   // Select the first item.
   view().SetSelectedCell(CellIndex{0u, CellType::kContent});
@@ -551,8 +549,8 @@
 // a tab with the autofill settings).
 TEST_F(PopupViewViewsTest, NoAutofillOptionsTriggeredOnTabPressed) {
   // Set up the popup and select the options cell.
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kSeparator, PopupItemId::kAutofillOptions});
+  CreateAndShowView({PopupItemId::kAddressEntry, PopupItemId::kSeparator,
+                     PopupItemId::kAutofillOptions});
   view().SetSelectedCell(CellIndex{2u, CellType::kContent});
   EXPECT_EQ(view().GetSelectedCell(),
             absl::make_optional<CellIndex>(2u, CellType::kContent));
@@ -567,8 +565,8 @@
 // This is a regression test for crbug.com/1309431 to ensure that we don't crash
 // when we press tab before a line is selected.
 TEST_F(PopupViewViewsTest, TabBeforeSelectingALine) {
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     PopupItemId::kSeparator, PopupItemId::kAutofillOptions});
+  CreateAndShowView({PopupItemId::kAddressEntry, PopupItemId::kSeparator,
+                     PopupItemId::kAutofillOptions});
   EXPECT_FALSE(view().GetSelectedCell().has_value());
 
   // The following should not crash:
@@ -576,8 +574,7 @@
 }
 
 TEST_F(PopupViewViewsTest, RemoveLine) {
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     Suggestion::FrontendId(PopupItemId::kAddressEntry),
+  CreateAndShowView({PopupItemId::kAddressEntry, PopupItemId::kAddressEntry,
                      PopupItemId::kAutofillOptions});
 
   // If no cell is selected, pressing delete has no effect.
@@ -601,8 +598,7 @@
 
 TEST_F(PopupViewViewsTest, RemoveAutofillRecordsNoAutocompleteDeletionMetrics) {
   base::HistogramTester histogram_tester;
-  CreateAndShowView({Suggestion::FrontendId(PopupItemId::kAddressEntry),
-                     Suggestion::FrontendId(PopupItemId::kAddressEntry),
+  CreateAndShowView({PopupItemId::kAddressEntry, PopupItemId::kAddressEntry,
                      PopupItemId::kAutofillOptions});
 
   view().SetSelectedCell(CellIndex{1u, CellType::kContent});
@@ -650,7 +646,7 @@
   suggestion.labels = {{Suggestion::Text(u"example.com")}};
   suggestion.voice_over = voice_over_value;
   suggestion.additional_label = u"\u2022\u2022\u2022\u2022";
-  suggestion.frontend_id = PopupItemId::kUsernameEntry;
+  suggestion.popup_item_id = PopupItemId::kUsernameEntry;
 
   // Create autofill menu.
   controller().set_suggestions({suggestion});
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h
index 77825f4..339c01f4 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h
@@ -186,11 +186,11 @@
   // an id.
   int GetAndIncrementNextMenuID();
 
-  const raw_ptr<Browser> browser_;
-  raw_ptr<Profile> profile_;
+  const raw_ptr<Browser, LeakedDanglingUntriaged> browser_;
+  raw_ptr<Profile, LeakedDanglingUntriaged> profile_;
 
   // Parent of menus.
-  raw_ptr<views::Widget> parent_;
+  raw_ptr<views::Widget, LeakedDanglingUntriaged> parent_;
 
   // Maps from menu id to BookmarkNode.
   MenuIDToNodeMap menu_id_to_node_map_;
diff --git a/chrome/browser/ui/views/commerce/price_tracking_view.h b/chrome/browser/ui/views/commerce/price_tracking_view.h
index c475ac0..196be29 100644
--- a/chrome/browser/ui/views/commerce/price_tracking_view.h
+++ b/chrome/browser/ui/views/commerce/price_tracking_view.h
@@ -34,7 +34,7 @@
   raw_ptr<views::Label> body_label_;
   raw_ptr<views::ToggleButton> toggle_button_;
 
-  raw_ptr<Profile> profile_;
+  raw_ptr<Profile, LeakedDanglingUntriaged> profile_;
   bool is_price_track_enabled_;
 
   base::WeakPtrFactory<PriceTrackingView> weak_ptr_factory_{this};
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_contents_view.cc b/chrome/browser/ui/views/download/bubble/download_bubble_contents_view.cc
new file mode 100644
index 0000000..4755516
--- /dev/null
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_contents_view.cc
@@ -0,0 +1,99 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/download/bubble/download_bubble_contents_view.h"
+
+#include <utility>
+
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_partial_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_security_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_dialog_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/views/layout/layout_types.h"
+#include "ui/views/view.h"
+
+DownloadBubbleContentsView::DownloadBubbleContentsView(
+    base::WeakPtr<Browser> browser,
+    base::WeakPtr<DownloadBubbleUIController> bubble_controller,
+    base::WeakPtr<DownloadBubbleNavigationHandler> navigation_handler,
+    bool primary_view_is_partial_view,
+    std::vector<DownloadUIModel::DownloadUIModelPtr> primary_view_models,
+    views::BubbleDialogDelegate* bubble_delegate)
+    : primary_view_row_count_(primary_view_models.size()) {
+  SetLayoutManager(std::make_unique<views::FlexLayout>())
+      ->SetOrientation(views::LayoutOrientation::kVertical);
+
+  std::unique_ptr<views::View> primary_view;
+  if (primary_view_is_partial_view) {
+    primary_view = DownloadBubblePartialView::Create(
+        browser, bubble_controller, navigation_handler,
+        std::move(primary_view_models),
+        base::BindOnce(&DownloadBubbleNavigationHandler::OnDialogInteracted,
+                       navigation_handler));
+  } else {
+    std::unique_ptr<views::View> rows_with_scroll =
+        DownloadBubbleRowListView::CreateWithScroll(
+            /*is_partial_view=*/false, browser, bubble_controller,
+            navigation_handler, std::move(primary_view_models),
+            ChromeLayoutProvider::Get()->GetDistanceMetric(
+                views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
+    primary_view = std::make_unique<DownloadDialogView>(
+        browser, std::move(rows_with_scroll), navigation_handler);
+  }
+  // The primary view can be null if there are no rows.
+  if (!primary_view) {
+    CHECK_EQ(primary_view_row_count_, 0);
+    primary_view = std::make_unique<views::View>();
+  }
+
+  primary_view_ = AddChildView(std::move(primary_view));
+  security_view_ = AddChildView(std::make_unique<DownloadBubbleSecurityView>(
+      bubble_controller, navigation_handler, bubble_delegate));
+
+  // Starts on the primary page.
+  SwitchToCurrentPage();
+}
+
+DownloadBubbleContentsView::~DownloadBubbleContentsView() = default;
+
+void DownloadBubbleContentsView::ShowPage(Page page) {
+  if (page_ == page) {
+    return;
+  }
+  page_ = page;
+  SwitchToCurrentPage();
+}
+
+DownloadBubbleContentsView::Page DownloadBubbleContentsView::VisiblePage()
+    const {
+  return page_;
+}
+
+void DownloadBubbleContentsView::UpdateSecurityView(
+    DownloadBubbleRowView* row) {
+  security_view_->UpdateSecurityView(row);
+}
+
+void DownloadBubbleContentsView::SwitchToCurrentPage() {
+  primary_view_->SetVisible(false);
+  security_view_->SetVisible(false);
+
+  switch (page_) {
+    case Page::kPrimary:
+      primary_view_->SetVisible(true);
+      break;
+    case Page::kSecurity:
+      security_view_->UpdateAccessibilityTextAndFocus();
+      security_view_->SetVisible(true);
+      break;
+  }
+}
+
+BEGIN_METADATA(DownloadBubbleContentsView, views::View)
+END_METADATA
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_contents_view.h b/chrome/browser/ui/views/download/bubble/download_bubble_contents_view.h
new file mode 100644
index 0000000..ab94a3d
--- /dev/null
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_contents_view.h
@@ -0,0 +1,83 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTENTS_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTENTS_VIEW_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/download/download_ui_model.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/view.h"
+
+class Browser;
+class DownloadBubbleNavigationHandler;
+class DownloadBubbleRowView;
+class DownloadBubbleSecurityView;
+class DownloadBubbleUIController;
+
+namespace views {
+class BubbleDialogDelegate;
+}  // namespace views
+
+// View that contains the contents of the download bubble. Owns and allows
+// switching between a primary page (either the "main" or "partial" view,
+// containing the download item rows), or the security page (which shows
+// warnings if applicable). Always opens up to the primary view by default,
+// before possibly being switched to the security view.
+class DownloadBubbleContentsView : public views::View {
+ public:
+  // Types of pages that this view can show.
+  enum class Page {
+    kPrimary,
+    kSecurity,
+  };
+
+  METADATA_HEADER(DownloadBubbleContentsView);
+
+  DownloadBubbleContentsView(
+      base::WeakPtr<Browser> browser,
+      base::WeakPtr<DownloadBubbleUIController> bubble_controller,
+      base::WeakPtr<DownloadBubbleNavigationHandler> navigation_handler,
+      // Whether the primary view is the partial view.
+      bool primary_view_is_partial_view,
+      // Models for rows that should go in the primary view.
+      std::vector<DownloadUIModel::DownloadUIModelPtr> primary_view_models,
+      // The owning bubble's delegate.
+      views::BubbleDialogDelegate* bubble_delegate);
+  ~DownloadBubbleContentsView() override;
+
+  DownloadBubbleContentsView(const DownloadBubbleContentsView&) = delete;
+  DownloadBubbleContentsView& operator=(const DownloadBubbleContentsView&) =
+      delete;
+
+  // Switches to the requested page by showing the page and hiding all other
+  // pages.
+  void ShowPage(Page page);
+
+  // Which page is currently visible.
+  Page VisiblePage() const;
+
+  // Forwards to `security_view_`. (Does not switch to the security view.)
+  void UpdateSecurityView(DownloadBubbleRowView* row);
+
+ private:
+  // Switches to the page that should currently be showing.
+  void SwitchToCurrentPage();
+
+  // May be a DownloadBubblePartialView or a DownloadDialogView (main view).
+  // TODO(crbug.com/1450660): Make them inherit from a shared subclass of View.
+  raw_ptr<views::View> primary_view_ = nullptr;
+  // The security view is hidden by default but may be switched to.
+  raw_ptr<DownloadBubbleSecurityView> security_view_ = nullptr;
+
+  // Number of download rows in the primary view.
+  // TODO(crbug.com/1450660): Expose this value.
+  int primary_view_row_count_ = 0;
+
+  // The currently visible page.
+  Page page_ = Page::kPrimary;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_BUBBLE_CONTENTS_VIEW_H_
diff --git a/chrome/browser/ui/views/download/bubble/download_bubble_security_view_unittest.cc b/chrome/browser/ui/views/download/bubble/download_bubble_security_view_unittest.cc
index 5dfbd01b..b54dab8 100644
--- a/chrome/browser/ui/views/download/bubble/download_bubble_security_view_unittest.cc
+++ b/chrome/browser/ui/views/download/bubble/download_bubble_security_view_unittest.cc
@@ -47,6 +47,7 @@
   void OpenSecurityDialog(DownloadBubbleRowView*) override {}
   void CloseDialog(views::Widget::ClosedReason) override {}
   void ResizeDialog() override {}
+  void OnDialogInteracted() override {}
   base::WeakPtr<DownloadBubbleNavigationHandler> GetWeakPtr() override {
     return weak_factory_.GetWeakPtr();
   }
diff --git a/chrome/browser/ui/views/download/bubble/download_dialog_view.cc b/chrome/browser/ui/views/download/bubble/download_dialog_view.cc
index 172ff1ad..df2d98c 100644
--- a/chrome/browser/ui/views/download/bubble/download_dialog_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_dialog_view.cc
@@ -109,12 +109,16 @@
 }  // namespace
 
 void DownloadDialogView::CloseBubble() {
-  navigation_handler_->CloseDialog(
-      views::Widget::ClosedReason::kCloseButtonClicked);
+  if (navigation_handler_) {
+    navigation_handler_->CloseDialog(
+        views::Widget::ClosedReason::kCloseButtonClicked);
+  }
 }
 
 void DownloadDialogView::ShowAllDownloads() {
-  chrome::ShowDownloads(browser_);
+  if (browser_) {
+    chrome::ShowDownloads(browser_.get());
+  }
 }
 
 void DownloadDialogView::AddHeader() {
@@ -154,10 +158,11 @@
 }
 
 DownloadDialogView::DownloadDialogView(
-    Browser* browser,
+    base::WeakPtr<Browser> browser,
     std::unique_ptr<views::View> row_list_scroll_view,
-    DownloadBubbleNavigationHandler* navigation_handler)
-    : navigation_handler_(navigation_handler), browser_(browser) {
+    base::WeakPtr<DownloadBubbleNavigationHandler> navigation_handler)
+    : navigation_handler_(std::move(navigation_handler)),
+      browser_(std::move(browser)) {
   SetLayoutManager(std::make_unique<views::FlexLayout>())
       ->SetOrientation(views::LayoutOrientation::kVertical);
   AddHeader();
@@ -165,5 +170,7 @@
   AddFooter();
 }
 
+DownloadDialogView::~DownloadDialogView() = default;
+
 BEGIN_METADATA(DownloadDialogView, views::View)
 END_METADATA
diff --git a/chrome/browser/ui/views/download/bubble/download_dialog_view.h b/chrome/browser/ui/views/download/bubble/download_dialog_view.h
index 0cef7a0..279d59d7 100644
--- a/chrome/browser/ui/views/download/bubble/download_dialog_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_dialog_view.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_DIALOG_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_DIALOG_VIEW_H_
 
-#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h"
 #include "ui/base/metadata/metadata_header_macros.h"
@@ -14,15 +14,21 @@
 
 class Browser;
 
+// This view represents the 'main view' that is shown when the user clicks on
+// the download toolbar button. Unlike the partial view, it does not
+// automatically close. It also has a header and close button, as well as a
+// footer with a link to chrome://downloads.
 class DownloadDialogView : public views::View {
  public:
   METADATA_HEADER(DownloadDialogView);
   DownloadDialogView(const DownloadDialogView&) = delete;
   DownloadDialogView& operator=(const DownloadDialogView&) = delete;
 
-  DownloadDialogView(Browser* browser,
-                     std::unique_ptr<views::View> row_list_scroll_view,
-                     DownloadBubbleNavigationHandler* navigation_handler);
+  DownloadDialogView(
+      base::WeakPtr<Browser> browser,
+      std::unique_ptr<views::View> row_list_scroll_view,
+      base::WeakPtr<DownloadBubbleNavigationHandler> navigation_handler);
+  ~DownloadDialogView() override;
 
  private:
   void CloseBubble();
@@ -30,8 +36,8 @@
   void AddHeader();
   void AddFooter();
 
-  raw_ptr<DownloadBubbleNavigationHandler> navigation_handler_ = nullptr;
-  raw_ptr<Browser> browser_ = nullptr;
+  base::WeakPtr<DownloadBubbleNavigationHandler> navigation_handler_;
+  base::WeakPtr<Browser> browser_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_DOWNLOAD_BUBBLE_DOWNLOAD_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
index 289d4474..4a70d7d 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.cc
@@ -30,12 +30,10 @@
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/download/bubble/download_bubble_partial_view.h"
+#include "chrome/browser/ui/views/download/bubble/download_bubble_contents_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_bubble_row_list_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_bubble_row_view.h"
-#include "chrome/browser/ui/views/download/bubble/download_bubble_security_view.h"
 #include "chrome/browser/ui/views/download/bubble/download_bubble_started_animation_views.h"
-#include "chrome/browser/ui/views/download/bubble/download_dialog_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/grit/generated_resources.h"
@@ -341,7 +339,7 @@
     if (create_auto_close_timer_ && !auto_close_bubble_timer_) {
       CreateAutoCloseTimer();
     }
-    CreateBubbleDialogDelegate(GetPrimaryView());
+    CreateBubbleDialogDelegate();
   }
   if (auto_close_bubble_timer_) {
     auto_close_bubble_timer_->Reset();
@@ -436,30 +434,17 @@
   return false;
 }
 
-std::unique_ptr<views::View> DownloadToolbarButtonView::GetPrimaryView() {
-  if (is_primary_partial_view_) {
-    return DownloadBubblePartialView::Create(
-        browser_->AsWeakPtr(), bubble_controller_->GetWeakPtr(), GetWeakPtr(),
-        bubble_controller_->GetPartialView(),
-        base::BindOnce(&DownloadToolbarButtonView::DeactivateAutoClose,
-                       base::Unretained(this)));
-  }
-
-  std::unique_ptr<views::View> rows_with_scroll =
-      DownloadBubbleRowListView::CreateWithScroll(
-          /*is_partial_view=*/false, browser_->AsWeakPtr(),
-          bubble_controller_->GetWeakPtr(), GetWeakPtr(),
-          bubble_controller_->GetMainView(),
-          ChromeLayoutProvider::Get()->GetDistanceMetric(
-              views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
-  // raw ptr is safe as the toolbar view owns the bubble.
-  return std::make_unique<DownloadDialogView>(
-      browser_, std::move(rows_with_scroll), this);
+std::vector<DownloadUIModel::DownloadUIModelPtr>
+DownloadToolbarButtonView::GetPrimaryViewModels() {
+  return is_primary_partial_view_ ? bubble_controller_->GetPartialView()
+                                  : bubble_controller_->GetMainView();
 }
 
 void DownloadToolbarButtonView::OpenPrimaryDialog() {
-  primary_view_->SetVisible(true);
-  security_view_->SetVisible(false);
+  if (!bubble_delegate_) {
+    return;
+  }
+  bubble_contents_->ShowPage(DownloadBubbleContentsView::Page::kPrimary);
   bubble_delegate_->SetButtons(ui::DIALOG_BUTTON_NONE);
   bubble_delegate_->SetDefaultButton(ui::DIALOG_BUTTON_NONE);
   bubble_delegate_->set_margins(GetPrimaryViewMargin());
@@ -468,11 +453,12 @@
 
 void DownloadToolbarButtonView::OpenSecurityDialog(
     DownloadBubbleRowView* download_row_view) {
-  security_view_->UpdateSecurityView(download_row_view);
-  primary_view_->SetVisible(false);
-  security_view_->SetVisible(true);
+  if (!bubble_delegate_) {
+    return;
+  }
+  bubble_contents_->UpdateSecurityView(download_row_view);
+  bubble_contents_->ShowPage(DownloadBubbleContentsView::Page::kSecurity);
   bubble_delegate_->set_margins(GetSecurityViewMargin());
-  security_view_->UpdateAccessibilityTextAndFocus();
   ResizeDialog();
 }
 
@@ -489,6 +475,10 @@
     bubble_delegate_->SizeToContents();
 }
 
+void DownloadToolbarButtonView::OnDialogInteracted() {
+  DeactivateAutoClose();
+}
+
 base::WeakPtr<DownloadBubbleNavigationHandler>
 DownloadToolbarButtonView::GetWeakPtr() {
   return weak_factory_.GetWeakPtr();
@@ -497,14 +487,15 @@
 void DownloadToolbarButtonView::OnBubbleClosing() {
   immersive_revealed_lock_.reset();
   bubble_delegate_ = nullptr;
-  primary_view_ = nullptr;
-  security_view_ = nullptr;
+  bubble_contents_ = nullptr;
 }
 
-void DownloadToolbarButtonView::CreateBubbleDialogDelegate(
-    std::unique_ptr<View> bubble_contents_view) {
-  if (!bubble_contents_view)
+void DownloadToolbarButtonView::CreateBubbleDialogDelegate() {
+  std::vector<DownloadUIModel::DownloadUIModelPtr> primary_view_models =
+      GetPrimaryViewModels();
+  if (primary_view_models.empty()) {
     return;
+  }
   // If the IPH is showing, close it to avoid showing the download dialog over
   // it.
   browser_->window()->CloseFeaturePromo(
@@ -520,18 +511,13 @@
   bubble_delegate->SetDefaultButton(ui::DIALOG_BUTTON_NONE);
   bubble_delegate->RegisterWindowClosingCallback(base::BindOnce(
       &DownloadToolbarButtonView::OnBubbleClosing, weak_factory_.GetWeakPtr()));
-  auto* switcher_view =
-      bubble_delegate->SetContentsView(std::make_unique<views::View>());
-  switcher_view->SetLayoutManager(std::make_unique<views::FlexLayout>())
-      ->SetOrientation(views::LayoutOrientation::kVertical);
-  primary_view_ = switcher_view->AddChildView(std::move(bubble_contents_view));
-  // raw ptr for this bubble_delegate is safe as it owns the
-  // DownloadBubbleSecurityView.
-  security_view_ =
-      switcher_view->AddChildView(std::make_unique<DownloadBubbleSecurityView>(
-          bubble_controller_->GetWeakPtr(), GetWeakPtr(),
-          bubble_delegate.get()));
-  security_view_->SetVisible(false);
+  auto bubble_contents = std::make_unique<DownloadBubbleContentsView>(
+      browser_->AsWeakPtr(), bubble_controller_->GetWeakPtr(), GetWeakPtr(),
+      is_primary_partial_view_, std::move(primary_view_models),
+      bubble_delegate.get());
+  bubble_contents_ = bubble_contents.get();
+  bubble_delegate->SetContentsView(std::move(bubble_contents));
+  // The contents view displays the primary view by default.
   bubble_delegate->set_margins(GetPrimaryViewMargin());
   bubble_delegate->SetEnableArrowKeyTraversal(true);
   bubble_delegate_ = bubble_delegate.get();
@@ -595,10 +581,20 @@
 }
 
 void DownloadToolbarButtonView::AutoClosePartialView() {
+  // Nothing to do if the bubble is not open.
+  if (!bubble_contents_) {
+    return;
+  }
+  // Don't close the security page.
+  if (bubble_contents_->VisiblePage() ==
+      DownloadBubbleContentsView::Page::kSecurity) {
+    return;
+  }
   if (!is_primary_partial_view_ || !auto_close_bubble_timer_) {
     return;
   }
-  if (primary_view_ && primary_view_->IsMouseHovered()) {
+  // Don't close if the user is hovering over the bubble.
+  if (bubble_contents_->IsMouseHovered()) {
     return;
   }
   HideDetails();
@@ -611,7 +607,7 @@
 void DownloadToolbarButtonView::ButtonPressed() {
   if (!bubble_delegate_) {
     is_primary_partial_view_ = false;
-    CreateBubbleDialogDelegate(GetPrimaryView());
+    CreateBubbleDialogDelegate();
   }
   controller_->OnButtonPressed();
 }
diff --git a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
index 902932d..d66a2cb2 100644
--- a/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
+++ b/chrome/browser/ui/views/download/bubble/download_toolbar_button_view.h
@@ -24,9 +24,9 @@
 class Browser;
 class BrowserView;
 class DownloadDisplayController;
+class DownloadBubbleContentsView;
 class DownloadBubbleUIController;
 class DownloadBubbleRowView;
-class DownloadBubbleSecurityView;
 
 class DownloadBubbleNavigationHandler {
  public:
@@ -35,6 +35,9 @@
   virtual void OpenSecurityDialog(DownloadBubbleRowView* download_row_view) = 0;
   virtual void CloseDialog(views::Widget::ClosedReason reason) = 0;
   virtual void ResizeDialog() = 0;
+  // Callback invoked when the dialog has been interacted with by hovering over
+  // or by focusing (on the partial view).
+  virtual void OnDialogInteracted() = 0;
   virtual base::WeakPtr<DownloadBubbleNavigationHandler> GetWeakPtr() = 0;
 };
 
@@ -79,6 +82,7 @@
   void OpenSecurityDialog(DownloadBubbleRowView* download_row_view) override;
   void CloseDialog(views::Widget::ClosedReason reason) override;
   void ResizeDialog() override;
+  void OnDialogInteracted() override;
   base::WeakPtr<DownloadBubbleNavigationHandler> GetWeakPtr() override;
 
   // BrowserListObserver
@@ -118,7 +122,7 @@
                                 SkColor badge_text_color);
 
   void ButtonPressed();
-  void CreateBubbleDialogDelegate(std::unique_ptr<View> bubble_contents_view);
+  void CreateBubbleDialogDelegate();
   void OnBubbleClosing();
 
   // Callback invoked when the partial view is closed.
@@ -131,8 +135,9 @@
   // been deactivated.
   void AutoClosePartialView();
 
-  // Get the primary view, which may be the full or the partial view.
-  std::unique_ptr<View> GetPrimaryView();
+  // Get the models for the primary view, which may be the full or the partial
+  // view.
+  std::vector<DownloadUIModel::DownloadUIModelPtr> GetPrimaryViewModels();
 
   // If |has_pending_download_started_animation_| is true, shows an animation of
   // a download icon moving upwards towards the toolbar icon.
@@ -147,8 +152,7 @@
   // Controller for keeping track of items for both main view and partial view.
   std::unique_ptr<DownloadBubbleUIController> bubble_controller_;
   raw_ptr<views::BubbleDialogDelegate> bubble_delegate_ = nullptr;
-  raw_ptr<View> primary_view_ = nullptr;
-  raw_ptr<DownloadBubbleSecurityView> security_view_ = nullptr;
+  raw_ptr<DownloadBubbleContentsView> bubble_contents_ = nullptr;
 
   // Marks whether there is a pending download started animation. This is needed
   // because the animation should only be triggered after the view has been
diff --git a/chrome/browser/ui/views/location_bar/location_bar_util.cc b/chrome/browser/ui/views/location_bar/location_bar_util.cc
index 4cdf6fb..230475e 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_util.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_util.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/location_bar/location_bar_util.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
+#include "components/omnibox/browser/omnibox_field_trial.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop.h"
@@ -15,7 +16,9 @@
 void ConfigureInkDropForRefresh2023(views::View* const host,
                                     const ChromeColorIds hover_color_id,
                                     const ChromeColorIds ripple_color_id) {
-  CHECK(features::IsChromeRefresh2023());
+  // TODO(crbug.com/1450984): Figure out if one of these are redundant.
+  CHECK(features::IsChromeRefresh2023() ||
+        OmniboxFieldTrial::IsChromeRefreshIconsEnabled());
 
   views::InkDrop::Get(host)->SetMode(views::InkDropHost::InkDropMode::ON);
   views::InkDrop::Get(host)->SetLayerRegion(views::LayerRegion::kAbove);
diff --git a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc
index 6393d39..10a3976c 100644
--- a/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/privacy_sandbox/privacy_sandbox_dialog_view_browsertest.cc
@@ -44,6 +44,9 @@
     if (name == "Notice") {
       prompt_type = PrivacySandboxService::PromptType::kNotice;
     }
+    if (name == "RestrictedNotice") {
+      prompt_type = PrivacySandboxService::PromptType::kM1NoticeRestricted;
+    }
     ASSERT_NE(prompt_type, PrivacySandboxService::PromptType::kNone);
 
     // Resize the browser window to guarantee enough space for the dialog.
diff --git a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
index 99ec61bf..20580ec 100644
--- a/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_tab_slot_controller.h
@@ -110,7 +110,7 @@
   }
 
  private:
-  raw_ptr<TabStripController> tab_strip_controller_;
+  raw_ptr<TabStripController, LeakedDanglingUntriaged> tab_strip_controller_;
   raw_ptr<TabContainer, DanglingUntriaged> tab_container_;
   ui::ListSelectionModel selection_model_;
   raw_ptr<Tab, DanglingUntriaged> active_tab_ = nullptr;
diff --git a/chrome/browser/ui/views/user_education/browser_user_education_service.cc b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
index 004cbf7..9b60fd6b 100644
--- a/chrome/browser/ui/views/user_education/browser_user_education_service.cc
+++ b/chrome/browser/ui/views/user_education/browser_user_education_service.cc
@@ -526,6 +526,27 @@
   if (tutorial_registry.IsTutorialRegistered(kTabGroupTutorialId))
     return;
 
+  {  // Menu item bubble test.
+    TutorialDescription test_description;
+    test_description.steps.emplace_back(
+        TutorialDescription::BubbleStep(kAppMenuButtonElementId)
+            .SetBubbleBodyText(IDS_OK)
+            .SetBubbleArrow(HelpBubbleArrow::kTopRight));
+    test_description.steps.emplace_back(
+        TutorialDescription::BubbleStep(AppMenuModel::kDownloadsMenuItem)
+            .SetBubbleBodyText(IDS_OK)
+            .SetBubbleArrow(HelpBubbleArrow::kRightCenter));
+    test_description.steps.emplace_back(
+        TutorialDescription::HiddenStep::WaitForHidden(
+            AppMenuModel::kDownloadsMenuItem));
+    test_description.steps.emplace_back(
+        TutorialDescription::BubbleStep(kTopContainerElementId)
+            .SetBubbleBodyText(IDS_OK)
+            .SetBubbleArrow(HelpBubbleArrow::kNone));
+    tutorial_registry.AddTutorial("Menu item bubble test tutorial",
+                                  std::move(test_description));
+  }
+
   {  // Tab Group Tutorials
 
     // The Description for kTabGroupTutorialId.
diff --git a/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc
index 72215f8..4f839b5 100644
--- a/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/user_education/help_bubble_view_interactive_uitest.cc
@@ -176,3 +176,47 @@
       PressButton(HelpBubbleView::kDefaultButtonIdForTesting),
       WaitForHide(HelpBubbleView::kHelpBubbleElementIdForTesting));
 }
+
+// This is a combined test for both help bubbles anchored to menus and menu
+// annotation.
+//
+// This test does work on Linux, however, because of the way events are routed
+// on Wayland specifically (and on Linux in general) the test itself isn't
+// reliable on Linux. It has been manually tested, and based on the way the
+// annotation event routing works, if it did not work (a) it would not work on
+// any platform, and (b) it would not be possible to close a menu by clicking
+// away from it and into e.g. the omnibox.
+#if !BUILDFLAG(IS_LINUX)
+IN_PROC_BROWSER_TEST_F(HelpBubbleViewInteractiveUiTest, AnnotateMenu) {
+  UNCALLED_MOCK_CALLBACK(base::OnceClosure, default_button_clicked);
+  constexpr char16_t kButton1Text[] = u"button 1";
+
+  user_education::HelpBubbleParams params = GetBubbleParams();
+
+  params.arrow = user_education::HelpBubbleArrow::kRightCenter;
+
+  user_education::HelpBubbleButtonParams button1;
+  button1.text = kButton1Text;
+  button1.is_default = true;
+  button1.callback = default_button_clicked.Get();
+  params.buttons.emplace_back(std::move(button1));
+
+  EXPECT_CALL(default_button_clicked, Run).Times(1);
+
+  RunTestSequence(
+      // Show the application menu and attach a bubble to a menu item.
+      PressButton(kAppMenuButtonElementId),
+      ShowHelpBubble(AppMenuModel::kDownloadsMenuItem, std::move(params)),
+
+      // Hover the default button and verify that the inkdrop is highlighted.
+      MoveMouseTo(HelpBubbleView::kDefaultButtonIdForTesting),
+
+      // TODO(dfried): figure out if we can determine if an inkdrop is in a
+      // hovered state; currently that information can't be accessed.
+
+      // Click the default button and verify that the help bubble closes but the
+      // menu does not.
+      ClickMouse(), WaitForHide(HelpBubbleView::kHelpBubbleElementIdForTesting),
+      EnsurePresent(AppMenuModel::kDownloadsMenuItem));
+}
+#endif
diff --git a/chrome/browser/ui/views/waffle/DIR_METADATA b/chrome/browser/ui/views/waffle/DIR_METADATA
new file mode 100644
index 0000000..6cfbfc2
--- /dev/null
+++ b/chrome/browser/ui/views/waffle/DIR_METADATA
@@ -0,0 +1 @@
+mixins: "//chrome/browser/ui/waffle/COMMON_METADATA"
diff --git a/chrome/browser/ui/views/waffle/OWNERS b/chrome/browser/ui/views/waffle/OWNERS
new file mode 100644
index 0000000..55c6bfb
--- /dev/null
+++ b/chrome/browser/ui/views/waffle/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/waffle/OWNERS
diff --git a/chrome/browser/ui/views/waffle/waffle_dialog_view.cc b/chrome/browser/ui/views/waffle/waffle_dialog_view.cc
new file mode 100644
index 0000000..686becd
--- /dev/null
+++ b/chrome/browser/ui/views/waffle/waffle_dialog_view.cc
@@ -0,0 +1,78 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/waffle/waffle_dialog_view.h"
+
+#include "base/feature_list.h"
+#include "base/functional/bind.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/signin_features.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
+#include "chrome/browser/ui/waffle/waffle_tab_helper.h"
+#include "chrome/browser/ui/webui/waffle/waffle_ui.h"
+#include "chrome/common/webui_url_constants.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/web_modal/web_contents_modal_dialog_host.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+
+namespace {
+// Temporary until the mocks are ready.
+constexpr int kDialogWidth = 512;
+constexpr int kDialogHeight = 569;
+}  // namespace
+
+void ShowWaffleDialog(Browser& browser) {
+  auto delegate = std::make_unique<views::DialogDelegate>();
+  delegate->SetButtons(ui::DIALOG_BUTTON_NONE);
+  delegate->SetModalType(ui::MODAL_TYPE_WINDOW);
+  delegate->SetShowCloseButton(true);
+  delegate->SetOwnedByWidget(true);
+
+  auto waffleDialogView = std::make_unique<WaffleDialogView>(&browser);
+  waffleDialogView->Initialize();
+  delegate->SetContentsView(std::move(waffleDialogView));
+
+  constrained_window::CreateBrowserModalDialogViews(
+      std::move(delegate), browser.window()->GetNativeWindow());
+}
+
+WaffleDialogView::WaffleDialogView(Browser* browser) : browser_(browser) {
+  CHECK(base::FeatureList::IsEnabled(kWaffle));
+  // Create the web view in the native dialog.
+  web_view_ =
+      AddChildView(std::make_unique<views::WebView>(browser->profile()));
+}
+
+void WaffleDialogView::Initialize() {
+  web_view_->LoadInitialURL(GURL(chrome::kChromeUIWaffleURL));
+
+  const int max_width = browser_->window()
+                            ->GetWebContentsModalDialogHost()
+                            ->GetMaximumDialogSize()
+                            .width();
+  const int width =
+      views::LayoutProvider::Get()->GetSnappedDialogWidth(kDialogWidth);
+  web_view_->SetPreferredSize(
+      gfx::Size(std::min(width, max_width), kDialogHeight));
+
+  auto* web_ui = web_view_->GetWebContents()
+                     ->GetWebUI()
+                     ->GetController()
+                     ->GetAs<WaffleUI>();
+  CHECK(web_ui);
+  // Unretained is fine because this outlives the inner web UI.
+  web_ui->Initialize(base::BindOnce(&WaffleDialogView::ShowNativeView,
+                                    base::Unretained(this)));
+
+  SetUseDefaultFillLayout(true);
+}
+
+void WaffleDialogView::ShowNativeView() {
+  GetWidget()->Show();
+  web_view_->RequestFocus();
+}
+
+BEGIN_METADATA(WaffleDialogView, views::View)
+END_METADATA
diff --git a/chrome/browser/ui/views/waffle/waffle_dialog_view.h b/chrome/browser/ui/views/waffle/waffle_dialog_view.h
new file mode 100644
index 0000000..d49a5da
--- /dev/null
+++ b/chrome/browser/ui/views/waffle/waffle_dialog_view.h
@@ -0,0 +1,35 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_WAFFLE_WAFFLE_DIALOG_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_WAFFLE_WAFFLE_DIALOG_VIEW_H_
+
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/views/view.h"
+
+class Browser;
+
+namespace views {
+class WebView;
+}
+
+// Implements the Waffle dialog as a View. The view contains a WebView
+// into which is loaded a WebUI page which renders the actual dialog content.
+class WaffleDialogView : public views::View {
+ public:
+  METADATA_HEADER(WaffleDialogView);
+  explicit WaffleDialogView(Browser* browser);
+
+  // Initialize WaffleDialogView's web_view_ element.
+  void Initialize();
+
+ private:
+  // Shows the dialog widget.
+  void ShowNativeView();
+
+  raw_ptr<views::WebView> web_view_ = nullptr;
+  const raw_ptr<const Browser> browser_;
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WAFFLE_WAFFLE_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/waffle/waffle_tab_helper.cc b/chrome/browser/ui/waffle/waffle_tab_helper.cc
new file mode 100644
index 0000000..4cffe14
--- /dev/null
+++ b/chrome/browser/ui/waffle/waffle_tab_helper.cc
@@ -0,0 +1,28 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/waffle/waffle_tab_helper.h"
+
+#include "chrome/browser/signin/signin_features.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/web_contents.h"
+
+WaffleTabHelper::~WaffleTabHelper() = default;
+
+WaffleTabHelper::WaffleTabHelper(content::WebContents* web_contents)
+    : WebContentsObserver(web_contents),
+      content::WebContentsUserData<WaffleTabHelper>(*web_contents) {
+  CHECK(base::FeatureList::IsEnabled(kWaffle));
+}
+
+void WaffleTabHelper::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (auto* browser = chrome::FindBrowserWithWebContents(
+          navigation_handle->GetWebContents())) {
+    ShowWaffleDialog(*browser);
+  }
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(WaffleTabHelper);
diff --git a/chrome/browser/ui/waffle/waffle_tab_helper.h b/chrome/browser/ui/waffle/waffle_tab_helper.h
new file mode 100644
index 0000000..fc85387
--- /dev/null
+++ b/chrome/browser/ui/waffle/waffle_tab_helper.h
@@ -0,0 +1,42 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WAFFLE_WAFFLE_TAB_HELPER_H_
+#define CHROME_BROWSER_UI_WAFFLE_WAFFLE_TAB_HELPER_H_
+
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}
+class Browser;
+
+// Helper class which watches `web_contents` to determine whether there is an
+// appropriate opportunity to show the WaffleDialogView.
+class WaffleTabHelper : public content::WebContentsObserver,
+                        public content::WebContentsUserData<WaffleTabHelper> {
+ public:
+  WaffleTabHelper(const WaffleTabHelper&) = delete;
+  WaffleTabHelper& operator=(const WaffleTabHelper&) = delete;
+  ~WaffleTabHelper() override;
+
+ private:
+  friend class content::WebContentsUserData<WaffleTabHelper>;
+
+  explicit WaffleTabHelper(content::WebContents* web_contents);
+
+  // content::WebContentsObserver:
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+// Implemented in `chrome/browser/ui/views/waffle/waffle_dialog_view.cc`
+// because there isn't a dependency between `chrome/browser/ui/` and
+// `chrome/browser/ui/views/`.
+void ShowWaffleDialog(Browser& browser);
+
+#endif  // CHROME_BROWSER_UI_WAFFLE_WAFFLE_TAB_HELPER_H_
diff --git a/chrome/browser/ui/web_applications/web_app_metrics_browsertest.cc b/chrome/browser/ui/web_applications/web_app_metrics_browsertest.cc
index b75823e..9a17942 100644
--- a/chrome/browser/ui/web_applications/web_app_metrics_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_metrics_browsertest.cc
@@ -222,7 +222,7 @@
   ukm::TestAutoSetUkmRecorder::ExpectEntryMetric(
       entry, UkmEntry::kCapturesLinksName, false);
   ukm::TestAutoSetUkmRecorder::ExpectEntryMetric(
-      entry, UkmEntry::kPromotableName, false);
+      entry, UkmEntry::kPromotableName, true);
   // Not in window, but is preinstalled, so should have session count (and would
   // be expected to have session time upon further interaction).
   ukm::TestAutoSetUkmRecorder::ExpectEntryMetric(entry,
diff --git a/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc b/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
index 0f661dd..90d1b3a8 100644
--- a/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
+++ b/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.cc
@@ -189,6 +189,12 @@
   CallExternalAPI("doReload");
 }
 
+void EnrollmentScreenHandler::ResetEnrollmentScreen() {
+  // The empty string will be replaced by the correct initial step in the screen
+  // initialization code.
+  ShowStep(std::string());
+}
+
 void EnrollmentScreenHandler::ShowUserError(const std::string& email) {
   // Reset the state of the GAIA so after error user would retry enrollment and
   // start from enter your account view.
@@ -761,8 +767,8 @@
   help_app_->ShowHelpTopic(HelpAppLauncher::HELP_DEVICE_ATTRIBUTES);
 }
 
-void EnrollmentScreenHandler::ShowStep(const char* step) {
-  CallExternalAPI("showStep", std::string(step));
+void EnrollmentScreenHandler::ShowStep(const std::string& step) {
+  CallExternalAPI("showStep", step);
 }
 
 void EnrollmentScreenHandler::ShowError(int message_id, bool retry) {
diff --git a/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h b/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h
index 93ec7d06..eb405ad7 100644
--- a/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h
+++ b/chrome/browser/ui/webui/ash/login/enrollment_screen_handler.h
@@ -59,6 +59,7 @@
   void Hide() override;
   void ShowSigninScreen() override;
   void ReloadSigninScreen() override;
+  void ResetEnrollmentScreen() override;
   void ShowSkipConfirmationDialog() override;
   void ShowUserError(const std::string& email) override;
   void ShowEnrollmentDuringTrialNotAllowedError() override;
@@ -101,7 +102,7 @@
   void HandleOnLearnMore();
 
   // Shows a given enrollment step.
-  void ShowStep(const char* step);
+  void ShowStep(const std::string& step);
 
   // Display the given i18n resource as error message.
   void ShowError(int message_id, bool retry);
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc
index 0d304232..3ea0880 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler.cc
@@ -15,6 +15,10 @@
          prompt_type == PrivacySandboxService::PromptType::kM1Consent;
 }
 
+bool IsRestrictedNotice(PrivacySandboxService::PromptType prompt_type) {
+  return prompt_type == PrivacySandboxService::PromptType::kM1NoticeRestricted;
+}
+
 }  // namespace
 
 PrivacySandboxDialogHandler::PrivacySandboxDialogHandler(
@@ -73,6 +77,9 @@
   if (IsConsent(prompt_type_)) {
     NotifyServiceAboutPromptAction(
         PrivacySandboxService::PromptAction::kConsentClosedNoDecision);
+  } else if (IsRestrictedNotice(prompt_type_)) {
+    NotifyServiceAboutPromptAction(PrivacySandboxService::PromptAction::
+                                       kRestrictedNoticeClosedNoInteraction);
   } else {
     NotifyServiceAboutPromptAction(
         PrivacySandboxService::PromptAction::kNoticeClosedNoInteraction);
diff --git a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
index 327a333e..9ed15b7 100644
--- a/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
+++ b/chrome/browser/ui/webui/privacy_sandbox/privacy_sandbox_dialog_handler_unittest.cc
@@ -380,10 +380,9 @@
       *mock_privacy_sandbox_service(),
       PromptActionOccurred(
           PrivacySandboxService::PromptAction::kRestrictedNoticeOpenSettings));
-  EXPECT_CALL(
-      *mock_privacy_sandbox_service(),
-      PromptActionOccurred(
-          PrivacySandboxService::PromptAction::kNoticeClosedNoInteraction))
+  EXPECT_CALL(*mock_privacy_sandbox_service(),
+              PromptActionOccurred(PrivacySandboxService::PromptAction::
+                                       kRestrictedNoticeClosedNoInteraction))
       .Times(0);
 
   base::Value::List args;
@@ -401,10 +400,9 @@
       *mock_privacy_sandbox_service(),
       PromptActionOccurred(
           PrivacySandboxService::PromptAction::kRestrictedNoticeAcknowledge));
-  EXPECT_CALL(
-      *mock_privacy_sandbox_service(),
-      PromptActionOccurred(
-          PrivacySandboxService::PromptAction::kNoticeClosedNoInteraction))
+  EXPECT_CALL(*mock_privacy_sandbox_service(),
+              PromptActionOccurred(PrivacySandboxService::PromptAction::
+                                       kRestrictedNoticeClosedNoInteraction))
       .Times(0);
 
   base::Value::List args;
diff --git a/chrome/browser/ui/webui/sandbox/sandbox_handler.cc b/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
index 03f61ae..0f872ef 100644
--- a/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
+++ b/chrome/browser/ui/webui/sandbox/sandbox_handler.cc
@@ -87,6 +87,8 @@
       FeatureToValue(sandbox::policy::features::kRendererAppContainer));
   features.Append(
       FeatureToValue(sandbox::policy::features::kWinSboxAllowSystemFonts));
+  features.Append(
+      FeatureToValue(sandbox::policy::features::kWinSboxRendererCloseKsecDD));
   features.Append(FeatureToValue(
       sandbox::policy::features::kWinSboxDisableExtensionPoints));
   return features;
diff --git a/chrome/browser/ui/webui/waffle/waffle.mojom b/chrome/browser/ui/webui/waffle/waffle.mojom
index 3a2457e..297c3fcd 100644
--- a/chrome/browser/ui/webui/waffle/waffle.mojom
+++ b/chrome/browser/ui/webui/waffle/waffle.mojom
@@ -13,6 +13,7 @@
 
 // Browser-side handler for requests from WebUI page.
 interface PageHandler {
-  // Closes the Waffle dialog. Triggered by the closeClicked() call in TS.
-  CloseClicked();
+  // Displays the Waffle dialog. Triggered by the displayDialog() call in
+  // chrome/browser/resources/waffle/app.ts
+  DisplayDialog();
 };
diff --git a/chrome/browser/ui/webui/waffle/waffle_handler.cc b/chrome/browser/ui/webui/waffle/waffle_handler.cc
index 968772b..c9e3a5e5 100644
--- a/chrome/browser/ui/webui/waffle/waffle_handler.cc
+++ b/chrome/browser/ui/webui/waffle/waffle_handler.cc
@@ -4,13 +4,23 @@
 
 #include "chrome/browser/ui/webui/waffle/waffle_handler.h"
 
+#include "chrome/browser/signin/signin_features.h"
+
 WaffleHandler::WaffleHandler(
-    mojo::PendingReceiver<waffle::mojom::PageHandler> receiver)
-    : receiver_(this, std::move(receiver)) {}
+    mojo::PendingReceiver<waffle::mojom::PageHandler> receiver,
+    base::OnceClosure display_dialog_callback)
+    : receiver_(this, std::move(receiver)),
+      display_dialog_callback_(std::move(display_dialog_callback)) {
+  CHECK(base::FeatureList::IsEnabled(kWaffle));
+  // `display_dialog_callback` being null would indicate that the handler is
+  // created before calling `WaffleUI::Initialize()`, which should never happen.
+  CHECK(display_dialog_callback_);
+}
 
 WaffleHandler::~WaffleHandler() = default;
 
-// Triggered by closeClicked() call in TS.
-void WaffleHandler::CloseClicked() {
-  NOTIMPLEMENTED();
+void WaffleHandler::DisplayDialog() {
+  if (display_dialog_callback_) {
+    std::move(display_dialog_callback_).Run();
+  }
 }
diff --git a/chrome/browser/ui/webui/waffle/waffle_handler.h b/chrome/browser/ui/webui/waffle/waffle_handler.h
index 0bb4b9f..e59bcb0 100644
--- a/chrome/browser/ui/webui/waffle/waffle_handler.h
+++ b/chrome/browser/ui/webui/waffle/waffle_handler.h
@@ -5,13 +5,15 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_HANDLER_H_
 #define CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_HANDLER_H_
 
+#include "base/functional/callback_forward.h"
 #include "chrome/browser/ui/webui/waffle/waffle.mojom.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
 class WaffleHandler : public waffle::mojom::PageHandler {
  public:
   explicit WaffleHandler(
-      mojo::PendingReceiver<waffle::mojom::PageHandler> receiver);
+      mojo::PendingReceiver<waffle::mojom::PageHandler> receiver,
+      base::OnceClosure display_dialog_callback);
 
   WaffleHandler(const WaffleHandler&) = delete;
   WaffleHandler& operator=(const WaffleHandler&) = delete;
@@ -19,10 +21,11 @@
   ~WaffleHandler() override;
 
   // waffle::mojom::PageHandler:
-  void CloseClicked() override;
+  void DisplayDialog() override;
 
  private:
   mojo::Receiver<waffle::mojom::PageHandler> receiver_;
+  base::OnceClosure display_dialog_callback_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/waffle/waffle_ui.cc b/chrome/browser/ui/webui/waffle/waffle_ui.cc
index 248350f..744b6d4 100644
--- a/chrome/browser/ui/webui/waffle/waffle_ui.cc
+++ b/chrome/browser/ui/webui/waffle/waffle_ui.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/waffle/waffle_ui.h"
 
 #include "base/feature_list.h"
+#include "base/functional/callback_forward.h"
 #include "chrome/browser/signin/signin_features.h"
 #include "chrome/browser/ui/webui/waffle/waffle_handler.h"
 #include "chrome/browser/ui/webui/webui_util.h"
@@ -12,20 +13,18 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/waffle_resources.h"
 #include "chrome/grit/waffle_resources_map.h"
-#include "components/strings/grit/components_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
 
 WaffleUI::WaffleUI(content::WebUI* web_ui)
     : ui::MojoWebUIController(web_ui, true) {
-  DCHECK(base::FeatureList::IsEnabled(kWaffle));
+  CHECK(base::FeatureList::IsEnabled(kWaffle));
 
   content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
       web_ui->GetWebContents()->GetBrowserContext(),
       chrome::kChromeUIWaffleHost);
 
   source->AddLocalizedString("title", IDS_WAFFLE_PAGE_TITLE);
-  source->AddLocalizedString("closeButtonTitle", IDS_CLOSE);
 
   webui::SetupWebUIDataSource(
       source, base::make_span(kWaffleResources, kWaffleResourcesSize),
@@ -42,7 +41,13 @@
   page_factory_receiver_.Bind(std::move(receiver));
 }
 
+void WaffleUI::Initialize(base::OnceClosure display_dialog_callback) {
+  CHECK(display_dialog_callback);
+  display_dialog_callback_ = std::move(display_dialog_callback);
+}
+
 void WaffleUI::CreatePageHandler(
     mojo::PendingReceiver<waffle::mojom::PageHandler> receiver) {
-  page_handler_ = std::make_unique<WaffleHandler>(std::move(receiver));
+  page_handler_ = std::make_unique<WaffleHandler>(
+      std::move(receiver), std::move(display_dialog_callback_));
 }
diff --git a/chrome/browser/ui/webui/waffle/waffle_ui.h b/chrome/browser/ui/webui/waffle/waffle_ui.h
index 695985b..0e951e7 100644
--- a/chrome/browser/ui/webui/waffle/waffle_ui.h
+++ b/chrome/browser/ui/webui/waffle/waffle_ui.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_WAFFLE_WAFFLE_UI_H_
 
+#include "base/functional/callback_forward.h"
 #include "chrome/browser/ui/webui/waffle/waffle.mojom.h"
 #include "chrome/browser/ui/webui/waffle/waffle_handler.h"
 #include "ui/webui/mojo_web_ui_controller.h"
@@ -25,6 +26,12 @@
   void BindInterface(
       mojo::PendingReceiver<waffle::mojom::PageHandlerFactory> receiver);
 
+  // Initializes the callbacks that need to be passed to the handler.
+  // `display_dialog_callback` is how we display the waffle dialog. It will
+  // be called when the page content is laid out, so that the dialog will be
+  // able to measure the page to fit to its size.
+  void Initialize(base::OnceClosure display_dialog_callback);
+
  private:
   // waffle::mojom::PageHandlerFactory:
   void CreatePageHandler(
@@ -35,6 +42,8 @@
   mojo::Receiver<waffle::mojom::PageHandlerFactory> page_factory_receiver_{
       this};
 
+  base::OnceClosure display_dialog_callback_;
+
   WEB_UI_CONTROLLER_TYPE_DECL();
 };
 
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc
index a5721b3..5aab228 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc
@@ -287,7 +287,12 @@
         "Manifest `version` is not present. manifest_url: " +
         manifest_url.possibly_invalid_spec());
   }
-  std::string version_string = base::UTF16ToUTF8(*manifest.version);
+  std::string version_string;
+  if (!base::UTF16ToUTF8(manifest.version->data(), manifest.version->length(),
+                         &version_string)) {
+    return base::unexpected(
+        "Failed to convert manifest `version` from UTF16 to UTF8.");
+  }
 
   base::expected<std::array<uint32_t, 3>, IwaVersionParseError>
       version_components = ParseIwaVersionIntoComponents(version_string);
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc
index 640f17f..f073eae8 100644
--- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc
@@ -594,12 +594,17 @@
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     InstallIsolatedWebAppCommandInvalidVersionTest,
-    ::testing::Values(InvalidVersionParam{.version = absl::nullopt,
-                                          .error = "`version` is not present",
-                                          .test_name = "NoVersion"},
-                      InvalidVersionParam{.version = u"10",
-                                          .error = "Failed to parse `version`",
-                                          .test_name = "InvalidVersionFormat"}),
+    ::testing::Values(
+        InvalidVersionParam{.version = absl::nullopt,
+                            .error = "`version` is not present",
+                            .test_name = "NoVersion"},
+        InvalidVersionParam{
+            .version = u"\xD801",
+            .error = "Failed to convert manifest `version` from UTF16 to UTF8",
+            .test_name = "InvalidUtf8"},
+        InvalidVersionParam{.version = u"10",
+                            .error = "Failed to parse `version`",
+                            .test_name = "InvalidVersionFormat"}),
     [](const ::testing::TestParamInfo<
         InstallIsolatedWebAppCommandInvalidVersionTest::ParamType>& info) {
       return info.param.test_name;
diff --git a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.cc b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.cc
index 069396a..5a4adfa 100644
--- a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.cc
@@ -11,6 +11,8 @@
 
 #include "base/check_is_test.h"
 #include "base/check_op.h"
+#include "base/files/file.h"
+#include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -20,6 +22,7 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
+#include "base/types/expected.h"
 #include "chrome/browser/web_applications/isolated_web_apps/error/unusable_swbn_file_error.h"
 #include "components/web_package/mojom/web_bundle_parser.mojom.h"
 #include "components/web_package/signed_web_bundles/signed_web_bundle_integrity_block.h"
@@ -30,15 +33,161 @@
 #include "url/gurl.h"
 
 namespace web_app {
+namespace {
+// This is blocking operation.
+base::expected<uint64_t, base::File::Error> ReadLengthOfSharedFile(
+    scoped_refptr<web_package::SharedFile> file) {
+  int64_t length = (*file)->GetLength();
+  if (length < 0) {
+    return base::unexpected((*file)->GetLastFileError());
+  }
+  return static_cast<uint64_t>(length);
+}
+}  // namespace
+
+namespace internal {
+
+SafeWebBundleParserConnection::SafeWebBundleParserConnection(
+    base::FilePath web_bundle_path,
+    absl::optional<GURL> base_url)
+    : web_bundle_path_(std::move(web_bundle_path)),
+      base_url_(std::move(base_url)) {}
+
+SafeWebBundleParserConnection::~SafeWebBundleParserConnection() = default;
+
+void SafeWebBundleParserConnection::Initialize(
+    InitCompleteCallback init_complete_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_EQ(state_, State::kUninitialized);
+  state_ = State::kInitializing;
+
+  parser_ = std::make_unique<data_decoder::SafeWebBundleParser>(base_url_);
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock()},
+      base::BindOnce(
+          [](const base::FilePath& file_path) -> std::unique_ptr<base::File> {
+            return std::make_unique<base::File>(
+                file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+          },
+          web_bundle_path_),
+      base::BindOnce(&SafeWebBundleParserConnection::OnFileOpened,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(init_complete_callback)));
+}
+
+void SafeWebBundleParserConnection::StartProcessingDisconnects() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_EQ(state_, State::kConnected);
+  parser_->SetDisconnectCallback(
+      base::BindOnce(&SafeWebBundleParserConnection::OnParserDisconnected,
+                     // `base::Unretained` is okay to use here, since
+                     // `parser_` will be deleted before `this` is deleted.
+                     base::Unretained(this)));
+}
+
+void SafeWebBundleParserConnection::OnFileOpened(
+    InitCompleteCallback init_complete_callback,
+    std::unique_ptr<base::File> file) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_EQ(state_, State::kInitializing);
+
+  if (!file->IsValid()) {
+    UnusableSwbnFileError error = UnusableSwbnFileError(
+        UnusableSwbnFileError::Error::kIntegrityBlockParserInternalError,
+        base::File::ErrorToString(file->error_details()));
+    std::move(init_complete_callback).Run(base::unexpected(error));
+    return;
+  }
+
+  file_ = base::MakeRefCounted<web_package::SharedFile>(std::move(file));
+  file_->DuplicateFile(base::BindOnce(
+      &SafeWebBundleParserConnection::OnFileDuplicated,
+      weak_ptr_factory_.GetWeakPtr(), std::move(init_complete_callback)));
+}
+
+void SafeWebBundleParserConnection::OnFileDuplicated(
+    InitCompleteCallback init_complete_callback,
+    base::File file) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_EQ(state_, State::kInitializing);
+
+  base::File::Error file_error = parser_->OpenFile(std::move(file));
+  if (file_error != base::File::FILE_OK) {
+    UnusableSwbnFileError error = UnusableSwbnFileError(
+        UnusableSwbnFileError::Error::kIntegrityBlockParserInternalError,
+        base::File::ErrorToString(file_error));
+    std::move(init_complete_callback).Run(base::unexpected(error));
+    return;
+  }
+
+  state_ = State::kConnected;
+  std::move(init_complete_callback).Run(base::ok());
+}
+
+void SafeWebBundleParserConnection::OnParserDisconnected() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_EQ(state_, State::kConnected);
+
+  state_ = State::kDisconnected;
+  parser_ = nullptr;
+  if (!parser_disconnect_callback_for_testing_.is_null()) {
+    CHECK_IS_TEST();
+    parser_disconnect_callback_for_testing_.Run();
+  }
+}
+
+void SafeWebBundleParserConnection::Reconnect(
+    ReconnectCompleteCallback reconnect_callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(!parser_);
+  CHECK_EQ(state_, State::kDisconnected);
+  parser_ = std::make_unique<data_decoder::SafeWebBundleParser>(base_url_);
+  state_ = State::kReconnecting;
+
+  file_->DuplicateFile(base::BindOnce(
+      &SafeWebBundleParserConnection::ReconnectForFile,
+      weak_ptr_factory_.GetWeakPtr(), std::move(reconnect_callback)));
+}
+
+void SafeWebBundleParserConnection::ReconnectForFile(
+    ReconnectCompleteCallback reconnect_callback,
+    base::File file) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK_EQ(state_, State::kReconnecting);
+  base::File::Error file_error;
+  if (reconnection_file_error_for_testing_.has_value()) {
+    CHECK_IS_TEST();
+    file_error = *reconnection_file_error_for_testing_;
+  } else {
+    file_error = parser_->OpenFile(std::move(file));
+  }
+
+  base::expected<void, std::string> status;
+  if (file_error != base::File::FILE_OK) {
+    state_ = State::kDisconnected;
+    status = base::unexpected(base::File::ErrorToString(file_error));
+  } else {
+    state_ = State::kConnected;
+    StartProcessingDisconnects();
+  }
+
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(reconnect_callback), std::move(status)));
+}
+
+}  // namespace internal
 
 SignedWebBundleReader::SignedWebBundleReader(
     const base::FilePath& web_bundle_path,
     const absl::optional<GURL>& base_url,
     std::unique_ptr<web_package::SignedWebBundleSignatureVerifier>
         signature_verifier)
-    : web_bundle_path_(web_bundle_path),
-      base_url_(base_url),
-      signature_verifier_(std::move(signature_verifier)) {}
+    : signature_verifier_(std::move(signature_verifier)),
+      connection_(std::make_unique<internal::SafeWebBundleParserConnection>(
+          web_bundle_path,
+          base_url)) {}
 
 SignedWebBundleReader::~SignedWebBundleReader() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -60,73 +209,28 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK_EQ(state_, State::kUninitialized);
 
-  Initialize(std::move(integrity_block_result_callback),
-             std::move(read_error_callback));
-}
-
-void SignedWebBundleReader::Initialize(
-    IntegrityBlockReadResultCallback integrity_block_result_callback,
-    ReadErrorCallback read_error_callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  CHECK_EQ(state_, State::kUninitialized);
-
   state_ = State::kInitializing;
-
-  parser_ = std::make_unique<data_decoder::SafeWebBundleParser>(base_url_);
-
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(
-          [](const base::FilePath& file_path) -> std::unique_ptr<base::File> {
-            return std::make_unique<base::File>(
-                file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
-          },
-          web_bundle_path_),
-      base::BindOnce(&SignedWebBundleReader::OnFileOpened,
+  connection_->Initialize(
+      base::BindOnce(&SignedWebBundleReader::OnConnectionInitialized,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(integrity_block_result_callback),
                      std::move(read_error_callback)));
 }
 
-void SignedWebBundleReader::OnFileOpened(
+void SignedWebBundleReader::OnConnectionInitialized(
     IntegrityBlockReadResultCallback integrity_block_result_callback,
     ReadErrorCallback read_error_callback,
-    std::unique_ptr<base::File> file) {
+    base::expected<void, UnusableSwbnFileError> init_status) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK_EQ(state_, State::kInitializing);
 
-  if (!file->IsValid()) {
-    UnusableSwbnFileError error = UnusableSwbnFileError(
-        UnusableSwbnFileError::Error::kIntegrityBlockParserInternalError,
-        base::File::ErrorToString(file->error_details()));
-    FulfillWithError(std::move(read_error_callback), std::move(error));
+  if (!init_status.has_value()) {
+    FulfillWithError(std::move(read_error_callback),
+                     std::move(init_status.error()));
     return;
   }
 
-  file_ = base::MakeRefCounted<web_package::SharedFile>(std::move(file));
-  file_->DuplicateFile(base::BindOnce(
-      &SignedWebBundleReader::OnFileDuplicated, weak_ptr_factory_.GetWeakPtr(),
-      std::move(integrity_block_result_callback),
-      std::move(read_error_callback)));
-}
-
-void SignedWebBundleReader::OnFileDuplicated(
-    IntegrityBlockReadResultCallback integrity_block_result_callback,
-    ReadErrorCallback read_error_callback,
-    base::File file) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  CHECK_EQ(state_, State::kInitializing);
-
-  base::File::Error file_error = parser_->OpenFile(std::move(file));
-  if (file_error != base::File::FILE_OK) {
-    UnusableSwbnFileError error = UnusableSwbnFileError(
-        UnusableSwbnFileError::Error::kIntegrityBlockParserInternalError,
-        base::File::ErrorToString(file_error));
-    FulfillWithError(std::move(read_error_callback), std::move(error));
-    return;
-  }
-
-  parser_->ParseIntegrityBlock(
+  connection_->parser_->ParseIntegrityBlock(
       base::BindOnce(&SignedWebBundleReader::OnIntegrityBlockParsed,
                      weak_ptr_factory_.GetWeakPtr(),
                      std::move(integrity_block_result_callback),
@@ -188,7 +292,12 @@
               action.abort_message()));
       return;
     case SignatureVerificationAction::Type::kContinueAndVerifySignatures:
-      VerifySignatures(std::move(integrity_block), std::move(callback));
+      base::ThreadPool::PostTaskAndReplyWithResult(
+          FROM_HERE, {base::MayBlock()},
+          base::BindOnce(ReadLengthOfSharedFile, connection_->file_),
+          base::BindOnce(&SignedWebBundleReader::OnFileLengthRead,
+                         weak_ptr_factory_.GetWeakPtr(),
+                         std::move(integrity_block), std::move(callback)));
       return;
     case SignatureVerificationAction::Type::
         kContinueAndSkipSignatureVerification:
@@ -197,29 +306,6 @@
   }
 }
 
-void SignedWebBundleReader::VerifySignatures(
-    web_package::SignedWebBundleIntegrityBlock integrity_block,
-    ReadErrorCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  CHECK_EQ(state_, State::kInitializing);
-
-  base::ThreadPool::PostTaskAndReplyWithResult(
-      FROM_HERE, {base::MayBlock()},
-      base::BindOnce(
-          [](scoped_refptr<web_package::SharedFile> file)
-              -> base::expected<uint64_t, base::File::Error> {
-            int64_t length = (*file)->GetLength();
-            if (length < 0) {
-              return base::unexpected((*file)->GetLastFileError());
-            }
-            return static_cast<uint64_t>(length);
-          },
-          file_),
-      base::BindOnce(&SignedWebBundleReader::OnFileLengthRead,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(integrity_block),
-                     std::move(callback)));
-}
-
 void SignedWebBundleReader::OnFileLengthRead(
     web_package::SignedWebBundleIntegrityBlock integrity_block,
     ReadErrorCallback callback,
@@ -233,7 +319,7 @@
   }
 
   signature_verifier_->VerifySignatures(
-      file_, std::move(integrity_block),
+      connection_->file_, std::move(integrity_block),
       base::BindOnce(&SignedWebBundleReader::OnSignaturesVerified,
                      weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now(),
                      *file_length, std::move(callback)));
@@ -274,7 +360,7 @@
       << "The integrity block must have been read before reading metadata.";
   uint64_t metadata_offset = integrity_block_size_in_bytes_.value();
 
-  parser_->ParseMetadata(
+  connection_->parser_->ParseMetadata(
       metadata_offset,
       base::BindOnce(&SignedWebBundleReader::OnMetadataParsed,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -297,11 +383,7 @@
 
   state_ = State::kInitialized;
 
-  parser_->SetDisconnectCallback(
-      base::BindOnce(&SignedWebBundleReader::OnParserDisconnected,
-                     // `base::Unretained` is okay to use here, since
-                     // `parser_` will be deleted before `this` is deleted.
-                     base::Unretained(this)));
+  connection_->StartProcessingDisconnects();
 
   std::move(callback).Run(base::ok());
 }
@@ -310,9 +392,9 @@
                                              UnusableSwbnFileError error) {
   state_ = State::kError;
 
-  // This is an irrecoverable error state, thus we can safely delete `parser_`
-  // here to free up resources.
-  parser_.reset();
+  // This is an irrecoverable error state, thus we can safely delete
+  // `connection_` here to free up resources.
+  connection_.reset();
 
   std::move(callback).Run(base::unexpected(std::move(error)));
 }
@@ -355,10 +437,11 @@
   }
 
   auto response_location = entry_it->second->Clone();
-  if (is_disconnected_) {
+  if (connection_->is_disconnected()) {
     // Try reconnecting the parser if it hasn't been attempted yet.
     if (pending_read_responses_.empty()) {
-      Reconnect();
+      connection_->Reconnect(base::BindOnce(&SignedWebBundleReader::OnReconnect,
+                                            base::Unretained(this)));
     }
     pending_read_responses_.emplace_back(std::move(response_location),
                                          std::move(callback));
@@ -368,12 +451,35 @@
   ReadResponseInternal(std::move(response_location), std::move(callback));
 }
 
+void SignedWebBundleReader::OnReconnect(
+    base::expected<void, std::string> status) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  std::vector<std::pair<web_package::mojom::BundleResponseLocationPtr,
+                        ResponseCallback>>
+      read_tasks;
+  read_tasks.swap(pending_read_responses_);
+
+  for (auto& [response_location, response_callback] : read_tasks) {
+    if (!status.has_value()) {
+      base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+          FROM_HERE,
+          base::BindOnce(
+              std::move(response_callback),
+              base::unexpected(ReadResponseError::ForParserInternalError(
+                  "Unable to open file: " + status.error()))));
+    } else {
+      ReadResponseInternal(std::move(response_location),
+                           std::move(response_callback));
+    }
+  }
+}
+
 void SignedWebBundleReader::ReadResponseInternal(
     web_package::mojom::BundleResponseLocationPtr location,
     ResponseCallback callback) {
   CHECK_EQ(state_, State::kInitialized);
 
-  parser_->ParseResponse(
+  connection_->parser_->ParseResponse(
       location->offset, location->length,
       base::BindOnce(&SignedWebBundleReader::OnResponseParsed,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
@@ -405,8 +511,8 @@
       std::make_unique<mojo::DataPipeProducer>(std::move(producer_handle));
   mojo::DataPipeProducer* raw_producer = data_producer.get();
   raw_producer->Write(
-      file_->CreateDataSource(response->payload_offset,
-                              response->payload_length),
+      connection_->file_->CreateDataSource(response->payload_offset,
+                                           response->payload_length),
       base::BindOnce(
           // `producer` is passed to this callback purely for its lifetime
           // management so that it is deleted once this callback runs.
@@ -425,85 +531,12 @@
 
 void SignedWebBundleReader::SetParserDisconnectCallbackForTesting(
     base::RepeatingClosure callback) {
-  parser_disconnect_callback_for_testing_ = std::move(callback);
+  connection_->parser_disconnect_callback_for_testing_ = std::move(callback);
 }
 
 void SignedWebBundleReader::SetReconnectionFileErrorForTesting(
     base::File::Error file_error) {
-  reconnection_file_error_for_testing_ = file_error;
-}
-
-void SignedWebBundleReader::OnParserDisconnected() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!is_disconnected_);
-
-  is_disconnected_ = true;
-  parser_ = nullptr;
-  if (!parser_disconnect_callback_for_testing_.is_null()) {
-    CHECK_IS_TEST();
-    parser_disconnect_callback_for_testing_.Run();
-  }
-  // Reconnection will be attempted on the next call to `ReadResponse`.
-}
-
-void SignedWebBundleReader::Reconnect() {
-  DCHECK(!parser_);
-  parser_ = std::make_unique<data_decoder::SafeWebBundleParser>(base_url_);
-
-  file_->DuplicateFile(base::BindOnce(&SignedWebBundleReader::ReconnectForFile,
-                                      weak_ptr_factory_.GetWeakPtr()));
-}
-
-void SignedWebBundleReader::ReconnectForFile(base::File file) {
-  base::File::Error file_error;
-  if (reconnection_file_error_for_testing_.has_value()) {
-    CHECK_IS_TEST();
-    file_error = *reconnection_file_error_for_testing_;
-  } else {
-    file_error = parser_->OpenFile(std::move(file));
-  }
-
-  absl::optional<std::string> error;
-  if (file_error != base::File::FILE_OK) {
-    error = base::File::ErrorToString(file_error);
-  }
-  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&SignedWebBundleReader::DidReconnect,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(error)));
-}
-
-void SignedWebBundleReader::DidReconnect(absl::optional<std::string> error) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(is_disconnected_);
-  DCHECK(parser_);
-  std::vector<std::pair<web_package::mojom::BundleResponseLocationPtr,
-                        ResponseCallback>>
-      read_tasks;
-  read_tasks.swap(pending_read_responses_);
-
-  if (error) {
-    for (auto& [response_location, response_callback] : read_tasks) {
-      base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
-          FROM_HERE,
-          base::BindOnce(
-              std::move(response_callback),
-              base::unexpected(ReadResponseError::ForParserInternalError(
-                  "Unable to open file: " + *error))));
-    }
-    return;
-  }
-
-  is_disconnected_ = false;
-  parser_->SetDisconnectCallback(
-      base::BindOnce(&SignedWebBundleReader::OnParserDisconnected,
-                     // `base::Unretained` is okay to use here, since `parser_`
-                     // will be deleted before `this` is deleted.
-                     base::Unretained(this)));
-  for (auto& [response_location, response_callback] : read_tasks) {
-    ReadResponseInternal(std::move(response_location),
-                         std::move(response_callback));
-  }
+  connection_->reconnection_file_error_for_testing_ = file_error;
 }
 
 // static
diff --git a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.h b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.h
index 8593887..a216998 100644
--- a/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.h
+++ b/chrome/browser/web_applications/isolated_web_apps/signed_web_bundle_reader.h
@@ -32,6 +32,74 @@
 
 namespace web_app {
 
+namespace internal {
+
+// This class is responsible for establishing and maintaining of
+// the IPC connection for parsing of the Signed Web Bundle.
+class SafeWebBundleParserConnection {
+ public:
+  SafeWebBundleParserConnection(base::FilePath web_bundle_path,
+                                absl::optional<GURL> base_url);
+  ~SafeWebBundleParserConnection();
+
+  using InitCompleteCallback = base::OnceCallback<void(
+      base::expected<void, UnusableSwbnFileError> status)>;
+
+  using ReconnectCompleteCallback =
+      base::OnceCallback<void(base::expected<void, std::string> status)>;
+
+  // Please call this function before any other.
+  void Initialize(InitCompleteCallback init_complete_callback);
+
+  // Subscribes the instance of this class on OnParserDisconnected
+  // events.
+  void StartProcessingDisconnects();
+  bool is_disconnected() const { return state_ != State::kConnected; }
+  // Tries to reestablish IPC connection.
+  void Reconnect(ReconnectCompleteCallback reconnect_callback);
+
+  // The public fields below violate encapsulation. The problem is that
+  // there is no good way (so far) how to treat them. Returning
+  // const reference is currently not an option because reading
+  // is not a const function of these types (even though logically it is
+  // const).
+  // So far it is better to leave these fields in such an ugly form
+  // to pay attention to them in the nearest future.
+  // TODO(peletskyi): Make proper encapsulation here.
+  scoped_refptr<web_package::SharedFile> file_;
+  std::unique_ptr<data_decoder::SafeWebBundleParser> parser_;
+
+  // These fields we may not need after refactoring of the tests.
+  base::RepeatingClosure parser_disconnect_callback_for_testing_;
+  absl::optional<base::File::Error> reconnection_file_error_for_testing_;
+
+ private:
+  enum class State {
+    kUninitialized,
+    kInitializing,
+    kConnected,
+    kDisconnected,
+    kReconnecting,
+  };
+
+  void OnFileOpened(InitCompleteCallback init_complete_callback,
+                    std::unique_ptr<base::File> file);
+
+  void OnFileDuplicated(InitCompleteCallback init_complete_callback,
+                        base::File file);
+  void OnParserDisconnected();
+  void ReconnectForFile(ReconnectCompleteCallback reconnect_callback,
+                        base::File file);
+
+  base::FilePath web_bundle_path_;
+  absl::optional<GURL> base_url_;
+  State state_ = State::kUninitialized;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::WeakPtrFactory<SafeWebBundleParserConnection> weak_ptr_factory_{this};
+};
+}  // namespace internal
+
 // This class is a reader for Signed Web Bundles.
 //
 // `Create` returns a new instance of this class.
@@ -212,19 +280,10 @@
       std::unique_ptr<web_package::SignedWebBundleSignatureVerifier>
           signature_verifier);
 
-  void Initialize(
-      IntegrityBlockReadResultCallback integrity_block_result_callback,
-      ReadErrorCallback read_error_callback);
-
-  void OnFileOpened(
+  void OnConnectionInitialized(
       IntegrityBlockReadResultCallback integrity_block_result_callback,
       ReadErrorCallback read_error_callback,
-      std::unique_ptr<base::File> file);
-
-  void OnFileDuplicated(
-      IntegrityBlockReadResultCallback integrity_block_result_callback,
-      ReadErrorCallback read_error_callback,
-      base::File file);
+      base::expected<void, UnusableSwbnFileError> init_status);
 
   void OnIntegrityBlockParsed(
       IntegrityBlockReadResultCallback integrity_block_result_callback,
@@ -237,10 +296,6 @@
       ReadErrorCallback callback,
       SignatureVerificationAction action);
 
-  void VerifySignatures(
-      web_package::SignedWebBundleIntegrityBlock integrity_block,
-      ReadErrorCallback callback);
-
   void OnFileLengthRead(
       web_package::SignedWebBundleIntegrityBlock integrity_block,
       ReadErrorCallback callback,
@@ -270,29 +325,18 @@
                         web_package::mojom::BundleResponsePtr response,
                         web_package::mojom::BundleResponseParseErrorPtr error);
 
-  // The following methods are for reconnection handling if the
-  // `SafeWebBundleParser` disconnects at some point after integrity block and
+  // The following method is a callback for reconnection handling if the
+  // `SafeWebBundleParser` in the `SignedWebBundleParserConnection`
+  // disconnects at some point after integrity block and
   // metadata have been read. Reconnecting to a new parser will be attempted on
   // the next call to `ReadResponse`.
-  void OnParserDisconnected();
-  void Reconnect();
-  void ReconnectForFile(base::File file);
-  void DidReconnect(absl::optional<std::string> error);
+  void OnReconnect(base::expected<void, std::string> status);
 
   State state_ = State::kUninitialized;
 
-  bool is_disconnected_ = false;
-  base::FilePath web_bundle_path_;
-  absl::optional<GURL> base_url_;
   std::unique_ptr<web_package::SignedWebBundleSignatureVerifier>
       signature_verifier_;
 
-  std::unique_ptr<data_decoder::SafeWebBundleParser> parser_;
-  base::RepeatingClosure parser_disconnect_callback_for_testing_;
-  absl::optional<base::File::Error> reconnection_file_error_for_testing_;
-
-  scoped_refptr<web_package::SharedFile> file_;
-
   // Integrity Block
   absl::optional<uint64_t> integrity_block_size_in_bytes_;
 
@@ -306,6 +350,8 @@
                         ResponseCallback>>
       pending_read_responses_;
 
+  std::unique_ptr<internal::SafeWebBundleParserConnection> connection_;
+
   SEQUENCE_CHECKER(sequence_checker_);
   base::WeakPtrFactory<SignedWebBundleReader> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/webauthn/android/webauthn_request_delegate_android.cc b/chrome/browser/webauthn/android/webauthn_request_delegate_android.cc
index 8d05d24..4ab8130d 100644
--- a/chrome/browser/webauthn/android/webauthn_request_delegate_android.cc
+++ b/chrome/browser/webauthn/android/webauthn_request_delegate_android.cc
@@ -78,10 +78,14 @@
   if (is_conditional_request) {
     conditional_request_in_progress_ = true;
     ReportConditionalUiPasskeyCount(credentials.size());
-    ChromeWebAuthnCredentialsDelegateFactory::GetFactory(
-        content::WebContents::FromRenderFrameHost(frame_host))
-        ->GetDelegateForFrame(frame_host)
-        ->OnCredentialsReceived(std::move(display_credentials));
+    ChromeWebAuthnCredentialsDelegate* credentials_delegate =
+        ChromeWebAuthnCredentialsDelegateFactory::GetFactory(
+            content::WebContents::FromRenderFrameHost(frame_host))
+            ->GetDelegateForFrame(frame_host);
+    credentials_delegate->SetAndroidHybridAvailable(
+        ChromeWebAuthnCredentialsDelegate::AndroidHybridAvailable(
+            !hybrid_callback_.is_null()));
+    credentials_delegate->OnCredentialsReceived(std::move(display_credentials));
     return;
   }
 
@@ -90,17 +94,21 @@
   }
   touch_to_fill_controller_->Show(
       std::vector<password_manager::UiCredential>(), display_credentials,
-      std::make_unique<TouchToFillControllerWebAuthnDelegate>(this));
+      std::make_unique<TouchToFillControllerWebAuthnDelegate>(
+          this, !hybrid_callback_.is_null()));
 }
 
 void WebAuthnRequestDelegateAndroid::CleanupWebAuthnRequest(
     content::RenderFrameHost* frame_host) {
   if (conditional_request_in_progress_) {
     // Prevent autofill from offering WebAuthn credentials in the popup.
-    ChromeWebAuthnCredentialsDelegateFactory::GetFactory(
-        content::WebContents::FromRenderFrameHost(frame_host))
-        ->GetDelegateForFrame(frame_host)
-        ->NotifyWebAuthnRequestAborted();
+    ChromeWebAuthnCredentialsDelegate* credentials_delegate =
+        ChromeWebAuthnCredentialsDelegateFactory::GetFactory(
+            content::WebContents::FromRenderFrameHost(frame_host))
+            ->GetDelegateForFrame(frame_host);
+    credentials_delegate->NotifyWebAuthnRequestAborted();
+    credentials_delegate->SetAndroidHybridAvailable(
+        ChromeWebAuthnCredentialsDelegate::AndroidHybridAvailable(false));
   } else {
     touch_to_fill_controller_->Close();
   }
@@ -116,6 +124,12 @@
   }
 }
 
+void WebAuthnRequestDelegateAndroid::ShowHybridSignIn() {
+  if (hybrid_callback_) {
+    hybrid_callback_.Run();
+  }
+}
+
 content::WebContents* WebAuthnRequestDelegateAndroid::web_contents() {
   return web_contents_;
 }
diff --git a/chrome/browser/webauthn/android/webauthn_request_delegate_android.h b/chrome/browser/webauthn/android/webauthn_request_delegate_android.h
index b16ec623..c919419c 100644
--- a/chrome/browser/webauthn/android/webauthn_request_delegate_android.h
+++ b/chrome/browser/webauthn/android/webauthn_request_delegate_android.h
@@ -61,7 +61,7 @@
 
   // Tells the WebAuthn Java implementation the the user has selected the
   // option for hybrid sign-in, which should be handled by the platform.
-  void ShowHybridSignIn();
+  virtual void ShowHybridSignIn();
 
   // Returns the WebContents that owns this object.
   content::WebContents* web_contents();
diff --git a/chrome/browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc b/chrome/browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc
index cbd468b1..654bf29 100644
--- a/chrome/browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc
+++ b/chrome/browser/webauthn/chrome_webauthn_autofill_interactive_uitest.cc
@@ -159,7 +159,7 @@
     size_t webauthn_entry_count = 0;
     autofill::Suggestion webauthn_entry;
     for (size_t i = 0; i < suggestions.size(); ++i) {
-      if (suggestions[i].frontend_id ==
+      if (suggestions[i].popup_item_id ==
           autofill::PopupItemId::kWebauthnCredential) {
         webauthn_entry = suggestions[i];
         suggestion_index = i;
@@ -207,7 +207,7 @@
     autofill::Suggestion webauthn_entry;
     for (suggestion_index = 0; suggestion_index < suggestions.size();
          ++suggestion_index) {
-      if (suggestions[suggestion_index].frontend_id ==
+      if (suggestions[suggestion_index].popup_item_id ==
           autofill::PopupItemId::kWebauthnCredential) {
         webauthn_entry = suggestions[suggestion_index];
         break;
@@ -238,9 +238,9 @@
       popup_controller = autofill_client->popup_controller_for_testing();
     }
     for (const auto& suggestion : popup_controller->GetSuggestions()) {
-      EXPECT_NE(suggestion.frontend_id,
+      EXPECT_NE(suggestion.popup_item_id,
                 autofill::PopupItemId::kWebauthnCredential);
-      EXPECT_NE(suggestion.frontend_id,
+      EXPECT_NE(suggestion.popup_item_id,
                 autofill::PopupItemId::kWebauthnSignInWithAnotherDevice);
     }
   }
diff --git a/chrome/browser/webauthn/chrome_webauthn_autofill_mac_interactive_uitest.mm b/chrome/browser/webauthn/chrome_webauthn_autofill_mac_interactive_uitest.mm
index bddd3cf..d21835b 100644
--- a/chrome/browser/webauthn/chrome_webauthn_autofill_mac_interactive_uitest.mm
+++ b/chrome/browser/webauthn/chrome_webauthn_autofill_mac_interactive_uitest.mm
@@ -124,7 +124,7 @@
   autofill::Suggestion webauthn_entry;
   for (suggestion_index = 0; suggestion_index < suggestions.size();
        ++suggestion_index) {
-    if (suggestions[suggestion_index].frontend_id ==
+    if (suggestions[suggestion_index].popup_item_id ==
         autofill::PopupItemId::kWebauthnCredential) {
       webauthn_entry = suggestions[suggestion_index];
       break;
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 0003b8f..61967a2 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1685973473-6ce445e67da224e060747da32ddfde6dd29c7d35.profdata
+chrome-mac-arm-main-1685980729-fa83326bccfabab51437a6dfc854819e645b4aa9.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index b46d7f9..75b1964 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1685966279-d986259a1372e4bb8f3237a8b1d680ad90a28a9a.profdata
+chrome-win32-main-1685977067-6fb06967e4238d5c4b1fa6013f2db02bf96266a4.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 95deb42..72fddd4 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1685966279-51da2fb2dc36da8ea8849cfe1a298dbfcc8aa8f8.profdata
+chrome-win64-main-1685977067-9b2774c50b0d0ffbb9e2c5ac9f7b9f7448ee9709.profdata
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index f38530ce..947071f 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -661,6 +661,7 @@
 
 #if BUILDFLAG(ENABLE_WAFFLE_DESKTOP)
 const char kChromeUIWaffleHost[] = "waffle";
+const char kChromeUIWaffleURL[] = "chrome://waffle";
 #endif
 
 #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES)
diff --git a/chrome/renderer/accessibility/ax_tree_distiller.cc b/chrome/renderer/accessibility/ax_tree_distiller.cc
index ed1f4354..51b72b5 100644
--- a/chrome/renderer/accessibility/ax_tree_distiller.cc
+++ b/chrome/renderer/accessibility/ax_tree_distiller.cc
@@ -12,6 +12,9 @@
 #include "base/containers/contains.h"
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/renderer/render_frame.h"
+#include "content/public/renderer/render_thread.h"
+#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
 #include "ui/accessibility/accessibility_features.h"
 #include "ui/accessibility/ax_node.h"
@@ -91,16 +94,25 @@
     content::RenderFrame* render_frame,
     OnAXTreeDistilledCallback on_ax_tree_distilled_callback)
     : render_frame_(render_frame),
-      on_ax_tree_distilled_callback_(on_ax_tree_distilled_callback) {}
+      on_ax_tree_distilled_callback_(on_ax_tree_distilled_callback) {
+  // TODO(crbug.com/1450930): Use a global ukm recorder instance instead.
+  mojo::Remote<ukm::mojom::UkmRecorderFactory> factory;
+  content::RenderThread::Get()->BindHostReceiver(
+      factory.BindNewPipeAndPassReceiver());
+  ukm_recorder_ = ukm::MojoUkmRecorder::Create(*factory);
+}
 
 AXTreeDistiller::~AXTreeDistiller() = default;
 
 void AXTreeDistiller::Distill(const ui::AXTree& tree,
                               const ui::AXTreeUpdate& snapshot,
-                              const ukm::SourceId& ukm_source_id) {
+                              const ukm::SourceId ukm_source_id) {
+#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
+  base::TimeTicks start_time = base::TimeTicks::Now();
+#endif
   // Try with the algorithm first.
   std::vector<ui::AXNodeID> content_node_ids;
-  DistillViaAlgorithm(tree, &content_node_ids);
+  DistillViaAlgorithm(tree, ukm_source_id, &content_node_ids);
 
   // If Read Anything with Screen 2x is enabled and the main content extractor
   // is bound, kick off Screen 2x run, which distills the AXTree in the
@@ -108,7 +120,8 @@
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
   if (features::IsReadAnythingWithScreen2xEnabled() &&
       main_content_extractor_.is_bound()) {
-    DistillViaScreen2x(tree, snapshot, ukm_source_id, &content_node_ids);
+    DistillViaScreen2x(tree, snapshot, ukm_source_id, start_time,
+                       &content_node_ids);
     return;
   }
 #endif
@@ -119,19 +132,44 @@
 
 void AXTreeDistiller::DistillViaAlgorithm(
     const ui::AXTree& tree,
+    const ukm::SourceId ukm_source_id,
     std::vector<ui::AXNodeID>* content_node_ids) {
+  base::TimeTicks start_time = base::TimeTicks::Now();
   std::vector<const ui::AXNode*> content_root_nodes;
   GetContentRootNodes(tree.root(), &content_root_nodes);
   for (const ui::AXNode* content_root_node : content_root_nodes) {
     AddContentNodesToVector(content_root_node, content_node_ids);
   }
+  RecordRulesMetrics(ukm_source_id, base::TimeTicks::Now() - start_time,
+                     !content_node_ids->empty());
+}
+
+void AXTreeDistiller::RecordRulesMetrics(ukm::SourceId ukm_source_id,
+                                         base::TimeDelta elapsed_time,
+                                         bool success) {
+  if (success) {
+    base::UmaHistogramTimes(
+        "Accessibility.ReadAnything.RulesDistillationTime.Success",
+        elapsed_time);
+    ukm::builders::Accessibility_ReadAnything(ukm_source_id)
+        .SetRulesDistillationTime_Success(elapsed_time.InMilliseconds())
+        .Record(ukm_recorder_.get());
+  } else {
+    base::UmaHistogramTimes(
+        "Accessibility.ReadAnything.RulesDistillationTime.Failure",
+        elapsed_time);
+    ukm::builders::Accessibility_ReadAnything(ukm_source_id)
+        .SetRulesDistillationTime_Failure(elapsed_time.InMilliseconds())
+        .Record(ukm_recorder_.get());
+  }
 }
 
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
 void AXTreeDistiller::DistillViaScreen2x(
     const ui::AXTree& tree,
     const ui::AXTreeUpdate& snapshot,
-    const ukm::SourceId& ukm_source_id,
+    const ukm::SourceId ukm_source_id,
+    base::TimeTicks start_time,
     std::vector<ui::AXNodeID>* content_node_ids_algorithm) {
   DCHECK(main_content_extractor_.is_bound());
   // Make a copy of |content_node_ids_algorithm| rather than sending a pointer.
@@ -139,11 +177,13 @@
       snapshot, ukm_source_id,
       base::BindOnce(&AXTreeDistiller::ProcessScreen2xResult,
                      weak_ptr_factory_.GetWeakPtr(), tree.GetAXTreeID(),
-                     *content_node_ids_algorithm));
+                     ukm_source_id, start_time, *content_node_ids_algorithm));
 }
 
 void AXTreeDistiller::ProcessScreen2xResult(
     const ui::AXTreeID& tree_id,
+    const ukm::SourceId ukm_source_id,
+    base::TimeTicks start_time,
     std::vector<ui::AXNodeID> content_node_ids_algorithm,
     const std::vector<ui::AXNodeID>& content_node_ids_screen2x) {
   // Merge the results from the algorithm and from screen2x.
@@ -152,6 +192,8 @@
       content_node_ids_algorithm.push_back(content_node_id_screen2x);
     }
   }
+  RecordMergedMetrics(ukm_source_id, base::TimeTicks::Now() - start_time,
+                      !content_node_ids_algorithm.empty());
   on_ax_tree_distilled_callback_.Run(tree_id, content_node_ids_algorithm);
 
   // TODO(crbug.com/1266555): If no content nodes were identified, and
@@ -174,4 +216,24 @@
   on_ax_tree_distilled_callback_.Run(ui::AXTreeIDUnknown(),
                                      std::vector<ui::AXNodeID>());
 }
+
+void AXTreeDistiller::RecordMergedMetrics(ukm::SourceId ukm_source_id,
+                                          base::TimeDelta elapsed_time,
+                                          bool success) {
+  if (success) {
+    base::UmaHistogramTimes(
+        "Accessibility.ReadAnything.MergedDistillationTime.Success",
+        elapsed_time);
+    ukm::builders::Accessibility_ReadAnything(ukm_source_id)
+        .SetMergedDistillationTime_Success(elapsed_time.InMilliseconds())
+        .Record(ukm_recorder_.get());
+  } else {
+    base::UmaHistogramTimes(
+        "Accessibility.ReadAnything.MergedDistillationTime.Failure",
+        elapsed_time);
+    ukm::builders::Accessibility_ReadAnything(ukm_source_id)
+        .SetMergedDistillationTime_Failure(elapsed_time.InMilliseconds())
+        .Record(ukm_recorder_.get());
+  }
+}
 #endif
diff --git a/chrome/renderer/accessibility/ax_tree_distiller.h b/chrome/renderer/accessibility/ax_tree_distiller.h
index 2e2b6d3..0e7d326d 100644
--- a/chrome/renderer/accessibility/ax_tree_distiller.h
+++ b/chrome/renderer/accessibility/ax_tree_distiller.h
@@ -10,6 +10,7 @@
 
 #include "base/functional/callback.h"
 #include "base/scoped_observation.h"
+#include "base/time/time.h"
 #include "components/services/screen_ai/buildflags/buildflags.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "ui/accessibility/ax_node_id_forward.h"
@@ -30,6 +31,10 @@
 class AXTree;
 }
 
+namespace ukm {
+class MojoUkmRecorder;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // AXTreeDistiller
 //
@@ -60,7 +65,7 @@
   // algorithm in this process.
   virtual void Distill(const ui::AXTree& tree,
                        const ui::AXTreeUpdate& snapshot,
-                       const ukm::SourceId& ukm_source_id);
+                       const ukm::SourceId ukm_source_id);
 
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
   void ScreenAIServiceReady();
@@ -70,8 +75,48 @@
   // Distills the AXTree via a rules-based algorithm. Results are added to
   // |content_node_ids|.
   void DistillViaAlgorithm(const ui::AXTree& tree,
+                           const ukm::SourceId ukm_source_id,
                            std::vector<ui::AXNodeID>* content_node_ids);
 
+  void RecordRulesMetrics(const ukm::SourceId ukm_source_id,
+                          base::TimeDelta elapsed_time,
+                          bool success);
+
+#if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
+  // Passes |snapshot| to the Screen2x ML model, which identifes the main
+  // content nodes and calls |ProcessScreen2xResult()| on completion.
+  // |content_node_ids_algorithm| are the content nodes identified by the
+  // algorithm. They are passed along to the screen2x callback. start_time is
+  // the time when DistillViaAlgorithm started and is used for
+  // RecordMergedMetrics.
+  void DistillViaScreen2x(
+      const ui::AXTree& tree,
+      const ui::AXTreeUpdate& snapshot,
+      const ukm::SourceId ukm_source_id,
+      base::TimeTicks start_time,
+      std::vector<ui::AXNodeID>* content_node_ids_algorithm);
+
+  // Called by the Screen2x service from the utility process. Merges the result
+  // from the algorithm with the result from Screen2x and passes the merged
+  // vector to the callback.
+  void ProcessScreen2xResult(
+      const ui::AXTreeID& tree_id,
+      const ukm::SourceId ukm_source_id,
+      base::TimeTicks start_time,
+      std::vector<ui::AXNodeID> content_node_ids_algorithm,
+      const std::vector<ui::AXNodeID>& content_node_ids_screen2x);
+
+  // Called when the main content extractor is disconnected. Runs the callback
+  // with an empty list of content node IDs.
+  void OnMainContentExtractorDisconnected();
+
+  // Record time it takes for the merged algorithm (distillation via algorithm
+  // and via Screen2x) to run.
+  void RecordMergedMetrics(const ukm::SourceId ukm_source_id,
+                           base::TimeDelta elapsed_time,
+                           bool success);
+#endif  // BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
+
   // render_frame_ is only used in the ENABLE_SCREEN_AI_SERVICE buildflag.
   // Fuchsia does not build with that buildflag so it is throwing
   // -Wunused-private-field errors. [[maybe_unused]] suppresses them.
@@ -81,29 +126,9 @@
   // disconnected.
   OnAXTreeDistilledCallback on_ax_tree_distilled_callback_;
 
+  std::unique_ptr<ukm::MojoUkmRecorder> ukm_recorder_;
+
 #if BUILDFLAG(ENABLE_SCREEN_AI_SERVICE)
-  // Passes |snapshot| to the Screen2x ML model, which identifes the main
-  // content nodes and calls |ProcessScreen2xResult()| on completion.
-  // |content_node_ids_algorithm| are the content nodes identified by the
-  // algorithm. They are passed along to the screen2x callback.
-  void DistillViaScreen2x(
-      const ui::AXTree& tree,
-      const ui::AXTreeUpdate& snapshot,
-      const ukm::SourceId& ukm_source_id,
-      std::vector<ui::AXNodeID>* content_node_ids_algorithm);
-
-  // Called by the Screen2x service from the utility process. Merges the result
-  // from the algorithm with the result from Screen2x and passes the merged
-  // vector to the callback.
-  void ProcessScreen2xResult(
-      const ui::AXTreeID& tree_id,
-      std::vector<ui::AXNodeID> content_node_ids_algorithm,
-      const std::vector<ui::AXNodeID>& content_node_ids_screen2x);
-
-  // Called when the main content extractor is disconnected. Runs the callback
-  // with an empty list of content node IDs.
-  void OnMainContentExtractorDisconnected();
-
   // The remote of the Screen2x main content extractor. The receiver lives in
   // the utility process.
   mojo::Remote<screen_ai::mojom::Screen2xMainContentExtractor>
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
index 6ec7122..55453991 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -23,7 +23,7 @@
               Distill,
               (const ui::AXTree& tree,
                const ui::AXTreeUpdate& snapshot,
-               const ukm::SourceId& ukm_source_id),
+               const ukm::SourceId ukm_source_id),
               (override));
 };
 
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc
index 734ea2e..77c3b67 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.cc
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -96,7 +96,9 @@
         ->language_identification_event_callback()
         .Run(std::string(event.language()),
              static_cast<media::mojom::ConfidenceLevel>(
-                 event.confidence_level()));
+                 event.confidence_level()),
+             static_cast<media::mojom::AsrSwitchResult>(
+                 event.asr_switch_result()));
   }
 
   if (response.soda_type() == soda::chrome::SodaResponse::STOP) {
@@ -171,11 +173,12 @@
 
 void SpeechRecognitionRecognizerImpl::OnLanguageIdentificationEvent(
     const std::string& language,
-    const media::mojom::ConfidenceLevel confidence_level) {
+    const media::mojom::ConfidenceLevel confidence_level,
+    const media::mojom::AsrSwitchResult asr_switch_result) {
   if (client_remote_.is_bound()) {
     client_remote_->OnLanguageIdentificationEvent(
-        media::mojom::LanguageIdentificationEvent::New(language,
-                                                       confidence_level));
+        media::mojom::LanguageIdentificationEvent::New(
+            language, confidence_level, asr_switch_result));
   }
 }
 
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.h b/chrome/services/speech/speech_recognition_recognizer_impl.h
index d2d33ec6..3548894 100644
--- a/chrome/services/speech/speech_recognition_recognizer_impl.h
+++ b/chrome/services/speech/speech_recognition_recognizer_impl.h
@@ -33,7 +33,8 @@
 
   using OnLanguageIdentificationEventCallback = base::RepeatingCallback<void(
       const std::string& language,
-      const media::mojom::ConfidenceLevel confidence_level)>;
+      const media::mojom::ConfidenceLevel confidence_level,
+      const media::mojom::AsrSwitchResult asr_switch_result)>;
 
   using OnSpeechRecognitionStoppedCallback = base::RepeatingCallback<void()>;
 
@@ -104,7 +105,8 @@
 
   void OnLanguageIdentificationEvent(
       const std::string& language,
-      const media::mojom::ConfidenceLevel confidence_level);
+      const media::mojom::ConfidenceLevel confidence_level,
+      const media::mojom::AsrSwitchResult asr_switch_result);
 
   void OnRecognitionStoppedCallback();
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index cb00780..0eb4325 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2133,6 +2133,7 @@
       "../browser/page_load_metrics/observers/prerender_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/security_state_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/signed_exchange_page_load_metrics_browsertest.cc",
+      "../browser/page_load_metrics/observers/tab_strip_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/third_party_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/use_counter_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/page_load_metrics_browsertest.cc",
diff --git a/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js b/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
index 900911f..4dd25d8 100644
--- a/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
+++ b/chrome/test/data/extensions/platform_apps/web_view/newwindow/embedder.js
@@ -34,6 +34,9 @@
   embedder.guestWithLinkURL = embedder.baseGuestURL +
       '/extensions/platform_apps/web_view/newwindow' +
       '/guest_with_link.html';
+  embedder.guestOpenOnLoadURL = embedder.baseGuestURL +
+      '/extensions/platform_apps/web_view/newwindow' +
+      '/guest_opener_open_on_load.html';
 };
 
 /** @private */
@@ -652,6 +655,24 @@
   embedder.setUpNewWindowRequest_(webview, 'guest.html', '', testName);
 }
 
+// This is not a test in and of itself, but a means of creating a webview that
+// is left in an unattached state while its opener webview is also in an
+// unattached state, so that the C++ side can test it in that state.
+function testDestroyOpenerBeforeAttachment() {
+  embedder.test.succeed();
+
+  let webview = new WebView();
+  webview.src = embedder.guestOpenOnLoadURL;
+  document.body.appendChild(webview);
+
+  // By spinning forever here, we prevent `webview` from completing the
+  // attachment process. But since the guest is still created and it calls
+  // window.open, we have a situation where two unattached webviews have an
+  // opener relationship. The C++ side will test that we can shutdown safely in
+  // this case.
+  while (true) {}
+}
+
 embedder.test.testList = {
   'testNewWindowAttachAfterOpenerDestroyed':
       testNewWindowAttachAfterOpenerDestroyed,
@@ -675,7 +696,9 @@
       testNewWindowWebViewNameTakesPrecedence,
   'testNewWindowAndUpdateOpener': testNewWindowAndUpdateOpener,
   'testNewWindowDeferredAttachmentIndefinitely':
-      testNewWindowDeferredAttachmentIndefinitely
+      testNewWindowDeferredAttachmentIndefinitely,
+  'testDestroyOpenerBeforeAttachment':
+      testDestroyOpenerBeforeAttachment
 };
 
 onload = function() {
diff --git a/chrome/test/data/extensions/platform_apps/web_view/newwindow/guest_opener_open_on_load.html b/chrome/test/data/extensions/platform_apps/web_view/newwindow/guest_opener_open_on_load.html
new file mode 100644
index 0000000..e961feb3
--- /dev/null
+++ b/chrome/test/data/extensions/platform_apps/web_view/newwindow/guest_opener_open_on_load.html
@@ -0,0 +1,13 @@
+<!--
+Copyright 2023 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html>
+<body>
+<script>
+  // A guest that opens a new window on load.
+  window.open('guest.html');
+</script>
+</body>
+</html>
diff --git a/chrome/test/data/webui/chromeos/print_management/print_management_test.ts b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
index 94b4c193..a525cc7 100644
--- a/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
+++ b/chrome/test/data/webui/chromeos/print_management/print_management_test.ts
@@ -773,14 +773,24 @@
     await mojoApi_.whenCalled('getPrintJobs');
     flush();
 
-    // Assert that printer setup UI is not hidden when flag enabled.
+    // Assert that printer setup UI is not hidden and ongoing empty state
+    // message is hidden when flag enabled and there are no printer jobs.
+    assertTrue(
+        querySelector<HTMLElement>(page!, '#ongoingEmptyState')?.hidden as
+        boolean);
     assertFalse(
         querySelector<PrinterSetupInfoElement>(
             page!, PrinterSetupInfoElement.is)
             ?.hidden as boolean);
   });
 
-  test('CancelOngoingPrintJob', async () => {
+  // Verify expected elements render when there are no ongoing jobs, at least
+  // one historical job, and the printer setup flag is off.
+  test('CancelOngoingPrintJob_SetupAssistanceFlagOff', async () => {
+    // Ensure printer setup assistance flag is disabled for test.
+    loadTimeData.overrideValues({
+      isSetupAssistanceEnabled: false,
+    });
     const kId = 'fileA';
     const kTitle = 'titleA';
     const kTime =
@@ -808,10 +818,54 @@
         /*shouldAttemptCancel*/ true, expectedHistoryList);
     flush();
 
-    // Verify that there are no ongoing print jobs and history list is
-    // populated.
+    // Verify that there are no ongoing print jobs, history list is
+    // populated, and printer setup UI is hidden.
     assertTrue(!querySelector(page!, '#ongoingList'));
     verifyPrintJobs(expectedHistoryList, getHistoryPrintJobEntries(page!));
+    assertFalse(isVisible(querySelector<PrinterSetupInfoElement>(
+        page!, PrinterSetupInfoElement.is)));
+  });
+
+  // Verify expected elements render when there are no ongoing jobs, at least
+  // one historical job, and the printer setup flag is on.
+  test('CancelOngoingPrintJob_SetupAssistanceFlagOn', async () => {
+    // Ensure printer setup assistance flag is enabled for test.
+    loadTimeData.overrideValues({
+      isSetupAssistanceEnabled: true,
+    });
+    const kId = 'fileA';
+    const kTitle = 'titleA';
+    const kTime =
+        convertToMojoTime(new Date(Date.parse('February 5, 2020 03:23:00')));
+    const expectedArr = [
+      createJobEntry(
+          kId, kTitle, kTime, PrinterErrorCode.kNoError,
+          /*completedInfo=*/ undefined,
+          createOngoingPrintJobInfo(
+              /*printedPages=*/ 0, ActivePrintJobState.kStarted)),
+    ];
+
+    const expectedHistoryList = [createJobEntry(
+        kId, kTitle, kTime, PrinterErrorCode.kNoError,
+        createCompletedPrintJobInfo(PrintJobCompletionStatus.kCanceled))];
+
+    await initializePrintManagementApp(expectedArr);
+    await mojoApi_.whenCalled('getPrintJobs');
+    flush();
+    const jobEntries = getOngoingPrintJobEntries(page!);
+    verifyPrintJobs(expectedArr, jobEntries);
+
+    await simulateCancelPrintJob(
+        jobEntries[0]!, mojoApi_,
+        /*shouldAttemptCancel*/ true, expectedHistoryList);
+    flush();
+
+    // Verify that there are no ongoing print jobs, history list is
+    // populated, and printer setup UI is hidden.
+    assertTrue(!querySelector(page!, '#ongoingList'));
+    verifyPrintJobs(expectedHistoryList, getHistoryPrintJobEntries(page!));
+    assertFalse(isVisible(querySelector<PrinterSetupInfoElement>(
+        page!, PrinterSetupInfoElement.is)));
   });
 
   test('CancelOngoingPrintJobNotAttempted', async () => {
diff --git a/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts b/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
index 148bdc9..36a331c 100644
--- a/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
+++ b/chrome/test/data/webui/privacy_sandbox/privacy_sandbox_dialog_test.ts
@@ -767,13 +767,13 @@
 
   test('validDialog', async function() {
     await verifyActionOccured(
-        browserProxy, PrivacySandboxPromptAction.NOTICE_SHOWN);
+        browserProxy, PrivacySandboxPromptAction.RESTRICTED_NOTICE_SHOWN);
     assertTrue(!!page.shadowRoot!.querySelector('div'));
   });
 
   test('settingsClicked', async function() {
     await verifyActionOccured(
-        browserProxy, PrivacySandboxPromptAction.NOTICE_SHOWN);
+        browserProxy, PrivacySandboxPromptAction.RESTRICTED_NOTICE_SHOWN);
     testClickButton('#settingsButton', page);
     await verifyActionOccured(
         browserProxy,
@@ -782,7 +782,7 @@
 
   test('acknowledgeClicked', async function() {
     await verifyActionOccured(
-        browserProxy, PrivacySandboxPromptAction.NOTICE_SHOWN);
+        browserProxy, PrivacySandboxPromptAction.RESTRICTED_NOTICE_SHOWN);
     testClickButton('#ackButton', page);
     await verifyActionOccured(
         browserProxy, PrivacySandboxPromptAction.RESTRICTED_NOTICE_ACKNOWLEDGE);
@@ -794,7 +794,7 @@
   // resolved.
   test.skip('moreButton', async function() {
     await verifyActionOccured(
-        browserProxy, PrivacySandboxPromptAction.NOTICE_SHOWN);
+        browserProxy, PrivacySandboxPromptAction.RESTRICTED_NOTICE_SHOWN);
     await flushTasks();
 
     const scrollable: HTMLElement =
@@ -839,7 +839,8 @@
     }
 
     await verifyActionOccured(
-        browserProxy, PrivacySandboxPromptAction.NOTICE_MORE_BUTTON_CLICKED);
+        browserProxy,
+        PrivacySandboxPromptAction.RESTRICTED_NOTICE_MORE_BUTTON_CLICKED);
     await page.whenWasScrolledToBottomForTest();
 
     // After scrolling down, the "More" button is hidden and dialog button are
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn
index b9f33e5..a05effde 100644
--- a/chrome/test/data/webui/settings/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -116,7 +116,6 @@
     "app_management/fake_page_handler.ts",
     "app_management/file_handling_item_test.ts",
     "app_management/managed_apps_test.ts",
-    "app_management/pin_to_shelf_item_test.js",
     "app_management/plugin_vm_detail_view_test.js",
     "app_management/reducers_test.js",
     "app_management/resize_lock_item_test.js",
@@ -204,6 +203,7 @@
     "os_apps_page/app_management_page/borealis_detail_view_test.ts",
     "os_apps_page/app_management_page/chrome_app_detail_view_test.ts",
     "os_apps_page/app_management_page/main_view_test.ts",
+    "os_apps_page/app_management_page/pin_to_shelf_item_test.ts",
     "os_apps_page/app_management_page/pwa_detail_view_test.ts",
 
     "os_bluetooth_page/os_bluetooth_change_device_name_dialog_tests.js",
diff --git a/chrome/test/data/webui/settings/chromeos/app_management/pin_to_shelf_item_test.js b/chrome/test/data/webui/settings/chromeos/app_management/pin_to_shelf_item_test.js
deleted file mode 100644
index 3d3856a..0000000
--- a/chrome/test/data/webui/settings/chromeos/app_management/pin_to_shelf_item_test.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-'use strict';
-
-import {AppManagementStore} from 'chrome://os-settings/os_settings.js';
-import {convertOptionalBoolToBool} from 'chrome://resources/cr_components/app_management/util.js';
-import {setupFakeHandler, replaceBody} from './test_util.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {AppType} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
-
-suite('<app-management-pin-to-shelf-item>', () => {
-  let pinToShelfItem;
-  let fakeHandler;
-
-  setup(async () => {
-    fakeHandler = setupFakeHandler();
-    pinToShelfItem = document.createElement('app-management-pin-to-shelf-item');
-
-    replaceBody(pinToShelfItem);
-    flushTasks();
-  });
-
-  test('Toggle pin to shelf', async () => {
-    const arcOptions = {
-      type: AppType.kArc,
-      permissions: {},
-    };
-
-    // Add an arc app, and make it the currently selected app.
-    const app = await fakeHandler.addApp('app1', arcOptions);
-
-    await fakeHandler.flushPipesForTesting();
-    pinToShelfItem.app = app;
-    assertFalse(convertOptionalBoolToBool(
-        AppManagementStore.getInstance().data.apps[app.id].isPinned));
-
-    pinToShelfItem.click();
-    flushTasks();
-    await fakeHandler.flushPipesForTesting();
-
-    assertTrue(convertOptionalBoolToBool(
-        AppManagementStore.getInstance().data.apps[app.id].isPinned));
-  });
-});
diff --git a/chrome/test/data/webui/settings/chromeos/main_page_container_test.js b/chrome/test/data/webui/settings/chromeos/main_page_container_test.js
index e1bd69a..25db0f7 100644
--- a/chrome/test/data/webui/settings/chromeos/main_page_container_test.js
+++ b/chrome/test/data/webui/settings/chromeos/main_page_container_test.js
@@ -220,6 +220,12 @@
           assertEquals(1, numActive);
         }
 
+        suite('From Initial', () => {
+          test('to Root should only show Network page', () => {
+            assertOnlyActivePageIsVisible('internet');
+          });
+        });
+
         suite('From Root', () => {
           test('to Page should result in only one active page', async () => {
             // Simulate navigating from root to Network page
diff --git a/chrome/test/data/webui/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item_test.ts b/chrome/test/data/webui/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item_test.ts
new file mode 100644
index 0000000..64c18dd
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/os_apps_page/app_management_page/pin_to_shelf_item_test.ts
@@ -0,0 +1,56 @@
+// Copyright 2021 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://os-settings/lazy_load.js';
+
+import {AppManagementPinToShelfItemElement} from 'chrome://os-settings/lazy_load.js';
+import {AppManagementStore} from 'chrome://os-settings/os_settings.js';
+import {AppType} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js';
+import {convertOptionalBoolToBool} from 'chrome://resources/cr_components/app_management/util.js';
+import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+
+import {FakePageHandler} from '../../app_management/fake_page_handler.js';
+import {replaceBody, setupFakeHandler} from '../../app_management/test_util.js';
+
+suite('<app-management-pin-to-shelf-item>', () => {
+  let pinToShelfItem: AppManagementPinToShelfItemElement;
+  let fakeHandler: FakePageHandler;
+
+  setup(() => {
+    fakeHandler = setupFakeHandler();
+    pinToShelfItem = document.createElement('app-management-pin-to-shelf-item');
+
+    replaceBody(pinToShelfItem);
+    flushTasks();
+  });
+
+  teardown(() => {
+    pinToShelfItem.remove();
+  });
+
+  test('Toggle pin to shelf', async () => {
+    const arcOptions = {
+      type: AppType.kArc,
+      permissions: {},
+    };
+
+    // Add an arc app, and make it the currently selected app.
+    const app = await fakeHandler.addApp('app1', arcOptions);
+
+    await fakeHandler.flushPipesForTesting();
+    pinToShelfItem.app = app;
+    let selectedApp = AppManagementStore.getInstance().data.apps[app.id];
+    assertTrue(!!selectedApp);
+    assertFalse(convertOptionalBoolToBool(selectedApp.isPinned));
+
+    pinToShelfItem.click();
+    flushTasks();
+    await fakeHandler.flushPipesForTesting();
+
+    selectedApp = AppManagementStore.getInstance().data.apps[app.id];
+    assertTrue(!!selectedApp);
+    assertTrue(convertOptionalBoolToBool(selectedApp.isPinned));
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index e1c8d0cf..6fafe62 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -192,7 +192,6 @@
  ['AppManagementFileHandlingItem', 'app_management/file_handling_item_test.js'],
  ['AppManagementManagedApps', 'app_management/managed_apps_test.js'],
  ['AppManagementPage', 'app_management/app_management_page_tests.js'],
- ['AppManagementPinToShelfItem', 'app_management/pin_to_shelf_item_test.js'],
  [
    'AppManagementPluginVmDetailView',
    'app_management/plugin_vm_detail_view_test.js',
@@ -503,6 +502,10 @@
    'os_apps_page/app_management_page/main_view_test.js'
  ],
  [
+   'OsAppsPageAppManagementPagePinToShelfItem',
+   'os_apps_page/app_management_page/pin_to_shelf_item_test.js'
+ ],
+ [
    'OsAppsPageAppManagementPagePwaDetailView',
    'os_apps_page/app_management_page/pwa_detail_view_test.js'
  ],
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_page_visibility_test.ts b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_page_visibility_test.ts
index 40bac88..52f5ddf 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_page_visibility_test.ts
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_ui/os_settings_ui_page_visibility_test.ts
@@ -11,7 +11,7 @@
 
 import 'chrome://os-settings/os_settings.js';
 
-import {createRoutesForTesting, CrSettingsPrefs, MainPageContainerElement, OsSettingsMainElement, OsSettingsMenuElement, OsSettingsSectionElement, OsSettingsUiElement, Router} from 'chrome://os-settings/os_settings.js';
+import {createRoutesForTesting, CrSettingsPrefs, MainPageContainerElement, OsSettingsMainElement, OsSettingsMenuElement, OsSettingsRoutes, OsSettingsUiElement, Router} from 'chrome://os-settings/os_settings.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -23,6 +23,7 @@
   let settingsMain: OsSettingsMainElement;
   let mainPageContainer: MainPageContainerElement;
   let menu: OsSettingsMenuElement;
+  let testRoutes: OsSettingsRoutes;
 
   async function createUi() {
     ui = document.createElement('os-settings-ui');
@@ -51,17 +52,36 @@
     flush();
   }
 
-  function queryActivePages(): NodeListOf<OsSettingsSectionElement> {
-    return mainPageContainer.shadowRoot!
-        .querySelectorAll<OsSettingsSectionElement>(
-            `os-settings-section[active]`);
-  }
-
   function queryMenuItem(pageName: string): HTMLElement|null {
     return menu.shadowRoot!.querySelector<HTMLElement>(
         `a.item[data-page-name='${pageName}']`);
   }
 
+  /**
+   * Asserts the following:
+   * - Only one page is marked active
+   * - Active page does not have style "display: none"
+   * - Inactive pages have style "display: none"
+   */
+  function assertOnlyActivePageIsVisible(pageName: string): void {
+    const pages =
+        mainPageContainer.shadowRoot!.querySelectorAll('os-settings-section');
+    let numActive = 0;
+
+    for (const page of pages) {
+      const displayStyle = getComputedStyle(page).display;
+      if (page.hasAttribute('active')) {
+        numActive++;
+        assertNotEquals('none', displayStyle);
+        assertEquals(pageName, page.section);
+      } else {
+        assertEquals('none', displayStyle);
+      }
+    }
+
+    assertEquals(1, numActive);
+  }
+
   suiteSetup(async () => {
     loadTimeData.overrideValues({
       isRevampWayfindingEnabled: true,
@@ -69,7 +89,7 @@
     });
 
     // Recreate routes and Router so Kerberos route exists
-    const testRoutes = createRoutesForTesting();
+    testRoutes = createRoutesForTesting();
     Router.resetInstanceForTesting(new Router(testRoutes));
 
     await createUi();
@@ -84,6 +104,11 @@
     assertTrue(document.body.classList.contains('revamp-wayfinding-enabled'));
   });
 
+  test('Network page should be the default visible page', () => {
+    Router.getInstance().navigateTo(testRoutes.BASIC);
+    assertOnlyActivePageIsVisible('internet');
+  });
+
   const pageNames = [
     'apps',
     'bluetooth',
@@ -105,7 +130,7 @@
   ];
   for (const pageName of pageNames) {
     test(
-        `Clicking menu item for ${pageName} page should show that page`,
+        `Clicking menu item for ${pageName} page should show only that page`,
         async () => {
           const pageReadyPromise = eventToPromise('show-container', window);
 
@@ -116,13 +141,7 @@
 
           await pageReadyPromise;
 
-          const activePages = queryActivePages();
-          assertEquals(1, activePages.length);
-
-          const page = activePages[0];
-          assert(page);
-          assertEquals(pageName, page.section);
-          assertNotEquals('none', getComputedStyle(page).display);
+          assertOnlyActivePageIsVisible(pageName);
         });
   }
 });
diff --git a/chrome/updater/mac/signing/config_factory.py b/chrome/updater/mac/signing/config_factory.py
index 3cae0ed..00b3da3 100644
--- a/chrome/updater/mac/signing/config_factory.py
+++ b/chrome/updater/mac/signing/config_factory.py
@@ -11,5 +11,11 @@
 
 def get_invoker_class():
     """Returns the subclass of |invoker.Interface| to use."""
+    try:
+        from signing.internal_invoker import Invoker
+        return Invoker
+    except ImportError as e:
+        pass
+
     from signing.standard_invoker import Invoker
     return Invoker
diff --git a/components/autofill/core/browser/autocomplete_history_manager.cc b/components/autofill/core/browser/autocomplete_history_manager.cc
index c5e7945..77550406 100644
--- a/components/autofill/core/browser/autocomplete_history_manager.cc
+++ b/components/autofill/core/browser/autocomplete_history_manager.cc
@@ -156,14 +156,14 @@
 void AutocompleteHistoryManager::OnRemoveCurrentSingleFieldSuggestion(
     const std::u16string& field_name,
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {
+    PopupItemId popup_item_id) {
   if (profile_database_)
     profile_database_->RemoveFormValueForElementName(field_name, value);
 }
 
 void AutocompleteHistoryManager::OnSingleFieldSuggestionSelected(
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {
+    PopupItemId popup_item_id) {
   // Try to find the AutofillEntry associated with the given suggestion.
   auto last_entries_iter = last_entries_.find(value);
   if (last_entries_iter == last_entries_.end()) {
diff --git a/components/autofill/core/browser/autocomplete_history_manager.h b/components/autofill/core/browser/autocomplete_history_manager.h
index 037472c..a6c735c4 100644
--- a/components/autofill/core/browser/autocomplete_history_manager.h
+++ b/components/autofill/core/browser/autocomplete_history_manager.h
@@ -54,13 +54,11 @@
   void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields,
                                   bool is_autocomplete_enabled) override;
   void CancelPendingQueries(const SuggestionsHandler* handler) override;
-  void OnRemoveCurrentSingleFieldSuggestion(
-      const std::u16string& field_name,
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override;
-  void OnSingleFieldSuggestionSelected(
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override;
+  void OnRemoveCurrentSingleFieldSuggestion(const std::u16string& field_name,
+                                            const std::u16string& value,
+                                            PopupItemId popup_item_id) override;
+  void OnSingleFieldSuggestionSelected(const std::u16string& value,
+                                       PopupItemId popup_item_id) override;
 
   // Initializes the instance with the given parameters.
   // |profile_database_| is a profile-scope DB used to access autocomplete data.
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 69af57e..c34405e 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -20,6 +20,7 @@
 #include "components/autofill/core/browser/payments/legal_message_line.h"
 #include "components/autofill/core/browser/payments/risk_data_loader.h"
 #include "components/autofill/core/browser/ui/fast_checkout_client.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/popup_types.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/aliases.h"
@@ -780,7 +781,7 @@
   virtual bool IsContextSecure() const = 0;
 
   // Handles simple actions for the autofill popups.
-  virtual void ExecuteCommand(Suggestion::FrontendId id) = 0;
+  virtual void ExecuteCommand(PopupItemId popup_item_id) = 0;
 
   // Returns a LogManager instance. May be null for platforms that don't support
   // this.
diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc
index 487cfde..72c74f8 100644
--- a/components/autofill/core/browser/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/autofill_external_delegate.cc
@@ -45,9 +45,9 @@
 
 // Returns true if the suggestion entry is an Autofill warning message.
 // Warning messages should display on top of suggestion list.
-bool IsAutofillWarningEntry(Suggestion::FrontendId frontend_id) {
-  return frontend_id == PopupItemId::kInsecureContextPaymentDisabledMessage ||
-         frontend_id == PopupItemId::kMixedFormMessage;
+bool IsAutofillWarningEntry(PopupItemId popup_item_id) {
+  return popup_item_id == PopupItemId::kInsecureContextPaymentDisabledMessage ||
+         popup_item_id == PopupItemId::kMixedFormMessage;
 }
 
 }  // namespace
@@ -97,7 +97,7 @@
   if (should_show_scan_credit_card_) {
     Suggestion scan_credit_card(
         l10n_util::GetStringUTF16(IDS_AUTOFILL_SCAN_CREDIT_CARD));
-    scan_credit_card.frontend_id = PopupItemId::kScanCreditCard;
+    scan_credit_card.popup_item_id = PopupItemId::kScanCreditCard;
     scan_credit_card.icon = "scanCreditCardIcon";
     suggestions.push_back(scan_credit_card);
   }
@@ -106,7 +106,8 @@
   // suggestions.
   has_autofill_suggestions_ = false;
   for (auto& suggestion : suggestions) {
-    if (suggestion.frontend_id.is_an_address_or_card_popup_item_id()) {
+    if (suggestion.popup_item_id == PopupItemId::kAddressEntry ||
+        suggestion.popup_item_id == PopupItemId::kCreditCardEntry) {
       has_autofill_suggestions_ = true;
       break;
     }
@@ -115,7 +116,7 @@
   if (should_show_cards_from_account_option_) {
     suggestions.emplace_back(
         l10n_util::GetStringUTF16(IDS_AUTOFILL_SHOW_ACCOUNT_CARDS));
-    suggestions.back().frontend_id = PopupItemId::kShowAccountCards;
+    suggestions.back().popup_item_id = PopupItemId::kShowAccountCards;
     suggestions.back().icon = "google";
   }
 
@@ -196,20 +197,20 @@
     const Suggestion& suggestion) {
   ClearPreviewedForm();
 
-  const Suggestion::FrontendId frontend_id = suggestion.frontend_id;
   const Suggestion::BackendId backend_id =
       suggestion.GetPayload<Suggestion::BackendId>();
 
   // Only preview the data if it is a profile or a virtual card.
-  if (frontend_id.is_an_address_or_card_popup_item_id()) {
-    FillAutofillFormData(frontend_id, backend_id, true,
+  if (suggestion.popup_item_id == PopupItemId::kAddressEntry ||
+      suggestion.popup_item_id == PopupItemId::kCreditCardEntry) {
+    FillAutofillFormData(suggestion.popup_item_id, backend_id, true,
                          AutofillTriggerSource::kKeyboardAccessory);
-  } else if (frontend_id == PopupItemId::kAutocompleteEntry ||
-             frontend_id == PopupItemId::kIbanEntry ||
-             frontend_id == PopupItemId::kMerchantPromoCodeEntry) {
+  } else if (suggestion.popup_item_id == PopupItemId::kAutocompleteEntry ||
+             suggestion.popup_item_id == PopupItemId::kIbanEntry ||
+             suggestion.popup_item_id == PopupItemId::kMerchantPromoCodeEntry) {
     driver_->RendererShouldPreviewFieldWithValue(query_field_.global_id(),
                                                  suggestion.main_text.value);
-  } else if (frontend_id == PopupItemId::kVirtualCreditCardEntry) {
+  } else if (suggestion.popup_item_id == PopupItemId::kVirtualCreditCardEntry) {
     manager_->FillOrPreviewVirtualCardInformation(
         mojom::RendererFormDataAction::kPreview, backend_id.value(),
         query_form_, query_field_, AutofillTriggerSource::kKeyboardAccessory);
@@ -218,7 +219,7 @@
 
 void AutofillExternalDelegate::DidAcceptSuggestion(const Suggestion& suggestion,
                                                    int position) {
-  switch (suggestion.frontend_id.as_popup_item_id()) {
+  switch (suggestion.popup_item_id) {
     case PopupItemId::kAutofillOptions:
       // User selected 'Autofill Options'.
       autofill_metrics::LogAutofillSelectedManageEntry(popup_type_);
@@ -246,7 +247,7 @@
           query_field_.global_id(),
           suggestion.GetPayload<Suggestion::ValueToFill>().value());
       manager_->OnSingleFieldSuggestionSelected(suggestion.main_text.value,
-                                                suggestion.frontend_id,
+                                                suggestion.popup_item_id,
                                                 query_form_, query_field_);
       break;
     case PopupItemId::kAutocompleteEntry:
@@ -258,7 +259,7 @@
       driver_->RendererShouldFillFieldWithValue(query_field_.global_id(),
                                                 suggestion.main_text.value);
       manager_->OnSingleFieldSuggestionSelected(suggestion.main_text.value,
-                                                suggestion.frontend_id,
+                                                suggestion.popup_item_id,
                                                 query_form_, query_field_);
       break;
     case PopupItemId::kScanCreditCard:
@@ -267,7 +268,7 @@
                          GetWeakPtr(), AutofillTriggerSource::kPopup));
       break;
     case PopupItemId::kCreditCardSigninPromo:
-      manager_->client()->ExecuteCommand(suggestion.frontend_id);
+      manager_->client()->ExecuteCommand(suggestion.popup_item_id);
       break;
     case PopupItemId::kShowAccountCards:
       manager_->OnUserAcceptedCardsFromAccountOption();
@@ -281,9 +282,9 @@
       break;
     case PopupItemId::kVirtualCreditCardEntry:
       // There can be multiple virtual credit cards that all rely on
-      // PopupItemId::kVirtualCreditCardEntry as a frontend_id. In this case,
-      // the payload contains the backend id, which is a GUID that identifies
-      // the actually chosen credit card.
+      // PopupItemId::kVirtualCreditCardEntry as a `popup_item_id`. In this
+      // case, the payload contains the backend id, which is a GUID that
+      // identifies the actually chosen credit card.
       manager_->FillOrPreviewVirtualCardInformation(
           mojom::RendererFormDataAction::kFill,
           suggestion.GetPayload<Suggestion::BackendId>().value(), query_form_,
@@ -292,14 +293,15 @@
     case PopupItemId::kSeePromoCodeDetails:
       manager_->OnSeePromoCodeOfferDetailsSelected(
           suggestion.GetPayload<GURL>(), suggestion.main_text.value,
-          suggestion.frontend_id, query_form_, query_field_);
+          suggestion.popup_item_id, query_form_, query_field_);
       break;
     default:
-      if (suggestion.frontend_id.is_an_address_or_card_popup_item_id()) {
+      if (suggestion.popup_item_id == PopupItemId::kAddressEntry ||
+          suggestion.popup_item_id == PopupItemId::kCreditCardEntry) {
         autofill_metrics::LogAutofillSuggestionAcceptedIndex(
             position, popup_type_, manager_->client()->IsOffTheRecord());
       }
-      FillAutofillFormData(suggestion.frontend_id,
+      FillAutofillFormData(suggestion.popup_item_id,
                            suggestion.GetPayload<Suggestion::BackendId>(),
                            false, AutofillTriggerSource::kPopup);
       break;
@@ -307,12 +309,12 @@
 
   if (should_show_scan_credit_card_) {
     AutofillMetrics::LogScanCreditCardPromptMetric(
-        suggestion.frontend_id == PopupItemId::kScanCreditCard
+        suggestion.popup_item_id == PopupItemId::kScanCreditCard
             ? AutofillMetrics::SCAN_CARD_ITEM_SELECTED
             : AutofillMetrics::SCAN_CARD_OTHER_ITEM_SELECTED);
   }
 
-  if (suggestion.frontend_id == PopupItemId::kShowAccountCards) {
+  if (suggestion.popup_item_id == PopupItemId::kShowAccountCards) {
     should_show_cards_from_account_option_ = false;
     manager_->RefetchCardsAndUpdatePopup(query_form_, query_field_);
   } else {
@@ -322,25 +324,26 @@
 
 bool AutofillExternalDelegate::GetDeletionConfirmationText(
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id,
+    PopupItemId popup_item_id,
     Suggestion::BackendId backend_id,
     std::u16string* title,
     std::u16string* body) {
-  return manager_->GetDeletionConfirmationText(value, frontend_id, backend_id,
+  return manager_->GetDeletionConfirmationText(value, popup_item_id, backend_id,
                                                title, body);
 }
 
 bool AutofillExternalDelegate::RemoveSuggestion(
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id,
+    PopupItemId popup_item_id,
     Suggestion::BackendId backend_id) {
-  if (frontend_id.is_an_address_or_card_popup_item_id()) {
+  if (popup_item_id == PopupItemId::kAddressEntry ||
+      popup_item_id == PopupItemId::kCreditCardEntry) {
     return manager_->RemoveAutofillProfileOrCreditCard(backend_id);
   }
 
-  if (frontend_id == PopupItemId::kAutocompleteEntry) {
+  if (popup_item_id == PopupItemId::kAutocompleteEntry) {
     manager_->RemoveCurrentSingleFieldSuggestion(query_field_.name, value,
-                                                 frontend_id);
+                                                 popup_item_id);
     return true;
   }
 
@@ -391,12 +394,12 @@
 }
 
 void AutofillExternalDelegate::FillAutofillFormData(
-    Suggestion::FrontendId frontend_id,
+    PopupItemId popup_item_id,
     Suggestion::BackendId backend_id,
     bool is_preview,
     const AutofillTriggerSource trigger_source) {
   // If the selected element is a warning we don't want to do anything.
-  if (IsAutofillWarningEntry(frontend_id)) {
+  if (IsAutofillWarningEntry(popup_item_id)) {
     return;
   }
 
@@ -413,8 +416,8 @@
 void AutofillExternalDelegate::PossiblyRemoveAutofillWarnings(
     std::vector<Suggestion>* suggestions) {
   while (suggestions->size() > 1 &&
-         IsAutofillWarningEntry(suggestions->front().frontend_id) &&
-         !IsAutofillWarningEntry(suggestions->back().frontend_id)) {
+         IsAutofillWarningEntry(suggestions->front().popup_item_id) &&
+         !IsAutofillWarningEntry(suggestions->back().popup_item_id)) {
     // If we received warnings instead of suggestions from Autofill but regular
     // suggestions from autocomplete, don't show the Autofill warnings.
     suggestions->erase(suggestions->begin());
@@ -443,8 +446,8 @@
       value = base::i18n::ToUpper(value);
 #endif
 
-    suggestions->push_back(Suggestion(value));
-    suggestions->back().frontend_id = PopupItemId::kClearForm;
+    suggestions->emplace_back(value);
+    suggestions->back().popup_item_id = PopupItemId::kClearForm;
     suggestions->back().icon = "clearIcon";
     suggestions->back().acceptance_a11y_announcement =
         l10n_util::GetStringUTF16(IDS_AUTOFILL_A11Y_ANNOUNCE_CLEARED_FORM);
@@ -452,8 +455,8 @@
 
   // Append the 'Autofill settings' menu item, or the menu item specified in the
   // popup layout experiment.
-  suggestions->push_back(Suggestion(GetSettingsSuggestionValue()));
-  suggestions->back().frontend_id = PopupItemId::kAutofillOptions;
+  suggestions->emplace_back(GetSettingsSuggestionValue());
+  suggestions->back().popup_item_id = PopupItemId::kAutofillOptions;
   suggestions->back().icon = "settingsIcon";
 
   // On Android and Desktop, Google Pay branding is shown along with Settings.
@@ -480,7 +483,7 @@
   std::set<std::u16string> data_list_set(data_list_values_.begin(),
                                          data_list_values_.end());
   base::EraseIf(*suggestions, [&data_list_set](const Suggestion& suggestion) {
-    return suggestion.frontend_id == PopupItemId::kAutocompleteEntry &&
+    return suggestion.popup_item_id == PopupItemId::kAutocompleteEntry &&
            base::Contains(data_list_set, suggestion.main_text.value);
   });
 
@@ -500,7 +503,7 @@
     (*suggestions)[i].main_text = Suggestion::Text(
         data_list_values_[i], Suggestion::Text::IsPrimary(true));
     (*suggestions)[i].labels = {{Suggestion::Text(data_list_labels_[i])}};
-    (*suggestions)[i].frontend_id = PopupItemId::kDatalistEntry;
+    (*suggestions)[i].popup_item_id = PopupItemId::kDatalistEntry;
   }
 }
 
diff --git a/components/autofill/core/browser/autofill_external_delegate.h b/components/autofill/core/browser/autofill_external_delegate.h
index be59cdd..afbfe40 100644
--- a/components/autofill/core/browser/autofill_external_delegate.h
+++ b/components/autofill/core/browser/autofill_external_delegate.h
@@ -15,6 +15,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/autofill_trigger_source.h"
 #include "components/autofill/core/browser/ui/autofill_popup_delegate.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/aliases.h"
 #include "components/autofill/core/common/form_data.h"
@@ -51,12 +52,12 @@
   void DidSelectSuggestion(const Suggestion& suggestion) override;
   void DidAcceptSuggestion(const Suggestion& suggestion, int position) override;
   bool GetDeletionConfirmationText(const std::u16string& value,
-                                   Suggestion::FrontendId frontend_id,
+                                   PopupItemId popup_item_id,
                                    Suggestion::BackendId backend_id,
                                    std::u16string* title,
                                    std::u16string* body) override;
   bool RemoveSuggestion(const std::u16string& value,
-                        Suggestion::FrontendId frontend_id,
+                        PopupItemId popup_item_id,
                         Suggestion::BackendId backend_id) override;
   void ClearPreviewedForm() override;
 
@@ -129,7 +130,7 @@
   // If `is_preview` is true then this is just a preview to show the user what
   // would be selected and if `is_preview` is false then the user has selected
   // this data.
-  void FillAutofillFormData(Suggestion::FrontendId frontend_id,
+  void FillAutofillFormData(PopupItemId popup_item_id,
                             Suggestion::BackendId backend_id,
                             bool is_preview,
                             const AutofillTriggerSource trigger_source);
diff --git a/components/autofill/core/browser/autofill_external_delegate_unittest.cc b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
index 15e346a..f2460ad2 100644
--- a/components/autofill/core/browser/autofill_external_delegate_unittest.cc
+++ b/components/autofill/core/browser/autofill_external_delegate_unittest.cc
@@ -43,11 +43,6 @@
 namespace autofill {
 
 namespace {
-
-// A constant value to use as an Autofill profile ID.
-constexpr Suggestion::FrontendId kAutofillProfileId =
-    Suggestion::FrontendId(kAddressEntry);
-
 class MockAutofillDriver : public TestAutofillDriver {
  public:
   MockAutofillDriver() = default;
@@ -90,7 +85,7 @@
                const std::vector<std::u16string>& lables),
               (override));
   MOCK_METHOD(void, HideAutofillPopup, (PopupHidingReason), (override));
-  MOCK_METHOD(void, ExecuteCommand, (Suggestion::FrontendId), (override));
+  MOCK_METHOD(void, ExecuteCommand, (PopupItemId), (override));
   MOCK_METHOD(void,
               OpenPromoCodeOfferDetailsURL,
               (const GURL& url),
@@ -209,7 +204,7 @@
   void IssueOnSuggestionsReturned(FieldGlobalId field_id) {
     std::vector<Suggestion> suggestions;
     suggestions.emplace_back();
-    suggestions[0].frontend_id = kAutofillProfileId;
+    suggestions[0].popup_item_id = PopupItemId::kAddressEntry;
     external_delegate_->OnSuggestionsReturned(field_id, suggestions,
                                               AutoselectFirstSuggestion(false));
   }
@@ -248,11 +243,11 @@
   // This should call ShowAutofillPopup.
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, autofill_item,
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions,
-              SuggestionVectorIdsAre(kAutofillProfileId,
+              SuggestionVectorIdsAre(PopupItemId::kAddressEntry,
 #if !BUILDFLAG(IS_ANDROID)
                                      PopupItemId::kSeparator,
 #endif
@@ -290,7 +285,7 @@
   // This should call ShowAutofillPopup.
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, autofill_item,
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions,
@@ -298,7 +293,7 @@
 #if !BUILDFLAG(IS_ANDROID)
                                      PopupItemId::kSeparator,
 #endif
-                                     kAutofillProfileId,
+                                     PopupItemId::kAddressEntry,
 #if !BUILDFLAG(IS_ANDROID)
                                      PopupItemId::kSeparator,
 #endif
@@ -343,7 +338,7 @@
   // Ensure the popup is displayed.
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, autofill_item,
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions,
@@ -351,7 +346,7 @@
 #if !BUILDFLAG(IS_ANDROID)
                                      PopupItemId::kSeparator,
 #endif
-                                     kAutofillProfileId,
+                                     PopupItemId::kAddressEntry,
 #if !BUILDFLAG(IS_ANDROID)
                                      PopupItemId::kSeparator,
 #endif
@@ -396,7 +391,7 @@
   autofill_item[0].main_text =
       Suggestion::Text(u"Rick", Suggestion::Text::IsPrimary(true));
   autofill_item[0].labels = {{Suggestion::Text(u"Deckard")}};
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, autofill_item,
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions,
@@ -405,7 +400,7 @@
 #if !BUILDFLAG(IS_ANDROID)
                                      PopupItemId::kSeparator,
 #endif
-                                     kAutofillProfileId,
+                                     PopupItemId::kAddressEntry,
 #if !BUILDFLAG(IS_ANDROID)
                                      PopupItemId::kSeparator,
 #endif
@@ -437,11 +432,11 @@
   autocomplete_items.emplace_back();
   autocomplete_items[0].main_text =
       Suggestion::Text(u"Rick", Suggestion::Text::IsPrimary(true));
-  autocomplete_items[0].frontend_id = PopupItemId::kAutocompleteEntry;
+  autocomplete_items[0].popup_item_id = PopupItemId::kAutocompleteEntry;
   autocomplete_items.emplace_back();
   autocomplete_items[1].main_text =
       Suggestion::Text(u"Cain", Suggestion::Text::IsPrimary(true));
-  autocomplete_items[1].frontend_id = PopupItemId::kAutocompleteEntry;
+  autocomplete_items[1].popup_item_id = PopupItemId::kAutocompleteEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, autocomplete_items,
                                             AutoselectFirstSuggestion(false));
   EXPECT_THAT(open_args.suggestions,
@@ -468,7 +463,7 @@
   // This should call ShowAutofillPopup.
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id =
+  autofill_item[0].popup_item_id =
       PopupItemId::kInsecureContextPaymentDisabledMessage;
   external_delegate_->OnSuggestionsReturned(field_id_, autofill_item,
                                             AutoselectFirstSuggestion(false));
@@ -494,12 +489,12 @@
   // This should call ShowAutofillPopup.
   std::vector<Suggestion> suggestions;
   suggestions.emplace_back();
-  suggestions[0].frontend_id =
+  suggestions[0].popup_item_id =
       PopupItemId::kInsecureContextPaymentDisabledMessage;
   suggestions.emplace_back();
   suggestions[1].main_text =
       Suggestion::Text(u"Rick", Suggestion::Text::IsPrimary(true));
-  suggestions[1].frontend_id = PopupItemId::kAutocompleteEntry;
+  suggestions[1].popup_item_id = PopupItemId::kAutocompleteEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, suggestions,
                                             AutoselectFirstSuggestion(false));
 
@@ -516,7 +511,7 @@
       .Times(0);
   EXPECT_CALL(*autofill_driver_, RendererShouldClearPreviewedForm()).Times(1);
   const Suggestion suggestion{
-      Suggestion::FrontendId(kInsecureContextPaymentDisabledMessage)};
+      PopupItemId::kInsecureContextPaymentDisabledMessage};
   external_delegate_->DidSelectSuggestion(suggestion);
 
   // Ensure it doesn't try to fill the form in with the negative id.
@@ -545,7 +540,7 @@
   suggestions[0].main_text.value = masked_iban_value;
   suggestions[0].labels = {{Suggestion::Text(u"My doctor's IBAN")}};
   suggestions[0].payload = Suggestion::ValueToFill(ummasked_iban_value);
-  suggestions[0].frontend_id = PopupItemId::kIbanEntry;
+  suggestions[0].popup_item_id = PopupItemId::kIbanEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, suggestions,
                                             AutoselectFirstSuggestion(false));
 
@@ -579,7 +574,7 @@
   std::u16string promo_code_value = u"PROMOCODE1234";
   suggestions[0].main_text.value = promo_code_value;
   suggestions[0].labels = {{Suggestion::Text(u"12.34% off your purchase!")}};
-  suggestions[0].frontend_id = PopupItemId::kMerchantPromoCodeEntry;
+  suggestions[0].popup_item_id = PopupItemId::kMerchantPromoCodeEntry;
   external_delegate_->OnSuggestionsReturned(field_id_, suggestions,
                                             AutoselectFirstSuggestion(false));
 
@@ -624,8 +619,8 @@
   EXPECT_CALL(
       *browser_autofill_manager_,
       FillOrPreviewForm(mojom::RendererFormDataAction::kPreview, _, _, _, _));
-  external_delegate_->DidSelectSuggestion(test::CreateAutofillSuggestion(
-      Suggestion::FrontendId(kAutofillProfileId), u"baz foo"));
+  external_delegate_->DidSelectSuggestion(
+      test::CreateAutofillSuggestion(PopupItemId::kAddressEntry, u"baz foo"));
 
   // Ensure selecting an autocomplete entry will cause any previews to
   // get cleared.
@@ -683,7 +678,7 @@
       FillOrPreviewForm(mojom::RendererFormDataAction::kFill, _, _, _, _));
 
   external_delegate_->DidAcceptSuggestion(
-      test::CreateAutofillSuggestion(kAutofillProfileId, dummy_string),
+      test::CreateAutofillSuggestion(PopupItemId::kAddressEntry, dummy_string),
       2);  // Row 2
 }
 
@@ -776,8 +771,8 @@
 // Test that autofill client will start the signin flow after the user accepted
 // the suggestion to sign in.
 TEST_F(AutofillExternalDelegateUnitTest, SigninPromoMenuItem) {
-  EXPECT_CALL(autofill_client_, ExecuteCommand(Suggestion::FrontendId(
-                                    PopupItemId::kCreditCardSigninPromo)));
+  EXPECT_CALL(autofill_client_,
+              ExecuteCommand(PopupItemId::kCreditCardSigninPromo));
   EXPECT_CALL(autofill_client_,
               HideAutofillPopup(PopupHidingReason::kAcceptSuggestion));
 
@@ -810,7 +805,7 @@
 
   std::vector<Suggestion> autofill_items;
   autofill_items.emplace_back();
-  autofill_items[0].frontend_id = PopupItemId::kAutocompleteEntry;
+  autofill_items[0].popup_item_id = PopupItemId::kAutocompleteEntry;
 
   // Ensure the popup tries to show itself, despite autocomplete="off".
   EXPECT_CALL(autofill_client_, ShowAutofillPopup);
@@ -829,9 +824,8 @@
   EXPECT_CALL(*autofill_driver_,
               RendererShouldFillFieldWithValue(field_id_, dummy_string));
   EXPECT_CALL(*autofill_client_.GetMockAutocompleteHistoryManager(),
-              OnSingleFieldSuggestionSelected(
-                  dummy_string,
-                  Suggestion::FrontendId(PopupItemId::kAutocompleteEntry)))
+              OnSingleFieldSuggestionSelected(dummy_string,
+                                              PopupItemId::kAutocompleteEntry))
       .Times(1);
   base::HistogramTester histogram_tester;
 
@@ -848,8 +842,7 @@
               RendererShouldFillFieldWithValue(field_id_, dummy_string));
   EXPECT_CALL(*autofill_client_.GetMockMerchantPromoCodeManager(),
               OnSingleFieldSuggestionSelected(
-                  dummy_string,
-                  Suggestion::FrontendId(PopupItemId::kMerchantPromoCodeEntry)))
+                  dummy_string, PopupItemId::kMerchantPromoCodeEntry))
       .Times(1);
   external_delegate_->DidAcceptSuggestion(
       test::CreateAutofillSuggestion(PopupItemId::kMerchantPromoCodeEntry,
@@ -861,10 +854,9 @@
   // Test that IBANs get autofilled.
   EXPECT_CALL(*autofill_driver_,
               RendererShouldFillFieldWithValue(field_id_, ummasked_iban_value));
-  EXPECT_CALL(
-      *autofill_client_.GetMockIBANManager(),
-      OnSingleFieldSuggestionSelected(
-          masked_iban_value, Suggestion::FrontendId(PopupItemId::kIbanEntry)));
+  EXPECT_CALL(*autofill_client_.GetMockIBANManager(),
+              OnSingleFieldSuggestionSelected(masked_iban_value,
+                                              PopupItemId::kIbanEntry));
   external_delegate_->DidAcceptSuggestion(
       test::CreateAutofillSuggestion(
           PopupItemId::kIbanEntry, masked_iban_value,
@@ -881,7 +873,7 @@
 
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
 
   // This should call ShowAutofillPopup.
   external_delegate_->OnSuggestionsReturned(
@@ -914,7 +906,7 @@
 
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
 
   // This should call ShowAutofillPopup.
   external_delegate_->OnSuggestionsReturned(
@@ -936,7 +928,7 @@
 
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
   autofill_item[0].main_text.is_primary = Suggestion::Text::IsPrimary(true);
 
   // This should call ShowAutofillPopup.
@@ -987,7 +979,7 @@
 
   std::vector<Suggestion> autofill_item;
   autofill_item.emplace_back();
-  autofill_item[0].frontend_id = kAutofillProfileId;
+  autofill_item[0].popup_item_id = PopupItemId::kAddressEntry;
   autofill_item[0].main_text.is_primary = Suggestion::Text::IsPrimary(true);
 
   external_delegate_->OnSuggestionsReturned(field_id_, autofill_item,
diff --git a/components/autofill/core/browser/autofill_suggestion_generator.cc b/components/autofill/core/browser/autofill_suggestion_generator.cc
index 0b08d72e..41f1c1b 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator.cc
@@ -142,7 +142,7 @@
   }
 
   for (auto& suggestion : suggestions) {
-    suggestion.frontend_id = kAddressEntry;
+    suggestion.popup_item_id = PopupItemId::kAddressEntry;
 
     // Populate feature IPH for externally created account profiles.
     const AutofillProfile* profile = personal_data_->GetProfileByGUID(
@@ -234,7 +234,7 @@
 // static
 Suggestion AutofillSuggestionGenerator::CreateSeparator() {
   Suggestion suggestion;
-  suggestion.frontend_id = PopupItemId::kSeparator;
+  suggestion.popup_item_id = PopupItemId::kSeparator;
   return suggestion;
 }
 
@@ -242,7 +242,7 @@
 Suggestion AutofillSuggestionGenerator::CreateManagePaymentMethodsEntry() {
   Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_PAYMENT_METHODS));
-  suggestion.frontend_id = PopupItemId::kAutofillOptions;
+  suggestion.popup_item_id = PopupItemId::kAutofillOptions;
   suggestion.icon = "settingsIcon";
   return suggestion;
 }
@@ -298,7 +298,7 @@
     suggestion.custom_icon =
         ui::ResourceBundle::GetSharedInstance().GetImageNamed(
             IDR_AUTOFILL_IBAN);
-    suggestion.frontend_id = PopupItemId::kIbanEntry;
+    suggestion.popup_item_id = PopupItemId::kIbanEntry;
     suggestion.payload = Suggestion::ValueToFill(iban->GetStrippedValue());
     suggestion.main_text.value = iban->GetIdentifierStringForAutofillDisplay();
     if (!iban->nickname().empty())
@@ -331,7 +331,7 @@
     }
     suggestion.payload = Suggestion::BackendId(
         base::NumberToString(promo_code_offer->GetOfferId()));
-    suggestion.frontend_id = PopupItemId::kMerchantPromoCodeEntry;
+    suggestion.popup_item_id = PopupItemId::kMerchantPromoCodeEntry;
 
     // Every offer for a given merchant leads to the same GURL, so we grab the
     // first offer's offer details url as the payload for the footer to set
@@ -356,7 +356,7 @@
     suggestions.emplace_back(l10n_util::GetStringUTF16(
         IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT));
     Suggestion& suggestion = suggestions.back();
-    suggestion.frontend_id = PopupItemId::kSeePromoCodeDetails;
+    suggestion.popup_item_id = PopupItemId::kSeePromoCodeDetails;
 
     // We set the payload for the footer as |footer_offer_details_url|, which is
     // the offer details url of the first offer we had for this merchant. We
@@ -466,8 +466,8 @@
 
   Suggestion suggestion;
   suggestion.icon = credit_card.CardIconStringForAutofillSuggestion();
-  CHECK(suggestion.frontend_id == kAutocompleteEntry);
-  suggestion.frontend_id = kCreditCardEntry;
+  CHECK(suggestion.popup_item_id == PopupItemId::kAutocompleteEntry);
+  suggestion.popup_item_id = PopupItemId::kCreditCardEntry;
   suggestion.payload = Suggestion::BackendId(credit_card.guid());
   suggestion.match = prefix_matched_suggestion ? Suggestion::PREFIX_MATCH
                                                : Suggestion::SUBSTRING_MATCH;
@@ -633,7 +633,7 @@
     suggestion.payload = Suggestion::BackendId(server_duplicate_card->guid());
   }
 
-  suggestion.frontend_id = PopupItemId::kVirtualCreditCardEntry;
+  suggestion.popup_item_id = PopupItemId::kVirtualCreditCardEntry;
   suggestion.feature_for_iph =
       feature_engagement::kIPHAutofillVirtualCardSuggestionFeature.name;
 
diff --git a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
index 904eef8..8d93192b 100644
--- a/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/autofill_suggestion_generator_unittest.cc
@@ -646,7 +646,7 @@
   ASSERT_EQ(iban_suggestions[0].labels.size(), 1u);
   ASSERT_EQ(iban_suggestions[0].labels[0].size(), 1u);
   EXPECT_EQ(iban_suggestions[0].labels[0][0].value, u"My doctor's IBAN");
-  EXPECT_EQ(iban_suggestions[0].frontend_id, PopupItemId::kIbanEntry);
+  EXPECT_EQ(iban_suggestions[0].popup_item_id, PopupItemId::kIbanEntry);
   EXPECT_TRUE(
       AreImagesEqual(iban_suggestions[0].custom_icon, CreateFakeImage()));
 
@@ -657,7 +657,7 @@
   ASSERT_EQ(iban_suggestions[1].labels.size(), 1u);
   ASSERT_EQ(iban_suggestions[1].labels[0].size(), 1u);
   EXPECT_EQ(iban_suggestions[1].labels[0][0].value, u"My brother's IBAN");
-  EXPECT_EQ(iban_suggestions[1].frontend_id, PopupItemId::kIbanEntry);
+  EXPECT_EQ(iban_suggestions[1].popup_item_id, PopupItemId::kIbanEntry);
   EXPECT_TRUE(
       AreImagesEqual(iban_suggestions[1].custom_icon, CreateFakeImage()));
 
@@ -668,7 +668,7 @@
   ASSERT_EQ(iban_suggestions[2].labels.size(), 1u);
   ASSERT_EQ(iban_suggestions[2].labels[0].size(), 1u);
   EXPECT_EQ(iban_suggestions[2].labels[0][0].value, u"My teacher's IBAN");
-  EXPECT_EQ(iban_suggestions[2].frontend_id, PopupItemId::kIbanEntry);
+  EXPECT_EQ(iban_suggestions[2].popup_item_id, PopupItemId::kIbanEntry);
   EXPECT_TRUE(
       AreImagesEqual(iban_suggestions[2].custom_icon, CreateFakeImage()));
 
@@ -677,15 +677,15 @@
   EXPECT_EQ(iban_suggestions[3].GetPayload<Suggestion::ValueToFill>().value(),
             iban3.GetStrippedValue());
   EXPECT_EQ(iban_suggestions[3].labels.size(), 0u);
-  EXPECT_EQ(iban_suggestions[3].frontend_id, PopupItemId::kIbanEntry);
+  EXPECT_EQ(iban_suggestions[3].popup_item_id, PopupItemId::kIbanEntry);
   EXPECT_TRUE(
       AreImagesEqual(iban_suggestions[3].custom_icon, CreateFakeImage()));
 
-  EXPECT_EQ(iban_suggestions[4].frontend_id, PopupItemId::kSeparator);
+  EXPECT_EQ(iban_suggestions[4].popup_item_id, PopupItemId::kSeparator);
 
   EXPECT_EQ(iban_suggestions[5].main_text.value,
             l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_PAYMENT_METHODS));
-  EXPECT_EQ(iban_suggestions[5].frontend_id, PopupItemId::kAutofillOptions);
+  EXPECT_EQ(iban_suggestions[5].popup_item_id, PopupItemId::kAutofillOptions);
 
   CleanUpIbanImageResources();
 }
@@ -730,7 +730,7 @@
             u"test_value_prop_text_1");
   EXPECT_EQ(promo_code_suggestions[0].GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("1"));
-  EXPECT_EQ(promo_code_suggestions[0].frontend_id,
+  EXPECT_EQ(promo_code_suggestions[0].popup_item_id,
             PopupItemId::kMerchantPromoCodeEntry);
 
   EXPECT_EQ(promo_code_suggestions[1].main_text.value, u"test_promo_code_2");
@@ -742,17 +742,17 @@
             u"test_value_prop_text_2");
   EXPECT_EQ(promo_code_suggestions[1].GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("2"));
-  EXPECT_EQ(promo_code_suggestions[1].frontend_id,
+  EXPECT_EQ(promo_code_suggestions[1].popup_item_id,
             PopupItemId::kMerchantPromoCodeEntry);
 
-  EXPECT_EQ(promo_code_suggestions[2].frontend_id, PopupItemId::kSeparator);
+  EXPECT_EQ(promo_code_suggestions[2].popup_item_id, PopupItemId::kSeparator);
 
   EXPECT_EQ(promo_code_suggestions[3].main_text.value,
             l10n_util::GetStringUTF16(
                 IDS_AUTOFILL_PROMO_CODE_SUGGESTIONS_FOOTER_TEXT));
   EXPECT_EQ(promo_code_suggestions[3].GetPayload<GURL>(),
             offer1.GetOfferDetailsUrl().spec());
-  EXPECT_EQ(promo_code_suggestions[3].frontend_id,
+  EXPECT_EQ(promo_code_suggestions[3].popup_item_id,
             PopupItemId::kSeePromoCodeDetails);
 }
 
@@ -778,7 +778,7 @@
             u"test_value_prop_text_1");
   EXPECT_FALSE(
       absl::holds_alternative<GURL>(promo_code_suggestions[0].payload));
-  EXPECT_EQ(promo_code_suggestions[0].frontend_id,
+  EXPECT_EQ(promo_code_suggestions[0].popup_item_id,
             PopupItemId::kMerchantPromoCodeEntry);
 }
 
@@ -1130,7 +1130,7 @@
           /*virtual_card_option=*/true,
           /*card_linked_offer_available=*/false);
 
-  EXPECT_EQ(virtual_card_suggestion.frontend_id,
+  EXPECT_EQ(virtual_card_suggestion.popup_item_id,
             PopupItemId::kVirtualCreditCardEntry);
   EXPECT_EQ(virtual_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
@@ -1143,8 +1143,7 @@
           /*virtual_card_option=*/false,
           /*card_linked_offer_available=*/false);
 
-  EXPECT_EQ(real_card_suggestion.frontend_id.as_popup_item_id(),
-            kCreditCardEntry);
+  EXPECT_EQ(real_card_suggestion.popup_item_id, PopupItemId::kCreditCardEntry);
   EXPECT_EQ(real_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
   EXPECT_EQ(VerifyCardArtImageExpectation(real_card_suggestion, card_art_url,
@@ -1163,8 +1162,7 @@
           /*virtual_card_option=*/false,
           /*card_linked_offer_available=*/false);
 
-  EXPECT_EQ(real_card_suggestion.frontend_id.as_popup_item_id(),
-            kCreditCardEntry);
+  EXPECT_EQ(real_card_suggestion.popup_item_id, PopupItemId::kCreditCardEntry);
   EXPECT_EQ(real_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
   EXPECT_TRUE(VerifyCardArtImageExpectation(real_card_suggestion, GURL(),
@@ -1193,7 +1191,7 @@
           /*virtual_card_option=*/true,
           /*card_linked_offer_available=*/false);
 
-  EXPECT_EQ(virtual_card_suggestion.frontend_id,
+  EXPECT_EQ(virtual_card_suggestion.popup_item_id,
             PopupItemId::kVirtualCreditCardEntry);
   EXPECT_EQ(virtual_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
@@ -1206,8 +1204,7 @@
           /*virtual_card_option=*/false,
           /*card_linked_offer_available=*/false);
 
-  EXPECT_EQ(real_card_suggestion.frontend_id.as_popup_item_id(),
-            kCreditCardEntry);
+  EXPECT_EQ(real_card_suggestion.popup_item_id, PopupItemId::kCreditCardEntry);
   EXPECT_EQ(real_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000002"));
   EXPECT_EQ(VerifyCardArtImageExpectation(real_card_suggestion, card_art_url,
@@ -1335,7 +1332,7 @@
           /*virtual_card_option=*/true,
           /*card_linked_offer_available=*/true);
 
-  EXPECT_EQ(virtual_card_suggestion.frontend_id,
+  EXPECT_EQ(virtual_card_suggestion.popup_item_id,
             PopupItemId::kVirtualCreditCardEntry);
   EXPECT_EQ(virtual_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
@@ -1348,8 +1345,7 @@
           /*virtual_card_option=*/false,
           /*card_linked_offer_available=*/true);
 
-  EXPECT_EQ(real_card_suggestion.frontend_id.as_popup_item_id(),
-            kCreditCardEntry);
+  EXPECT_EQ(real_card_suggestion.popup_item_id, PopupItemId::kCreditCardEntry);
   EXPECT_EQ(real_card_suggestion.GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId("00000000-0000-0000-0000-000000000001"));
 
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc
index dd814bd..7a94fdc 100644
--- a/components/autofill/core/browser/autofill_test_utils.cc
+++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -24,6 +24,7 @@
 #include "components/autofill/core/browser/data_model/iban.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/randomized_encoder.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_constants.h"
@@ -1228,11 +1229,11 @@
   }
 }
 
-Suggestion CreateAutofillSuggestion(Suggestion::FrontendId frontend_id,
+Suggestion CreateAutofillSuggestion(PopupItemId popup_item_id,
                                     const std::u16string& main_text_value,
                                     const Suggestion::Payload& payload) {
   Suggestion suggestion;
-  suggestion.frontend_id = frontend_id;
+  suggestion.popup_item_id = popup_item_id;
   suggestion.main_text.value = main_text_value;
   suggestion.payload = payload;
   return suggestion;
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h
index 6bcd0b9c..a3b7ba0 100644
--- a/components/autofill/core/browser/autofill_test_utils.h
+++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -24,6 +24,7 @@
 #include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
 #include "components/autofill/core/browser/proto/api_v1.pb.h"
 #include "components/autofill/core/browser/proto/server.pb.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 
 class PrefService;
@@ -553,7 +554,7 @@
     ::autofill::AutofillQueryResponse_FormSuggestion* form_suggestion);
 
 Suggestion CreateAutofillSuggestion(
-    Suggestion::FrontendId frontend_id,
+    PopupItemId popup_item_id,
     const std::u16string& main_text_value = std::u16string(),
     const Suggestion::Payload& payload = Suggestion::Payload());
 
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 169fe71..d2a4761 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -80,6 +80,7 @@
 #include "components/autofill/core/browser/suggestions_context.h"
 #include "components/autofill/core/browser/ui/payments/bubble_show_options.h"
 #include "components/autofill/core/browser/ui/popup_item_ids.h"
+#include "components/autofill/core/browser/ui/popup_types.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/browser/validation.h"
 #include "components/autofill/core/common/autocomplete_parsing_util.h"
@@ -203,9 +204,9 @@
   }
 }
 
-FillDataType GetEventTypeFromSingleFieldSuggestionFrontendId(
-    Suggestion::FrontendId frontend_id) {
-  switch (frontend_id.as_popup_item_id()) {
+FillDataType GetEventTypeFromSingleFieldSuggestionPopupItemId(
+    PopupItemId popup_item_id) {
+  switch (popup_item_id) {
     case PopupItemId::kAutocompleteEntry:
       return FillDataType::kSingleFieldFormFillerAutocomplete;
     case PopupItemId::kMerchantPromoCodeEntry:
@@ -1574,11 +1575,11 @@
 
 bool BrowserAutofillManager::GetDeletionConfirmationText(
     const std::u16string& value,
-    Suggestion::FrontendId identifier,
+    PopupItemId popup_item_id,
     Suggestion::BackendId backend_id,
     std::u16string* title,
     std::u16string* body) {
-  if (identifier == PopupItemId::kAutocompleteEntry) {
+  if (popup_item_id == PopupItemId::kAutocompleteEntry) {
     if (title)
       title->assign(value);
     if (body) {
@@ -1589,7 +1590,8 @@
     return true;
   }
 
-  if (!identifier.is_an_address_or_card_popup_item_id()) {
+  if (popup_item_id != PopupItemId::kAddressEntry &&
+      popup_item_id != PopupItemId::kCreditCardEntry) {
     return false;
   }
 
@@ -1645,25 +1647,26 @@
 void BrowserAutofillManager::RemoveCurrentSingleFieldSuggestion(
     const std::u16string& name,
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {
+    PopupItemId popup_item_id) {
   single_field_form_fill_router_->OnRemoveCurrentSingleFieldSuggestion(
-      name, value, frontend_id);
+      name, value, popup_item_id);
 }
 
 void BrowserAutofillManager::OnSingleFieldSuggestionSelected(
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id,
+    PopupItemId popup_item_id,
     const FormData& form,
     const FormFieldData& field) {
-  single_field_form_fill_router_->OnSingleFieldSuggestionSelected(value,
-                                                                  frontend_id);
+  single_field_form_fill_router_->OnSingleFieldSuggestionSelected(
+      value, popup_item_id);
 
   AutofillField* autofill_trigger_field = GetAutofillField(form, field);
   if (!autofill_trigger_field) {
     return;
   }
   autofill_trigger_field->AppendLogEventIfNotRepeated(TriggerFillFieldLogEvent{
-      .data_type = GetEventTypeFromSingleFieldSuggestionFrontendId(frontend_id),
+      .data_type =
+          GetEventTypeFromSingleFieldSuggestionPopupItemId(popup_item_id),
       .associated_country_code = "",
       .timestamp = AutofillClock::Now()});
 }
@@ -3233,7 +3236,7 @@
     } else {
       Suggestion warning_suggestion(
           l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_MIXED_FORM));
-      warning_suggestion.frontend_id = PopupItemId::kMixedFormMessage;
+      warning_suggestion.popup_item_id = PopupItemId::kMixedFormMessage;
       suggestions->emplace_back(warning_suggestion);
     }
     return;
@@ -3312,7 +3315,7 @@
   if (ShouldShowVirtualCardOption(context->form_structure)) {
     suggestions->emplace_back(l10n_util::GetStringUTF16(
         IDS_AUTOFILL_CLOUD_TOKEN_DROPDOWN_OPTION_LABEL));
-    suggestions->back().frontend_id = PopupItemId::kUseVirtualCard;
+    suggestions->back().popup_item_id = PopupItemId::kUseVirtualCard;
   }
 #endif
 
@@ -3325,7 +3328,7 @@
     // credit card autofill HTTP warning experiment is enabled.
     Suggestion warning_suggestion(
         l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION));
-    warning_suggestion.frontend_id =
+    warning_suggestion.popup_item_id =
         PopupItemId::kInsecureContextPaymentDisabledMessage;
     suggestions->assign(1, warning_suggestion);
   }
@@ -3448,11 +3451,11 @@
 void BrowserAutofillManager::OnSeePromoCodeOfferDetailsSelected(
     const GURL& offer_details_url,
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id,
+    PopupItemId popup_item_id,
     const FormData& form,
     const FormFieldData& field) {
   client()->OpenPromoCodeOfferDetailsURL(offer_details_url);
-  OnSingleFieldSuggestionSelected(value, frontend_id, form, field);
+  OnSingleFieldSuggestionSelected(value, popup_item_id, form, field);
 }
 
 void BrowserAutofillManager::ProcessFieldLogEventsInForm(
diff --git a/components/autofill/core/browser/browser_autofill_manager.h b/components/autofill/core/browser/browser_autofill_manager.h
index ee0430f8..10c07d6 100644
--- a/components/autofill/core/browser/browser_autofill_manager.h
+++ b/components/autofill/core/browser/browser_autofill_manager.h
@@ -40,6 +40,7 @@
 #include "components/autofill/core/browser/single_field_form_fill_router.h"
 #include "components/autofill/core/browser/sync_utils.h"
 #include "components/autofill/core/browser/ui/fast_checkout_delegate.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/popup_types.h"
 #include "components/autofill/core/browser/ui/touch_to_fill_delegate.h"
 #include "components/autofill/core/common/dense_set.h"
@@ -189,7 +190,7 @@
   // Returns true if the value/identifier is deletable. Fills out
   // |title| and |body| with relevant user-facing text.
   bool GetDeletionConfirmationText(const std::u16string& value,
-                                   Suggestion::FrontendId identifier,
+                                   PopupItemId popup_item_id,
                                    Suggestion::BackendId backend_id,
                                    std::u16string* title,
                                    std::u16string* body);
@@ -198,16 +199,16 @@
   // from the database. Returns true if deletion is allowed.
   bool RemoveAutofillProfileOrCreditCard(Suggestion::BackendId backend_id);
 
-  // Remove the specified suggestion from single field filling. |frontend_id| is
-  // the PopupItemId of the suggestion.
+  // Remove the specified suggestion from single field filling. `popup_item_id`
+  // is the PopupItemId of the suggestion.
   void RemoveCurrentSingleFieldSuggestion(const std::u16string& name,
                                           const std::u16string& value,
-                                          Suggestion::FrontendId frontend_id);
+                                          PopupItemId popup_item_id);
 
   // Invoked when the user selected |value| in a suggestions list from single
-  // field filling. |frontend_id| is the PopupItemId of the suggestion.
+  // field filling. `popup_item_id` is the PopupItemId of the suggestion.
   void OnSingleFieldSuggestionSelected(const std::u16string& value,
-                                       Suggestion::FrontendId frontend_id,
+                                       PopupItemId popup_item_id,
                                        const FormData& form,
                                        const FormFieldData& field);
 
@@ -310,7 +311,7 @@
   // then logs that the promo code suggestions footer was selected.
   void OnSeePromoCodeOfferDetailsSelected(const GURL& offer_details_url,
                                           const std::u16string& value,
-                                          Suggestion::FrontendId frontend_id,
+                                          PopupItemId popup_item_id,
                                           const FormData& form,
                                           const FormFieldData& field);
 
diff --git a/components/autofill/core/browser/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
index 5f7efde..f8cce4e 100644
--- a/components/autofill/core/browser/browser_autofill_manager_unittest.cc
+++ b/components/autofill/core/browser/browser_autofill_manager_unittest.cc
@@ -1151,11 +1151,11 @@
            test::ObfuscatedCardDigitsAsUTF8(last_four, ObfuscationLength());
   }
 
- protected:
   TestPersonalDataManager& personal_data() {
     return *autofill_client_.GetPersonalDataManager();
   }
 
+ protected:
   base::test::TaskEnvironment task_environment_;
   test::AutofillUnitTestEnvironment autofill_test_environment_;
   NiceMock<MockAutofillClient> autofill_client_;
@@ -1567,12 +1567,9 @@
   // Check that suggestions are made for the field that has the autocomplete
   // attribute.
   GetAutofillSuggestions(form, form.fields[0]);
-  CheckSuggestions(
-      form.fields[0].global_id(),
-      Suggestion("Charles", "", "",
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Elvis", "", "",
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[0].global_id(),
+                   Suggestion("Charles", "", "", PopupItemId::kAddressEntry),
+                   Suggestion("Elvis", "", "", PopupItemId::kAddressEntry));
 
   // Check that there are no suggestions for the field without the autocomplete
   // attribute.
@@ -1601,20 +1598,18 @@
   FormsSeen({form});
 
   GetAutofillSuggestions(form, form.fields[0]);
-  CheckSuggestions(
-      form.fields[0].global_id(),
-      Suggestion("Charles", "Charles Hardin Holley", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Elvis", "Elvis Aaron Presley", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[0].global_id(),
+                   Suggestion("Charles", "Charles Hardin Holley",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry),
+                   Suggestion("Elvis", "Elvis Aaron Presley", kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 
   GetAutofillSuggestions(form, form.fields[1]);
-  CheckSuggestions(
-      form.fields[1].global_id(),
-      Suggestion("Holley", "Charles Hardin Holley", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Presley", "Elvis Aaron Presley", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[1].global_id(),
+                   Suggestion("Holley", "Charles Hardin Holley",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry),
+                   Suggestion("Presley", "Elvis Aaron Presley",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 
 // Test that the call is properly forwarded to its SingleFieldFormFillRouter.
@@ -1624,27 +1619,25 @@
   test::CreateTestAddressFormData(&form);
   FormFieldData field = form.fields[0];
 
-  EXPECT_CALL(
-      *single_field_form_fill_router_,
-      OnSingleFieldSuggestionSelected(
-          test_value, Suggestion::FrontendId(PopupItemId::kAutocompleteEntry)))
-      .Times(1);
-
-  browser_autofill_manager_->OnSingleFieldSuggestionSelected(
-      test_value, PopupItemId::kAutocompleteEntry, form, field);
-
-  EXPECT_CALL(
-      *single_field_form_fill_router_,
-      OnSingleFieldSuggestionSelected(
-          test_value, Suggestion::FrontendId(PopupItemId::kAutocompleteEntry)))
+  EXPECT_CALL(*single_field_form_fill_router_,
+              OnSingleFieldSuggestionSelected(test_value,
+                                              PopupItemId::kAutocompleteEntry))
       .Times(1);
 
   browser_autofill_manager_->OnSingleFieldSuggestionSelected(
       test_value, PopupItemId::kAutocompleteEntry, form, field);
 
   EXPECT_CALL(*single_field_form_fill_router_,
-              OnSingleFieldSuggestionSelected(
-                  test_value, Suggestion::FrontendId(PopupItemId::kIbanEntry)))
+              OnSingleFieldSuggestionSelected(test_value,
+                                              PopupItemId::kAutocompleteEntry))
+      .Times(1);
+
+  browser_autofill_manager_->OnSingleFieldSuggestionSelected(
+      test_value, PopupItemId::kAutocompleteEntry, form, field);
+
+  EXPECT_CALL(
+      *single_field_form_fill_router_,
+      OnSingleFieldSuggestionSelected(test_value, PopupItemId::kIbanEntry))
       .Times(1);
 
   browser_autofill_manager_->OnSingleFieldSuggestionSelected(
@@ -1652,8 +1645,7 @@
 
   EXPECT_CALL(*single_field_form_fill_router_,
               OnSingleFieldSuggestionSelected(
-                  test_value,
-                  Suggestion::FrontendId(PopupItemId::kMerchantPromoCodeEntry)))
+                  test_value, PopupItemId::kMerchantPromoCodeEntry))
       .Times(1);
 
   browser_autofill_manager_->OnSingleFieldSuggestionSelected(
@@ -1697,12 +1689,11 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      form.fields[0].global_id(),
-      Suggestion("Charles", label1, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Elvis", label2, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[0].global_id(),
+                   Suggestion("Charles", label1, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry),
+                   Suggestion("Elvis", label2, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Test that we return only matching address profile suggestions when the
@@ -1729,10 +1720,9 @@
       label = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      field.global_id(),
-      Suggestion("Elvis", label, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(field.global_id(),
+                   Suggestion("Elvis", label, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Tests that we return address profile suggestions values when the section
@@ -1782,9 +1772,9 @@
       CheckSuggestions(
           field.global_id(),
           Suggestion("Googler", "1600 Amphitheater pkwy", kAddressEntryIcon,
-                     Suggestion::FrontendId(PopupItemId::kAddressEntry)),
+                     PopupItemId::kAddressEntry),
           Suggestion("Grimes", "1234 Smith Blvd.", kAddressEntryIcon,
-                     Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+                     PopupItemId::kAddressEntry));
       break;
     case EnabledFeature::kNone:
       // Test that we sent the right values to the external delegate. No labels
@@ -1792,13 +1782,11 @@
       CheckSuggestions(
           field.global_id(),
           Suggestion("Googler", "1600 Amphitheater pkwy", kAddressEntryIcon,
-                     Suggestion::FrontendId(PopupItemId::kAddressEntry)),
+                     PopupItemId::kAddressEntry),
           Suggestion("Grimes", "1234 Smith Blvd., Carl Grimes",
-                     kAddressEntryIcon,
-                     Suggestion::FrontendId(PopupItemId::kAddressEntry)),
+                     kAddressEntryIcon, PopupItemId::kAddressEntry),
           Suggestion("Grimes", "1234 Smith Blvd., Robin Grimes",
-                     kAddressEntryIcon,
-                     Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+                     kAddressEntryIcon, PopupItemId::kAddressEntry));
   }
 }
 
@@ -1831,10 +1819,9 @@
       label = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      field.global_id(),
-      Suggestion("Elvis", label, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(field.global_id(),
+                   Suggestion("Elvis", label, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Test that we return no suggestions when the form has no relevant fields.
@@ -1902,12 +1889,11 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      form.fields[0].global_id(),
-      Suggestion("Charles", label1, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Elvis", label2, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[0].global_id(),
+                   Suggestion("Charles", label1, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry),
+                   Suggestion("Elvis", label2, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Test that we return no suggestions when autofill is disabled.
@@ -1931,10 +1917,9 @@
        OnSuggestionsReturned_CallsExternalDelegate) {
   FieldGlobalId field_id = test::MakeFieldGlobalId();
   std::vector<Suggestion> suggestions = {
-      Suggestion("Charles", "123 Apple St.", "",
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
+      Suggestion("Charles", "123 Apple St.", "", PopupItemId::kAddressEntry),
       Suggestion("Elvis", "3734 Elvis Presley Blvd.", "",
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry))};
+                 PopupItemId::kAddressEntry)};
 
   {
     browser_autofill_manager_->OnSuggestionsReturned(
@@ -2246,7 +2231,7 @@
   CheckSuggestions(
       form.fields[0].global_id(),
       Suggestion(l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_MIXED_FORM), "",
-                 "", Suggestion::FrontendId(kMixedFormMessage)));
+                 "", PopupItemId::kMixedFormMessage));
 
   // Clear the test credit cards and try again -- we should still show the
   // mixed form warning.
@@ -2255,7 +2240,7 @@
   CheckSuggestions(
       form.fields[0].global_id(),
       Suggestion(l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_MIXED_FORM), "",
-                 "", Suggestion::FrontendId(kMixedFormMessage)));
+                 "", PopupItemId::kMixedFormMessage));
 }
 
 // Test that we return credit card suggestions for secure pages that have an
@@ -2719,12 +2704,11 @@
   }
 
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      form.fields[0].global_id(),
-      Suggestion("Charles", label1, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Elvis", label2, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[0].global_id(),
+                   Suggestion("Charles", label1, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry),
+                   Suggestion("Elvis", label2, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 
   FormFieldData field;
   test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field);
@@ -2772,8 +2756,7 @@
       field.global_id(),
       Suggestion(
           l10n_util::GetStringUTF8(IDS_AUTOFILL_WARNING_INSECURE_CONNECTION),
-          "", "",
-          Suggestion::FrontendId(kInsecureContextPaymentDisabledMessage)));
+          "", "", PopupItemId::kInsecureContextPaymentDisabledMessage));
 
   // Clear the test credit cards and try again -- we shouldn't return a warning.
   personal_data().ClearCreditCards();
@@ -3347,12 +3330,11 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      form.fields[0].global_id(),
-      Suggestion("Charles", label1, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Elvis", label2, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[0].global_id(),
+                   Suggestion("Charles", label1, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry),
+                   Suggestion("Elvis", label2, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Test that nothing breaks when there are single field form fill (Autocomplete)
@@ -3377,10 +3359,9 @@
   AutocompleteSuggestionsReturned(field.global_id(), suggestions);
 
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      field.global_id(),
-      Suggestion("one", "", "", Suggestion::FrontendId(kAutocompleteEntry)),
-      Suggestion("two", "", "", Suggestion::FrontendId(kAutocompleteEntry)));
+  CheckSuggestions(field.global_id(),
+                   Suggestion("one", "", "", PopupItemId::kAutocompleteEntry),
+                   Suggestion("two", "", "", PopupItemId::kAutocompleteEntry));
 }
 
 // Test that we do not return duplicate values drawn from multiple profiles when
@@ -3424,10 +3405,9 @@
       label = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      field.global_id(),
-      Suggestion("Elvis", label, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(field.global_id(),
+                   Suggestion("Elvis", label, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 TEST_P(SuggestionMatchingTest, GetProfileSuggestions_FancyPhone) {
@@ -3493,12 +3473,10 @@
   // Test that we sent the right values to the external delegate.
   CheckSuggestions(
       form.fields[9].global_id(),
-      Suggestion(value1, label1, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion(value2, label2, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
+      Suggestion(value1, label1, kAddressEntryIcon, PopupItemId::kAddressEntry),
+      Suggestion(value2, label2, kAddressEntryIcon, PopupItemId::kAddressEntry),
       Suggestion(value3, label3, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+                 PopupItemId::kAddressEntry));
 }
 
 TEST_F(BrowserAutofillManagerTest,
@@ -3539,19 +3517,17 @@
   GetAutofillSuggestions(form, phone_prefix);
 
   // Test that we sent the right prefix values to the external delegate.
-  CheckSuggestions(
-      form.fields[2].global_id(),
-      Suggestion("356", "1800FLOWERS", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[2].global_id(),
+                   Suggestion("356", "1800FLOWERS", kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 
   const FormFieldData& phone_suffix = form.fields[3];
   GetAutofillSuggestions(form, phone_suffix);
 
   // Test that we sent the right suffix values to the external delegate.
-  CheckSuggestions(
-      form.fields[3].global_id(),
-      Suggestion("9377", "1800FLOWERS", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[3].global_id(),
+                   Suggestion("9377", "1800FLOWERS", kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Tests that the suggestion consists of phone number without the country code
@@ -3572,10 +3548,9 @@
 
   GetAutofillSuggestions(form, form.fields[9]);
 
-  CheckSuggestions(
-      form.fields[9].global_id(),
-      Suggestion("123456789", "Natty Bumppo", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[9].global_id(),
+                   Suggestion("123456789", "Natty Bumppo", kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Tests that we return email profile suggestions values
@@ -3618,10 +3593,9 @@
   personal_data().AddProfile(profile);
 
   GetAutofillSuggestions(form, form.fields[2]);
-  CheckSuggestions(
-      form.fields[2].global_id(),
-      Suggestion("test@example.com", "Natty Bumppo", kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(form.fields[2].global_id(),
+                   Suggestion("test@example.com", "Natty Bumppo",
+                              kAddressEntryIcon, PopupItemId::kAddressEntry));
 }
 
 // Test that we correctly fill an address form.
@@ -7044,9 +7018,9 @@
   EXPECT_EQ(0, personal_data().num_times_save_imported_profile_called());
 }
 
-// Test that we save form data when a <select> in the form contains the
-// default value.
-TEST_F(BrowserAutofillManagerTest, FormSubmittedSelectWithDefaultValue) {
+void DoTestFormSubmittedControlWithDefaultValue(
+    BrowserAutofillManagerTest* test,
+    const std::string& form_control_type) {
   // Set up our form data.
   FormData form;
   test::CreateTestAddressFormData(&form);
@@ -7055,27 +7029,39 @@
   // reject default values for text fields.
   FormFieldData* state_field = form.FindFieldByName(u"state");
   ASSERT_TRUE(state_field != nullptr);
-  state_field->form_control_type = "select-one";
+  state_field->form_control_type = form_control_type;
   state_field->value = base::UTF8ToUTF16(kElvisAddressFillData.state);
 
-  FormsSeen({form});
+  test->FormsSeen({form});
 
   // Fill the form.
   FormData response_data;
-  FillAutofillFormDataAndSaveResults(form, form.fields[3], kElvisProfileGuid,
-                                     &response_data);
+  test->FillAutofillFormDataAndSaveResults(form, form.fields[3],
+                                           kElvisProfileGuid, &response_data);
 
-  FormSubmitted(response_data);
-  ASSERT_EQ(1, personal_data().num_times_save_imported_profile_called());
+  test->FormSubmitted(response_data);
+  const TestPersonalDataManager& personal_data = test->personal_data();
+  ASSERT_EQ(1, personal_data.num_times_save_imported_profile_called());
   EXPECT_EQ(u"Tennessee",
-            personal_data().last_save_imported_profile()->GetRawInfo(
+            personal_data.last_save_imported_profile()->GetRawInfo(
                 ADDRESS_HOME_STATE));
 }
 
-// Test that we save form data when a non-country, non-state <select> in the
-// form contains the default value.
-TEST_F(BrowserAutofillManagerTest,
-       FormSubmittedNonAddressSelectWithDefaultValue) {
+// Test that we save form data when a <select> in the form contains the
+// default value.
+TEST_F(BrowserAutofillManagerTest, FormSubmittedSelectWithDefaultValue) {
+  DoTestFormSubmittedControlWithDefaultValue(this, "select-one");
+}
+
+// Test that we save form data when a <selectmenu> in the form contains the
+// default value.
+TEST_F(BrowserAutofillManagerTest, FormSubmittedSelectMenuWithDefaultValue) {
+  DoTestFormSubmittedControlWithDefaultValue(this, "selectmenu");
+}
+
+void DoTestFormSubmittedNonAddressControlWithDefaultValue(
+    BrowserAutofillManagerTest* test,
+    const std::string& form_control_type) {
   // Set up our form data.
   FormData form;
   test::CreateTestAddressFormData(&form);
@@ -7090,7 +7076,7 @@
   FormFieldData country_code_field;
   test::CreateTestFormField("Country Code", "countrycode", "1", "text",
                             "tel-country-code", &country_code_field);
-  country_code_field.form_control_type = "select-one";
+  country_code_field.form_control_type = form_control_type;
   form.fields.push_back(country_code_field);
 
   FormFieldData phonenumber_field;
@@ -7098,25 +7084,40 @@
                             "tel-national", &phonenumber_field);
   form.fields.push_back(phonenumber_field);
 
-  FormsSeen({form});
+  test->FormsSeen({form});
 
   // Fill the form.
   FormData response_data;
-  FillAutofillFormDataAndSaveResults(form, form.fields[3], kElvisProfileGuid,
-                                     &response_data);
+  test->FillAutofillFormDataAndSaveResults(form, form.fields[3],
+                                           kElvisProfileGuid, &response_data);
 
-  FormSubmitted(response_data);
+  test->FormSubmitted(response_data);
 
   // Value of country code field should have been saved.
-  ASSERT_EQ(1, personal_data().num_times_save_imported_profile_called());
+  const TestPersonalDataManager& personal_data = test->personal_data();
+  ASSERT_EQ(1, personal_data.num_times_save_imported_profile_called());
   std::u16string formatted_phone_number =
-      personal_data().last_save_imported_profile()->GetRawInfo(
+      personal_data.last_save_imported_profile()->GetRawInfo(
           PHONE_HOME_WHOLE_NUMBER);
   std::u16string phone_number_numbers_only;
   base::RemoveChars(formatted_phone_number, u"+- ", &phone_number_numbers_only);
   EXPECT_TRUE(base::StartsWith(phone_number_numbers_only, u"1"));
 }
 
+// Test that we save form data when a non-country, non-state <select> in the
+// form contains the default value.
+TEST_F(BrowserAutofillManagerTest,
+       FormSubmittedNonAddressSelectWithDefaultValue) {
+  DoTestFormSubmittedNonAddressControlWithDefaultValue(this, "select-one");
+}
+
+// Test that we save form data when a non-country, non-state <selectmenu> in the
+// form contains the default value.
+TEST_F(BrowserAutofillManagerTest,
+       FormSubmittedNonAddressSelectMenuWithDefaultValue) {
+  DoTestFormSubmittedNonAddressControlWithDefaultValue(this, "selectmenu");
+}
+
 struct ProfileMatchingTypesTestCase {
   const char* input_value;         // The value to input in the field.
   ServerFieldTypeSet field_types;  // The expected field types to be determined.
@@ -8411,12 +8412,11 @@
       label2 = "3734 Elvis Presley Blvd.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      field.global_id(),
-      Suggestion("buddy@gmail.com", label1, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("theking@gmail.com", label2, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(field.global_id(),
+                   Suggestion("buddy@gmail.com", label1, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry),
+                   Suggestion("theking@gmail.com", label2, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Verify that typing "apple" will match "123 Apple St." when substring matching
@@ -8451,10 +8451,9 @@
       label = "123 Apple St.";
   }
   // Test that we sent the right values to the external delegate.
-  CheckSuggestions(
-      field.global_id(),
-      Suggestion("123 Apple St., unit 6", label, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(field.global_id(),
+                   Suggestion("123 Apple St., unit 6", label, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 // Verify that typing "mail" will not match any of the "@gmail.com" email
@@ -8627,12 +8626,11 @@
       label1 = "1234 Smith Blvd., Carl Shawn Smith Grimes";
       label2 = "1234 Smith Blvd., Robin Adam Smith Grimes";
   }
-  CheckSuggestions(
-      field.global_id(),
-      Suggestion("Shawn Smith", label1, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)),
-      Suggestion("Adam Smith", label2, kAddressEntryIcon,
-                 Suggestion::FrontendId(PopupItemId::kAddressEntry)));
+  CheckSuggestions(field.global_id(),
+                   Suggestion("Shawn Smith", label1, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry),
+                   Suggestion("Adam Smith", label2, kAddressEntryIcon,
+                              PopupItemId::kAddressEntry));
 }
 
 TEST_F(BrowserAutofillManagerTest, ShouldUploadForm) {
diff --git a/components/autofill/core/browser/form_data_importer.cc b/components/autofill/core/browser/form_data_importer.cc
index fdebd53..8e2e363 100644
--- a/components/autofill/core/browser/form_data_importer.cc
+++ b/components/autofill/core/browser/form_data_importer.cc
@@ -1062,8 +1062,9 @@
       std::move(guid_of_card_if_no_interactive_authentication_flow_completed);
 }
 
-absl::optional<std::string>&
-FormDataImporter::GetGuidOfCardIfNoInteractiveAuthenticationFlowCompleted() {
+const absl::optional<std::string>&
+FormDataImporter::GetGuidOfCardIfNoInteractiveAuthenticationFlowCompleted()
+    const {
   return guid_of_card_if_no_interactive_authentication_flow_completed_;
 }
 
diff --git a/components/autofill/core/browser/form_data_importer.h b/components/autofill/core/browser/form_data_importer.h
index 70875de..3360a93 100644
--- a/components/autofill/core/browser/form_data_importer.h
+++ b/components/autofill/core/browser/form_data_importer.h
@@ -142,8 +142,8 @@
   void SetGuidOfCardIfNoInteractiveAuthenticationFlowCompleted(
       absl::optional<std::string>
           guid_of_card_if_no_interactive_authentication_flow_completed);
-  absl::optional<std::string>&
-  GetGuidOfCardIfNoInteractiveAuthenticationFlowCompleted();
+  const absl::optional<std::string>&
+  GetGuidOfCardIfNoInteractiveAuthenticationFlowCompleted() const;
 
  protected:
   void set_credit_card_save_manager_for_testing(
@@ -394,7 +394,7 @@
   // is empty upon form submission, it implies that the most recent autofill had
   // an interactive authentication. Set when
   // `SetGuidOfCardIfNoInteractiveAuthenticationFlowCompleted()` is called, and
-  // cleared on pag navigation.
+  // cleared on page navigation.
   absl::optional<std::string>
       guid_of_card_if_no_interactive_authentication_flow_completed_;
 
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index d8466d8e..e54a90a 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -958,7 +958,7 @@
         // During form parsing (as in "assigning field types to fields")
         // the `value` represents the initial value found at page load and needs
         // to be preserved.
-        if (field->form_control_type != "select-one") {
+        if (!field->IsSelectOrSelectMenuElement()) {
           field->value = cached_field->value;
           value_from_dynamic_change_form_ = true;
         }
@@ -975,7 +975,7 @@
         const bool field_is_neither_state_nor_country =
             field->server_type() != ADDRESS_HOME_COUNTRY &&
             field->server_type() != ADDRESS_HOME_STATE;
-        if (field->form_control_type != "select-one" &&
+        if (!field->IsSelectOrSelectMenuElement() &&
             same_value_as_on_page_load && field_is_neither_state_nor_country) {
           field->value = std::u16string();
         }
diff --git a/components/autofill/core/browser/iban_manager.cc b/components/autofill/core/browser/iban_manager.cc
index 8f3ace9..be72c5e 100644
--- a/components/autofill/core/browser/iban_manager.cc
+++ b/components/autofill/core/browser/iban_manager.cc
@@ -140,9 +140,8 @@
   uma_recorder_.OnIbanSuggestionsShown(query_handler.field_id_);
 }
 
-void IBANManager::OnSingleFieldSuggestionSelected(
-    const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {
+void IBANManager::OnSingleFieldSuggestionSelected(const std::u16string& value,
+                                                  PopupItemId popup_item_id) {
   uma_recorder_.OnIbanSuggestionSelected();
 }
 
diff --git a/components/autofill/core/browser/iban_manager.h b/components/autofill/core/browser/iban_manager.h
index b5d4fc6..105b91b7 100644
--- a/components/autofill/core/browser/iban_manager.h
+++ b/components/autofill/core/browser/iban_manager.h
@@ -12,6 +12,7 @@
 #include "components/autofill/core/browser/metrics/payments/iban_metrics.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/single_field_form_filler.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/webdata/common/web_data_service_consumer.h"
 
@@ -51,10 +52,9 @@
   void OnRemoveCurrentSingleFieldSuggestion(
       const std::u16string& field_name,
       const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override {}
-  void OnSingleFieldSuggestionSelected(
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override;
+      PopupItemId popup_item_id) override {}
+  void OnSingleFieldSuggestionSelected(const std::u16string& value,
+                                       PopupItemId popup_item_id) override;
 
   base::WeakPtr<IBANManager> GetWeakPtr();
 
diff --git a/components/autofill/core/browser/iban_manager_unittest.cc b/components/autofill/core/browser/iban_manager_unittest.cc
index e74fa1a2..e43390a 100644
--- a/components/autofill/core/browser/iban_manager_unittest.cc
+++ b/components/autofill/core/browser/iban_manager_unittest.cc
@@ -115,20 +115,20 @@
                                     base::StringPiece nickname) {
     IBAN iban = SetUpIBAN(value, nickname);
     Suggestion iban_suggestion(iban.GetIdentifierStringForAutofillDisplay());
-    iban_suggestion.frontend_id = PopupItemId::kIbanEntry;
+    iban_suggestion.popup_item_id = PopupItemId::kIbanEntry;
     return iban_suggestion;
   }
 
   Suggestion SetUpSeparator() {
     Suggestion separator;
-    separator.frontend_id = PopupItemId::kSeparator;
+    separator.popup_item_id = PopupItemId::kSeparator;
     return separator;
   }
 
   Suggestion SetUpFooterManagePaymentMethods() {
     Suggestion footer_suggestion(
         l10n_util::GetStringUTF16(IDS_AUTOFILL_MANAGE_PAYMENT_METHODS));
-    footer_suggestion.frontend_id = PopupItemId::kAutofillOptions;
+    footer_suggestion.popup_item_id = PopupItemId::kAutofillOptions;
     footer_suggestion.icon = "settingsIcon";
     return footer_suggestion;
   }
@@ -144,9 +144,9 @@
   raw_ptr<ui::ResourceBundle> original_resource_bundle_;
 };
 
-MATCHER_P(MatchesTextAndFrontendId, suggestion, "") {
+MATCHER_P(MatchesTextAndPopupItemId, suggestion, "") {
   return arg.main_text == suggestion.main_text &&
-         arg.frontend_id == suggestion.frontend_id;
+         arg.popup_item_id == suggestion.popup_item_id;
 }
 
 TEST_F(IBANManagerTest, ShowsIBANSuggestions) {
@@ -160,12 +160,12 @@
 
   // Setting up mock to verify that the handler is returned a list of
   // IBAN-based suggestions.
-  EXPECT_CALL(
-      suggestions_handler_,
-      OnSuggestionsReturned(
-          test_field.global_id(), AutoselectFirstSuggestion(false),
-          testing::IsSupersetOf({MatchesTextAndFrontendId(iban_suggestion_0),
-                                 MatchesTextAndFrontendId(iban_suggestion_1)})))
+  EXPECT_CALL(suggestions_handler_,
+              OnSuggestionsReturned(
+                  test_field.global_id(), AutoselectFirstSuggestion(false),
+                  testing::IsSupersetOf(
+                      {MatchesTextAndPopupItemId(iban_suggestion_0),
+                       MatchesTextAndPopupItemId(iban_suggestion_1)})))
       .Times(1);
 
   // Simulate request for suggestions.
@@ -211,9 +211,9 @@
               OnSuggestionsReturned(
                   test_field.global_id(), AutoselectFirstSuggestion(false),
                   testing::UnorderedElementsAre(
-                      MatchesTextAndFrontendId(iban_suggestion_0),
-                      MatchesTextAndFrontendId(iban_suggestion_1),
-                      MatchesTextAndFrontendId(iban_suggestion_2))))
+                      MatchesTextAndPopupItemId(iban_suggestion_0),
+                      MatchesTextAndPopupItemId(iban_suggestion_1),
+                      MatchesTextAndPopupItemId(iban_suggestion_2))))
       .Times(1);
 
   // Simulate request for suggestions.
@@ -268,10 +268,10 @@
               OnSuggestionsReturned(
                   test_field.global_id(), AutoselectFirstSuggestion(false),
                   testing::UnorderedElementsAre(
-                      MatchesTextAndFrontendId(iban_suggestion_0),
-                      MatchesTextAndFrontendId(iban_suggestion_1),
-                      MatchesTextAndFrontendId(iban_suggestion_2),
-                      MatchesTextAndFrontendId(iban_suggestion_3))))
+                      MatchesTextAndPopupItemId(iban_suggestion_0),
+                      MatchesTextAndPopupItemId(iban_suggestion_1),
+                      MatchesTextAndPopupItemId(iban_suggestion_2),
+                      MatchesTextAndPopupItemId(iban_suggestion_3))))
       .Times(1);
 
   // Simulate request for suggestions.
@@ -292,9 +292,9 @@
               OnSuggestionsReturned(
                   test_field.global_id(), AutoselectFirstSuggestion(false),
                   testing::UnorderedElementsAre(
-                      MatchesTextAndFrontendId(iban_suggestion_0),
-                      MatchesTextAndFrontendId(iban_suggestion_2),
-                      MatchesTextAndFrontendId(iban_suggestion_3))))
+                      MatchesTextAndPopupItemId(iban_suggestion_0),
+                      MatchesTextAndPopupItemId(iban_suggestion_2),
+                      MatchesTextAndPopupItemId(iban_suggestion_3))))
       .Times(1);
 
   // Simulate request for suggestions.
@@ -354,11 +354,11 @@
 
   // Setting up mock to verify that the handler is returned a list of
   // IBAN-based suggestions.
-  EXPECT_CALL(
-      suggestions_handler_,
-      OnSuggestionsReturned(
-          test_field.global_id(), AutoselectFirstSuggestion(false),
-          testing::IsSupersetOf({MatchesTextAndFrontendId(iban_suggestion_0)})))
+  EXPECT_CALL(suggestions_handler_,
+              OnSuggestionsReturned(
+                  test_field.global_id(), AutoselectFirstSuggestion(false),
+                  testing::IsSupersetOf(
+                      {MatchesTextAndPopupItemId(iban_suggestion_0)})))
       .Times(1);
 
   // Simulate request for suggestions.
diff --git a/components/autofill/core/browser/merchant_promo_code_manager.cc b/components/autofill/core/browser/merchant_promo_code_manager.cc
index 0b9ee361..f194544 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager.cc
+++ b/components/autofill/core/browser/merchant_promo_code_manager.cc
@@ -58,12 +58,12 @@
 void MerchantPromoCodeManager::OnRemoveCurrentSingleFieldSuggestion(
     const std::u16string& field_name,
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {}
+    PopupItemId popup_item_id) {}
 
 void MerchantPromoCodeManager::OnSingleFieldSuggestionSelected(
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {
-  uma_recorder_.OnOfferSuggestionSelected(frontend_id);
+    PopupItemId popup_item_id) {
+  uma_recorder_.OnOfferSuggestionSelected(popup_item_id);
 }
 
 void MerchantPromoCodeManager::Init(PersonalDataManager* personal_data_manager,
@@ -106,8 +106,8 @@
 }
 
 void MerchantPromoCodeManager::UMARecorder::OnOfferSuggestionSelected(
-    Suggestion::FrontendId frontend_id) {
-  if (frontend_id == PopupItemId::kMerchantPromoCodeEntry) {
+    PopupItemId popup_item_id) {
+  if (popup_item_id == PopupItemId::kMerchantPromoCodeEntry) {
     // We log every time an individual offer suggestion is selected, regardless
     // if the user is repeatedly autofilling the same field.
     autofill_metrics::LogIndividualOfferSuggestionEvent(
@@ -123,7 +123,7 @@
               kOfferSuggestionSelectedOnce,
           AutofillOfferData::OfferType::GPAY_PROMO_CODE_OFFER);
     }
-  } else if (frontend_id == PopupItemId::kSeePromoCodeDetails) {
+  } else if (popup_item_id == PopupItemId::kSeePromoCodeDetails) {
     // We log every time the see offer details suggestion in the footer is
     // selected, regardless if the user is repeatedly autofilling the same
     // field.
diff --git a/components/autofill/core/browser/merchant_promo_code_manager.h b/components/autofill/core/browser/merchant_promo_code_manager.h
index ff60317..9590d67f 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager.h
+++ b/components/autofill/core/browser/merchant_promo_code_manager.h
@@ -9,6 +9,7 @@
 #include "components/autofill/core/browser/autofill_subject.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/single_field_form_filler.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/common/unique_ids.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/webdata/common/web_data_service_consumer.h"
@@ -45,13 +46,11 @@
   void OnWillSubmitFormWithFields(const std::vector<FormFieldData>& fields,
                                   bool is_autocomplete_enabled) override;
   void CancelPendingQueries(const SuggestionsHandler* handler) override;
-  void OnRemoveCurrentSingleFieldSuggestion(
-      const std::u16string& field_name,
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override;
-  void OnSingleFieldSuggestionSelected(
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override;
+  void OnRemoveCurrentSingleFieldSuggestion(const std::u16string& field_name,
+                                            const std::u16string& value,
+                                            PopupItemId popup_item_id) override;
+  void OnSingleFieldSuggestionSelected(const std::u16string& value,
+                                       PopupItemId popup_item_id) override;
 
   // Initializes the instance with the given parameters. |personal_data_manager|
   // is a profile-scope data manager used to retrieve promo code offers from the
@@ -84,7 +83,7 @@
     void OnOffersSuggestionsShown(
         const FieldGlobalId& field_global_id,
         const std::vector<const AutofillOfferData*>& offers);
-    void OnOfferSuggestionSelected(Suggestion::FrontendId frontend_id);
+    void OnOfferSuggestionSelected(PopupItemId popup_item_id);
 
    private:
     // The global id of the field that most recently had suggestions shown.
diff --git a/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc b/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
index 2422c6ba8..2bee1645 100644
--- a/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
+++ b/components/autofill/core/browser/merchant_promo_code_manager_unittest.cc
@@ -124,7 +124,7 @@
           _, autoselect_first_suggestion,
           UnorderedElementsAre(
               Field(&Suggestion::main_text, promo_code_suggestion.main_text),
-              Field(&Suggestion::frontend_id, PopupItemId::kSeparator),
+              Field(&Suggestion::popup_item_id, PopupItemId::kSeparator),
               Field(&Suggestion::main_text, footer_suggestion.main_text))))
       .Times(3);
 
@@ -540,7 +540,7 @@
   SetUpPromoCodeOffer(last_committed_origin_url,
                       GURL("https://offer-details-url.com/"));
 
-  // Check that non promo code frontend id's do not log as offer suggestion
+  // Check that non promo code popup item id's do not log as offer suggestion
   // selected.
   merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
       test_promo_code, PopupItemId::kAutocompleteEntry);
@@ -605,7 +605,7 @@
   SetUpPromoCodeOffer(last_committed_origin_url,
                       GURL("https://offer-details-url.com/"));
 
-  // Check that non promo code footer frontend id's do not log as offer
+  // Check that non promo code footer popup item id's do not log as offer
   // suggestions footer selected.
   merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
       test_promo_code, PopupItemId::kAutocompleteEntry);
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_test_base.h b/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
index 21175cf..4cc2e22 100644
--- a/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
+++ b/components/autofill/core/browser/metrics/autofill_metrics_test_base.h
@@ -37,7 +37,7 @@
  public:
   MockAutofillClient();
   ~MockAutofillClient() override;
-  MOCK_METHOD(void, ExecuteCommand, (Suggestion::FrontendId), (override));
+  MOCK_METHOD(void, ExecuteCommand, (PopupItemId), (override));
   MOCK_METHOD(bool, IsTouchToFillCreditCardSupported, (), (override));
   MOCK_METHOD(bool,
               ShowTouchToFillCreditCard,
diff --git a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
index 07eeb34..98d98c7 100644
--- a/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
+++ b/components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.cc
@@ -460,7 +460,7 @@
 
 bool CreditCardFormEventLogger::DoSuggestionsIncludeVirtualCard() {
   auto is_virtual_card = [](const Suggestion& suggestion) {
-    return suggestion.frontend_id == PopupItemId::kVirtualCreditCardEntry;
+    return suggestion.popup_item_id == PopupItemId::kVirtualCreditCardEntry;
   };
   return base::ranges::any_of(suggestions_, is_virtual_card);
 }
diff --git a/components/autofill/core/browser/mock_autocomplete_history_manager.h b/components/autofill/core/browser/mock_autocomplete_history_manager.h
index dacf958..995235be 100644
--- a/components/autofill/core/browser/mock_autocomplete_history_manager.h
+++ b/components/autofill/core/browser/mock_autocomplete_history_manager.h
@@ -40,13 +40,11 @@
               (override));
   MOCK_METHOD(void,
               OnRemoveCurrentSingleFieldSuggestion,
-              (const std::u16string&,
-               const std::u16string&,
-               Suggestion::FrontendId),
+              (const std::u16string&, const std::u16string&, PopupItemId),
               (override));
   MOCK_METHOD(void,
               OnSingleFieldSuggestionSelected,
-              (const std::u16string&, Suggestion::FrontendId),
+              (const std::u16string&, PopupItemId),
               (override));
 };
 
diff --git a/components/autofill/core/browser/mock_iban_manager.h b/components/autofill/core/browser/mock_iban_manager.h
index 3fc4915..ecf2bd0 100644
--- a/components/autofill/core/browser/mock_iban_manager.h
+++ b/components/autofill/core/browser/mock_iban_manager.h
@@ -8,6 +8,7 @@
 #include "base/memory/weak_ptr.h"
 #include "components/autofill/core/browser/iban_manager.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill {
@@ -37,13 +38,11 @@
               (override));
   MOCK_METHOD(void,
               OnRemoveCurrentSingleFieldSuggestion,
-              (const std::u16string&,
-               const std::u16string&,
-               Suggestion::FrontendId),
+              (const std::u16string&, const std::u16string&, PopupItemId),
               (override));
   MOCK_METHOD(void,
               OnSingleFieldSuggestionSelected,
-              (const std::u16string&, Suggestion::FrontendId),
+              (const std::u16string&, PopupItemId),
               (override));
 };
 
diff --git a/components/autofill/core/browser/mock_merchant_promo_code_manager.h b/components/autofill/core/browser/mock_merchant_promo_code_manager.h
index 8b35e1e..72a25d1 100644
--- a/components/autofill/core/browser/mock_merchant_promo_code_manager.h
+++ b/components/autofill/core/browser/mock_merchant_promo_code_manager.h
@@ -36,13 +36,11 @@
               (override));
   MOCK_METHOD(void,
               OnRemoveCurrentSingleFieldSuggestion,
-              (const std::u16string&,
-               const std::u16string&,
-               Suggestion::FrontendId),
+              (const std::u16string&, const std::u16string&, PopupItemId),
               (override));
   MOCK_METHOD(void,
               OnSingleFieldSuggestionSelected,
-              (const std::u16string&, Suggestion::FrontendId),
+              (const std::u16string&, PopupItemId),
               (override));
 };
 
diff --git a/components/autofill/core/browser/mock_single_field_form_fill_router.h b/components/autofill/core/browser/mock_single_field_form_fill_router.h
index 2b565665..af00b2e 100644
--- a/components/autofill/core/browser/mock_single_field_form_fill_router.h
+++ b/components/autofill/core/browser/mock_single_field_form_fill_router.h
@@ -46,13 +46,11 @@
               (override));
   MOCK_METHOD(void,
               OnRemoveCurrentSingleFieldSuggestion,
-              (const std::u16string&,
-               const std::u16string&,
-               Suggestion::FrontendId),
+              (const std::u16string&, const std::u16string&, PopupItemId),
               (override));
   MOCK_METHOD(void,
               OnSingleFieldSuggestionSelected,
-              (const std::u16string&, Suggestion::FrontendId),
+              (const std::u16string&, PopupItemId),
               (override));
 };
 
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 092b097..f911fd2 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -2542,8 +2542,7 @@
   const absl::optional<std::string>& guid =
       autofill_client_.GetFormDataImporter()
           ->GetGuidOfCardIfNoInteractiveAuthenticationFlowCompleted();
-  EXPECT_TRUE(guid.has_value());
-  EXPECT_EQ(guid.value(), kTestGUID);
+  EXPECT_EQ(guid, kTestGUID);
 
   // Expect the metrics are logged correctly.
   histogram_tester.ExpectUniqueSample(
diff --git a/components/autofill/core/browser/single_field_form_fill_router.cc b/components/autofill/core/browser/single_field_form_fill_router.cc
index bada62a..43e57502 100644
--- a/components/autofill/core/browser/single_field_form_fill_router.cc
+++ b/components/autofill/core/browser/single_field_form_fill_router.cc
@@ -102,33 +102,33 @@
 void SingleFieldFormFillRouter::OnRemoveCurrentSingleFieldSuggestion(
     const std::u16string& field_name,
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {
+    PopupItemId popup_item_id) {
   if (merchant_promo_code_manager_ &&
-      frontend_id == PopupItemId::kMerchantPromoCodeEntry) {
+      popup_item_id == PopupItemId::kMerchantPromoCodeEntry) {
     merchant_promo_code_manager_->OnRemoveCurrentSingleFieldSuggestion(
-        field_name, value, frontend_id);
-  } else if (iban_manager_ && frontend_id == PopupItemId::kIbanEntry) {
+        field_name, value, popup_item_id);
+  } else if (iban_manager_ && popup_item_id == PopupItemId::kIbanEntry) {
     iban_manager_->OnRemoveCurrentSingleFieldSuggestion(field_name, value,
-                                                        frontend_id);
+                                                        popup_item_id);
   } else {
     autocomplete_history_manager_->OnRemoveCurrentSingleFieldSuggestion(
-        field_name, value, frontend_id);
+        field_name, value, popup_item_id);
   }
 }
 
 void SingleFieldFormFillRouter::OnSingleFieldSuggestionSelected(
     const std::u16string& value,
-    Suggestion::FrontendId frontend_id) {
+    PopupItemId popup_item_id) {
   if (merchant_promo_code_manager_ &&
-      (frontend_id == PopupItemId::kMerchantPromoCodeEntry ||
-       frontend_id == PopupItemId::kSeePromoCodeDetails)) {
-    merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(value,
-                                                                  frontend_id);
-  } else if (iban_manager_ && frontend_id == PopupItemId::kIbanEntry) {
-    iban_manager_->OnSingleFieldSuggestionSelected(value, frontend_id);
+      (popup_item_id == PopupItemId::kMerchantPromoCodeEntry ||
+       popup_item_id == PopupItemId::kSeePromoCodeDetails)) {
+    merchant_promo_code_manager_->OnSingleFieldSuggestionSelected(
+        value, popup_item_id);
+  } else if (iban_manager_ && popup_item_id == PopupItemId::kIbanEntry) {
+    iban_manager_->OnSingleFieldSuggestionSelected(value, popup_item_id);
   } else {
-    autocomplete_history_manager_->OnSingleFieldSuggestionSelected(value,
-                                                                   frontend_id);
+    autocomplete_history_manager_->OnSingleFieldSuggestionSelected(
+        value, popup_item_id);
   }
 }
 
diff --git a/components/autofill/core/browser/single_field_form_fill_router.h b/components/autofill/core/browser/single_field_form_fill_router.h
index aa40903..84a5808 100644
--- a/components/autofill/core/browser/single_field_form_fill_router.h
+++ b/components/autofill/core/browser/single_field_form_fill_router.h
@@ -55,13 +55,11 @@
                                   bool is_autocomplete_enabled) override;
   void CancelPendingQueries(
       const SingleFieldFormFiller::SuggestionsHandler* handler) override;
-  void OnRemoveCurrentSingleFieldSuggestion(
-      const std::u16string& field_name,
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override;
-  void OnSingleFieldSuggestionSelected(
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) override;
+  void OnRemoveCurrentSingleFieldSuggestion(const std::u16string& field_name,
+                                            const std::u16string& value,
+                                            PopupItemId popup_item_id) override;
+  void OnSingleFieldSuggestionSelected(const std::u16string& value,
+                                       PopupItemId popup_item_id) override;
 
  private:
   // Handles autocompleting single fields.
diff --git a/components/autofill/core/browser/single_field_form_filler.h b/components/autofill/core/browser/single_field_form_filler.h
index 2e26353..79572d2d 100644
--- a/components/autofill/core/browser/single_field_form_filler.h
+++ b/components/autofill/core/browser/single_field_form_filler.h
@@ -75,19 +75,18 @@
   virtual void CancelPendingQueries(const SuggestionsHandler* handler) = 0;
 
   // If applicable, removes the currently-selected suggestion from the database.
-  // |frontend_id| is the PopupItemId of the suggestion to be removed.
+  // `popup_item_id` is the PopupItemId of the suggestion to be removed.
   virtual void OnRemoveCurrentSingleFieldSuggestion(
       const std::u16string& field_name,
       const std::u16string& value,
-      Suggestion::FrontendId frontend_id) = 0;
+      PopupItemId popup_item_id) = 0;
 
   // Invoked when the user selects |value| in the list of suggestions. For
   // Autocomplete, this function logs the DaysSinceLastUse of the Autocomplete
-  // entry associated with |value|. |frontend_id| is the PopupItemId of the
+  // entry associated with |value|. `popup_item_id` is the PopupItemId of the
   // suggestion selected.
-  virtual void OnSingleFieldSuggestionSelected(
-      const std::u16string& value,
-      Suggestion::FrontendId frontend_id) = 0;
+  virtual void OnSingleFieldSuggestionSelected(const std::u16string& value,
+                                               PopupItemId popup_item_id) = 0;
 
  protected:
   // Internal data object used to keep a request's context to associate it
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index f66858e..95fc09d 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -43,6 +43,7 @@
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/autofill/core/browser/ui/mock_fast_checkout_client.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/popup_types.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -446,7 +447,7 @@
     return form_origin_.SchemeIs("https");
   }
 
-  void ExecuteCommand(Suggestion::FrontendId id) override {}
+  void ExecuteCommand(PopupItemId popup_item_id) override {}
 
   void OpenPromoCodeOfferDetailsURL(const GURL& url) override {}
 
diff --git a/components/autofill/core/browser/test_autofill_external_delegate.cc b/components/autofill/core/browser/test_autofill_external_delegate.cc
index a41f6161..e4d4894 100644
--- a/components/autofill/core/browser/test_autofill_external_delegate.cc
+++ b/components/autofill/core/browser/test_autofill_external_delegate.cc
@@ -99,7 +99,8 @@
               suggestions_[i].minor_text.value);
     EXPECT_EQ(expected_suggestions[i].labels, suggestions_[i].labels);
     EXPECT_EQ(expected_suggestions[i].icon, suggestions_[i].icon);
-    EXPECT_EQ(expected_suggestions[i].frontend_id, suggestions_[i].frontend_id);
+    EXPECT_EQ(expected_suggestions[i].popup_item_id,
+              suggestions_[i].popup_item_id);
   }
   ASSERT_EQ(expected_num_suggestions, suggestions_.size());
 }
diff --git a/components/autofill/core/browser/test_browser_autofill_manager.h b/components/autofill/core/browser/test_browser_autofill_manager.h
index 6e060f1..2cb0fd2b 100644
--- a/components/autofill/core/browser/test_browser_autofill_manager.h
+++ b/components/autofill/core/browser/test_browser_autofill_manager.h
@@ -127,13 +127,6 @@
 
   void SetExpectedObservedSubmission(bool expected);
 
-  struct MakeFrontendIdParams {
-    std::string credit_card_id;
-    std::string profile_id;
-  };
-
-  Suggestion::FrontendId MakeFrontendId(const MakeFrontendIdParams& params);
-
  private:
   bool autofill_profile_enabled_ = true;
   bool autofill_credit_card_enabled_ = true;
diff --git a/components/autofill/core/browser/test_personal_data_manager.h b/components/autofill/core/browser/test_personal_data_manager.h
index 906d73b..b1efcd5 100644
--- a/components/autofill/core/browser/test_personal_data_manager.h
+++ b/components/autofill/core/browser/test_personal_data_manager.h
@@ -127,7 +127,7 @@
     return num_times_save_imported_profile_called_;
   }
 
-  AutofillProfile* last_save_imported_profile() {
+  const AutofillProfile* last_save_imported_profile() const {
     return last_save_imported_profile_.get();
   }
 
diff --git a/components/autofill/core/browser/ui/autofill_popup_delegate.h b/components/autofill/core/browser/ui/autofill_popup_delegate.h
index 9e7aecd4..9eca591 100644
--- a/components/autofill/core/browser/ui/autofill_popup_delegate.h
+++ b/components/autofill/core/browser/ui/autofill_popup_delegate.h
@@ -45,7 +45,7 @@
   // Returns whether the given value can be deleted, and if true,
   // fills out |title| and |body|.
   virtual bool GetDeletionConfirmationText(const std::u16string& value,
-                                           Suggestion::FrontendId frontend_id,
+                                           PopupItemId popup_item_id,
                                            Suggestion::BackendId backend_id,
                                            std::u16string* title,
                                            std::u16string* body) = 0;
@@ -53,7 +53,7 @@
   // Delete the described suggestion. Returns true if something was deleted,
   // or false if deletion is not allowed.
   virtual bool RemoveSuggestion(const std::u16string& value,
-                                Suggestion::FrontendId frontend_id,
+                                PopupItemId popup_item_id,
                                 Suggestion::BackendId backend_id) = 0;
 
   // Informs the delegate that the Autofill previewed form should be cleared.
diff --git a/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h b/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h
index 53e9cc9..17cffea 100644
--- a/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h
+++ b/components/autofill/core/browser/ui/mock_autofill_popup_delegate.h
@@ -34,7 +34,7 @@
   MOCK_METHOD(bool,
               GetDeletionConfirmationText,
               (const std::u16string& value,
-               Suggestion::FrontendId frontend_id,
+               PopupItemId popup_item_id,
                Suggestion::BackendId backend_id,
                std::u16string* title,
                std::u16string* body),
@@ -42,7 +42,7 @@
   MOCK_METHOD(bool,
               RemoveSuggestion,
               (const std::u16string& value,
-               Suggestion::FrontendId frontend_id,
+               PopupItemId popup_item_id,
                Suggestion::BackendId backend_id),
               (override));
   MOCK_METHOD(void, ClearPreviewedForm, (), (override));
diff --git a/components/autofill/core/browser/ui/popup_item_ids.h b/components/autofill/core/browser/ui/popup_item_ids.h
index aa05cbe..b5eed81 100644
--- a/components/autofill/core/browser/ui/popup_item_ids.h
+++ b/components/autofill/core/browser/ui/popup_item_ids.h
@@ -7,40 +7,39 @@
 
 namespace autofill {
 
-// TODO(crbug.com/1394920): Remove the values from this enum.
 // This enum defines item identifiers for Autofill popup controller.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.autofill
-enum PopupItemId {
-  kCreditCardEntry = 2,
-  kAddressEntry = 1,
-  kAutocompleteEntry = 0,
-  kInsecureContextPaymentDisabledMessage = -1,
-  kPasswordEntry = -2,
-  kSeparator = -3,
-  kClearForm = -4,
-  kAutofillOptions = -5,
-  kDatalistEntry = -6,
-  kScanCreditCard = -7,
-  kTitle = -8,
-  kCreditCardSigninPromo = -9,
-  kUsernameEntry = -11,
-  kAllSavedPasswordsEntry = -13,
-  kGeneratePasswordEntry = -14,
-  kShowAccountCards = -15,
-  kPasswordAccountStorageOptIn = -16,
-  kUseVirtualCard = -18,
-  kPasswordAccountStorageOptInAndGenerate = -21,
-  kAccountStoragePasswordEntry = -22,
-  kAccountStorageUsernameEntry = -23,
-  kPasswordAccountStorageReSignin = -24,
-  kPasswordAccountStorageEmpty = -25,
-  kMixedFormMessage = -26,
-  kVirtualCreditCardEntry = -27,
-  kWebauthnCredential = -28,
-  kMerchantPromoCodeEntry = -29,
-  kSeePromoCodeDetails = -30,
-  kWebauthnSignInWithAnotherDevice = -31,
-  kIbanEntry = -32,
+enum class PopupItemId : int {
+  kCreditCardEntry,
+  kAddressEntry,
+  kAutocompleteEntry,
+  kInsecureContextPaymentDisabledMessage,
+  kPasswordEntry,
+  kSeparator,
+  kClearForm,
+  kAutofillOptions,
+  kDatalistEntry,
+  kScanCreditCard,
+  kTitle,
+  kCreditCardSigninPromo,
+  kUsernameEntry,
+  kAllSavedPasswordsEntry,
+  kGeneratePasswordEntry,
+  kShowAccountCards,
+  kPasswordAccountStorageOptIn,
+  kUseVirtualCard,
+  kPasswordAccountStorageOptInAndGenerate,
+  kAccountStoragePasswordEntry,
+  kAccountStorageUsernameEntry,
+  kPasswordAccountStorageReSignin,
+  kPasswordAccountStorageEmpty,
+  kMixedFormMessage,
+  kVirtualCreditCardEntry,
+  kWebauthnCredential,
+  kMerchantPromoCodeEntry,
+  kSeePromoCodeDetails,
+  kWebauthnSignInWithAnotherDevice,
+  kIbanEntry,
 };
 
 // List of `PopupItemId` that trigger filling a value into an input element
diff --git a/components/autofill/core/browser/ui/suggestion.cc b/components/autofill/core/browser/ui/suggestion.cc
index cb20132..b844650 100644
--- a/components/autofill/core/browser/ui/suggestion.cc
+++ b/components/autofill/core/browser/ui/suggestion.cc
@@ -9,37 +9,10 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "base/types/cxx23_to_underlying.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 
 namespace autofill {
 
-bool operator==(Suggestion::FrontendId lhs, Suggestion::FrontendId rhs) {
-  return lhs.as_popup_item_id() == rhs.as_popup_item_id();
-}
-
-bool operator==(Suggestion::FrontendId lhs, PopupItemId rhs) {
-  return lhs == Suggestion::FrontendId(rhs);
-}
-
-bool operator==(PopupItemId lhs, Suggestion::FrontendId rhs) {
-  return Suggestion::FrontendId(lhs) == rhs;
-}
-
-bool operator!=(Suggestion::FrontendId lhs, Suggestion::FrontendId rhs) {
-  return !(lhs == rhs);
-}
-
-bool operator!=(Suggestion::FrontendId lhs, PopupItemId rhs) {
-  return !(lhs == rhs);
-}
-
-bool operator!=(PopupItemId lhs, Suggestion::FrontendId rhs) {
-  return !(lhs == rhs);
-}
-
-std::ostream& operator<<(std::ostream& os, Suggestion::FrontendId id) {
-  return os << base::to_underlying(id.as_popup_item_id());
-}
-
 Suggestion::Text::Text() = default;
 
 Suggestion::Text::Text(std::u16string value,
@@ -69,14 +42,14 @@
 Suggestion::Suggestion(std::u16string main_text)
     : main_text(std::move(main_text), Text::IsPrimary(true)) {}
 
-Suggestion::Suggestion(Suggestion::FrontendId frontend_id)
-    : frontend_id(frontend_id) {}
+Suggestion::Suggestion(PopupItemId popup_item_id)
+    : popup_item_id(popup_item_id) {}
 
 Suggestion::Suggestion(base::StringPiece main_text,
                        base::StringPiece label,
                        std::string icon,
-                       Suggestion::FrontendId frontend_id)
-    : frontend_id(frontend_id),
+                       PopupItemId popup_item_id)
+    : popup_item_id(popup_item_id),
       main_text(base::UTF8ToUTF16(main_text), Text::IsPrimary(true)),
       icon(std::move(icon)) {
   if (!label.empty())
@@ -87,8 +60,8 @@
                        base::StringPiece minor_text,
                        base::StringPiece label,
                        std::string icon,
-                       Suggestion::FrontendId frontend_id)
-    : frontend_id(frontend_id),
+                       PopupItemId popup_item_id)
+    : popup_item_id(popup_item_id),
       main_text(base::UTF8ToUTF16(main_text), Text::IsPrimary(true)),
       minor_text(base::UTF8ToUTF16(minor_text)),
       icon(std::move(icon)) {
diff --git a/components/autofill/core/browser/ui/suggestion.h b/components/autofill/core/browser/ui/suggestion.h
index 5cb818e..a3ab0d13 100644
--- a/components/autofill/core/browser/ui/suggestion.h
+++ b/components/autofill/core/browser/ui/suggestion.h
@@ -27,31 +27,6 @@
   using ValueToFill = base::StrongAlias<struct ValueToFill, std::u16string>;
   using Payload = absl::variant<BackendId, GURL, ValueToFill>;
 
-  // A frontend ID is just a PopupItemId.
-  // Frontend IDs are deprecated and will be eliminated: crbug.com/1394920.
-  //
-  // TODO(crbug.com/1394920): Convert frontend id into a PopupItemId.
-  class FrontendId {
-   public:
-    constexpr FrontendId() : value_(kAutocompleteEntry) {}
-    constexpr FrontendId(  // NOLINT(google-explicit-constructor)
-        PopupItemId popup_item_id)
-        : value_(popup_item_id) {}
-
-    // Returns the content of the variant as `PopupItemId`, even if it holds a
-    // raw integer.
-    PopupItemId as_popup_item_id() const {
-      return static_cast<PopupItemId>(value_);
-    }
-
-    bool is_an_address_or_card_popup_item_id() const {
-      return value_ == kAddressEntry || value_ == kCreditCardEntry;
-    }
-
-   private:
-    PopupItemId value_;
-  };
-
   enum MatchMode {
     PREFIX_MATCH,    // for prefix matched suggestions;
     SUBSTRING_MATCH  // for substring matched suggestions;
@@ -86,18 +61,18 @@
 
   Suggestion();
   explicit Suggestion(std::u16string main_text);
-  explicit Suggestion(Suggestion::FrontendId frontend_id);
+  explicit Suggestion(PopupItemId popup_item_id);
   // Constructor for unit tests. It will convert the strings from UTF-8 to
   // UTF-16.
   Suggestion(base::StringPiece main_text,
              base::StringPiece label,
              std::string icon,
-             Suggestion::FrontendId frontend_id);
+             PopupItemId popup_item_id);
   Suggestion(base::StringPiece main_text,
              base::StringPiece minor_text,
              base::StringPiece label,
              std::string icon,
-             Suggestion::FrontendId frontend_id);
+             PopupItemId popup_item_id);
   Suggestion(const Suggestion& other);
   Suggestion(Suggestion&& other);
   Suggestion& operator=(const Suggestion& other);
@@ -114,7 +89,7 @@
 
 #if DCHECK_IS_ON()
   bool Invariant() const {
-    switch (frontend_id.as_popup_item_id()) {
+    switch (popup_item_id) {
       case PopupItemId::kSeePromoCodeDetails:
         return absl::holds_alternative<GURL>(payload);
       case PopupItemId::kIbanEntry:
@@ -133,12 +108,8 @@
   // shown other than main_text.
   Payload payload;
 
-  // TODO(crbug.com/1325509): Convert |frontend_id| from an int to a
-  // PopupItemId.
-  // ID for the frontend to use in identifying the particular result. Positive
-  // values are sent over IPC to identify the item selected. Negative values
-  // (see popup_item_ids.h) have special built-in meanings.
-  Suggestion::FrontendId frontend_id{};
+  // Determines popup identifier for the suggestion.
+  PopupItemId popup_item_id = PopupItemId::kAutocompleteEntry;
 
   // The texts that will be displayed on the first line in a suggestion. The
   // order of showing the two texts on the first line depends on whether it is
@@ -201,20 +172,12 @@
   absl::optional<std::u16string> acceptance_a11y_announcement;
 };
 
-bool operator==(Suggestion::FrontendId lhs, Suggestion::FrontendId rhs);
-bool operator==(Suggestion::FrontendId lhs, PopupItemId rhs);
-bool operator==(PopupItemId lhs, Suggestion::FrontendId rhs);
-bool operator!=(Suggestion::FrontendId lhs, Suggestion::FrontendId rhs);
-bool operator!=(Suggestion::FrontendId lhs, PopupItemId rhs);
-bool operator!=(PopupItemId lhs, Suggestion::FrontendId rhs);
-
-std::ostream& operator<<(std::ostream& os, Suggestion::FrontendId id);
-
 #if defined(UNIT_TEST)
 inline void PrintTo(const Suggestion& suggestion, std::ostream* os) {
   *os << std::endl
-      << "Suggestion (frontend_id:" << suggestion.frontend_id
-      << ", main_text:\"" << suggestion.main_text.value << "\""
+      << "Suggestion (popup_item_id:"
+      << base::to_underlying(suggestion.popup_item_id) << ", main_text:\""
+      << suggestion.main_text.value << "\""
       << (suggestion.main_text.is_primary ? "(Primary)" : "(Not Primary)")
       << ", minor_text:\"" << suggestion.minor_text.value << "\""
       << (suggestion.minor_text.is_primary ? "(Primary)" : "(Not Primary)")
diff --git a/components/autofill/core/browser/ui/suggestion_test_helpers.h b/components/autofill/core/browser/ui/suggestion_test_helpers.h
index 292ed2f9..0949122 100644
--- a/components/autofill/core/browser/ui/suggestion_test_helpers.h
+++ b/components/autofill/core/browser/ui/suggestion_test_helpers.h
@@ -13,8 +13,8 @@
 
 template <class... Matchers>
 inline auto SuggestionVectorIdsAre(const Matchers&... matchers) {
-  return ::testing::ElementsAre(
-      ::testing::Field("frontend_id", &Suggestion::frontend_id, matchers)...);
+  return ::testing::ElementsAre(::testing::Field(
+      "popup_item_id", &Suggestion::popup_item_id, matchers)...);
 }
 
 template <class... Matchers>
diff --git a/components/autofill/core/common/form_field_data.h b/components/autofill/core/common/form_field_data.h
index 7311c5b..d74fa929 100644
--- a/components/autofill/core/common/form_field_data.h
+++ b/components/autofill/core/common/form_field_data.h
@@ -217,6 +217,8 @@
   bool IsPasswordInputElement() const;
 
   // Returns true for `form_control_type` select-one or selectmenu.
+  //
+  // <select> and <selectmenu>s should be treated identically by autofill.
   bool IsSelectOrSelectMenuElement() const;
 
   // Returns true if the field is focusable to the user.
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm
index a6e2aa9..385c43b 100644
--- a/components/autofill/ios/browser/autofill_agent.mm
+++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -473,7 +473,7 @@
       autofill::Suggestion autofill_suggestion;
       autofill_suggestion.main_text.value =
           SysNSStringToUTF16(suggestion.value);
-      autofill_suggestion.frontend_id = suggestion.popupItemId;
+      autofill_suggestion.popup_item_id = suggestion.popupItemId;
       if (!suggestion.backendIdentifier.length) {
         autofill_suggestion.payload = autofill::Suggestion::BackendId();
       } else {
@@ -532,7 +532,8 @@
       autofillManager->OnUserAcceptedCardsFromAccountOption();
     }
   } else {
-    NOTREACHED() << "unknown identifier " << suggestion.popupItemId;
+    NOTREACHED() << "unknown identifier "
+                 << base::to_underlying(suggestion.popupItemId);
   }
 }
 
@@ -634,11 +635,14 @@
     NSString* value = nil;
     NSString* displayDescription = nil;
     UIImage* icon = nil;
-    if (popup_suggestion.frontend_id ==
+    if (popup_suggestion.popup_item_id ==
             autofill::PopupItemId::kAutocompleteEntry ||
-        popup_suggestion.frontend_id.is_an_address_or_card_popup_item_id()) {
+        popup_suggestion.popup_item_id ==
+            autofill::PopupItemId::kAddressEntry ||
+        popup_suggestion.popup_item_id ==
+            autofill::PopupItemId::kCreditCardEntry) {
       // Filter out any key/value suggestions if the user hasn't typed yet.
-      if (popup_suggestion.frontend_id ==
+      if (popup_suggestion.popup_item_id ==
               autofill::PopupItemId::kAutocompleteEntry &&
           [_typedValue length] == 0) {
         continue;
@@ -670,11 +674,11 @@
                      .ToUIImage();
         }
       }
-    } else if (popup_suggestion.frontend_id ==
+    } else if (popup_suggestion.popup_item_id ==
                autofill::PopupItemId::kClearForm) {
       // Show the "clear form" button.
       value = SysUTF16ToNSString(popup_suggestion.main_text.value);
-    } else if (popup_suggestion.frontend_id ==
+    } else if (popup_suggestion.popup_item_id ==
                autofill::PopupItemId::kShowAccountCards) {
       // Show opt-in for showing cards from account.
       value = SysUTF16ToNSString(popup_suggestion.main_text.value);
@@ -692,8 +696,7 @@
                suggestionWithValue:value
                 displayDescription:displayDescription
                               icon:icon
-                       popupItemId:popup_suggestion.frontend_id
-                                       .as_popup_item_id()
+                       popupItemId:popup_suggestion.popup_item_id
                  backendIdentifier:
                      SysUTF8ToNSString(
                          popup_suggestion
@@ -708,7 +711,7 @@
     }
 
     // Put "clear form" entry at the front of the suggestions.
-    if (popup_suggestion.frontend_id == autofill::PopupItemId::kClearForm) {
+    if (popup_suggestion.popup_item_id == autofill::PopupItemId::kClearForm) {
       [suggestions insertObject:suggestion atIndex:0];
     } else {
       [suggestions addObject:suggestion];
diff --git a/components/autofill/ios/browser/autofill_agent_unittests.mm b/components/autofill/ios/browser/autofill_agent_unittests.mm
index fb43a23..fa64686 100644
--- a/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -277,8 +277,7 @@
   // Initialize suggestion.
   std::vector<autofill::Suggestion> autofillSuggestions = {
       autofill::Suggestion("", "", "visaCC",
-                           autofill::Suggestion::FrontendId(
-                               autofill::PopupItemId::kCreditCardEntry)),
+                           autofill::PopupItemId::kCreditCardEntry),
       // This suggestion has a valid credit card icon, but the Suggestion type
       // (kShowAccountCards) is wrong.
       autofill::Suggestion("", "", "visaCC",
@@ -329,10 +328,8 @@
       .WillRepeatedly(testing::Return(PopupType::kCreditCards));
 
   const std::string emptyIcon = "";
-  std::vector<autofill::Suggestion> autofillSuggestions = {
-      autofill::Suggestion("", "", emptyIcon,
-                           autofill::Suggestion::FrontendId(
-                               autofill::PopupItemId::kCreditCardEntry))};
+  std::vector<autofill::Suggestion> autofillSuggestions = {autofill::Suggestion(
+      "", "", emptyIcon, autofill::PopupItemId::kCreditCardEntry)};
 
   // Completion handler to retrieve suggestions.
   auto completionHandler = ^(NSArray<FormSuggestion*>* suggestions,
@@ -375,8 +372,7 @@
   // Initialize suggestion, initially without a custom icon.
   std::vector<autofill::Suggestion> autofillSuggestions = {
       autofill::Suggestion("", "", suggestion_network_icon,
-                           autofill::Suggestion::FrontendId(
-                               autofill::PopupItemId::kCreditCardEntry))};
+                           autofill::PopupItemId::kCreditCardEntry)};
   ASSERT_TRUE(autofillSuggestions[0].custom_icon.IsEmpty());
 
   // When the custom icon is not present, the default icon should be used.
@@ -408,12 +404,10 @@
 
   // Make the suggestions available to AutofillAgent.
   std::vector<autofill::Suggestion> autofillSuggestions;
-  autofillSuggestions.push_back(autofill::Suggestion(
-      "", "", "",
-      autofill::Suggestion::FrontendId(autofill::PopupItemId::kAddressEntry)));
-  autofillSuggestions.push_back(autofill::Suggestion(
-      "", "", "",
-      autofill::Suggestion::FrontendId(autofill::PopupItemId::kAddressEntry)));
+  autofillSuggestions.push_back(
+      autofill::Suggestion("", "", "", autofill::PopupItemId::kAddressEntry));
+  autofillSuggestions.push_back(
+      autofill::Suggestion("", "", "", autofill::PopupItemId::kAddressEntry));
   autofillSuggestions.push_back(
       autofill::Suggestion("", "", "", PopupItemId::kClearForm));
   [autofill_agent_
@@ -464,14 +458,10 @@
 
   // Make the suggestions available to AutofillAgent.
   std::vector<autofill::Suggestion> autofillSuggestions;
-  autofillSuggestions.push_back(
-      autofill::Suggestion("", "", "",
-                           autofill::Suggestion::FrontendId(
-                               autofill::PopupItemId::kCreditCardEntry)));
-  autofillSuggestions.push_back(
-      autofill::Suggestion("", "", "",
-                           autofill::Suggestion::FrontendId(
-                               autofill::PopupItemId::kCreditCardEntry)));
+  autofillSuggestions.push_back(autofill::Suggestion(
+      "", "", "", autofill::PopupItemId::kCreditCardEntry));
+  autofillSuggestions.push_back(autofill::Suggestion(
+      "", "", "", autofill::PopupItemId::kCreditCardEntry));
   autofillSuggestions.push_back(
       autofill::Suggestion("", "", "", PopupItemId::kClearForm));
   [autofill_agent_
diff --git a/components/components_chromium_strings.grd b/components/components_chromium_strings.grd
index fcd4388..73c4c6a5 100644
--- a/components/components_chromium_strings.grd
+++ b/components/components_chromium_strings.grd
@@ -296,9 +296,18 @@
       </if>
 
       <!-- Page Info -->
-      <message name="IDS_PAGE_INFO_INTERNAL_PAGE" desc="Message to display in the page info bubble when the page you are on is a chrome:// page or about:something.">
-        You're viewing a secure Chromium page
-      </message>
+      <if expr="_is_chrome_for_testing_branded">
+        <then>
+          <message name="IDS_PAGE_INFO_INTERNAL_PAGE" desc="Message to display in the page info bubble when the page you are on is a chrome:// page or about:something.">
+            You're viewing a secure Chrome for Testing page
+          </message>
+        </then>
+        <else>
+          <message name="IDS_PAGE_INFO_INTERNAL_PAGE" desc="Message to display in the page info bubble when the page you are on is a chrome:// page or about:something.">
+            You're viewing a secure Chromium page
+          </message>
+        </else>
+      </if>
 
       <!-- Session Crash -->
       <message name="IDS_SESSION_CRASHED_VIEW_MESSAGE" desc="Message shown when the last session didn't exit cleanly.">
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
index 5d060ea5..0c452dc 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/CronetTestRule.java
@@ -34,7 +34,6 @@
 import java.lang.annotation.Target;
 import java.net.URL;
 import java.net.URLStreamHandlerFactory;
-import java.util.function.Function;
 
 /**
  * Custom TestRule for Cronet instrumentation tests.
@@ -534,26 +533,30 @@
         AUTOMATIC,
     }
 
+    // This is a replacement for java.util.function.Function as Function is only available
+    // starting android API level 24.
+    private interface EngineBuilderSupplier {
+        ExperimentalCronetEngine.Builder getCronetEngineBuilder(Context context);
+    }
+
     public enum CronetImplementation {
-        STATICALLY_LINKED(
-                (context)
-                        -> (ExperimentalCronetEngine.Builder) new NativeCronetProvider(context)
-                                   .createBuilder()),
+        STATICALLY_LINKED(context
+                -> (ExperimentalCronetEngine.Builder) new NativeCronetProvider(context)
+                           .createBuilder()),
         FALLBACK((context)
                          -> (ExperimentalCronetEngine.Builder) new JavaCronetProvider(context)
                                     .createBuilder()),
         AOSP_PLATFORM(
                 (context) -> { throw new UnsupportedOperationException("Not implemented yet"); });
 
-        private final Function<Context, ExperimentalCronetEngine.Builder> mBuilderSupplier;
+        private final EngineBuilderSupplier mEngineSupplier;
 
-        private CronetImplementation(
-                Function<Context, ExperimentalCronetEngine.Builder> builderSupplier) {
-            this.mBuilderSupplier = builderSupplier;
+        private CronetImplementation(EngineBuilderSupplier engineSupplier) {
+            this.mEngineSupplier = engineSupplier;
         }
 
         ExperimentalCronetEngine.Builder createBuilder(Context context) {
-            return mBuilderSupplier.apply(context);
+            return mEngineSupplier.getCronetEngineBuilder(context);
         }
 
         private void verifyCronetEngineInstance(CronetEngine engine) {
diff --git a/components/guest_view/browser/guest_view_manager.cc b/components/guest_view/browser/guest_view_manager.cc
index 7739b5a..2847d7db7 100644
--- a/components/guest_view/browser/guest_view_manager.cc
+++ b/components/guest_view/browser/guest_view_manager.cc
@@ -353,7 +353,20 @@
 
 void GuestViewManager::EmbedderProcessDestroyed(int embedder_process_id) {
   embedders_observed_.erase(embedder_process_id);
+
+  // We can't just call std::multimap::erase here because destroying a guest
+  // could trigger the destruction of another guest which is also owned by
+  // `owned_guests_`. Recursively calling std::multimap::erase is unsafe (see
+  // https://crbug.com/1450397). So we take ownership of all of the guests that
+  // will be destroyed before erasing the entries from the map.
+  std::vector<std::unique_ptr<GuestViewBase>> guests_to_destroy;
+  const auto destroy_range = owned_guests_.equal_range(embedder_process_id);
+  for (auto it = destroy_range.first; it != destroy_range.second; ++it) {
+    guests_to_destroy.push_back(std::move(it->second));
+  }
   owned_guests_.erase(embedder_process_id);
+  guests_to_destroy.clear();
+
   CallViewDestructionCallbacks(embedder_process_id);
 }
 
diff --git a/components/guest_view/browser/test_guest_view_manager.cc b/components/guest_view/browser/test_guest_view_manager.cc
index 0a5acac..fac07a1 100644
--- a/components/guest_view/browser/test_guest_view_manager.cc
+++ b/components/guest_view/browser/test_guest_view_manager.cc
@@ -122,14 +122,15 @@
 }
 
 void TestGuestViewManager::WaitForNumGuestsCreated(size_t count) {
-  if (count == num_guests_created_)
+  if (count == num_guests_created_) {
     return;
+  }
 
-  waiting_for_guests_created_ = true;
   expected_num_guests_created_ = count;
 
   num_created_run_loop_ = std::make_unique<base::RunLoop>();
   num_created_run_loop_->Run();
+  num_created_run_loop_ = nullptr;
 }
 
 void TestGuestViewManager::WaitUntilAttached(GuestViewBase* guest_view) {
@@ -173,13 +174,11 @@
     created_run_loop_->Quit();
 
   ++num_guests_created_;
-  if (!waiting_for_guests_created_ &&
-      num_guests_created_ != expected_num_guests_created_) {
-    return;
-  }
 
-  if (num_created_run_loop_)
+  if (num_created_run_loop_ &&
+      num_guests_created_ == expected_num_guests_created_) {
     num_created_run_loop_->Quit();
+  }
 }
 
 void TestGuestViewManager::AttachGuest(int embedder_process_id,
diff --git a/components/guest_view/browser/test_guest_view_manager.h b/components/guest_view/browser/test_guest_view_manager.h
index 2bbc2b99..876ed221 100644
--- a/components/guest_view/browser/test_guest_view_manager.h
+++ b/components/guest_view/browser/test_guest_view_manager.h
@@ -119,7 +119,6 @@
   size_t num_guests_created_ = 0;
   size_t expected_num_guests_created_ = 0;
   int num_views_garbage_collected_ = 0;
-  bool waiting_for_guests_created_ = false;
 
   // Tracks the life time of the GuestView's main FrameTreeNode. The main FTN
   // has the same lifesspan as the GuestView.
diff --git a/components/live_caption/caption_bubble_controller.h b/components/live_caption/caption_bubble_controller.h
index 32d1fc1..05d683b 100644
--- a/components/live_caption/caption_bubble_controller.h
+++ b/components/live_caption/caption_bubble_controller.h
@@ -69,6 +69,10 @@
   virtual bool IsGenericErrorMessageVisibleForTesting() = 0;
   virtual std::string GetBubbleLabelTextForTesting() = 0;
   virtual void CloseActiveModelForTesting() = 0;
+
+  virtual void OnLanguageIdentificationEvent(
+      CaptionBubbleContext* caption_bubble_context,
+      const media::mojom::LanguageIdentificationEventPtr& event) = 0;
 };
 
 }  // namespace captions
diff --git a/components/live_caption/live_caption_controller.cc b/components/live_caption/live_caption_controller.cc
index 5bca6b4..39ddc8c 100644
--- a/components/live_caption/live_caption_controller.cc
+++ b/components/live_caption/live_caption_controller.cc
@@ -250,8 +250,13 @@
 }
 
 void LiveCaptionController::OnLanguageIdentificationEvent(
+    CaptionBubbleContext* caption_bubble_context,
     const media::mojom::LanguageIdentificationEventPtr& event) {
   // TODO(crbug.com/1175357): Implement the UI for language identification.
+  if (caption_bubble_controller_) {
+    return caption_bubble_controller_->OnLanguageIdentificationEvent(
+        caption_bubble_context, event);
+  }
 }
 
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
diff --git a/components/live_caption/live_caption_controller.h b/components/live_caption/live_caption_controller.h
index fcec207..81b9b2c 100644
--- a/components/live_caption/live_caption_controller.h
+++ b/components/live_caption/live_caption_controller.h
@@ -62,6 +62,7 @@
                              const media::SpeechRecognitionResult& result);
 
   void OnLanguageIdentificationEvent(
+      CaptionBubbleContext* caption_bubble_context,
       const media::mojom::LanguageIdentificationEventPtr& event);
 
   // Alerts the CaptionBubbleController that there is an error in the speech
diff --git a/components/live_caption/live_caption_ui_remote_driver.cc b/components/live_caption/live_caption_ui_remote_driver.cc
index 1ad57df..f0b4df1 100644
--- a/components/live_caption/live_caption_ui_remote_driver.cc
+++ b/components/live_caption/live_caption_ui_remote_driver.cc
@@ -36,7 +36,7 @@
 
 void LiveCaptionUiRemoteDriver::OnLanguageIdentificationEvent(
     media::mojom::LanguageIdentificationEventPtr event) {
-  controller_->OnLanguageIdentificationEvent(std::move(event));
+  controller_->OnLanguageIdentificationEvent(&context_, std::move(event));
 }
 
 void LiveCaptionUiRemoteDriver::OnSpeechRecognitionError() {
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index 1bd0da79..c805815 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -435,7 +435,14 @@
       prefs::kLiveTranslateEnabled,
       base::BindRepeating(&CaptionBubble::OnLiveTranslateEnabledChanged,
                           base::Unretained(this)));
-
+  pref_change_registrar_->Add(
+      prefs::kLiveCaptionLanguageCode,
+      base::BindRepeating(&CaptionBubble::OnLiveCaptionLanguageChanged,
+                          base::Unretained(this)));
+  pref_change_registrar_->Add(
+      prefs::kLiveTranslateTargetLanguageCode,
+      base::BindRepeating(&CaptionBubble::OnLiveTranslateTargetLanguageChanged,
+                          base::Unretained(this)));
   inactivity_timer_ = std::make_unique<base::RetainingOneShotTimer>(
       FROM_HERE, base::Seconds(kNoActivityIntervalSeconds),
       base::BindRepeating(&CaptionBubble::OnInactivityTimeout,
@@ -475,10 +482,6 @@
       views::BoxLayout::Orientation::kHorizontal));
 
   views::View* right_header_container = new views::View();
-  right_header_container
-      ->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal))
-      ->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);
   views::View* left_header_container = new views::View();
 
   views::View* content_container = new views::View();
@@ -643,34 +646,51 @@
       content_container->AddChildView(std::move(collapse_button));
 
   if (base::FeatureList::IsEnabled(media::kLiveTranslate)) {
-    auto live_translate_label = std::make_unique<views::StyledLabel>();
-    live_translate_label->SetVisible(
-        profile_prefs_->GetBoolean(prefs::kLiveTranslateEnabled));
-    live_translate_label->SetDisplayedOnBackgroundColor(SK_ColorTRANSPARENT);
-    live_translate_label->SetHorizontalAlignment(
+    auto language_label = std::make_unique<views::StyledLabel>();
+    language_label->SetDisplayedOnBackgroundColor(SK_ColorTRANSPARENT);
+    language_label->SetHorizontalAlignment(
         gfx::HorizontalAlignment::ALIGN_LEFT);
-    live_translate_label->GetViewAccessibility().OverrideIsIgnored(true);
+    language_label->GetViewAccessibility().OverrideIsIgnored(true);
 
-    source_language_ = speech::GetLanguageDisplayName(
-        profile_prefs_->GetString(prefs::kLiveCaptionLanguageCode),
-        application_locale_);
-    target_language_ = speech::GetLanguageDisplayName(
-        profile_prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode),
-        application_locale_);
-    std::u16string label_text = l10n_util::GetStringFUTF16(
-        IDS_LIVE_CAPTION_TRANSLATED_CAPTIONS, source_language_,
-        target_language_, &live_translate_label_offsets_);
-    live_translate_label->SetText(label_text);
-    live_translate_label_ =
-        left_header_container->AddChildView(std::move(live_translate_label));
+    source_language_code_ =
+        profile_prefs_->GetString(prefs::kLiveCaptionLanguageCode);
+    source_language_text_ = speech::GetLanguageDisplayName(
+        source_language_code_, application_locale_);
+    target_language_code_ =
+        profile_prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode);
+    target_language_text_ = speech::GetLanguageDisplayName(
+        target_language_code_, application_locale_);
+    language_label_ =
+        left_header_container->AddChildView(std::move(language_label));
+    UpdateLanguageLabelText();
+
+    auto caption_settings_button = BuildImageButton(
+        base::BindRepeating(&CaptionBubble::CaptionSettingsButtonPressed,
+                            base::Unretained(this)),
+        IDS_LIVE_CAPTION_BUBBLE_CAPTION_SETTINGS);
+    caption_settings_button_ =
+        left_header_container->AddChildView(std::move(caption_settings_button));
   }
 
-  auto caption_settings_button = BuildImageButton(
-      base::BindRepeating(&CaptionBubble::CaptionSettingsButtonPressed,
-                          base::Unretained(this)),
-      IDS_LIVE_CAPTION_BUBBLE_CAPTION_SETTINGS);
-  caption_settings_button_ =
-      left_header_container->AddChildView(std::move(caption_settings_button));
+  std::unique_ptr<views::BoxLayout> right_header_container_layout =
+      std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kHorizontal);
+  right_header_container_layout->set_main_axis_alignment(
+      views::BoxLayout::MainAxisAlignment::kEnd);
+  right_header_container_layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kCenter);
+  right_header_container->SetLayoutManager(
+      std::move(right_header_container_layout));
+  std::unique_ptr<views::BoxLayout> left_header_container_layout =
+      std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kHorizontal,
+          gfx::Insets::TLBR(
+              0, close_button_->GetBorder()->GetInsets().width() / 2, 0, 0));
+  left_header_container_layout->set_cross_axis_alignment(
+      views::BoxLayout::CrossAxisAlignment::kCenter);
+  left_header_container->SetLayoutManager(
+      std::move(left_header_container_layout));
+
   left_header_container_ =
       header_container->AddChildView(std::move(left_header_container));
   header_container->AddChildView(std::move(right_header_container));
@@ -728,8 +748,30 @@
 }
 
 void CaptionBubble::OnLiveTranslateEnabledChanged() {
-  live_translate_label_->SetVisible(
-      profile_prefs_->GetBoolean(prefs::kLiveTranslateEnabled));
+  UpdateLanguageLabelText();
+  SetTextColor();
+  Redraw();
+}
+
+void CaptionBubble::OnLiveCaptionLanguageChanged() {
+  source_language_code_ =
+      profile_prefs_->GetString(prefs::kLiveCaptionLanguageCode);
+  source_language_text_ = speech::GetLanguageDisplayName(source_language_code_,
+                                                         application_locale_);
+
+  UpdateLanguageLabelText();
+  SetTextColor();
+  Redraw();
+}
+
+void CaptionBubble::OnLiveTranslateTargetLanguageChanged() {
+  target_language_code_ =
+      profile_prefs_->GetString(prefs::kLiveTranslateTargetLanguageCode);
+  target_language_text_ = speech::GetLanguageDisplayName(target_language_code_,
+                                                         application_locale_);
+
+  UpdateLanguageLabelText();
+  SetTextColor();
   Redraw();
 }
 
@@ -826,6 +868,28 @@
     ResetInactivityTimer();
 }
 
+void CaptionBubble::OnAutoDetectedLanguageChanged() {
+  if (!base::FeatureList::IsEnabled(media::kLiveTranslate)) {
+    return;
+  }
+
+  source_language_code_ = model_->GetAutoDetectedLanguageCode();
+  source_language_text_ = speech::GetLanguageDisplayName(source_language_code_,
+                                                         application_locale_);
+
+  if (l10n_util::GetLanguage(
+          profile_prefs_->GetString(prefs::kLiveCaptionLanguageCode)) !=
+      l10n_util::GetLanguage(source_language_code_)) {
+    // Append `(auto-detected)` to the end of the source language text.
+    source_language_text_ = l10n_util::GetStringFUTF16(
+        IDS_LIVE_CAPTION_AUTODETECTED_SOURCE_LANGUAGE, source_language_text_);
+  }
+
+  UpdateLanguageLabelText();
+  SetTextColor();
+  Redraw();
+}
+
 bool CaptionBubble::ThemeColorsChanged() {
   const auto* const color_provider = GetColorProvider();
   SkColor text_color =
@@ -989,8 +1053,8 @@
   label_->SetMaximumWidth(kMaxWidthDip * textScaleFactor - kSidePaddingDip * 2);
   title_->SetLineHeight(kLineHeightDip * textScaleFactor);
   if (base::FeatureList::IsEnabled(media::kLiveTranslate)) {
-    live_translate_label_->SetLineHeight(kLiveTranslateLabelLineHeightDip *
-                                         textScaleFactor);
+    language_label_->SetLineHeight(kLiveTranslateLabelLineHeightDip *
+                                   textScaleFactor);
   }
   generic_error_text_->SetLineHeight(kLineHeightDip * textScaleFactor);
   generic_error_icon_->SetImageSize(
@@ -1029,16 +1093,16 @@
     const gfx::FontList live_translate_font_list =
         GetFontList(kLiveTranslateLabelFontSizePx);
 
-    views::StyledLabel::RangeStyleInfo live_translate_label_style;
-    live_translate_label_style.custom_font = live_translate_font_list;
-    live_translate_label_style.override_color = color_provider->GetColor(
+    views::StyledLabel::RangeStyleInfo language_label_style;
+    language_label_style.custom_font = live_translate_font_list;
+    language_label_style.override_color = color_provider->GetColor(
         ui::kColorLiveCaptionBubbleForegroundSecondary);
 
     views::StyledLabel::RangeStyleInfo live_translate_language_style;
     live_translate_language_style.custom_font = live_translate_font_list;
     live_translate_language_style.override_color = text_color;
 
-    UpdateLiveTranslateLabelStyle(live_translate_label_style,
+    UpdateLiveTranslateLabelStyle(language_label_style,
                                   live_translate_language_style);
   }
 #if BUILDFLAG(IS_WIN)
@@ -1118,20 +1182,42 @@
 void CaptionBubble::UpdateLiveTranslateLabelStyle(
     views::StyledLabel::RangeStyleInfo label_style,
     views::StyledLabel::RangeStyleInfo languages_style) {
-  live_translate_label_->AddStyleRange(
-      gfx::Range(0, live_translate_label_offsets_[0]), label_style);
-  live_translate_label_->AddStyleRange(
-      gfx::Range(live_translate_label_offsets_[0],
-                 live_translate_label_offsets_[0] + source_language_.length()),
+  // Update the style of the label and source language.
+  language_label_->AddStyleRange(gfx::Range(0, language_label_offsets_[0]),
+                                 label_style);
+  language_label_->AddStyleRange(
+      gfx::Range(language_label_offsets_[0],
+                 language_label_offsets_[0] + source_language_text_.length()),
       languages_style);
-  live_translate_label_->AddStyleRange(
-      gfx::Range(live_translate_label_offsets_[0] + source_language_.length(),
-                 live_translate_label_offsets_[1]),
-      label_style);
-  live_translate_label_->AddStyleRange(
-      gfx::Range(live_translate_label_offsets_[1],
-                 live_translate_label_offsets_[1] + target_language_.length()),
-      languages_style);
+
+  // Update the style of the target language if applicable.
+  if (language_label_offsets_.size() > 1) {
+    language_label_->AddStyleRange(
+        gfx::Range(language_label_offsets_[0] + source_language_text_.length(),
+                   language_label_offsets_[1]),
+        label_style);
+    language_label_->AddStyleRange(
+        gfx::Range(language_label_offsets_[1],
+                   language_label_offsets_[1] + target_language_text_.length()),
+        languages_style);
+  }
+}
+
+void CaptionBubble::UpdateLanguageLabelText() {
+  language_label_offsets_.clear();
+
+  if (profile_prefs_->GetBoolean(prefs::kLiveTranslateEnabled) &&
+      l10n_util::GetLanguage(source_language_code_) !=
+          l10n_util::GetLanguage(target_language_code_)) {
+    language_label_->SetText(l10n_util::GetStringFUTF16(
+        IDS_LIVE_CAPTION_TRANSLATED_CAPTION_LANGUAGE, source_language_text_,
+        target_language_text_, &language_label_offsets_));
+  } else {
+    size_t offset;
+    language_label_->SetText(l10n_util::GetStringFUTF16(
+        IDS_LIVE_CAPTION_CAPTION_LANGUAGE, source_language_text_, &offset));
+    language_label_offsets_.push_back(offset);
+  }
 }
 
 void CaptionBubble::RepositionInContextRect(CaptionBubbleModel::Id model_id,
@@ -1175,16 +1261,15 @@
   auto button_size = close_button_->GetPreferredSize();
   left_header_container_->SetPreferredSize(
       gfx::Size(width - 3 * button_size.width(), button_size.height()));
-  int left_header_padding =
-      base::FeatureList::IsEnabled(media::kLiveTranslate) &&
-              live_translate_label_->GetVisible()
-          ? caption_settings_button_->GetBorder()->GetInsets().width() / 2
-          : 0;
-  left_header_container_
-      ->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::Orientation::kHorizontal,
-          gfx::Insets::TLBR(0, left_header_padding, 0, 0), 0))
-      ->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
+
+  if (base::FeatureList::IsEnabled(media::kLiveTranslate)) {
+    // Set the maximum width of the live translate label to ensure that the
+    // caption settings button doesn't get crowded out.
+    language_label_->SizeToFit(
+        left_header_container_->GetPreferredSize().width() -
+        button_size.width());
+  }
+
 #if BUILDFLAG(IS_WIN)
   // The Media Foundation renderer error message should not scale with the
   // user's caption style preference.
@@ -1289,8 +1374,8 @@
   return static_cast<views::Label*>(label_);
 }
 
-views::StyledLabel* CaptionBubble::GetLiveTranslateLabelForTesting() {
-  return static_cast<views::StyledLabel*>(live_translate_label_);
+views::StyledLabel* CaptionBubble::GetLanguageLabelForTesting() {
+  return static_cast<views::StyledLabel*>(language_label_);
 }
 
 bool CaptionBubble::IsGenericErrorMessageVisibleForTesting() const {
diff --git a/components/live_caption/views/caption_bubble.h b/components/live_caption/views/caption_bubble.h
index 14f4dd3..65d072a 100644
--- a/components/live_caption/views/caption_bubble.h
+++ b/components/live_caption/views/caption_bubble.h
@@ -96,7 +96,7 @@
   bool HasActivity();
 
   views::Label* GetLabelForTesting();
-  views::StyledLabel* GetLiveTranslateLabelForTesting();
+  views::StyledLabel* GetLanguageLabelForTesting();
   bool IsGenericErrorMessageVisibleForTesting() const;
   base::RetainingOneShotTimer* GetInactivityTimerForTesting();
   void set_tick_clock_for_testing(const base::TickClock* tick_clock) {
@@ -125,6 +125,8 @@
                              const gfx::Rect& new_bounds) override;
   void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
   void OnLiveTranslateEnabledChanged();
+  void OnLiveCaptionLanguageChanged();
+  void OnLiveTranslateTargetLanguageChanged();
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   std::u16string GetAccessibleWindowTitle() const override;
   void OnThemeChanged() override;
@@ -146,6 +148,10 @@
   // has changed. Sets the text of the caption bubble to the model's text.
   void OnTextChanged();
 
+  // Called by CaptionBubbleModel to notify this object that the model's
+  // auto-detected language has changed.
+  void OnAutoDetectedLanguageChanged();
+
   // Used to prevent propagating theme changes when no theme colors have
   // changed. Returns whether the caption theme colors have changed since the
   // last time this function was called.
@@ -188,6 +194,7 @@
   void UpdateLiveTranslateLabelStyle(
       views::StyledLabel::RangeStyleInfo label_style,
       views::StyledLabel::RangeStyleInfo languages_style);
+  void UpdateLanguageLabelText();
 
   // Places the bubble at the bottom center of the context widget for the active
   // model, ensuring that it's positioned where the user will spot it. If there
@@ -219,12 +226,14 @@
   raw_ptr<CaptionBubbleLabel> label_;
   raw_ptr<views::Label> title_;
   raw_ptr<views::Label> generic_error_text_;
-  raw_ptr<views::StyledLabel> live_translate_label_;
+  raw_ptr<views::StyledLabel> language_label_;
   raw_ptr<views::View> header_container_;
   raw_ptr<views::View> left_header_container_;
-  std::u16string source_language_;
-  std::u16string target_language_;
-  std::vector<size_t> live_translate_label_offsets_;
+  std::string source_language_code_;
+  std::string target_language_code_;
+  std::u16string source_language_text_;
+  std::u16string target_language_text_;
+  std::vector<size_t> language_label_offsets_;
   raw_ptr<views::ImageView> generic_error_icon_;
   raw_ptr<views::View> generic_error_message_;
   raw_ptr<views::ImageButton> back_to_tab_button_;
@@ -236,6 +245,10 @@
   raw_ptr<views::ImageButton> caption_settings_button_;
   raw_ptr<CaptionBubbleFrameView> frame_;
 
+  // Flag indicating whether the current source language does not match the user
+  // preference source language.
+  bool auto_detected_language_switched_ = false;
+
 #if BUILDFLAG(IS_WIN)
   raw_ptr<views::StyledLabel> media_foundation_renderer_error_text_;
   raw_ptr<views::ImageView> media_foundation_renderer_error_icon_;
diff --git a/components/live_caption/views/caption_bubble_controller_views.cc b/components/live_caption/views/caption_bubble_controller_views.cc
index 16621f7f..2030aa69 100644
--- a/components/live_caption/views/caption_bubble_controller_views.cc
+++ b/components/live_caption/views/caption_bubble_controller_views.cc
@@ -206,4 +206,21 @@
   return caption_bubble_;
 }
 
+void CaptionBubbleControllerViews::OnLanguageIdentificationEvent(
+    CaptionBubbleContext* caption_bubble_context,
+    const media::mojom::LanguageIdentificationEventPtr& event) {
+  if (!caption_bubble_) {
+    return;
+  }
+  SetActiveModel(caption_bubble_context);
+  if (active_model_->IsClosed()) {
+    return;
+  }
+
+  if (event->asr_switch_result ==
+      media::mojom::AsrSwitchResult::kSwitchSucceeded) {
+    active_model_->SetLanguage(event->language);
+  }
+}
+
 }  // namespace captions
diff --git a/components/live_caption/views/caption_bubble_controller_views.h b/components/live_caption/views/caption_bubble_controller_views.h
index 29a27085..a32f9c7 100644
--- a/components/live_caption/views/caption_bubble_controller_views.h
+++ b/components/live_caption/views/caption_bubble_controller_views.h
@@ -64,6 +64,9 @@
   bool IsWidgetVisibleForTesting() override;
   bool IsGenericErrorMessageVisibleForTesting() override;
   std::string GetBubbleLabelTextForTesting() override;
+  void OnLanguageIdentificationEvent(
+      CaptionBubbleContext* caption_bubble_context,
+      const media::mojom::LanguageIdentificationEventPtr& event) override;
   void CloseActiveModelForTesting() override;
   views::Widget* GetCaptionWidgetForTesting();
   CaptionBubble* GetCaptionBubbleForTesting();
diff --git a/components/live_caption/views/caption_bubble_model.cc b/components/live_caption/views/caption_bubble_model.cc
index 7eb28f1c..7b5c599 100644
--- a/components/live_caption/views/caption_bubble_model.cc
+++ b/components/live_caption/views/caption_bubble_model.cc
@@ -52,6 +52,12 @@
     observer_->OnTextChanged();
 }
 
+void CaptionBubbleModel::OnAutoDetectedLanguageChanged() {
+  if (observer_) {
+    observer_->OnAutoDetectedLanguageChanged();
+  }
+}
+
 void CaptionBubbleModel::SetPartialText(const std::string& partial_text) {
   partial_text_ = partial_text;
   OnTextChanged();
@@ -112,6 +118,15 @@
   }
 }
 
+void CaptionBubbleModel::SetLanguage(const std::string& language_code) {
+  if (!observer_) {
+    return;
+  }
+
+  auto_detected_language_code_ = language_code;
+  OnAutoDetectedLanguageChanged();
+}
+
 // static
 CaptionBubbleModel::Id CaptionBubbleModel::GetNextId() {
   static Id::Generator generator;
diff --git a/components/live_caption/views/caption_bubble_model.h b/components/live_caption/views/caption_bubble_model.h
index 0749fbd3..c4fa46b 100644
--- a/components/live_caption/views/caption_bubble_model.h
+++ b/components/live_caption/views/caption_bubble_model.h
@@ -92,8 +92,16 @@
   std::string GetFullText() const { return final_text_ + partial_text_; }
   CaptionBubbleContext* GetContext() { return context_; }
 
+  // Returns the auto-detected language code or an empty string if the language
+  // was not automatically switched.
+  std::string GetAutoDetectedLanguageCode() const {
+    return auto_detected_language_code_;
+  }
+
   Id unique_id() const { return unique_id_; }
 
+  void SetLanguage(const std::string& language_code);
+
  private:
   // Generates the next unique id.
   static Id GetNextId();
@@ -101,11 +109,17 @@
   // Alert the observer that a change has occurred to the model text.
   void OnTextChanged();
 
+  // Alert the observer that the auto-detected language of the model has
+  // changed.
+  void OnAutoDetectedLanguageChanged();
+
   const Id unique_id_;
 
   std::string final_text_;
   std::string partial_text_;
 
+  std::string auto_detected_language_code_ = std::string();
+
   // Whether the bubble has been closed by the user.
   bool is_closed_ = false;
 
diff --git a/components/live_caption_strings.grdp b/components/live_caption_strings.grdp
index f24fa22..afa81372 100644
--- a/components/live_caption_strings.grdp
+++ b/components/live_caption_strings.grdp
@@ -37,9 +37,15 @@
   <message name="IDS_LIVE_CAPTION_BUBBLE_APPEAR_SCREENREADER_ANNOUNCEMENT" desc="Announcement to screen readers on ChromeOS when the Live Caption bubble appears to inform users of how to focus the bubble.">
     Live Caption visible, use window switcher to focus
   </message>
-  <message name="IDS_LIVE_CAPTION_TRANSLATED_CAPTIONS" desc="Label for the Live Caption bubble displaying the source and target translation languages.">
+  <message name="IDS_LIVE_CAPTION_CAPTION_LANGUAGE" desc="Label for the Live Caption bubble displaying the source language.">
+    Captioning <ph name="SOURCE">$1<ex>Spanish</ex></ph>
+  </message>
+  <message name="IDS_LIVE_CAPTION_TRANSLATED_CAPTION_LANGUAGE" desc="Label for the Live Caption bubble displaying the source and target translation languages.">
     Translating <ph name="SOURCE">$1<ex>Spanish</ex></ph> to <ph name="TARGET">$2<ex>English</ex></ph>
   </message>
+  <message name="IDS_LIVE_CAPTION_AUTODETECTED_SOURCE_LANGUAGE" desc="Label for the auto-detected source language displayed when the language of an audio stream differs from the language set by the user preference.">
+    <ph name="SOURCE">$1<ex>Spanish</ex></ph> (auto-detected)
+  </message>
   <message name="IDS_LIVE_CAPTION_BUBBLE_CAPTION_SETTINGS" desc="Tooltip for the caption settings button">
     Caption settings
   </message>
diff --git a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_AUTODETECTED_SOURCE_LANGUAGE.png.sha1 b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_AUTODETECTED_SOURCE_LANGUAGE.png.sha1
new file mode 100644
index 0000000..66276be
--- /dev/null
+++ b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_AUTODETECTED_SOURCE_LANGUAGE.png.sha1
@@ -0,0 +1 @@
+b78fde8dac2537171d701599c4eaef7ccf328c9e
\ No newline at end of file
diff --git a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_CAPTION_LANGUAGE.png.sha1 b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_CAPTION_LANGUAGE.png.sha1
new file mode 100644
index 0000000..66276be
--- /dev/null
+++ b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_CAPTION_LANGUAGE.png.sha1
@@ -0,0 +1 @@
+b78fde8dac2537171d701599c4eaef7ccf328c9e
\ No newline at end of file
diff --git a/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_TRANSLATED_CAPTIONS.png.sha1 b/components/live_caption_strings_grdp/IDS_LIVE_CAPTION_TRANSLATED_CAPTION_LANGUAGE.png.sha1
similarity index 100%
rename from components/live_caption_strings_grdp/IDS_LIVE_CAPTION_TRANSLATED_CAPTIONS.png.sha1
rename to components/live_caption_strings_grdp/IDS_LIVE_CAPTION_TRANSLATED_CAPTION_LANGUAGE.png.sha1
diff --git a/components/page_info/android/BUILD.gn b/components/page_info/android/BUILD.gn
index d57bcfe..2300f84 100644
--- a/components/page_info/android/BUILD.gn
+++ b/components/page_info/android/BUILD.gn
@@ -68,7 +68,6 @@
     "java/src/org/chromium/components/page_info/PageInfoCookiesController.java",
     "java/src/org/chromium/components/page_info/PageInfoCookiesPreference.java",
     "java/src/org/chromium/components/page_info/PageInfoDialog.java",
-    "java/src/org/chromium/components/page_info/PageInfoDiscoverabilityMetrics.java",
     "java/src/org/chromium/components/page_info/PageInfoFeatures.java",
     "java/src/org/chromium/components/page_info/PageInfoHighlight.java",
     "java/src/org/chromium/components/page_info/PageInfoMainController.java",
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDiscoverabilityMetrics.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDiscoverabilityMetrics.java
deleted file mode 100644
index 346d7016..0000000
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoDiscoverabilityMetrics.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.components.page_info;
-
-import android.os.SystemClock;
-
-import androidx.annotation.IntDef;
-
-import org.chromium.base.metrics.RecordHistogram;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A class for controlling the page info discoverability metrics reporting.
- */
-public class PageInfoDiscoverabilityMetrics {
-    /**
-     * UMA statistics for PageInfoDiscoverability. Do not reorder or remove existing fields. All
-     * values here should have corresponding entries in WebsiteSettingsDiscoverabilityAction area of
-     * enums.xml.
-     */
-    @IntDef({DiscoverabilityAction.PERMISSION_ICON_SHOWN, DiscoverabilityAction.PAGE_INFO_OPENED,
-            DiscoverabilityAction.PERMISSIONS_OPENED, DiscoverabilityAction.PERMISSION_CHANGED,
-            DiscoverabilityAction.STORE_ICON_SHOWN,
-            DiscoverabilityAction.PAGE_INFO_OPENED_FROM_STORE_ICON,
-            DiscoverabilityAction.STORE_INFO_OPENED})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface DiscoverabilityAction {
-        int PERMISSION_ICON_SHOWN = 0;
-        int PAGE_INFO_OPENED = 1;
-        int PERMISSIONS_OPENED = 2;
-        int PERMISSION_CHANGED = 3;
-        int STORE_ICON_SHOWN = 4;
-        int PAGE_INFO_OPENED_FROM_STORE_ICON = 5;
-        int STORE_INFO_OPENED = 6;
-        int NUM_ENTRIES = 7;
-    }
-    private Long mPermissionIconShownTime;
-    private Long mPageInfoOpenedTime;
-    private Long mStoreIconShownTime;
-    private Long mPageInfoOpenedFromStoreIconTime;
-
-    public void recordDiscoverabilityAction(@DiscoverabilityAction int action) {
-        RecordHistogram.recordEnumeratedHistogram("WebsiteSettings.Discoverability.Action", action,
-                DiscoverabilityAction.NUM_ENTRIES);
-
-        switch (action) {
-            case DiscoverabilityAction.PERMISSION_ICON_SHOWN:
-                mPermissionIconShownTime = SystemClock.elapsedRealtime();
-                break;
-            case DiscoverabilityAction.PAGE_INFO_OPENED:
-                assert mPermissionIconShownTime != null;
-                mPageInfoOpenedTime = SystemClock.elapsedRealtime();
-                RecordHistogram.recordTimesHistogram("WebsiteSettings.Discoverability.TimeToOpen",
-                        mPageInfoOpenedTime - mPermissionIconShownTime);
-                mPermissionIconShownTime = null;
-                break;
-            case DiscoverabilityAction.PERMISSIONS_OPENED:
-                // A user can open permissions multiple times but we only want to include the first
-                // time after the page info was opened.
-                if (mPageInfoOpenedTime != null) {
-                    RecordHistogram.recordMediumTimesHistogram(
-                            "WebsiteSettings.Discoverability.TimeToClickHighlight",
-                            SystemClock.elapsedRealtime() - mPageInfoOpenedTime);
-                }
-                mPageInfoOpenedTime = null;
-                break;
-            case DiscoverabilityAction.STORE_ICON_SHOWN:
-                mStoreIconShownTime = SystemClock.elapsedRealtime();
-                break;
-            case DiscoverabilityAction.PAGE_INFO_OPENED_FROM_STORE_ICON:
-                assert mStoreIconShownTime != null;
-                mPageInfoOpenedFromStoreIconTime = SystemClock.elapsedRealtime();
-                RecordHistogram.recordTimesHistogram(
-                        "WebsiteSettings.Discoverability.TimeToOpenFromStoreIcon",
-                        mPageInfoOpenedFromStoreIconTime - mStoreIconShownTime);
-                mStoreIconShownTime = null;
-                break;
-            case DiscoverabilityAction.STORE_INFO_OPENED:
-                // A user can open store info multiple times but we only want to include the first
-                // time after the page info was opened.
-                if (mPageInfoOpenedFromStoreIconTime != null) {
-                    RecordHistogram.recordMediumTimesHistogram(
-                            "WebsiteSettings.Discoverability.TimeToClickHighlightStoreInfo",
-                            SystemClock.elapsedRealtime() - mPageInfoOpenedFromStoreIconTime);
-                }
-                mPageInfoOpenedFromStoreIconTime = null;
-                break;
-            default:
-                break;
-        }
-    }
-}
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java
index cd39795..ad964c7 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoPermissionsController.java
@@ -23,7 +23,6 @@
 import org.chromium.components.browsing_data.DeleteBrowsingDataAction;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.embedder_support.util.Origin;
-import org.chromium.components.page_info.PageInfoDiscoverabilityMetrics.DiscoverabilityAction;
 import org.chromium.content_public.browser.BrowserContextHandle;
 
 import java.util.Collection;
@@ -54,8 +53,6 @@
     private int mHighlightedPermission;
     @ColorRes
     private int mHighlightColor;
-    private final PageInfoDiscoverabilityMetrics mDiscoverabilityMetrics =
-            new PageInfoDiscoverabilityMetrics();
 
     public PageInfoPermissionsController(PageInfoMainController mainController,
             PageInfoRowView view, PageInfoControllerDelegate delegate,
@@ -71,10 +68,6 @@
     }
 
     private void launchSubpage() {
-        if (mHighlightedPermission != ContentSettingsType.DEFAULT) {
-            mDiscoverabilityMetrics.recordDiscoverabilityAction(
-                    DiscoverabilityAction.PERMISSIONS_OPENED);
-        }
         mMainController.recordAction(PageInfoAction.PAGE_INFO_PERMISSION_DIALOG_OPENED);
         mMainController.launchSubpage(this);
     }
@@ -227,10 +220,6 @@
 
     @Override
     public void onPermissionChanged() {
-        if (mHighlightedPermission != ContentSettingsType.DEFAULT) {
-            mDiscoverabilityMetrics.recordDiscoverabilityAction(
-                    DiscoverabilityAction.PERMISSION_CHANGED);
-        }
         mMainController.recordAction(PageInfoAction.PAGE_INFO_CHANGED_PERMISSION);
         mDataIsStale = true;
     }
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
index bea2263..d827490 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/observer_list.h"
+#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "components/page_load_metrics/browser/metrics_lifecycle_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_interface.h"
@@ -112,6 +113,7 @@
     metrics = new MetricsWebContentsObserver(web_contents,
                                              std::move(embedder_interface));
     web_contents->SetUserData(UserDataKey(), base::WrapUnique(metrics));
+    metrics->created_ = base::TimeTicks::Now();
   }
   return metrics;
 }
@@ -1260,6 +1262,10 @@
     tracker->OnSharedStorageWorkletHostCreated();
 }
 
+base::TimeTicks MetricsWebContentsObserver::GetCreated() {
+  return created_;
+}
+
 // This contains some bugs. RenderFrameHost::IsActive is not relevant to
 // determine what members we have to search.
 //
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.h b/components/page_load_metrics/browser/metrics_web_contents_observer.h
index 8102b58..75171fc 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.h
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.h
@@ -168,6 +168,9 @@
   // Called when a `SharedStorageWorkletHost` is created for `rfh`.
   void OnSharedStorageWorkletHostCreated(content::RenderFrameHost* rfh);
 
+  // Returns the time this MetricsWebContentsObserver was created.
+  base::TimeTicks GetCreated();
+
  protected:
   // Protected rather than private so that derived test classes can call
   // constructor.
@@ -359,6 +362,8 @@
 
   bool web_contents_will_soon_be_destroyed_ = false;
 
+  base::TimeTicks created_;
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
 
diff --git a/components/password_manager/core/browser/mock_webauthn_credentials_delegate.h b/components/password_manager/core/browser/mock_webauthn_credentials_delegate.h
index cbe256c..4a775f6e 100644
--- a/components/password_manager/core/browser/mock_webauthn_credentials_delegate.h
+++ b/components/password_manager/core/browser/mock_webauthn_credentials_delegate.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "build/build_config.h"
 #include "components/password_manager/core/browser/passkey_credential.h"
 #include "components/password_manager/core/browser/webauthn_credentials_delegate.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -32,6 +33,10 @@
               (),
               (const override));
   MOCK_METHOD(void, RetrievePasskeys, (base::OnceClosure), (override));
+#if BUILDFLAG(IS_ANDROID)
+  MOCK_METHOD(void, ShowAndroidHybridSignIn, (), (override));
+  MOCK_METHOD(bool, IsAndroidHybridAvailable, (), (const override));
+#endif
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index 3e9e887..d0ac5ad 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -135,14 +135,14 @@
       *suggestion.voice_over += suggestion.labels[0][0].value;
     }
     if (from_account_store) {
-      suggestion.frontend_id =
+      suggestion.popup_item_id =
           is_password_field
               ? autofill::PopupItemId::kAccountStoragePasswordEntry
               : autofill::PopupItemId::kAccountStorageUsernameEntry;
     } else {
-      suggestion.frontend_id = is_password_field
-                                   ? autofill::PopupItemId::kPasswordEntry
-                                   : autofill::PopupItemId::kUsernameEntry;
+      suggestion.popup_item_id = is_password_field
+                                     ? autofill::PopupItemId::kPasswordEntry
+                                     : autofill::PopupItemId::kUsernameEntry;
     }
     suggestion.match =
         show_all || base::StartsWith(lower_suggestion, lower_contents,
@@ -203,7 +203,7 @@
     std::vector<autofill::Suggestion>* suggestions) {
   bool has_no_fillable_suggestions = base::ranges::none_of(
       *suggestions,
-      [](autofill::Suggestion::FrontendId id) {
+      [](autofill::PopupItemId id) {
         return id == autofill::PopupItemId::kUsernameEntry ||
                id == autofill::PopupItemId::kPasswordEntry ||
                id == autofill::PopupItemId::kAccountStorageUsernameEntry ||
@@ -211,16 +211,16 @@
                id == autofill::PopupItemId::kGeneratePasswordEntry ||
                id == autofill::PopupItemId::kWebauthnCredential;
       },
-      &autofill::Suggestion::frontend_id);
+      &autofill::Suggestion::popup_item_id);
   if (has_no_fillable_suggestions)
     return;
 
   bool has_webauthn_credential = base::ranges::any_of(
       *suggestions,
-      [](autofill::Suggestion::FrontendId id) {
-        return id == autofill::PopupItemId::kWebauthnCredential;
+      [](autofill::PopupItemId popup_item_id) {
+        return popup_item_id == autofill::PopupItemId::kWebauthnCredential;
       },
-      &autofill::Suggestion::frontend_id);
+      &autofill::Suggestion::popup_item_id);
 
 #if !BUILDFLAG(IS_ANDROID)
   // Add a separator before the manage option unless there are no suggestions
@@ -235,7 +235,7 @@
       has_webauthn_credential
           ? IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS_AND_PASSKEYS
           : IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS));
-  suggestion.frontend_id = autofill::PopupItemId::kAllSavedPasswordsEntry;
+  suggestion.popup_item_id = autofill::PopupItemId::kAllSavedPasswordsEntry;
   if (base::FeatureList::IsEnabled(
           password_manager::features::kEnablePasswordsAccountStorage)) {
     // The UI code will pick up an icon from the resources based on the string.
@@ -251,7 +251,7 @@
   autofill::Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_USE_DEVICE_PASSKEY));
   suggestion.icon = "device";
-  suggestion.frontend_id =
+  suggestion.popup_item_id =
       autofill::PopupItemId::kWebauthnSignInWithAnotherDevice;
   return suggestion;
 }
@@ -262,7 +262,7 @@
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD));
   // The UI code will pick up an icon from the resources based on the string.
   suggestion.icon = "keyIcon";
-  suggestion.frontend_id = autofill::PopupItemId::kGeneratePasswordEntry;
+  suggestion.popup_item_id = autofill::PopupItemId::kGeneratePasswordEntry;
   return suggestion;
 }
 
@@ -270,7 +270,8 @@
 autofill::Suggestion CreateEntryToOptInToAccountStorageThenFill() {
   autofill::Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORE));
-  suggestion.frontend_id = autofill::PopupItemId::kPasswordAccountStorageOptIn;
+  suggestion.popup_item_id =
+      autofill::PopupItemId::kPasswordAccountStorageOptIn;
   suggestion.icon = "google";
   return suggestion;
 }
@@ -279,7 +280,7 @@
 autofill::Suggestion CreateEntryToOptInToAccountStorageThenGenerate() {
   autofill::Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_GENERATE_PASSWORD));
-  suggestion.frontend_id =
+  suggestion.popup_item_id =
       autofill::PopupItemId::kPasswordAccountStorageOptInAndGenerate;
   suggestion.icon = "keyIcon";
   return suggestion;
@@ -289,7 +290,7 @@
 autofill::Suggestion CreateEntryToReSignin() {
   autofill::Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_RE_SIGNIN_ACCOUNT_STORE));
-  suggestion.frontend_id =
+  suggestion.popup_item_id =
       autofill::PopupItemId::kPasswordAccountStorageReSignin;
   suggestion.icon = "google";
   return suggestion;
@@ -299,7 +300,8 @@
 autofill::Suggestion CreateAccountStorageEmptyEntry() {
   autofill::Suggestion suggestion(
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_NO_ACCOUNT_STORE_MATCHES));
-  suggestion.frontend_id = autofill::PopupItemId::kPasswordAccountStorageEmpty;
+  suggestion.popup_item_id =
+      autofill::PopupItemId::kPasswordAccountStorageEmpty;
   suggestion.icon = "empty";
   return suggestion;
 }
@@ -307,21 +309,21 @@
 bool ContainsOtherThanManagePasswords(
     base::span<const autofill::Suggestion> suggestions) {
   return base::ranges::any_of(suggestions, [](const auto& s) {
-    return s.frontend_id != autofill::PopupItemId::kAllSavedPasswordsEntry;
+    return s.popup_item_id != autofill::PopupItemId::kAllSavedPasswordsEntry;
   });
 }
 
 bool AreSuggestionForPasswordField(
     base::span<const autofill::Suggestion> suggestions) {
   return base::ranges::any_of(suggestions, [](const auto& suggestion) {
-    return suggestion.frontend_id == autofill::PopupItemId::kPasswordEntry;
+    return suggestion.popup_item_id == autofill::PopupItemId::kPasswordEntry;
   });
 }
 
 bool HasLoadingSuggestion(base::span<const autofill::Suggestion> suggestions,
                           autofill::PopupItemId item_id) {
   return base::ranges::any_of(suggestions, [&item_id](const auto& suggestion) {
-    return suggestion.frontend_id == item_id && suggestion.is_loading;
+    return suggestion.popup_item_id == item_id && suggestion.is_loading;
   });
 }
 
@@ -338,7 +340,7 @@
   new_suggestions.reserve(suggestions.size());
   base::ranges::copy(suggestions, std::back_inserter(new_suggestions));
   auto unlock_iter = base::ranges::find(new_suggestions, unlock_item,
-                                        &autofill::Suggestion::frontend_id);
+                                        &autofill::Suggestion::popup_item_id);
   unlock_iter->is_loading = is_loading;
   return new_suggestions;
 }
@@ -386,21 +388,25 @@
 void PasswordAutofillManager::DidSelectSuggestion(
     const autofill::Suggestion& suggestion) {
   ClearPreviewedForm();
-
-  const autofill::Suggestion::FrontendId frontend_id = suggestion.frontend_id;
-  if (frontend_id == autofill::PopupItemId::kAllSavedPasswordsEntry ||
-      frontend_id == autofill::PopupItemId::kPasswordAccountStorageEmpty ||
-      frontend_id == autofill::PopupItemId::kGeneratePasswordEntry ||
-      frontend_id == autofill::PopupItemId::kPasswordAccountStorageOptIn ||
-      frontend_id == autofill::PopupItemId::kPasswordAccountStorageReSignin ||
-      frontend_id ==
+  if (suggestion.popup_item_id ==
+          autofill::PopupItemId::kAllSavedPasswordsEntry ||
+      suggestion.popup_item_id ==
+          autofill::PopupItemId::kPasswordAccountStorageEmpty ||
+      suggestion.popup_item_id ==
+          autofill::PopupItemId::kGeneratePasswordEntry ||
+      suggestion.popup_item_id ==
+          autofill::PopupItemId::kPasswordAccountStorageOptIn ||
+      suggestion.popup_item_id ==
+          autofill::PopupItemId::kPasswordAccountStorageReSignin ||
+      suggestion.popup_item_id ==
           autofill::PopupItemId::kPasswordAccountStorageOptInAndGenerate ||
-      frontend_id == autofill::PopupItemId::kWebauthnSignInWithAnotherDevice) {
+      suggestion.popup_item_id ==
+          autofill::PopupItemId::kWebauthnSignInWithAnotherDevice) {
     return;
   }
 
   PreviewSuggestion(GetUsernameFromSuggestion(suggestion.main_text.value),
-                    frontend_id);
+                    suggestion.popup_item_id);
 }
 
 void PasswordAutofillManager::OnUnlockItemAccepted(
@@ -427,7 +433,7 @@
     const autofill::Suggestion& suggestion,
     int position) {
   using metrics_util::PasswordDropdownSelectedOption;
-  switch (suggestion.frontend_id.as_popup_item_id()) {
+  switch (suggestion.popup_item_id) {
     case autofill::PopupItemId::kGeneratePasswordEntry:
       password_client_->GeneratePassword(PasswordGenerationType::kAutomatic);
       metrics_util::LogPasswordDropdownItemSelected(
@@ -458,9 +464,9 @@
       break;
     case autofill::PopupItemId::kPasswordAccountStorageOptIn:
     case autofill::PopupItemId::kPasswordAccountStorageOptInAndGenerate:
-      OnUnlockItemAccepted(suggestion.frontend_id.as_popup_item_id());
+      OnUnlockItemAccepted(suggestion.popup_item_id);
       metrics_util::LogPasswordDropdownItemSelected(
-          suggestion.frontend_id ==
+          suggestion.popup_item_id ==
                   autofill::PopupItemId::kPasswordAccountStorageOptIn
               ? PasswordDropdownSelectedOption::kUnlockAccountStorePasswords
               : PasswordDropdownSelectedOption::kUnlockAccountStoreGeneration,
@@ -502,7 +508,7 @@
                                                       password_client_)) {
         bool success = FillSuggestion(
             GetUsernameFromSuggestion(suggestion.main_text.value),
-            suggestion.frontend_id);
+            suggestion.popup_item_id);
         DCHECK(success);
       } else {
         authenticator_ = std::move(authenticator);
@@ -511,17 +517,18 @@
             device_reauth::DeviceAuthRequester::kAutofillSuggestion,
             base::BindOnce(&PasswordAutofillManager::OnBiometricReauthCompleted,
                            weak_ptr_factory_.GetWeakPtr(),
-                           suggestion.main_text.value, suggestion.frontend_id),
+                           suggestion.main_text.value,
+                           suggestion.popup_item_id),
             /*use_last_valid_auth=*/true);
 #elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
         const std::u16string origin =
             base::UTF8ToUTF16(GetShownOrigin(url::Origin::Create(
                 password_manager_driver_->GetLastCommittedURL())));
 
-        auto on_reath_complete =
-            base::BindOnce(&PasswordAutofillManager::OnBiometricReauthCompleted,
-                           weak_ptr_factory_.GetWeakPtr(),
-                           suggestion.main_text.value, suggestion.frontend_id);
+        auto on_reath_complete = base::BindOnce(
+            &PasswordAutofillManager::OnBiometricReauthCompleted,
+            weak_ptr_factory_.GetWeakPtr(), suggestion.main_text.value,
+            suggestion.popup_item_id);
 
         authenticator_->AuthenticateWithMessage(
             l10n_util::GetStringFUTF16(IDS_PASSWORD_MANAGER_FILLING_REAUTH,
@@ -540,7 +547,7 @@
 
 bool PasswordAutofillManager::GetDeletionConfirmationText(
     const std::u16string& value,
-    autofill::Suggestion::FrontendId frontend_id,
+    autofill::PopupItemId popup_item_id,
     autofill::Suggestion::BackendId backend_id,
     std::u16string* title,
     std::u16string* body) {
@@ -549,7 +556,7 @@
 
 bool PasswordAutofillManager::RemoveSuggestion(
     const std::u16string& value,
-    autofill::Suggestion::FrontendId frontend_id,
+    autofill::PopupItemId popup_item_id,
     autofill::Suggestion::BackendId backend_id) {
   // Password suggestions cannot be deleted this way.
   // See http://crbug.com/329038#c15
@@ -730,7 +737,7 @@
         [this](const auto& passkey) {
           autofill::Suggestion suggestion(ToUsernameString(passkey.username()));
           suggestion.icon = "globeIcon";
-          suggestion.frontend_id = autofill::PopupItemId::kWebauthnCredential;
+          suggestion.popup_item_id = autofill::PopupItemId::kWebauthnCredential;
           suggestion.custom_icon = page_favicon_;
           suggestion.payload = autofill::Suggestion::BackendId(
               base::Base64Encode(passkey.credential_id()));
@@ -787,7 +794,7 @@
   metrics_util::PasswordDropdownState dropdown_state =
       metrics_util::PasswordDropdownState::kStandard;
   for (const auto& suggestion : suggestions) {
-    switch (suggestion.frontend_id.as_popup_item_id()) {
+    switch (suggestion.popup_item_id) {
       case autofill::PopupItemId::kGeneratePasswordEntry:
         // TODO(crbug.com/1062709): Revisit metrics for the "opt in and
         // generate" button.
@@ -836,10 +843,10 @@
 
 bool PasswordAutofillManager::FillSuggestion(
     const std::u16string& username,
-    autofill::Suggestion::FrontendId item_id) {
+    autofill::PopupItemId popup_item_id) {
   autofill::PasswordAndMetadata password_and_meta_data;
   if (fill_data_ &&
-      GetPasswordAndMetadataForUsername(username, item_id, *fill_data_,
+      GetPasswordAndMetadataForUsername(username, popup_item_id, *fill_data_,
                                         &password_and_meta_data)) {
     bool is_android_credential =
         FacetURI::FromPotentiallyInvalidSpec(password_and_meta_data.realm)
@@ -854,8 +861,8 @@
 
 bool PasswordAutofillManager::PreviewSuggestion(
     const std::u16string& username,
-    autofill::Suggestion::FrontendId item_id) {
-  if (item_id == autofill::PopupItemId::kWebauthnCredential) {
+    autofill::PopupItemId popup_item_id) {
+  if (popup_item_id == autofill::PopupItemId::kWebauthnCredential) {
     password_manager_driver_->PreviewSuggestion(username, /*password=*/u"");
     return true;
   }
@@ -865,7 +872,7 @@
   }
   autofill::PasswordAndMetadata password_and_meta_data;
   if (fill_data_ &&
-      GetPasswordAndMetadataForUsername(username, item_id, *fill_data_,
+      GetPasswordAndMetadataForUsername(username, popup_item_id, *fill_data_,
                                         &password_and_meta_data)) {
     password_manager_driver_->PreviewSuggestion(
         username, password_and_meta_data.password_value);
@@ -876,7 +883,7 @@
 
 bool PasswordAutofillManager::GetPasswordAndMetadataForUsername(
     const std::u16string& current_username,
-    autofill::Suggestion::FrontendId item_id,
+    autofill::PopupItemId popup_item_id,
     const autofill::PasswordFormFillData& fill_data,
     autofill::PasswordAndMetadata* password_and_meta_data) {
   // TODO(dubroy): When password access requires some kind of authentication
@@ -884,8 +891,8 @@
   // fetch the actual password. See crbug.com/178358 for more context.
 
   bool item_uses_account_store =
-      item_id == autofill::PopupItemId::kAccountStorageUsernameEntry ||
-      item_id == autofill::PopupItemId::kAccountStoragePasswordEntry;
+      popup_item_id == autofill::PopupItemId::kAccountStorageUsernameEntry ||
+      popup_item_id == autofill::PopupItemId::kAccountStoragePasswordEntry;
 
   // Look for any suitable matches to current field text.
   if (fill_data.preferred_login.username_value == current_username &&
@@ -953,14 +960,15 @@
 
 void PasswordAutofillManager::OnBiometricReauthCompleted(
     const std::u16string& value,
-    autofill::Suggestion::FrontendId frontend_id,
+    autofill::PopupItemId popup_item_id,
     bool auth_succeeded) {
   authenticator_.reset();
   base::UmaHistogramBoolean(
       "PasswordManager.PasswordFilling.AuthenticationResult", auth_succeeded);
   if (!auth_succeeded)
     return;
-  bool success = FillSuggestion(GetUsernameFromSuggestion(value), frontend_id);
+  bool success =
+      FillSuggestion(GetUsernameFromSuggestion(value), popup_item_id);
   DCHECK(success);
 }
 
diff --git a/components/password_manager/core/browser/password_autofill_manager.h b/components/password_manager/core/browser/password_autofill_manager.h
index 6a8242f0..1c78ad7 100644
--- a/components/password_manager/core/browser/password_autofill_manager.h
+++ b/components/password_manager/core/browser/password_autofill_manager.h
@@ -15,6 +15,7 @@
 #include "base/types/strong_alias.h"
 #include "components/autofill/core/browser/autofill_client.h"
 #include "components/autofill/core/browser/ui/autofill_popup_delegate.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
@@ -54,16 +55,17 @@
   void OnPopupShown() override;
   void OnPopupHidden() override;
   void OnPopupSuppressed() override;
+
   void DidSelectSuggestion(const autofill::Suggestion& suggestion) override;
   void DidAcceptSuggestion(const autofill::Suggestion& suggestion,
                            int position) override;
   bool GetDeletionConfirmationText(const std::u16string& value,
-                                   autofill::Suggestion::FrontendId frontend_id,
+                                   autofill::PopupItemId popup_item_id,
                                    autofill::Suggestion::BackendId backend_id,
                                    std::u16string* title,
                                    std::u16string* body) override;
   bool RemoveSuggestion(const std::u16string& value,
-                        autofill::Suggestion::FrontendId frontend_id,
+                        autofill::PopupItemId popup_item_id,
                         autofill::Suggestion::BackendId backend_id) override;
   void ClearPreviewedForm() override;
   autofill::PopupType GetPopupType() const override;
@@ -150,19 +152,19 @@
   void UpdatePopup(const std::vector<autofill::Suggestion>& suggestions);
 
   // Attempts to find and fill the suggestions with the user name |username| and
-  // the |item_id| indicating the store (account-stored or local). Returns true
-  // if it was successful.
+  // the `popup_item_id` indicating the store (account-stored or local). Returns
+  // true if it was successful.
   bool FillSuggestion(const std::u16string& username,
-                      autofill::Suggestion::FrontendId item_id);
+                      autofill::PopupItemId popup_item_id);
 
   // Attempts to find and preview the suggestions with the user name |username|
-  // and the |item_id| indicating the store (account-stored or local). Returns
-  // true if it was successful.
+  // and the `popup_item_id` indicating the store (account-stored or local).
+  // Returns true if it was successful.
   bool PreviewSuggestion(const std::u16string& username,
-                         autofill::Suggestion::FrontendId item_id);
+                         autofill::PopupItemId popup_item_id);
 
   // If one of the login mappings in |fill_data| matches |current_username| and
-  // |item_id| (indicating whether a credential is stored in account or
+  // `popup_item_id` (indicating whether a credential is stored in account or
   // locally), return true and assign the password and the original signon
   // realm to |password_and_meta_data|. Note that if the credential comes from
   // the same realm as the one we're filling to, the |realm| field will be left
@@ -170,7 +172,7 @@
   // Otherwise, returns false and leaves |password_and_meta_data| untouched.
   bool GetPasswordAndMetadataForUsername(
       const std::u16string& current_username,
-      autofill::Suggestion::FrontendId item_id,
+      autofill::PopupItemId popup_item_id,
       const autofill::PasswordFormFillData& fill_data,
       autofill::PasswordAndMetadata* password_and_meta_data);
 
@@ -196,9 +198,9 @@
       PasswordManagerClient::ReauthSucceeded reauth_succeeded);
 
   // Called when the biometric reauth that guards password filling completes.
-  // |frontend_id| identifies the suggestion that was selected for filling.
+  // `popup_item_id` identifies the suggestion that was selected for filling.
   void OnBiometricReauthCompleted(const std::u16string& username_value,
-                                  autofill::Suggestion::FrontendId frontend_id,
+                                  autofill::PopupItemId popup_item_id,
                                   bool auth_succeded);
 
   // Cancels an ongoing biometric re-authentication. Usually, because
diff --git a/components/password_manager/core/browser/password_autofill_manager_unittest.cc b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
index 1883fb30..d7658d2 100644
--- a/components/password_manager/core/browser/password_autofill_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_autofill_manager_unittest.cc
@@ -228,10 +228,7 @@
               HideAutofillPopup,
               (autofill::PopupHidingReason),
               (override));
-  MOCK_METHOD(void,
-              ExecuteCommand,
-              (autofill::Suggestion::FrontendId),
-              (override));
+  MOCK_METHOD(void, ExecuteCommand, (autofill::PopupItemId), (override));
 };
 
 base::CancelableTaskTracker::TaskId
@@ -249,26 +246,26 @@
   std::vector<Suggestion> suggestions;
   suggestions.emplace_back(
       /*value=*/"User1", /*label=*/"PW1", /*icon=*/"",
-      /*frontend_id=*/autofill::PopupItemId::kPasswordEntry);
+      /*popup_item_id=*/autofill::PopupItemId::kPasswordEntry);
   suggestions.emplace_back(
       /*value=*/"Show all pwds", /*label=*/"", /*icon=*/"",
-      /*frontend_id=*/autofill::PopupItemId::kAllSavedPasswordsEntry);
+      /*popup_item_id=*/autofill::PopupItemId::kAllSavedPasswordsEntry);
   if (has_opt_in_and_fill) {
     suggestions.emplace_back(
         /*value=*/"Unlock passwords and fill", /*label=*/"", /*icon=*/"",
-        /*frontend_id=*/
+        /*popup_item_id=*/
         autofill::PopupItemId::kPasswordAccountStorageOptIn);
   }
   if (has_opt_in_and_generate) {
     suggestions.emplace_back(
         /*value=*/"Unlock passwords and generate", /*label=*/"", /*icon=*/"",
-        /*frontend_id=*/
+        /*popup_item_id=*/
         autofill::PopupItemId::kPasswordAccountStorageOptInAndGenerate);
   }
   if (has_re_signin) {
     suggestions.emplace_back(
         /*value=*/"Sign in to access passwords", /*label=*/"", /*icon=*/"",
-        /*frontend_id=*/
+        /*popup_item_id=*/
         autofill::PopupItemId::kPasswordAccountStorageReSignin);
   }
   return suggestions;
@@ -924,7 +921,7 @@
   // loading state which the DeleteFillData() call will end.
   Suggestion unlock_suggestion(
       /*main_text=*/"Unlock passwords and fill", /*label=*/"", /*icon=*/"",
-      /*frontend_id=*/
+      /*popup_item_id=*/
       autofill::PopupItemId::kPasswordAccountStorageOptIn);
   unlock_suggestion.is_loading = Suggestion::IsLoading(true);
   EXPECT_CALL(autofill_client, GetPopupSuggestions)
@@ -1600,10 +1597,10 @@
       gfx::RectF(), base::i18n::RIGHT_TO_LEFT,
       /*show_password_suggestions=*/true);
   EXPECT_THAT(open_args.suggestions,
-              Not(Contains(Field(&autofill::Suggestion::frontend_id,
+              Not(Contains(Field(&autofill::Suggestion::popup_item_id,
                                  Eq(regular_generate_id)))));
   EXPECT_THAT(open_args.suggestions,
-              Contains(Field(&autofill::Suggestion::frontend_id,
+              Contains(Field(&autofill::Suggestion::popup_item_id,
                              Eq(opt_in_and_generate_id))));
 }
 
@@ -2112,7 +2109,7 @@
                   autofill::PopupItemId::kAllSavedPasswordsEntry));
   EXPECT_EQ(open_args.suggestions[0].GetPayload<Suggestion::BackendId>(),
             Suggestion::BackendId(kIdBase64));
-  EXPECT_EQ(open_args.suggestions[0].frontend_id,
+  EXPECT_EQ(open_args.suggestions[0].popup_item_id,
             autofill::PopupItemId::kWebauthnCredential);
   EXPECT_EQ(open_args.suggestions[0].main_text.value, kName);
   EXPECT_TRUE(
@@ -2280,7 +2277,7 @@
   Suggestion suggestion;
   suggestion.main_text.value =
       l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_USE_DEVICE_PASSKEY);
-  suggestion.frontend_id =
+  suggestion.popup_item_id =
       autofill::PopupItemId::kWebauthnSignInWithAnotherDevice;
   suggestion.payload = autofill::Suggestion::BackendId();
   password_autofill_manager_->DidAcceptSuggestion(suggestion, /*position=*/1);
diff --git a/components/password_manager/core/browser/password_manager_util_unittest.cc b/components/password_manager/core/browser/password_manager_util_unittest.cc
index b66ac8b..32015ba1 100644
--- a/components/password_manager/core/browser/password_manager_util_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_util_unittest.cc
@@ -21,6 +21,7 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/local_card_migration_manager.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/popup_types.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "components/autofill/core/common/password_generation_util.h"
@@ -281,10 +282,7 @@
               (const std::u16string&, const std::u16string&),
               (override));
   MOCK_METHOD(bool, IsContextSecure, (), (const, override));
-  MOCK_METHOD(void,
-              ExecuteCommand,
-              (autofill::Suggestion::FrontendId),
-              (override));
+  MOCK_METHOD(void, ExecuteCommand, (autofill::PopupItemId), (override));
   MOCK_METHOD(autofill::LogManager*, GetLogManager, (), (const, override));
   MOCK_METHOD(const autofill::AutofillAblationStudy&,
               GetAblationStudy,
diff --git a/components/password_manager/core/browser/webauthn_credentials_delegate.h b/components/password_manager/core/browser/webauthn_credentials_delegate.h
index 390ab23..5dfa8c87 100644
--- a/components/password_manager/core/browser/webauthn_credentials_delegate.h
+++ b/components/password_manager/core/browser/webauthn_credentials_delegate.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/functional/callback.h"
+#include "build/build_config.h"
 #include "components/password_manager/core/browser/passkey_credential.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -39,6 +40,15 @@
   // |callback| is invoked when credentials have been received, which could be
   // immediately.
   virtual void RetrievePasskeys(base::OnceCallback<void()> callback) = 0;
+
+#if BUILDFLAG(IS_ANDROID)
+  // Called to start the hybrid sign-in flow in Play Services.
+  virtual void ShowAndroidHybridSignIn() = 0;
+
+  // Returns true if hybrid sign-in is available, and the option should be
+  // shown on conditional UI autofill surfaces.
+  virtual bool IsAndroidHybridAvailable() const = 0;
+#endif
 };
 
 }  // namespace password_manager
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 594f7f1..d0aeba8 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -3711,16 +3711,32 @@
 // Information about an eSIM profile installed on an EUICC (Embedded Universal
 // Integrated Circuit Card).
 message ESimProfileInfo {
-  // ICCID of the eSIM profile. Upto 22 decimal digits encoded
-  // as ASCII string.
+  // ICCID of the eSIM profile. Up to 22 decimal digits encoded as ASCII string.
   // Example: 3554020401587593554020.
   optional string iccid = 1;
 
-  // SMDP (Subscription Management - Data Preparation) server address
-  // that was used to install this eSIM profile. Upto 255 characters
+  // SM-DP+ (Subscription Manager - Data Preparation) activation code that was
+  // used to install this eSIM profile. The "address" terminology is historical
+  // since the string is not strictly just an address. This field should be
+  // considered mutually exclusive with |smds_address|. Up to 255 characters
+  // encoded as UTF8 string.
+  // Example: LPA:1$esim.example.com$matchingId
+  optional string smdp_address = 2;
+
+  // SM-DS (Subscription Manager - Discovery Service) activation code that was
+  // used to install this eSIM profile. The "address" terminology is historical
+  // since the string is not strictly just an address. This field should be
+  // considered mutually exclusive with |smdp_address|. Up to 255 characters
   // encoded as UTF8 string.
   // Example: LPA:1$esim.example.com$
-  optional string smdp_address = 2;
+  optional string smds_address = 3;
+
+  // The name of the policy-defined network that resulted in the installation of
+  // this eSIM profile. This metadata is required to guarantee that multiple
+  // policy-defined networks that are configured with the same activation code
+  // are handled correctly, and will result in the appropriate number of eSIM
+  // profiles being installed.
+  optional string name = 4;
 }
 
 // Request from device to server to update information about EUICCs
diff --git a/components/segmentation_platform/internal/data_collection/training_data_cache.cc b/components/segmentation_platform/internal/data_collection/training_data_cache.cc
index ba6564a..3679bef6 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_cache.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_cache.cc
@@ -40,8 +40,10 @@
     segment_data.erase(it);
     std::move(callback).Run(result);
   } else {
+    // TODO (ritikagup@) : Add handling for default models, if required.
     segment_info_database_->GetTrainingData(
-        segment_id, request_id, /*delete_from_db=*/true, std::move(callback));
+        segment_id, proto::ModelSource::SERVER_MODEL_SOURCE, request_id,
+        /*delete_from_db=*/true, std::move(callback));
   }
 }
 
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
index 0812304..f7308f3f 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.cc
@@ -233,8 +233,9 @@
     param->output_metric_hash = hash;
     param->output_value = static_cast<float>(sample);
     for (auto segment : segments) {
+      // TODO (ritikagup@) : Add handling for default models, if required.
       segment_info_database_->GetSegmentInfo(
-          segment,
+          segment, proto::ModelSource::SERVER_MODEL_SOURCE,
           base::BindOnce(
               &TrainingDataCollectorImpl::OnUmaUpdatedReportForSegmentInfo,
               weak_ptr_factory_.GetWeakPtr(), param));
@@ -251,8 +252,9 @@
   if (it != immediate_trigger_user_actions_.end()) {
     auto segments = it->second;
     for (auto segment : segments) {
+      // TODO (ritikagup@) : Add handling for default models, if required.
       segment_info_database_->GetSegmentInfo(
-          segment,
+          segment, ModelSource::SERVER_MODEL_SOURCE,
           base::BindOnce(
               &TrainingDataCollectorImpl::OnUmaUpdatedReportForSegmentInfo,
               weak_ptr_factory_.GetWeakPtr(), absl::nullopt));
@@ -430,7 +432,9 @@
     TrainingRequestId request_id,
     const TrainingLabels& param,
     SuccessCallback callback) {
-  auto segment_info = segment_info_database_->GetCachedSegmentInfo(segment_id);
+  // TODO (ritikagup@) : Add handling for default models, if required.
+  auto segment_info = segment_info_database_->GetCachedSegmentInfo(
+      segment_id, proto::ModelSource::SERVER_MODEL_SOURCE);
   if (!segment_info) {
     return;
   }
diff --git a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
index 26bd002e..d8854bd4 100644
--- a/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
+++ b/components/segmentation_platform/internal/data_collection/training_data_collector_impl.h
@@ -27,6 +27,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace segmentation_platform {
+using proto::ModelSource;
 using proto::SegmentId;
 
 class SegmentationResultPrefs;
diff --git a/components/segmentation_platform/internal/database/segment_info_database.cc b/components/segmentation_platform/internal/database/segment_info_database.cc
index 31ab364..9ed31a3d 100644
--- a/components/segmentation_platform/internal/database/segment_info_database.cc
+++ b/components/segmentation_platform/internal/database/segment_info_database.cc
@@ -48,16 +48,19 @@
 }
 
 void SegmentInfoDatabase::GetSegmentInfo(SegmentId segment_id,
+                                         ModelSource model_source,
                                          SegmentInfoCallback callback) {
   std::move(callback).Run(cache_->GetSegmentInfo(segment_id));
 }
 
 absl::optional<SegmentInfo> SegmentInfoDatabase::GetCachedSegmentInfo(
-    SegmentId segment_id) {
+    SegmentId segment_id,
+    ModelSource model_source) {
   return cache_->GetSegmentInfo(segment_id);
 }
 
 void SegmentInfoDatabase::GetTrainingData(SegmentId segment_id,
+                                          ModelSource model_source,
                                           TrainingRequestId request_id,
                                           bool delete_from_db,
                                           TrainingDataCallback callback) {
diff --git a/components/segmentation_platform/internal/database/segment_info_database.h b/components/segmentation_platform/internal/database/segment_info_database.h
index 66e7428..987d7c16 100644
--- a/components/segmentation_platform/internal/database/segment_info_database.h
+++ b/components/segmentation_platform/internal/database/segment_info_database.h
@@ -20,6 +20,7 @@
 
 namespace segmentation_platform {
 
+using proto::ModelSource;
 using proto::SegmentId;
 
 namespace proto {
@@ -53,22 +54,26 @@
 
   virtual void Initialize(SuccessCallback callback);
 
-  // Called to get metadata for a given list of segments.
+  // Called to get metadata for a given list of server model segments.
   virtual void GetSegmentInfoForSegments(
       const base::flat_set<SegmentId>& segment_ids,
       MultipleSegmentInfoCallback callback);
 
-  // Called to get the metadata for a given segment.
+  // Called to get the metadata for a given segment. ModelSource defines whether
+  // to give metadata from server/default models for the given segment.
   virtual void GetSegmentInfo(SegmentId segment_id,
+                              ModelSource model_source,
                               SegmentInfoCallback callback);
 
   virtual absl::optional<SegmentInfo> GetCachedSegmentInfo(
-      SegmentId segment_id);
+      SegmentId segment_id,
+      ModelSource model_source);
 
-  // Called to get the training data for a given segment and request ID. If
-  // delete_from_db is set to true, it will delete the corresponding entry in
-  // the cache and in the database.
+  // Called to get the training data for a given segment with given model source
+  // and request ID. If delete_from_db is set to true, it will delete the
+  // corresponding entry in the cache and in the database.
   virtual void GetTrainingData(SegmentId segment_id,
+                               ModelSource model_source,
                                TrainingRequestId request_id,
                                bool delete_from_db,
                                TrainingDataCallback callback);
diff --git a/components/segmentation_platform/internal/database/segment_info_database_unittest.cc b/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
index 909cb3e..b1c6cd4 100644
--- a/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
+++ b/components/segmentation_platform/internal/database/segment_info_database_unittest.cc
@@ -24,14 +24,18 @@
     SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB;
 const SegmentId kSegmentId2 = SegmentId::OPTIMIZATION_TARGET_SEGMENTATION_SHARE;
 
-std::string ToString(SegmentId segment_id) {
+const ModelSource kServerModelSource = ModelSource::SERVER_MODEL_SOURCE;
+
+std::string ToString(SegmentId segment_id, ModelSource model_source) {
   return base::NumberToString(static_cast<int>(segment_id));
 }
 
 proto::SegmentInfo CreateSegment(SegmentId segment_id,
+                                 ModelSource model_source,
                                  absl::optional<int> result = absl::nullopt) {
   proto::SegmentInfo info;
   info.set_segment_id(segment_id);
+  info.set_model_source(model_source);
 
   if (result.has_value()) {
     info.mutable_prediction_result()->add_result(result.value());
@@ -77,13 +81,20 @@
     segment_db_.reset();
   }
 
-  void VerifyDb(base::flat_set<SegmentId> expected_ids) {
-    EXPECT_EQ(expected_ids.size(), db_entries_.size());
-    for (auto segment_id : expected_ids)
-      EXPECT_TRUE(db_entries_.find(ToString(segment_id)) != db_entries_.end());
+  void VerifyDb(base::flat_set<std::pair<SegmentId, ModelSource>>
+                    expected_ids_with_model_source) {
+    EXPECT_EQ(expected_ids_with_model_source.size(), db_entries_.size());
+    for (auto segment_id_and_model_source : expected_ids_with_model_source) {
+      EXPECT_TRUE(
+          db_entries_.find(ToString(segment_id_and_model_source.first,
+                                    segment_id_and_model_source.second)) !=
+          db_entries_.end());
+    }
   }
 
-  void WriteResult(SegmentId segment_id, absl::optional<float> result) {
+  void WriteResult(SegmentId segment_id,
+                   ModelSource model_source,
+                   absl::optional<float> result) {
     proto::PredictionResult prediction_result;
     if (result.has_value())
       prediction_result.add_result(result.value());
@@ -99,7 +110,10 @@
     db_->UpdateCallback(true);
   }
 
-  void WriteTrainingData(SegmentId segment_id, int64_t request_id, float data) {
+  void WriteTrainingData(SegmentId segment_id,
+                         ModelSource model_source,
+                         int64_t request_id,
+                         float data) {
     proto::TrainingData training_data;
     training_data.add_inputs(data);
     training_data.set_request_id(request_id);
@@ -112,12 +126,14 @@
   }
 
   void VerifyResult(SegmentId segment_id,
+                    ModelSource model_source,
                     absl::optional<float> result,
                     absl::optional<std::vector<ModelProvider::Request>>
                         training_inputs = absl::nullopt) {
     segment_db_->GetSegmentInfo(
-        segment_id, base::BindOnce(&SegmentInfoDatabaseTest::OnGetSegment,
-                                   base::Unretained(this)));
+        segment_id, model_source,
+        base::BindOnce(&SegmentInfoDatabaseTest::OnGetSegment,
+                       base::Unretained(this)));
     if (!segment_info_cache_->GetSegmentInfo(segment_id).has_value()) {
       db_->GetCallback(true);
     }
@@ -177,21 +193,23 @@
 TEST_F(SegmentInfoDatabaseTest, Get) {
   // Initialize DB with one entry.
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId), CreateSegment(kSegmentId)));
+      std::make_pair(ToString(kSegmentId, kServerModelSource),
+                     CreateSegment(kSegmentId, kServerModelSource)));
   SetUpDB();
 
   segment_db_->Initialize(base::DoNothing());
   db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK);
   db_->LoadCallback(true);
-  VerifyDb({kSegmentId});
+  VerifyDb({std::make_pair(kSegmentId, kServerModelSource)});
 
   // Get all segments.
   ExecuteAndVerifyGetSegmentInfoForSegments({kSegmentId});
 
   // Get a single segment.
   segment_db_->GetSegmentInfo(
-      kSegmentId, base::BindOnce(&SegmentInfoDatabaseTest::OnGetSegment,
-                                 base::Unretained(this)));
+      kSegmentId, kServerModelSource,
+      base::BindOnce(&SegmentInfoDatabaseTest::OnGetSegment,
+                     base::Unretained(this)));
   if (!segment_info_cache_->GetSegmentInfo(kSegmentId).has_value()) {
     db_->GetCallback(true);
   }
@@ -202,7 +220,8 @@
 TEST_F(SegmentInfoDatabaseTest, Update) {
   // Initialize DB with one entry.
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId), CreateSegment(kSegmentId)));
+      std::make_pair(ToString(kSegmentId, kServerModelSource),
+                     CreateSegment(kSegmentId, kServerModelSource)));
   SetUpDB();
 
   segment_db_->Initialize(base::DoNothing());
@@ -215,16 +234,19 @@
   VerifyDb({});
 
   // Insert a segment and verify.
-  segment_db_->UpdateSegment(kSegmentId, CreateSegment(kSegmentId),
+  segment_db_->UpdateSegment(kSegmentId,
+                             CreateSegment(kSegmentId, kServerModelSource),
                              base::DoNothing());
   db_->UpdateCallback(true);
-  VerifyDb({kSegmentId});
+  VerifyDb({std::make_pair(kSegmentId, kServerModelSource)});
 
   // Insert another segment and verify.
-  segment_db_->UpdateSegment(kSegmentId2, CreateSegment(kSegmentId2),
+  segment_db_->UpdateSegment(kSegmentId2,
+                             CreateSegment(kSegmentId2, kServerModelSource),
                              base::DoNothing());
   db_->UpdateCallback(true);
-  VerifyDb({kSegmentId, kSegmentId2});
+  VerifyDb({std::make_pair(kSegmentId, kServerModelSource),
+            std::make_pair(kSegmentId2, kServerModelSource)});
 
   // Verify GetSegmentInfoForSegments.
   ExecuteAndVerifyGetSegmentInfoForSegments({kSegmentId2});
@@ -237,9 +259,11 @@
 TEST_F(SegmentInfoDatabaseTest, UpdateMultipleSegments) {
   // Initialize DB with two entry.
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId), CreateSegment(kSegmentId)));
+      std::make_pair(ToString(kSegmentId, kServerModelSource),
+                     CreateSegment(kSegmentId, kServerModelSource)));
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId2), CreateSegment(kSegmentId2)));
+      std::make_pair(ToString(kSegmentId2, kServerModelSource),
+                     CreateSegment(kSegmentId2, kServerModelSource)));
   SetUpDB();
 
   segment_db_->Initialize(base::DoNothing());
@@ -255,16 +279,18 @@
   // Insert multiple segments and verify.
   std::vector<std::pair<SegmentId, proto::SegmentInfo>> segments_to_update;
   segments_to_update.emplace_back(
-      std::make_pair(kSegmentId, CreateSegment(kSegmentId)));
+      kSegmentId, CreateSegment(kSegmentId, kServerModelSource));
   segments_to_update.emplace_back(
-      std::make_pair(kSegmentId2, CreateSegment(kSegmentId2)));
+      kSegmentId2, CreateSegment(kSegmentId2, kServerModelSource));
   segment_db_->UpdateMultipleSegments(segments_to_update, {},
                                       base::DoNothing());
   db_->UpdateCallback(true);
-  VerifyDb({kSegmentId, kSegmentId2});
+  VerifyDb({std::make_pair(kSegmentId, kServerModelSource),
+            std::make_pair(kSegmentId2, kServerModelSource)});
 
   // Update one of the existing segment and verify.
-  proto::SegmentInfo segment_info = CreateSegment(kSegmentId2);
+  proto::SegmentInfo segment_info =
+      CreateSegment(kSegmentId2, kServerModelSource);
   segment_info.mutable_prediction_result()->add_result(0.9f);
   // Add this entry to `segments_to_update`.
   segments_to_update.clear();
@@ -273,8 +299,9 @@
   segment_db_->UpdateMultipleSegments(segments_to_update, {},
                                       base::DoNothing());
   db_->UpdateCallback(true);
-  VerifyDb({kSegmentId, kSegmentId2});
-  VerifyResult(kSegmentId2, 0.9f);
+  VerifyDb({std::make_pair(kSegmentId, kServerModelSource),
+            std::make_pair(kSegmentId2, kServerModelSource)});
+  VerifyResult(kSegmentId2, kServerModelSource, 0.9f);
 
   // Verify GetSegmentInfoForSegments.
   ExecuteAndVerifyGetSegmentInfoForSegments({kSegmentId2});
@@ -287,8 +314,9 @@
 TEST_F(SegmentInfoDatabaseTest, WriteResult) {
   // Initialize DB with cache enabled and one entry.
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId), CreateSegment(kSegmentId)));
-  VerifyDb({kSegmentId});
+      std::make_pair(ToString(kSegmentId, kServerModelSource),
+                     CreateSegment(kSegmentId, kServerModelSource)));
+  VerifyDb({{kSegmentId, kServerModelSource}});
   SetUpDB();
 
   segment_db_->Initialize(base::DoNothing());
@@ -300,24 +328,25 @@
   EXPECT_TRUE(segment_info_cache_->GetSegmentInfo(kSegmentId).has_value());
 
   // Update results and verify that db is updated.
-  WriteResult(kSegmentId, 0.4f);
+  WriteResult(kSegmentId, kServerModelSource, 0.4f);
 
   // Verify that cache is updated.
-  VerifyResult(kSegmentId, 0.4f);
+  VerifyResult(kSegmentId, kServerModelSource, 0.4f);
 
   // Overwrite results and verify.
-  WriteResult(kSegmentId, 0.9f);
-  VerifyResult(kSegmentId, 0.9f);
+  WriteResult(kSegmentId, kServerModelSource, 0.9f);
+  VerifyResult(kSegmentId, kServerModelSource, 0.9f);
 
   // Clear results and verify.
-  WriteResult(kSegmentId, absl::nullopt);
-  VerifyResult(kSegmentId, absl::nullopt);
+  WriteResult(kSegmentId, kServerModelSource, absl::nullopt);
+  VerifyResult(kSegmentId, kServerModelSource, absl::nullopt);
 }
 
 TEST_F(SegmentInfoDatabaseTest, WriteTrainingData) {
   // Initialize DB with one entry.
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId), CreateSegment(kSegmentId)));
+      std::make_pair(ToString(kSegmentId, kServerModelSource),
+                     CreateSegment(kSegmentId, kServerModelSource)));
   SetUpDB();
 
   segment_db_->Initialize(base::DoNothing());
@@ -328,30 +357,36 @@
   std::vector<ModelProvider::Request> expected_training_inputs;
 
   // Add training data and verify.
-  WriteTrainingData(kSegmentId, /*request_id=*/0, /*data=*/0.4f);
+  WriteTrainingData(kSegmentId, kServerModelSource, /*request_id=*/0,
+                    /*data=*/0.4f);
   expected_training_inputs.push_back({0.4f});
-  VerifyResult(kSegmentId, absl::nullopt, expected_training_inputs);
+  VerifyResult(kSegmentId, kServerModelSource, absl::nullopt,
+               expected_training_inputs);
 
   // Add another training data and verify.
   int64_t request_id = 1;
-  WriteTrainingData(kSegmentId, request_id, /*data=*/0.9f);
+  WriteTrainingData(kSegmentId, kServerModelSource, request_id, /*data=*/0.9f);
   expected_training_inputs.push_back({0.9f});
-  VerifyResult(kSegmentId, absl::nullopt, expected_training_inputs);
+  VerifyResult(kSegmentId, kServerModelSource, absl::nullopt,
+               expected_training_inputs);
 
   // Remove the last training data and verify.
-  segment_db_->GetTrainingData(kSegmentId,
+  segment_db_->GetTrainingData(kSegmentId, kServerModelSource,
                                TrainingRequestId::FromUnsafeValue(request_id),
                                /*delete_from_db=*/true, base::DoNothing());
   expected_training_inputs.pop_back();
-  VerifyResult(kSegmentId, absl::nullopt, expected_training_inputs);
+  VerifyResult(kSegmentId, kServerModelSource, absl::nullopt,
+               expected_training_inputs);
 }
 
 TEST_F(SegmentInfoDatabaseTest, WriteResultForTwoSegments) {
   // Initialize DB with two entries.
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId), CreateSegment(kSegmentId)));
+      std::make_pair(ToString(kSegmentId, kServerModelSource),
+                     CreateSegment(kSegmentId, kServerModelSource)));
   db_entries_.insert(
-      std::make_pair(ToString(kSegmentId2), CreateSegment(kSegmentId2)));
+      std::make_pair(ToString(kSegmentId2, kServerModelSource),
+                     CreateSegment(kSegmentId2, kServerModelSource)));
   SetUpDB();
 
   segment_db_->Initialize(base::DoNothing());
@@ -359,14 +394,14 @@
   db_->LoadCallback(true);
 
   // Update results for first segment.
-  WriteResult(kSegmentId, 0.4f);
+  WriteResult(kSegmentId, kServerModelSource, 0.4f);
 
   // Update results for second segment.
-  WriteResult(kSegmentId2, 0.9f);
+  WriteResult(kSegmentId2, kServerModelSource, 0.9f);
 
   // Verify results for both segments.
-  VerifyResult(kSegmentId, 0.4f);
-  VerifyResult(kSegmentId2, 0.9f);
+  VerifyResult(kSegmentId, kServerModelSource, 0.4f);
+  VerifyResult(kSegmentId2, kServerModelSource, 0.9f);
 }
 
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.cc b/components/segmentation_platform/internal/database/test_segment_info_database.cc
index 3203c89..51ecde2a 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.cc
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.cc
@@ -37,12 +37,14 @@
 }
 
 void TestSegmentInfoDatabase::GetSegmentInfo(SegmentId segment_id,
+                                             ModelSource model_source,
                                              SegmentInfoCallback callback) {
-  std::move(callback).Run(GetCachedSegmentInfo(segment_id));
+  std::move(callback).Run(GetCachedSegmentInfo(segment_id, model_source));
 }
 
 absl::optional<SegmentInfo> TestSegmentInfoDatabase::GetCachedSegmentInfo(
-    SegmentId segment_id) {
+    SegmentId segment_id,
+    ModelSource model_source) {
   auto result =
       base::ranges::find(segment_infos_, segment_id,
                          &std::pair<SegmentId, proto::SegmentInfo>::first);
@@ -92,6 +94,7 @@
 }
 
 void TestSegmentInfoDatabase::GetTrainingData(SegmentId segment_id,
+                                              ModelSource model_source,
                                               TrainingRequestId request_id,
                                               bool delete_from_db,
                                               TrainingDataCallback callback) {
diff --git a/components/segmentation_platform/internal/database/test_segment_info_database.h b/components/segmentation_platform/internal/database/test_segment_info_database.h
index 01d57fc..1812927f 100644
--- a/components/segmentation_platform/internal/database/test_segment_info_database.h
+++ b/components/segmentation_platform/internal/database/test_segment_info_database.h
@@ -29,9 +29,11 @@
   void GetSegmentInfoForSegments(const base::flat_set<SegmentId>& segment_ids,
                                  MultipleSegmentInfoCallback callback) override;
   void GetSegmentInfo(SegmentId segment_id,
+                      ModelSource model_source,
                       SegmentInfoCallback callback) override;
   absl::optional<SegmentInfo> GetCachedSegmentInfo(
-      SegmentId segment_id) override;
+      SegmentId segment_id,
+      ModelSource model_source) override;
   void UpdateSegment(SegmentId segment_id,
                      absl::optional<proto::SegmentInfo> segment_info,
                      SuccessCallback callback) override;
@@ -42,6 +44,7 @@
                         const proto::TrainingData& data,
                         SuccessCallback callback) override;
   void GetTrainingData(SegmentId segment_id,
+                       ModelSource model_source,
                        TrainingRequestId request_id,
                        bool delete_from_db,
                        TrainingDataCallback callback) override;
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
index 608fc2b5..b9ba9c9 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl.cc
@@ -79,9 +79,9 @@
       segment_id, /* processed = */ false, validation);
   if (validation != metadata_utils::ValidationResult::kValidationSuccess)
     return;
-
+  // TODO (ritikagup@) : Add handling for default models, if required.
   segment_database_->GetSegmentInfo(
-      segment_id,
+      segment_id, proto::ModelSource::SERVER_MODEL_SOURCE,
       base::BindOnce(
           &ModelExecutionManagerImpl::OnSegmentInfoFetchedForModelUpdate,
           weak_ptr_factory_.GetWeakPtr(), segment_id, std::move(metadata),
diff --git a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
index e0c4f9e..63ef24f 100644
--- a/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
+++ b/components/segmentation_platform/internal/execution/model_execution_manager_impl_unittest.cc
@@ -58,7 +58,9 @@
               (override));
   MOCK_METHOD(void,
               GetSegmentInfo,
-              (SegmentId segment_id, SegmentInfoCallback callback),
+              (SegmentId segment_id,
+               proto::ModelSource model_source,
+               SegmentInfoCallback callback),
               (override));
   MOCK_METHOD(void,
               UpdateSegment,
@@ -142,7 +144,7 @@
 
   // Verify that the ModelExecutionManager never invokes its
   // SegmentInfoDatabase, nor invokes the callback.
-  EXPECT_CALL(*mock_segment_database_ptr, GetSegmentInfo(_, _)).Times(0);
+  EXPECT_CALL(*mock_segment_database_ptr, GetSegmentInfo(_, _, _)).Times(0);
   EXPECT_CALL(callback, Run(_)).Times(0);
   model_provider_data_.model_providers_callbacks[segment_id].Run(
       segment_id, metadata, kModelVersion);
@@ -174,7 +176,8 @@
   EXPECT_CALL(db_callback, Run(_)).WillOnce(SaveArg<0>(&segment_info_from_db));
 
   // Fetch SegmentInfo from the database.
-  segment_database_->GetSegmentInfo(segment_id, db_callback.Get());
+  segment_database_->GetSegmentInfo(
+      segment_id, proto::ModelSource::SERVER_MODEL_SOURCE, db_callback.Get());
   EXPECT_TRUE(segment_info_from_db.has_value());
   EXPECT_EQ(segment_id, segment_info_from_db->segment_id());
 
@@ -203,7 +206,8 @@
   absl::optional<proto::SegmentInfo> segment_info_from_db_1;
   EXPECT_CALL(db_callback_1, Run(_))
       .WillOnce(SaveArg<0>(&segment_info_from_db_1));
-  segment_database_->GetSegmentInfo(segment_id, db_callback_1.Get());
+  segment_database_->GetSegmentInfo(
+      segment_id, proto::ModelSource::SERVER_MODEL_SOURCE, db_callback_1.Get());
   EXPECT_TRUE(segment_info_from_db_1.has_value());
   EXPECT_EQ(segment_id, segment_info_from_db_1->segment_id());
 
@@ -271,7 +275,8 @@
   absl::optional<proto::SegmentInfo> segment_info_from_db_2;
   EXPECT_CALL(db_callback_2, Run(_))
       .WillOnce(SaveArg<0>(&segment_info_from_db_2));
-  segment_database_->GetSegmentInfo(segment_id, db_callback_2.Get());
+  segment_database_->GetSegmentInfo(
+      segment_id, proto::ModelSource::SERVER_MODEL_SOURCE, db_callback_2.Get());
   EXPECT_TRUE(segment_info_from_db_2.has_value());
   EXPECT_EQ(segment_id, segment_info_from_db_2->segment_id());
   EXPECT_EQ(clock_.Now().ToDeltaSinceWindowsEpoch().InSeconds(),
diff --git a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/AndroidStylusWritingHandler.java b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/AndroidStylusWritingHandler.java
index f989fbcf..a47bd20 100644
--- a/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/AndroidStylusWritingHandler.java
+++ b/components/stylus_handwriting/android/java/src/org/chromium/components/stylus_handwriting/AndroidStylusWritingHandler.java
@@ -41,11 +41,18 @@
     private final InputMethodManager mInputMethodManager;
     private View mTargetView;
 
+    @OptIn(markerClass = androidx.core.os.BuildCompat.PrereleaseSdkCheck.class)
     public static boolean isEnabled(Context context) {
         if (!BuildInfo.isAtLeastT()) return false;
 
-        int value = Settings.Global.getInt(
-                context.getContentResolver(), "stylus_handwriting_enabled", -1);
+        int value = -1;
+        if (BuildCompat.isAtLeastU()) {
+            value = Settings.Secure.getInt(
+                    context.getContentResolver(), "stylus_handwriting_enabled", -1);
+        } else {
+            value = Settings.Global.getInt(
+                    context.getContentResolver(), "stylus_handwriting_enabled", -1);
+        }
 
         if (value != 1) {
             Log.d(TAG, "Stylus feature disabled.", value);
diff --git a/components/user_education/DEPS b/components/user_education/DEPS
index a74f63f36..a94f8e6 100644
--- a/components/user_education/DEPS
+++ b/components/user_education/DEPS
@@ -6,5 +6,6 @@
   "+ui/accessibility",
   "+ui/base",
   "+ui/color",
+  "+ui/events",
   "+ui/gfx",
 ]
diff --git a/components/user_education/views/help_bubble_view.cc b/components/user_education/views/help_bubble_view.cc
index a523680c..fd0d62c 100644
--- a/components/user_education/views/help_bubble_view.cc
+++ b/components/user_education/views/help_bubble_view.cc
@@ -13,6 +13,7 @@
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/user_metrics.h"
+#include "base/notreached.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
 #include "components/strings/grit/components_strings.h"
@@ -27,9 +28,13 @@
 #include "ui/base/models/image_model.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/color/color_provider.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/types/event_type.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/rect_f.h"
 #include "ui/gfx/paint_vector_icon.h"
@@ -52,8 +57,8 @@
 #include "ui/views/controls/highlight_path_generator.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
-#include "ui/views/controls/menu/menu_scroll_view_container.h"
-#include "ui/views/event_monitor.h"
+#include "ui/views/controls/menu/menu_config.h"
+#include "ui/views/controls/menu/menu_item_view.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/layout/flex_layout_types.h"
@@ -65,6 +70,7 @@
 #include "ui/views/view_class_properties.h"
 #include "ui/views/view_tracker.h"
 #include "ui/views/view_utils.h"
+#include "ui/views/widget/widget.h"
 
 namespace user_education {
 
@@ -295,8 +301,164 @@
 BEGIN_METADATA(DotView, views::View)
 END_METADATA
 
+views::MenuItemView* GetAnchorAsMenuItem(
+    const views::BubbleDialogDelegate* delegate) {
+  return views::AsViewClass<views::MenuItemView>(delegate->GetAnchorView());
+}
+
 }  // namespace
 
+namespace internal {
+
+// Because menus use event capture, a help bubble anchored to a menu cannot
+// respond to events in the normal way. However, help bubbles are not
+// complicated and only have buttons. When a help bubble is anchored to a menu,
+// this object will monitor events that would be captured by the menu, and
+// ensures that the buttons on the help bubble still behavior predictably.
+class MenuEventMonitor {
+ public:
+  MenuEventMonitor(HelpBubbleView* help_bubble, views::MenuItemView* menu_item)
+      : help_bubble_(help_bubble),
+        callback_handle_(menu_item->GetMenuController()->SetAnnotationCallback(
+            base::BindRepeating(&MenuEventMonitor::OnEvent,
+                                base::Unretained(this)))) {}
+
+  ~MenuEventMonitor() = default;
+
+ private:
+  bool OnEvent(const ui::LocatedEvent& event) {
+    gfx::Point screen_coords;
+    screen_coords = event.root_location();
+
+    const views::Widget* const widget = help_bubble_->GetWidget();
+    if (!widget || !widget->GetWindowBoundsInScreen().Contains(screen_coords)) {
+      return false;
+    }
+
+    views::Button* const target_button = GetButtonAt(screen_coords);
+    const gfx::Point target_point =
+        target_button
+            ? views::View::ConvertPointFromScreen(target_button, screen_coords)
+            : gfx::Point();
+
+    switch (event.type()) {
+      // Pass mouse events on to the button as normal.
+      case ui::ET_MOUSE_PRESSED:
+        if (target_button) {
+          auto* const mouse_event = event.AsMouseEvent();
+          target_button->OnMousePressed(ui::MouseEvent(
+              ui::ET_MOUSE_PRESSED, gfx::PointF(target_point),
+              gfx::PointF(screen_coords), mouse_event->time_stamp(),
+              mouse_event->flags(), mouse_event->changed_button_flags()));
+        }
+        break;
+      case ui::ET_MOUSE_RELEASED:
+        if (target_button) {
+          auto* const mouse_event = event.AsMouseEvent();
+          target_button->OnMouseReleased(ui::MouseEvent(
+              ui::ET_MOUSE_RELEASED, gfx::PointF(target_point),
+              gfx::PointF(screen_coords), mouse_event->time_stamp(),
+              mouse_event->flags(), mouse_event->changed_button_flags()));
+        }
+        break;
+
+      // Touch events are not processed directly by Views; they are typically
+      // converted to something else. So, convert them to mouse clicks for the
+      // purpose of pressing buttons.
+      case ui::ET_TOUCH_PRESSED:
+        if (target_button) {
+          auto* const touch_event = event.AsTouchEvent();
+          target_button->OnMousePressed(ui::MouseEvent(
+              ui::ET_MOUSE_PRESSED, gfx::PointF(target_point),
+              gfx::PointF(screen_coords), touch_event->time_stamp(),
+              touch_event->flags() | ui::EF_LEFT_MOUSE_BUTTON |
+                  ui::EF_FROM_TOUCH,
+              ui::EF_LEFT_MOUSE_BUTTON));
+        }
+        break;
+      case ui::ET_TOUCH_RELEASED:
+        if (target_button) {
+          auto* const touch_event = event.AsTouchEvent();
+          target_button->OnMouseReleased(ui::MouseEvent(
+              ui::ET_MOUSE_RELEASED, gfx::PointF(target_point),
+              gfx::PointF(screen_coords), touch_event->time_stamp(),
+              touch_event->flags() | ui::EF_LEFT_MOUSE_BUTTON |
+                  ui::EF_FROM_TOUCH,
+              ui::EF_LEFT_MOUSE_BUTTON));
+        }
+        break;
+
+      // If a gesture is received, forward it as-is.
+      case ui::ET_GESTURE_TAP:
+        if (target_button) {
+          auto* const gesture = event.AsGestureEvent();
+          ui::GestureEvent tap(gesture->x(), gesture->y(), gesture->flags(),
+                               gesture->time_stamp(), gesture->details(),
+                               gesture->unique_touch_event_id());
+          target_button->button_controller()->OnGestureEvent(&tap);
+        }
+        break;
+
+      // Mouse moves could be routed through the inkdrop controller but it's
+      // easier to just set hovered state directly.
+      case ui::ET_MOUSE_MOVED:
+        if (target_button != hovered_button_) {
+          if (hovered_button_) {
+            views::InkDrop* const ink_drop =
+                views::InkDrop::Get(hovered_button_)->GetInkDrop();
+            if (ink_drop) {
+              ink_drop->SetHovered(false);
+            }
+          }
+          if (target_button) {
+            views::InkDrop* const ink_drop =
+                views::InkDrop::Get(target_button)->GetInkDrop();
+            if (ink_drop) {
+              ink_drop->SetHovered(true);
+            }
+          }
+          hovered_button_ = target_button;
+        }
+        break;
+      default:
+        return false;
+    }
+
+    return true;
+  }
+
+  // Gets which (if any) of the help bubble buttons are at the given
+  // `screen_coords`.
+  views::Button* GetButtonAt(const gfx::Point& screen_coords) const {
+    if (IsInButton(screen_coords, help_bubble_->close_button_)) {
+      return help_bubble_->close_button_;
+    }
+    if (IsInButton(screen_coords, help_bubble_->default_button_)) {
+      return help_bubble_->default_button_;
+    }
+    for (auto* const button : help_bubble_->non_default_buttons_) {
+      if (IsInButton(screen_coords, button)) {
+        return button;
+      }
+    }
+    return nullptr;
+  }
+
+  // Returns whether `screen_coords` are in `button`, which may be null.
+  static bool IsInButton(const gfx::Point& screen_coords,
+                         const views::Button* button) {
+    return button && button->HitTestPoint(views::View::ConvertPointFromScreen(
+                         button, screen_coords));
+  }
+
+  const raw_ptr<HelpBubbleView> help_bubble_;
+  raw_ptr<views::Button> hovered_button_ = nullptr;
+  // std::unique_ptr<views::EventMonitor> event_monitor_;
+  views::MenuController::AnnotationCallbackHandle callback_handle_;
+};
+
+}  // namespace internal
+
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HelpBubbleView,
                                       kHelpBubbleElementIdForTesting);
 DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(HelpBubbleView,
@@ -651,10 +813,13 @@
   SizeToContents();
 
   widget->ShowInactive();
-  auto* const anchor_bubble =
-      anchor.view->GetWidget()->widget_delegate()->AsBubbleDialogDelegate();
-  if (anchor_bubble)
+  if (auto* const anchor_bubble =
+          anchor_widget()->widget_delegate()->AsBubbleDialogDelegate()) {
     anchor_pin_ = anchor_bubble->PreventCloseOnDeactivate();
+  } else if (auto* const menu_item = GetAnchorAsMenuItem(this)) {
+    menu_event_monitor_ =
+        std::make_unique<internal::MenuEventMonitor>(this, menu_item);
+  }
   MaybeStartAutoCloseTimer();
 }
 
@@ -775,12 +940,7 @@
   // issues in Weston, the windowing environment used on chrome's Wayland
   // testbots:
   // https://gitlab.freedesktop.org/wayland/weston/-/issues/669
-  const views::View* const contents =
-      const_cast<HelpBubbleView*>(this)->anchor_widget()->GetContentsView();
-  if (contents &&
-      views::IsViewClass<views::MenuScrollViewContainer>(contents)) {
-    params->requires_accelerated_widget = true;
-  }
+  params->requires_accelerated_widget = (GetAnchorAsMenuItem(this) != nullptr);
 #endif
 }
 
diff --git a/components/user_education/views/help_bubble_view.h b/components/user_education/views/help_bubble_view.h
index 9bc3cae..d2db8ce 100644
--- a/components/user_education/views/help_bubble_view.h
+++ b/components/user_education/views/help_bubble_view.h
@@ -34,6 +34,8 @@
 
 namespace internal {
 
+class MenuEventMonitor;
+
 // Describes how a help bubble should be anchored to a Views element, beyond
 // what is specified by the HelpBubbleParams. Should only be instantiated by
 // classes derived from HelpBubbleFactory (or in tests).
@@ -93,6 +95,7 @@
   FRIEND_TEST_ALL_PREFIXES(HelpBubbleViewTimeoutTest,
                            RespectsProvidedTimeoutAfterActivate);
   friend class HelpBubbleViewsTest;
+  friend class internal::MenuEventMonitor;
 
   void MaybeStartAutoCloseTimer();
 
@@ -125,6 +128,10 @@
   // focus, even if it's marked as close_on_deactivate.
   std::unique_ptr<CloseOnDeactivatePin> anchor_pin_;
 
+  // Sniffs events intended for a menu to ensure that for bubbles anchored to
+  // menus, hover, click, and tap events are still registered.
+  std::unique_ptr<internal::MenuEventMonitor> menu_event_monitor_;
+
   // Auto close timeout. If the value is 0 (default), the bubble never times
   // out.
   base::TimeDelta timeout_;
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index 48ff8af..ddce6f3 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -177,8 +177,6 @@
              "AllowBypassRenderPassQuads",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// TODO(crbug.com/1357744): Solve the vulkan flakiness issue before enabling
-// this on Linux.
 BASE_FEATURE(kAllowUndamagedNonrootRenderPassToSkip,
              "AllowUndamagedNonrootRenderPassToSkip",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/web_resource/resource_request_allowed_notifier.h b/components/web_resource/resource_request_allowed_notifier.h
index 76531f0..5d3764d 100644
--- a/components/web_resource/resource_request_allowed_notifier.h
+++ b/components/web_resource/resource_request_allowed_notifier.h
@@ -139,8 +139,8 @@
   raw_ptr<Observer> observer_;
 
   NetworkConnectionTrackerGetter network_connection_tracker_getter_;
-  raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_ =
-      nullptr;
+  raw_ptr<network::NetworkConnectionTracker, LeakedDanglingUntriaged>
+      network_connection_tracker_ = nullptr;
   network::mojom::ConnectionType connection_type_ =
       network::mojom::ConnectionType::CONNECTION_UNKNOWN;
   bool connection_initialized_ = false;
diff --git a/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc b/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc
index 1982c10..f721dbe 100644
--- a/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc
+++ b/components/webapps/browser/android/add_to_homescreen_data_fetcher.cc
@@ -65,8 +65,8 @@
   InstallableParams params;
   params.check_eligibility = true;
   params.valid_manifest = true;
-  params.has_worker = !features::SkipInstallServiceWorkerCheck();
-  params.wait_for_worker = !features::SkipInstallServiceWorkerCheck();
+  params.has_worker = false;
+  params.wait_for_worker = false;
   return params;
 }
 
diff --git a/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc b/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
index 5963f34..be49578 100644
--- a/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
+++ b/components/webapps/browser/android/add_to_homescreen_data_fetcher_unittest.cc
@@ -429,85 +429,6 @@
   EXPECT_TRUE(fetcher->shortcut_info().best_primary_icon_url.is_empty());
 }
 
-// Check that the AddToHomescreenDataFetcher::Observer methods are called if
-// the service worker check times out on a page that is installable (i.e. it's
-// taken too long). This should use the short_name and icon from the manifest,
-// but not be WebAPK-compatible. Only relevant when checking WebAPK
-// compatibility.
-TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutPwa) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kSkipServiceWorkerCheckInstallOnly);
-
-  SetManifest(BuildDefaultManifest());
-  SetShouldServiceWorkerTimeOut(true);
-
-  // Check where InstallableManager finishes working after the timeout and
-  // determines PWA-ness.
-  base::HistogramTester histograms;
-  ObserverWaiter waiter;
-  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
-             blink::mojom::DisplayMode::kStandalone, false,
-             InstallableStatusCode::DATA_TIMED_OUT);
-  CheckHistograms(histograms);
-
-  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-            GURL(kDefaultIconUrl));
-}
-
-TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutNonPwa) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kSkipServiceWorkerCheckInstallOnly);
-
-  SetManifest(BuildDefaultManifest());
-  SetShouldServiceWorkerTimeOut(true);
-  SetHasServiceWorker(false);
-
-  // Check where InstallableManager finishes working after the timeout and
-  // determines non-PWA-ness.
-  base::HistogramTester histograms;
-  ObserverWaiter waiter;
-  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
-             blink::mojom::DisplayMode::kStandalone, false,
-             InstallableStatusCode::DATA_TIMED_OUT);
-  CheckHistograms(histograms);
-
-  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-            GURL(kDefaultIconUrl));
-}
-
-TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerCheckTimesOutUnknown) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kSkipServiceWorkerCheckInstallOnly);
-
-  SetManifest(BuildDefaultManifest());
-  SetShouldServiceWorkerTimeOut(true);
-  SetHasServiceWorker(false);
-
-  // Check where InstallableManager doesn't finish working after the timeout.
-  // This is akin to waiting for a service worker forever.
-  base::HistogramTester histograms;
-  ObserverWaiter waiter;
-  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
-             blink::mojom::DisplayMode::kStandalone, false,
-             InstallableStatusCode::DATA_TIMED_OUT);
-
-  // Navigate to ensure the histograms are written.
-  NavigateAndCommit(GURL("about:blank"));
-  CheckHistograms(histograms);
-
-  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-            GURL(kDefaultIconUrl));
-}
-
 TEST_F(AddToHomescreenDataFetcherTest, InstallableManifest) {
   // Test a site that has an offline-capable service worker.
   SetManifest(BuildDefaultManifest());
@@ -529,85 +450,6 @@
   CheckHistograms(histograms);
 }
 
-TEST_F(AddToHomescreenDataFetcherTest, ManifestNameClobbersWebApplicationName) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kSkipServiceWorkerCheckInstallOnly);
-
-  // Test that when the manifest provides Manifest::name but not
-  // Manifest::short_name that Manifest::name is used as the title.
-  {
-    // Check the case where we have no icons.
-    blink::mojom::ManifestPtr manifest = BuildDefaultManifest();
-    manifest->icons.clear();
-    manifest->short_name = absl::nullopt;
-    SetManifest(std::move(manifest));
-
-    ObserverWaiter waiter;
-    std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-    RunFetcher(fetcher.get(), waiter, kDefaultManifestName,
-               blink::mojom::DisplayMode::kStandalone, false,
-               InstallableStatusCode::NO_ACCEPTABLE_ICON);
-
-    EXPECT_TRUE(fetcher->shortcut_info().best_primary_icon_url.is_empty());
-    EXPECT_TRUE(base::EqualsASCII(fetcher->shortcut_info().short_name,
-                                  kDefaultManifestName));
-  }
-
-  blink::mojom::ManifestPtr manifest = BuildDefaultManifest();
-  manifest->short_name = absl::nullopt;
-  SetManifest(std::move(manifest));
-
-  {
-    // Check a site with no offline-capable service worker.
-    SetHasServiceWorker(false);
-    ObserverWaiter waiter;
-    std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-    RunFetcher(fetcher.get(), waiter, kDefaultManifestName,
-               blink::mojom::DisplayMode::kStandalone, false,
-               InstallableStatusCode::NOT_OFFLINE_CAPABLE);
-
-    EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-    EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-              GURL(kDefaultIconUrl));
-    EXPECT_TRUE(base::EqualsASCII(fetcher->shortcut_info().short_name,
-                                  kDefaultManifestName));
-  }
-
-  {
-    // Check a site where we time out waiting for the service worker.
-    SetShouldServiceWorkerTimeOut(true);
-    ObserverWaiter waiter;
-    std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-    RunFetcher(fetcher.get(), waiter, kDefaultManifestName,
-               blink::mojom::DisplayMode::kStandalone, false,
-               InstallableStatusCode::DATA_TIMED_OUT);
-
-    EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-    EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-              GURL(kDefaultIconUrl));
-    EXPECT_TRUE(base::EqualsASCII(fetcher->shortcut_info().short_name,
-                                  kDefaultManifestName));
-  }
-
-  {
-    // Check a site with an offline-capable service worker.
-    SetHasServiceWorker(true);
-    SetShouldServiceWorkerTimeOut(false);
-    ObserverWaiter waiter;
-    std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-    RunFetcher(fetcher.get(), waiter, kDefaultManifestName,
-               blink::mojom::DisplayMode::kStandalone, true,
-               InstallableStatusCode::NO_ERROR_DETECTED);
-
-    EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-    EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-              GURL(kDefaultIconUrl));
-    EXPECT_TRUE(base::EqualsASCII(fetcher->shortcut_info().short_name,
-                                  kDefaultManifestName));
-  }
-}
-
 TEST_F(AddToHomescreenDataFetcherTest, ManifestNoNameNoShortName) {
   // Test that when the manifest does not provide either Manifest::short_name
   // nor Manifest::name that:
@@ -635,56 +477,4 @@
             GURL(kDefaultIconUrl));
 }
 
-TEST_F(AddToHomescreenDataFetcherTest, NoServiceWorkerInstallable) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      features::kSkipServiceWorkerCheckInstallOnly);
-
-  SetManifest(BuildDefaultManifest());
-  SetHasServiceWorker(false);
-
-  // Check where InstallableManager doesn't finish working after the timeout.
-  // This is akin to waiting for a service worker forever.
-  base::HistogramTester histograms;
-  ObserverWaiter waiter;
-  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
-             kDefaultManifestName, blink::mojom::DisplayMode::kStandalone,
-             true /*is_webapk_compatible*/,
-             InstallableStatusCode::NO_ERROR_DETECTED);
-
-  // Navigate to ensure the histograms are written.
-  NavigateAndCommit(GURL("about:blank"));
-  CheckHistograms(histograms);
-
-  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-            GURL(kDefaultIconUrl));
-}
-
-TEST_F(AddToHomescreenDataFetcherTest, ServiceWorkerTimeOutInstallable) {
-  base::test::ScopedFeatureList scoped_feature_list(
-      features::kSkipServiceWorkerCheckInstallOnly);
-
-  SetManifest(BuildDefaultManifest());
-  SetShouldServiceWorkerTimeOut(true);
-
-  // Check where InstallableManager doesn't finish working after the timeout.
-  // This is akin to waiting for a service worker forever.
-  base::HistogramTester histograms;
-  ObserverWaiter waiter;
-  std::unique_ptr<AddToHomescreenDataFetcher> fetcher = BuildFetcher(&waiter);
-  RunFetcher(fetcher.get(), waiter, kDefaultManifestShortName,
-             kDefaultManifestName, blink::mojom::DisplayMode::kStandalone,
-             true /*is_webapk_compatible*/,
-             InstallableStatusCode::NO_ERROR_DETECTED);
-
-  // Navigate to ensure the histograms are written.
-  NavigateAndCommit(GURL("about:blank"));
-  CheckHistograms(histograms);
-
-  EXPECT_FALSE(fetcher->primary_icon().drawsNothing());
-  EXPECT_EQ(fetcher->shortcut_info().best_primary_icon_url,
-            GURL(kDefaultIconUrl));
-}
-
 }  // namespace webapps
diff --git a/components/webapps/browser/android/ambient_badge_manager.cc b/components/webapps/browser/android/ambient_badge_manager.cc
index 3b63848..fd500ab 100644
--- a/components/webapps/browser/android/ambient_badge_manager.cc
+++ b/components/webapps/browser/android/ambient_badge_manager.cc
@@ -155,7 +155,6 @@
   // if it's showing for web app (not native app), only show if the worker check
   // already passed.
   if (a2hs_params_->app_type == AddToHomescreenParams::AppType::WEBAPK &&
-      features::SkipServiceWorkerForInstallPromotion() &&
       !passed_worker_check_) {
     InstallableParams params = ParamsToPerformWorkerCheck();
     params.wait_for_worker = true;
diff --git a/components/webapps/browser/banners/app_banner_manager.cc b/components/webapps/browser/banners/app_banner_manager.cc
index 403784f..7f99f4d 100644
--- a/components/webapps/browser/banners/app_banner_manager.cc
+++ b/components/webapps/browser/banners/app_banner_manager.cc
@@ -47,6 +47,19 @@
 #include "third_party/skia/include/core/SkBitmap.h"
 
 namespace webapps {
+namespace {
+
+bool IsManifestUrlChange(const InstallableData& result) {
+  if (result.errors.empty()) {
+    return false;
+  }
+  if (result.errors[0] != MANIFEST_URL_CHANGED) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
 
 class AppBannerManager::StatusReporter {
  public:
@@ -276,26 +289,14 @@
 }
 
 bool AppBannerManager::CheckIfShouldShowBanner() {
-  if (ShouldBypassEngagementChecks())
+  if (ShouldBypassEngagementChecks()) {
     return true;
-
-  InstallableStatusCode code = ShouldShowBannerCode();
-  switch (code) {
-    case NO_ERROR_DETECTED:
-      return true;
-    case PREVIOUSLY_BLOCKED:
-      TrackDisplayEvent(DISPLAY_EVENT_BLOCKED_PREVIOUSLY);
-      break;
-    case PREVIOUSLY_IGNORED:
-      TrackDisplayEvent(DISPLAY_EVENT_IGNORED_PREVIOUSLY);
-      break;
-    case PACKAGE_NAME_OR_START_URL_EMPTY:
-      break;
-    default:
-      NOTREACHED();
   }
-  Stop(code);
-  return false;
+  if (GetAppIdentifier().empty()) {
+    Stop(PACKAGE_NAME_OR_START_URL_EMPTY);
+    return false;
+  }
+  return true;
 }
 
 bool AppBannerManager::ShouldDeferToRelatedNonWebApp() const {
@@ -342,37 +343,11 @@
   return false;
 }
 
-bool AppBannerManager::DidRetryInstallableManagerRequest(
-    const InstallableData& result) {
-  if (result.errors.empty())
-    return false;
-  if (result.errors[0] != MANIFEST_URL_CHANGED)
-    return false;
-  ReportStatus(MANIFEST_URL_CHANGED);
-  switch (state_) {
-    case State::FETCHING_MANIFEST:
-    case State::PENDING_INSTALLABLE_CHECK:
-      UpdateState(State::INACTIVE);
-      RequestAppBanner(validated_url_);
-      return true;
-    case State::INACTIVE:
-    case State::ACTIVE:
-    case State::FETCHING_NATIVE_DATA:
-    case State::PENDING_WORKER:
-    case State::PENDING_ENGAGEMENT:
-    case State::SENDING_EVENT:
-    case State::SENDING_EVENT_GOT_EARLY_PROMPT:
-    case State::PENDING_PROMPT_CANCELED:
-    case State::PENDING_PROMPT_NOT_CANCELED:
-    case State::COMPLETE:
-      NOTREACHED();
-      return false;
-  }
-}
-
 void AppBannerManager::OnDidGetManifest(const InstallableData& data) {
-  if (DidRetryInstallableManagerRequest(data))
+  // The pipeline will be restarted from DidUpdateWebManifestURL.
+  if (IsManifestUrlChange(data)) {
     return;
+  }
   UpdateState(State::ACTIVE);
   if (!data.NoBlockingErrors()) {
     Stop(data.errors[0]);
@@ -415,14 +390,6 @@
   return params;
 }
 
-InstallableParams AppBannerManager::ParamsToPerformWorkerCheck() {
-  InstallableParams params;
-  params.has_worker = true;
-  params.wait_for_worker = true;
-
-  return params;
-}
-
 void AppBannerManager::PerformInstallableChecks() {
   PerformInstallableWebAppCheck();
 }
@@ -441,8 +408,10 @@
 
 void AppBannerManager::OnDidPerformInstallableWebAppCheck(
     const InstallableData& data) {
-  if (DidRetryInstallableManagerRequest(data))
+  // The pipeline will be restarted from DidUpdateWebManifestURL.
+  if (IsManifestUrlChange(data)) {
     return;
+  }
 
   UpdateState(State::ACTIVE);
   if (data.valid_manifest)
@@ -481,45 +450,9 @@
   has_maskable_primary_icon_ = data.has_maskable_primary_icon;
   screenshots_ = *(data.screenshots);
 
-  if (features::SkipInstallServiceWorkerCheck() ||
-      base::FeatureList::IsEnabled(features::kCreateShortcutIgnoresManifest)) {
-    SetInstallableWebAppCheckResult(
-        InstallableWebAppCheckResult::kYes_ByUserRequest);
-  }
-
-  if (features::SkipServiceWorkerForInstallPromotion()) {
-    SetInstallableWebAppCheckResult(
-        InstallableWebAppCheckResult::kYes_Promotable);
-    CheckSufficientEngagement();
-    return;
-  }
-
-  PerformServiceWorkerCheck();
-}
-
-void AppBannerManager::PerformServiceWorkerCheck() {
-  UpdateState(State::PENDING_WORKER);
-  manager_->GetData(
-      ParamsToPerformWorkerCheck(),
-      base::BindOnce(&AppBannerManager::OnDidPerformWorkerCheck, GetWeakPtr()));
-}
-
-void AppBannerManager::OnDidPerformWorkerCheck(const InstallableData& data) {
-  if (!data.NoBlockingErrors()) {
-    TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER);
-    Stop(data.FirstNoBlockingError());
-    return;
-  }
-
-  passed_worker_check_ = true;
-
-  if (state_ == State::PENDING_WORKER) {
-    UpdateState(State::ACTIVE);
-
-    SetInstallableWebAppCheckResult(
-        InstallableWebAppCheckResult::kYes_Promotable);
-    CheckSufficientEngagement();
-  }
+  SetInstallableWebAppCheckResult(
+      InstallableWebAppCheckResult::kYes_Promotable);
+  CheckSufficientEngagement();
 }
 
 void AppBannerManager::CheckSufficientEngagement() {
@@ -579,10 +512,6 @@
       TrackBeforeInstallEvent(
           BEFORE_INSTALL_EVENT_PROMPT_NOT_CALLED_NOT_CANCELLED);
       break;
-    case State::PENDING_WORKER:
-      if (!passed_worker_check_)
-        TrackDisplayEvent(DISPLAY_EVENT_LACKS_SERVICE_WORKER);
-      break;
     case State::PENDING_ENGAGEMENT:
       if (!has_sufficient_engagement_)
         TrackDisplayEvent(DISPLAY_EVENT_NOT_VISITED_ENOUGH);
@@ -599,9 +528,6 @@
     case State::PENDING_PROMPT_CANCELED:
     case State::PENDING_PROMPT_NOT_CANCELED:
       return RENDERER_CANCELLED;
-    case State::PENDING_WORKER:
-      return passed_worker_check_ ? NO_ERROR_DETECTED
-                                  : NO_MATCHING_SERVICE_WORKER;
     case State::PENDING_ENGAGEMENT:
       return has_sufficient_engagement_ ? NO_ERROR_DETECTED
                                         : INSUFFICIENT_ENGAGEMENT;
@@ -730,7 +656,6 @@
   ResetCurrentPageData();
 
   if (handle->IsServedFromBackForwardCache()) {
-    UpdateState(State::INACTIVE);
     RequestAppBanner(validated_url_);
   }
 }
@@ -782,12 +707,14 @@
   GURL url = validated_url_;
   switch (state_) {
     case State::INACTIVE:
+      return;
     case State::FETCHING_MANIFEST:
     case State::PENDING_INSTALLABLE_CHECK:
+      UpdateState(State::INACTIVE);
+      RequestAppBanner(validated_url_);
       return;
     case State::ACTIVE:
     case State::FETCHING_NATIVE_DATA:
-    case State::PENDING_WORKER:
     case State::PENDING_ENGAGEMENT:
     case State::SENDING_EVENT:
     case State::SENDING_EVENT_GOT_EARLY_PROMPT:
@@ -801,7 +728,8 @@
         // re-compute that, instead of calling RequestAppBanner, DidFinishLoad
         // is called. That method will re-fetch the engagement data and re-set
         // that field.
-        RecheckInstallabilityForLoadedPage(url, false);
+        ResetCurrentPageData();
+        DidFinishLoad(nullptr, url);
       }
       return;
   }
@@ -866,7 +794,6 @@
     case State::FETCHING_MANIFEST:
     case State::FETCHING_NATIVE_DATA:
     case State::PENDING_INSTALLABLE_CHECK:
-    case State::PENDING_WORKER:
     case State::SENDING_EVENT:
     case State::SENDING_EVENT_GOT_EARLY_PROMPT:
       return true;
@@ -971,12 +898,6 @@
       AppBannerSettingsHelper::APP_BANNER_EVENT_COULD_SHOW, GetCurrentTime());
 }
 
-InstallableStatusCode AppBannerManager::ShouldShowBannerCode() {
-  if (GetAppIdentifier().empty())
-    return PACKAGE_NAME_OR_START_URL_EMPTY;
-  return NO_ERROR_DETECTED;
-}
-
 void AppBannerManager::OnBannerPromptReply(
     mojo::Remote<blink::mojom::AppBannerController> controller,
     blink::mojom::AppBannerPromptReply reply) {
diff --git a/components/webapps/browser/banners/app_banner_manager.h b/components/webapps/browser/banners/app_banner_manager.h
index a1ff6c53..3746b7e 100644
--- a/components/webapps/browser/banners/app_banner_manager.h
+++ b/components/webapps/browser/banners/app_banner_manager.h
@@ -90,9 +90,6 @@
     // engagement to trigger the banner.
     PENDING_ENGAGEMENT,
 
-    // The pipeline is waiting for service worker install to trigger the banner.
-    PENDING_WORKER,
-
     // The beforeinstallprompt event has been sent and the pipeline is waiting
     // for the response.
     SENDING_EVENT,
@@ -310,11 +307,6 @@
   // overwritten with a new app install for the current page.
   virtual bool ShouldAllowWebAppReplacementInstall();
 
-  // Possibly retries the installable manager request given the current state
-  // and the result. Returns |true| if the request was restarted.
-  // Currently only called during requests to InstallationManager
-  bool DidRetryInstallableManagerRequest(const InstallableData& result);
-
   // Callback invoked by the InstallableManager once it has fetched the page's
   // manifest.
   virtual void OnDidGetManifest(const InstallableData& data);
@@ -323,10 +315,6 @@
   // necessary for a web app banner.
   virtual InstallableParams ParamsToPerformInstallableWebAppCheck();
 
-  // Returns an InstallableParams object that requests service worker check
-  // only.
-  virtual InstallableParams ParamsToPerformWorkerCheck();
-
   // Run at the conclusion of OnDidGetManifest. For web app banners, this calls
   // back to the InstallableManager to continue checking criteria. For native
   // app banners, this checks whether native apps are preferred in the manifest,
@@ -340,15 +328,6 @@
   // all other installable properties.
   virtual void OnDidPerformInstallableWebAppCheck(const InstallableData& data);
 
-  // Run at the conclusion of OnDidPerformInstallableWebAppCheck. This calls
-  // back to the InstallableManager to continue checking service worker criteria
-  // for web app banners.
-  virtual void PerformServiceWorkerCheck();
-
-  // Callback invoked by the InstallableManager once it has finished checking
-  // service worker.
-  virtual void OnDidPerformWorkerCheck(const InstallableData& data);
-
   // Records that a banner was shown.
   void RecordDidShowBanner();
 
@@ -470,9 +449,6 @@
   // requesting that it be shown later.
   void DisplayAppBanner() override;
 
-  // Returns a status code indicating whether a banner should be shown.
-  InstallableStatusCode ShouldShowBannerCode();
-
   // Returns a status code based on the current state, to log when terminating.
   InstallableStatusCode TerminationCode() const;
 
diff --git a/components/webapps/browser/features.cc b/components/webapps/browser/features.cc
index 0464ea0..3b71660 100644
--- a/components/webapps/browser/features.cc
+++ b/components/webapps/browser/features.cc
@@ -79,36 +79,16 @@
              "CreateShortcutIgnoresManifest",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Skip the service worker install criteria check for installing. This affect
-// only the "installable" status but not "promotable".
-BASE_FEATURE(kSkipServiceWorkerCheckInstallOnly,
-             "SkipServiceWorkerCheckInstallOnly",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enables showing a detailed install dialog for user installs.
 BASE_FEATURE(kDesktopPWAsDetailedInstallDialog,
              "DesktopPWAsDetailedInstallDialog",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Enables sending the beforeinstallprompt without a service worker check.
-BASE_FEATURE(kSkipServiceWorkerForInstallPrompt,
-             "SkipServiceWorkerForInstallPromot",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Use segmentation to decide whether install prompt should be shown.
 BASE_FEATURE(kInstallPromptSegmentation,
              "InstallPromptSegmentation",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-bool SkipInstallServiceWorkerCheck() {
-  return base::FeatureList::IsEnabled(kSkipServiceWorkerCheckInstallOnly);
-}
-
-bool SkipServiceWorkerForInstallPromotion() {
-  return base::FeatureList::IsEnabled(kSkipServiceWorkerCheckInstallOnly) &&
-         base::FeatureList::IsEnabled(kSkipServiceWorkerForInstallPrompt);
-}
-
 // Keys to use when querying the variations params.
 BASE_FEATURE(kAppBannerTriggering,
              "AppBannerTriggering",
diff --git a/components/webapps/browser/features.h b/components/webapps/browser/features.h
index e940575..5a99b06 100644
--- a/components/webapps/browser/features.h
+++ b/components/webapps/browser/features.h
@@ -43,15 +43,10 @@
 #endif  // BUILDFLAG(IS_ANDROID)
 
 BASE_DECLARE_FEATURE(kCreateShortcutIgnoresManifest);
-BASE_DECLARE_FEATURE(kSkipServiceWorkerCheckInstallOnly);
 BASE_DECLARE_FEATURE(kDesktopPWAsDetailedInstallDialog);
-BASE_DECLARE_FEATURE(kSkipServiceWorkerForInstallPrompt);
 
 BASE_DECLARE_FEATURE(kInstallPromptSegmentation);
 
-bool SkipInstallServiceWorkerCheck();
-bool SkipServiceWorkerForInstallPromotion();
-
 BASE_DECLARE_FEATURE(kAppBannerTriggering);
 extern const base::FeatureParam<double> kBannerParamsEngagementTotalKey;
 extern const base::FeatureParam<int> kBannerParamsDaysAfterBannerDismissedKey;
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager.h b/content/browser/attribution_reporting/attribution_data_host_manager.h
index 89a894f..3d1d62d 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager.h
+++ b/content/browser/attribution_reporting/attribution_data_host_manager.h
@@ -15,7 +15,6 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-forward.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-forward.h"
 
 namespace attribution_reporting {
 class SuitableOrigin;
@@ -71,7 +70,6 @@
   virtual void NotifyNavigationRegistrationStarted(
       const blink::AttributionSrcToken& attribution_src_token,
       const attribution_reporting::SuitableOrigin& source_origin,
-      blink::mojom::AttributionNavigationType nav_type,
       bool is_within_fenced_frame,
       GlobalRenderFrameHostId render_frame_id,
       int64_t navigation_id) = 0;
@@ -88,7 +86,6 @@
       attribution_reporting::SuitableOrigin reporting_origin,
       const attribution_reporting::SuitableOrigin& source_origin,
       AttributionInputEvent input_event,
-      blink::mojom::AttributionNavigationType nav_type,
       bool is_within_fenced_frame,
       GlobalRenderFrameHostId render_frame_id,
       int64_t navigation_id,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
index d236da5..7367862 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.cc
@@ -58,7 +58,6 @@
 #include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom.h"
 #include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -71,7 +70,6 @@
 using ::attribution_reporting::mojom::RegistrationType;
 using ::attribution_reporting::mojom::SourceRegistrationError;
 using ::attribution_reporting::mojom::SourceType;
-using ::blink::mojom::AttributionNavigationType;
 
 // These values are persisted to logs. Entries should not be renumbered and
 // numeric values should never be reused.
@@ -129,17 +127,14 @@
                   RegistrationType registration_type,
                   bool is_within_fenced_frame,
                   AttributionInputEvent input_event,
-                  absl::optional<AttributionNavigationType> nav_type,
                   GlobalRenderFrameHostId render_frame_id,
                   absl::optional<int64_t> navigation_id)
       : context_origin_(std::move(context_origin)),
         registration_type_(registration_type),
         is_within_fenced_frame_(is_within_fenced_frame),
         input_event_(std::move(input_event)),
-        nav_type_(nav_type),
         render_frame_id_(render_frame_id),
         navigation_id_(navigation_id) {
-    DCHECK(!nav_type_ || registration_type_ == RegistrationType::kSource);
     DCHECK(!navigation_id_ || registration_type_ == RegistrationType::kSource);
   }
 
@@ -159,10 +154,6 @@
 
   absl::optional<int64_t> navigation_id() const { return navigation_id_; }
 
-  absl::optional<AttributionNavigationType> nav_type() const {
-    return nav_type_;
-  }
-
   GlobalRenderFrameHostId render_frame_id() const { return render_frame_id_; }
 
   const AttributionInputEvent& input_event() const { return input_event_; }
@@ -184,9 +175,6 @@
   // Logically const.
   AttributionInputEvent input_event_;
 
-  // Logically const.
-  absl::optional<AttributionNavigationType> nav_type_;
-
   // The ID of the topmost render frame host.
   // Logically const.
   GlobalRenderFrameHostId render_frame_id_;
@@ -222,7 +210,6 @@
     blink::AttributionSrcToken attribution_src_token;
 
     // Will not change over the course of the redirect chain.
-    AttributionNavigationType nav_type;
     int64_t navigation_id;
   };
 
@@ -412,11 +399,10 @@
     RegistrationType registration_type,
     GlobalRenderFrameHostId render_frame_id,
     int64_t last_navigation_id) {
-  ReceiverContext receiver_context(std::move(context_origin), registration_type,
-                                   is_within_fenced_frame,
-                                   /*input_event=*/AttributionInputEvent(),
-                                   /*nav_type=*/absl::nullopt, render_frame_id,
-                                   /*navigation_id=*/absl::nullopt);
+  ReceiverContext receiver_context(
+      std::move(context_origin), registration_type, is_within_fenced_frame,
+      /*input_event=*/AttributionInputEvent(), render_frame_id,
+      /*navigation_id=*/absl::nullopt);
 
   switch (registration_type) {
     case RegistrationType::kTrigger:
@@ -534,7 +520,6 @@
 void AttributionDataHostManagerImpl::NotifyNavigationRegistrationStarted(
     const blink::AttributionSrcToken& attribution_src_token,
     const SuitableOrigin& source_origin,
-    AttributionNavigationType nav_type,
     bool is_within_fenced_frame,
     GlobalRenderFrameHostId render_frame_id,
     int64_t navigation_id) {
@@ -555,7 +540,7 @@
     receivers_.Add(this, std::move(it->second.data_host),
                    ReceiverContext(source_origin, RegistrationType::kSource,
                                    is_within_fenced_frame,
-                                   std::move(it->second.input_event), nav_type,
+                                   std::move(it->second.input_event),
                                    render_frame_id, navigation_id));
 
     navigation_data_host_map_.erase(it);
@@ -571,7 +556,6 @@
     SuitableOrigin reporting_origin,
     const SuitableOrigin& source_origin,
     AttributionInputEvent input_event,
-    AttributionNavigationType nav_type,
     bool is_within_fenced_frame,
     GlobalRenderFrameHostId render_frame_id,
     int64_t navigation_id,
@@ -586,7 +570,6 @@
         render_frame_id,
         SourceRegistrations::ForegroundNavigation{
             .attribution_src_token = attribution_src_token,
-            .nav_type = nav_type,
             .navigation_id = navigation_id,
         });
     DCHECK(!it->registrations_complete());
@@ -654,9 +637,8 @@
     return;
   }
 
-  // TODO(csharrison): Remove `nav_type` via reverting crrev.com/c/4070064.
   auto source_type = SourceType::kEvent;
-  if (auto nav_type = context->nav_type()) {
+  if (context->navigation_id().has_value()) {
     source_type = SourceType::kNavigation;
   }
 
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
index 292d82c..1a1f911d 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl.h
@@ -80,7 +80,6 @@
   void NotifyNavigationRegistrationStarted(
       const blink::AttributionSrcToken& attribution_src_token,
       const attribution_reporting::SuitableOrigin& source_origin,
-      blink::mojom::AttributionNavigationType nav_type,
       bool is_within_fenced_frame,
       GlobalRenderFrameHostId render_frame_id,
       int64_t navigation_id) override;
@@ -90,7 +89,6 @@
       attribution_reporting::SuitableOrigin reporting_origin,
       const attribution_reporting::SuitableOrigin& source_origin,
       AttributionInputEvent input_event,
-      blink::mojom::AttributionNavigationType nav_type,
       bool is_within_fenced_frame,
       GlobalRenderFrameHostId render_frame_id,
       int64_t navigation_id,
diff --git a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
index d683570..68719b1e 100644
--- a/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_data_host_manager_impl_unittest.cc
@@ -61,7 +61,6 @@
 #include "third_party/abseil-cpp/absl/numeric/int128.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -78,8 +77,6 @@
 using ::attribution_reporting::mojom::SourceRegistrationError;
 using ::attribution_reporting::mojom::SourceType;
 
-using ::blink::mojom::AttributionNavigationType;
-
 using AttributionFilters = ::attribution_reporting::FiltersDisjunction;
 
 using ::testing::_;
@@ -545,7 +542,6 @@
 
     data_host_manager_.NotifyNavigationRegistrationStarted(
         attribution_src_token, page_origin,
-        AttributionNavigationType::kContextMenu,
         /*is_within_fenced_frame=*/false, kFrameId,
         /*navigation_id=*/kNavigationId);
 
@@ -621,7 +617,6 @@
 
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token, page_origin,
-      AttributionNavigationType::kContextMenu,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
   task_environment_.RunUntilIdle();
@@ -741,7 +736,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -790,7 +784,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -844,7 +837,6 @@
 
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token, source_origin,
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -863,7 +855,7 @@
                      kRegisterSourceJson);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporting_origin, source_origin,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/true);
@@ -903,7 +895,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -952,7 +943,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId, /*navigation_id=*/1);
   mojo::Remote<blink::mojom::AttributionDataHost> trigger_data_host_remote;
   data_host_manager_.RegisterDataHost(
@@ -973,7 +963,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token_2,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId, /*navigation_id=*/2);
   mojo::Remote<blink::mojom::AttributionDataHost> trigger_data_host_remote_2;
   data_host_manager_.RegisterDataHost(
@@ -996,7 +985,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token_3,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId, /*navigation_id=*/3);
   mojo::Remote<blink::mojom::AttributionDataHost> trigger_data_host_remote_3;
   data_host_manager_.RegisterDataHost(
@@ -1042,7 +1030,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -1074,12 +1061,12 @@
 
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationStarted(
-      attribution_src_token, source_site, AttributionNavigationType::kAnchor,
+      attribution_src_token, source_site,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
@@ -1088,7 +1075,7 @@
 
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, /*headers=*/nullptr, reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/true);
@@ -1115,7 +1102,7 @@
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       // The cross to web runtime feature defaults to false.
       network::AttributionReportingRuntimeFeatures(),
@@ -1148,7 +1135,7 @@
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       {network::AttributionReportingRuntimeFeature::kCrossAppWeb},
       /*is_final_response=*/false);
@@ -1176,7 +1163,7 @@
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       {network::AttributionReportingRuntimeFeature::kCrossAppWeb},
       /*is_final_response=*/false);
@@ -1219,7 +1206,7 @@
 
     data_host_manager_.NotifyNavigationRegistrationData(
         attribution_src_token, headers.get(), reporter, source_site,
-        AttributionInputEvent(), AttributionNavigationType::kAnchor,
+        AttributionInputEvent(),
         /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
         {network::AttributionReportingRuntimeFeature::kCrossAppWeb},
         /*is_final_response=*/false);
@@ -1232,7 +1219,7 @@
 
     data_host_manager_.NotifyNavigationRegistrationData(
         attribution_src_token, headers.get(), reporter, source_site,
-        AttributionInputEvent(), AttributionNavigationType::kAnchor,
+        AttributionInputEvent(),
         /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
         {network::AttributionReportingRuntimeFeature::kCrossAppWeb},
         /*is_final_response=*/false);
@@ -1263,7 +1250,7 @@
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       {network::AttributionReportingRuntimeFeature::kCrossAppWeb},
       /*is_final_response=*/false);
@@ -1285,7 +1272,7 @@
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/true);
@@ -1323,7 +1310,7 @@
 
     data_host_manager_.NotifyNavigationRegistrationData(
         attribution_src_token, headers.get(), reporter, source_site,
-        AttributionInputEvent(), AttributionNavigationType::kAnchor,
+        AttributionInputEvent(),
         /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
         network::AttributionReportingRuntimeFeatures(),
         /*is_final_response=*/false);
@@ -1337,7 +1324,7 @@
 
     data_host_manager_.NotifyNavigationRegistrationData(
         attribution_src_token, headers.get(), reporter, source_site,
-        AttributionInputEvent(), AttributionNavigationType::kAnchor,
+        AttributionInputEvent(),
         /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
         network::AttributionReportingRuntimeFeatures(),
         /*is_final_response=*/false);
@@ -1363,13 +1350,13 @@
   const blink::AttributionSrcToken attribution_src_token;
 
   data_host_manager_.NotifyNavigationRegistrationStarted(
-      attribution_src_token, source_site, AttributionNavigationType::kAnchor,
+      attribution_src_token, source_site,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
@@ -1378,7 +1365,7 @@
 
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
@@ -1403,7 +1390,7 @@
 
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationStarted(
-      attribution_src_token, source_site, AttributionNavigationType::kAnchor,
+      attribution_src_token, source_site,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -1413,7 +1400,7 @@
                      "!!!invalid json");
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
@@ -1424,7 +1411,7 @@
                      kRegisterSourceJson);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
@@ -1454,7 +1441,7 @@
 
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationStarted(
-      attribution_src_token, source_site, AttributionNavigationType::kAnchor,
+      attribution_src_token, source_site,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -1463,7 +1450,7 @@
                      kRegisterSourceJson);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
@@ -1480,7 +1467,7 @@
                                                  /*verifications=*/{});
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, /*headers=*/nullptr, reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
@@ -1517,18 +1504,18 @@
 
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationStarted(
-      attribution_src_token, source_site, AttributionNavigationType::kAnchor,
+      attribution_src_token, source_site,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
@@ -1575,20 +1562,20 @@
   const blink::AttributionSrcToken attribution_src_token;
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporter, source_site,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
 
   // Wait for parsing.
   data_host_manager_.NotifyNavigationRegistrationStarted(
-      attribution_src_token, source_site, AttributionNavigationType::kAnchor,
+      attribution_src_token, source_site,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -1669,7 +1656,6 @@
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, /*headers=*/nullptr, reporting_origin,
       source_origin, AttributionInputEvent(),
-      AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/true);
@@ -1722,7 +1708,6 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page1.example"),
-      AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -1790,8 +1775,8 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page.example"),
-      AttributionNavigationType::kAnchor, /*is_within_fenced_frame=*/false,
-      kFrameId, /*navigation_id=*/kNavigationId);
+      /*is_within_fenced_frame=*/false, kFrameId,
+      /*navigation_id=*/kNavigationId);
 
   // kNotFound = 1.
   histograms.ExpectUniqueSample(kNavigationDataHostStatusHistogram, 1, 1);
@@ -1811,8 +1796,8 @@
 
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token, *SuitableOrigin::Deserialize("https://s.test"),
-      AttributionNavigationType::kAnchor, /*is_within_fenced_frame=*/false,
-      kFrameId, /*navigation_id=*/kNavigationId);
+      /*is_within_fenced_frame=*/false, kFrameId,
+      /*navigation_id=*/kNavigationId);
 
   mojo::test::BadMessageObserver bad_message_observer;
 
@@ -1861,8 +1846,8 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       *SuitableOrigin::Deserialize("https://page.example"),
-      AttributionNavigationType::kAnchor, /*is_within_fenced_frame=*/false,
-      kFrameId, /*navigation_id=*/kNavigationId);
+      /*is_within_fenced_frame=*/false, kFrameId,
+      /*navigation_id=*/kNavigationId);
 
   auto reporting_origin =
       *SuitableOrigin::Deserialize("https://reporter.example");
@@ -1953,8 +1938,8 @@
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token,
       /*source_origin=*/*SuitableOrigin::Deserialize("https://source.test"),
-      AttributionNavigationType::kAnchor, /*is_within_fenced_frame=*/true,
-      kFrameId, /*navigation_id=*/kNavigationId);
+      /*is_within_fenced_frame=*/true, kFrameId,
+      /*navigation_id=*/kNavigationId);
 
   data_host_remote->SourceDataAvailable(
       /*reporting_origin=*/*SuitableOrigin::Deserialize("https://report.test"),
@@ -1977,7 +1962,7 @@
       attribution_src_token, headers.get(),
       /*reporting_origin=*/*SuitableOrigin::Deserialize("https://report.test"),
       /*source_origin=*/*SuitableOrigin::Deserialize("https://source.test"),
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/true, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/false);
@@ -2016,7 +2001,7 @@
   data_host_manager_.NotifyNavigationRegistrationData(
       blink::AttributionSrcToken(), /*headers=*/nullptr,
       *SuitableOrigin::Deserialize("https://reporter.example"), source_origin,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/true);
@@ -2051,7 +2036,7 @@
   data_host_manager_.NotifyNavigationRegistrationData(
       blink::AttributionSrcToken(), /*headers=*/nullptr,
       *SuitableOrigin::Create(reporting_origin), source_origin,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/true);
@@ -2100,7 +2085,6 @@
   // This is irrelevant to beacon source registrations.
   data_host_manager_.NotifyNavigationRegistrationStarted(
       blink::AttributionSrcToken(), source_origin,
-      AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -2152,7 +2136,6 @@
   data_host_manager_.NotifyNavigationRegistrationData(
       blink::AttributionSrcToken(), /*headers=*/nullptr, source_origin,
       source_origin, AttributionInputEvent(),
-      AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
@@ -2200,7 +2183,6 @@
   // This is irrelevant to beacon source registrations.
   data_host_manager_.NotifyNavigationRegistrationStarted(
       blink::AttributionSrcToken(), source_origin,
-      AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -2236,7 +2218,6 @@
   // This is irrelevant to beacon source registrations.
   data_host_manager_.NotifyNavigationRegistrationStarted(
       blink::AttributionSrcToken(), source_origin,
-      AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -2275,7 +2256,6 @@
   // This is irrelevant to beacon source registrations.
   data_host_manager_.NotifyNavigationRegistrationStarted(
       blink::AttributionSrcToken(), source_origin,
-      AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 }
@@ -2430,7 +2410,6 @@
 
   data_host_manager_.NotifyNavigationRegistrationStarted(
       attribution_src_token, source_origin,
-      blink::mojom::AttributionNavigationType::kAnchor,
       /*is_within_fenced_frame=*/false, kFrameId,
       /*navigation_id=*/kNavigationId);
 
@@ -2449,7 +2428,7 @@
                      kRegisterSourceJson);
   data_host_manager_.NotifyNavigationRegistrationData(
       attribution_src_token, headers.get(), reporting_origin, source_origin,
-      AttributionInputEvent(), AttributionNavigationType::kAnchor,
+      AttributionInputEvent(),
       /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
       network::AttributionReportingRuntimeFeatures(),
       /*is_final_response=*/true);
@@ -2720,7 +2699,7 @@
     const blink::AttributionSrcToken attribution_src_token;
     data_host_manager_.NotifyNavigationRegistrationData(
         attribution_src_token, headers.get(), reporter, source_site,
-        AttributionInputEvent(), AttributionNavigationType::kAnchor,
+        AttributionInputEvent(),
         /*is_within_fenced_frame=*/false, kFrameId, kNavigationId,
         network::AttributionReportingRuntimeFeatures(),
         /*is_final_response=*/false);
diff --git a/content/browser/attribution_reporting/attribution_host.cc b/content/browser/attribution_reporting/attribution_host.cc
index df0bb17..886b03de 100644
--- a/content/browser/attribution_reporting/attribution_host.cc
+++ b/content/browser/attribution_reporting/attribution_host.cc
@@ -193,7 +193,7 @@
   attribution_manager->GetDataHostManager()
       ->NotifyNavigationRegistrationStarted(
           impression->attribution_src_token, navigation_info.source_origin,
-          impression->nav_type, navigation_info.is_within_fenced_frame,
+          navigation_info.is_within_fenced_frame,
           navigation_info.initiator_root_frame_id,
           navigation_handle->GetNavigationId());
 }
@@ -261,7 +261,7 @@
   attribution_manager->GetDataHostManager()->NotifyNavigationRegistrationData(
       impression->attribution_src_token,
       navigation_handle->GetResponseHeaders(), std::move(*reporting_origin),
-      it->second.source_origin, it->second.input_event, impression->nav_type,
+      it->second.source_origin, it->second.input_event,
       it->second.is_within_fenced_frame, it->second.initiator_root_frame_id,
       navigation_handle->GetNavigationId(), impression->runtime_features,
       is_final_response);
diff --git a/content/browser/attribution_reporting/attribution_host_unittest.cc b/content/browser/attribution_reporting/attribution_host_unittest.cc
index c0f3ad30..2cfc1b9f 100644
--- a/content/browser/attribution_reporting/attribution_host_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_host_unittest.cc
@@ -69,7 +69,6 @@
 using ::testing::Return;
 
 using ::attribution_reporting::mojom::RegistrationType;
-using ::blink::mojom::AttributionNavigationType;
 
 const char kConversionUrl[] = "https://b.com";
 
@@ -180,13 +179,11 @@
 
 TEST_F(AttributionHostTest, ValidAttributionSrc_ForwardedToManager) {
   blink::Impression impression;
-  impression.nav_type = AttributionNavigationType::kWindowOpen;
 
   EXPECT_CALL(*mock_data_host_manager(),
               NotifyNavigationRegistrationStarted(
                   impression.attribution_src_token,
                   *SuitableOrigin::Deserialize("https://secure_impression.com"),
-                  impression.nav_type,
                   /*is_within_fenced_frame=*/false, main_rfh()->GetGlobalId(),
                   /*navigation_id=*/_));
 
@@ -200,7 +197,6 @@
 
 TEST_F(AttributionHostTest, ValidSourceRegistrations_ForwardedToManager) {
   blink::Impression impression;
-  impression.nav_type = AttributionNavigationType::kWindowOpen;
 
   auto redirect_headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
   auto headers = base::MakeRefCounted<net::HttpResponseHeaders>("");
@@ -218,33 +214,29 @@
   const SuitableOrigin d_origin = *SuitableOrigin::Create(d_url);
 
   GlobalRenderFrameHostId frame_id = main_rfh()->GetGlobalId();
-  EXPECT_CALL(
-      *mock_data_host_manager(),
-      NotifyNavigationRegistrationStarted(
-          impression.attribution_src_token, source_origin, impression.nav_type,
-          /*is_within_fenced_frame=*/false, frame_id,
-          /*navigation_id=*/_));
-  EXPECT_CALL(
-      *mock_data_host_manager(),
-      NotifyNavigationRegistrationData(
-          impression.attribution_src_token, redirect_headers.get(),
-          /*reporting_origin=*/b_origin, source_origin, _, impression.nav_type,
-          /*is_within_fenced_frame=*/false, frame_id, _, _,
-          /*is_final_response=*/false));
-  EXPECT_CALL(
-      *mock_data_host_manager(),
-      NotifyNavigationRegistrationData(
-          impression.attribution_src_token, redirect_headers.get(),
-          /*reporting_origin=*/c_origin, source_origin, _, impression.nav_type,
-          /*is_within_fenced_frame=*/false, frame_id, _, _,
-          /*is_final_response=*/false));
-  EXPECT_CALL(
-      *mock_data_host_manager(),
-      NotifyNavigationRegistrationData(
-          impression.attribution_src_token, headers.get(),
-          /*reporting_origin=*/d_origin, source_origin, _, impression.nav_type,
-          /*is_within_fenced_frame=*/false, frame_id, _, _,
-          /*is_final_response=*/true));
+  EXPECT_CALL(*mock_data_host_manager(),
+              NotifyNavigationRegistrationStarted(
+                  impression.attribution_src_token, source_origin,
+                  /*is_within_fenced_frame=*/false, frame_id,
+                  /*navigation_id=*/_));
+  EXPECT_CALL(*mock_data_host_manager(),
+              NotifyNavigationRegistrationData(
+                  impression.attribution_src_token, redirect_headers.get(),
+                  /*reporting_origin=*/b_origin, source_origin, _,
+                  /*is_within_fenced_frame=*/false, frame_id, _, _,
+                  /*is_final_response=*/false));
+  EXPECT_CALL(*mock_data_host_manager(),
+              NotifyNavigationRegistrationData(
+                  impression.attribution_src_token, redirect_headers.get(),
+                  /*reporting_origin=*/c_origin, source_origin, _,
+                  /*is_within_fenced_frame=*/false, frame_id, _, _,
+                  /*is_final_response=*/false));
+  EXPECT_CALL(*mock_data_host_manager(),
+              NotifyNavigationRegistrationData(
+                  impression.attribution_src_token, headers.get(),
+                  /*reporting_origin=*/d_origin, source_origin, _,
+                  /*is_within_fenced_frame=*/false, frame_id, _, _,
+                  /*is_final_response=*/true));
 
   contents()->NavigateAndCommit(GURL("https://secure_impression.com"));
 
@@ -306,7 +298,7 @@
 
   EXPECT_CALL(*mock_data_host_manager(),
               NotifyNavigationRegistrationData(impression.attribution_src_token,
-                                               _, _, _, _, _, _, _, _, _,
+                                               _, _, _, _, _, _, _, _,
                                                /*is_final_response=*/true));
 
   contents()->NavigateAndCommit(GURL("https://secure_impression.com"));
@@ -324,7 +316,7 @@
 
   EXPECT_CALL(*mock_data_host_manager(),
               NotifyNavigationRegistrationData(impression.attribution_src_token,
-                                               _, _, _, _, _, _, _, _, _,
+                                               _, _, _, _, _, _, _, _,
                                                /*is_final_response=*/true));
 
   contents()->NavigateAndCommit(GURL("https://secure_impression.com"));
@@ -692,7 +684,6 @@
 
 TEST_F(AttributionHostTest, ImpressionNavigation_FeaturePolicyChecked) {
   blink::Impression impression;
-  impression.nav_type = AttributionNavigationType::kWindowOpen;
 
   static constexpr char kAllowedOriginUrl[] = "https://a.test";
 
diff --git a/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h b/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h
index 634e85b..1d2411e 100644
--- a/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h
+++ b/content/browser/attribution_reporting/test/mock_attribution_data_host_manager.h
@@ -19,7 +19,6 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-forward.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-forward.h"
 
 namespace net {
 class HttpResponseHeaders;
@@ -55,7 +54,6 @@
               NotifyNavigationRegistrationStarted,
               (const blink::AttributionSrcToken& attribution_src_token,
                const attribution_reporting::SuitableOrigin& source_origin,
-               blink::mojom::AttributionNavigationType,
                bool is_within_fenced_frame,
                GlobalRenderFrameHostId,
                int64_t navigation_id),
@@ -68,7 +66,6 @@
                attribution_reporting::SuitableOrigin reporting_origin,
                const attribution_reporting::SuitableOrigin& source_origin,
                AttributionInputEvent input_event,
-               blink::mojom::AttributionNavigationType,
                bool is_within_fenced_frame,
                GlobalRenderFrameHostId,
                int64_t navigation_id,
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index 7784e60..6214bc2 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -470,9 +470,9 @@
 PrefetchContainer::Reader::GetCurrentNetworkContextToServe() const {
   const SinglePrefetch& this_prefetch = GetCurrentSinglePrefetchToServe();
 
-  const auto& network_context_itr = prefetch_container_.network_contexts_.find(
+  const auto& network_context_itr = prefetch_container_->network_contexts_.find(
       this_prefetch.is_isolated_network_context_required_);
-  if (network_context_itr == prefetch_container_.network_contexts_.end()) {
+  if (network_context_itr == prefetch_container_->network_contexts_.end()) {
     // Not set in unit tests.
     return nullptr;
   }
@@ -584,7 +584,7 @@
 
   // We don't want any of the cookie listeners for this prefetch to pick up
   // changes from the copy.
-  prefetch_container_.StopAllCookieListeners();
+  prefetch_container_->StopAllCookieListeners();
 
   GetCurrentSinglePrefetchToServe().cookie_copy_status_ =
       SinglePrefetch::CookieCopyStatus::kInProgress;
@@ -696,7 +696,7 @@
 
 void PrefetchContainer::Reader::OnPrefetchProbeResult(
     PrefetchProbeResult probe_result) {
-  prefetch_container_.probe_result_ = probe_result;
+  prefetch_container_->probe_result_ = probe_result;
 
   switch (probe_result) {
     case PrefetchProbeResult::kNoProbing:
@@ -705,14 +705,14 @@
       // Wait to update the prefetch status until the probe for the final
       // redirect hop is a success.
       if (index_redirect_chain_to_serve_ ==
-          prefetch_container_.redirect_chain_.size() - 1) {
-        prefetch_container_.SetPrefetchStatus(
+          prefetch_container_->redirect_chain_.size() - 1) {
+        prefetch_container_->SetPrefetchStatus(
             PrefetchStatus::kPrefetchResponseUsed);
       }
       break;
     case PrefetchProbeResult::kDNSProbeFailure:
     case PrefetchProbeResult::kTLSProbeFailure:
-      prefetch_container_.SetPrefetchStatusWithoutUpdatingTriggeringOutcome(
+      prefetch_container_->SetPrefetchStatusWithoutUpdatingTriggeringOutcome(
           PrefetchStatus::kPrefetchNotUsedProbeFailed);
       break;
     default:
@@ -800,8 +800,8 @@
 PrefetchContainer::Reader::GetCurrentSinglePrefetchToServe() const {
   DCHECK(index_redirect_chain_to_serve_ >= 0 &&
          index_redirect_chain_to_serve_ <
-             prefetch_container_.redirect_chain_.size());
-  return *prefetch_container_.redirect_chain_[index_redirect_chain_to_serve_];
+             prefetch_container_->redirect_chain_.size());
+  return *prefetch_container_->redirect_chain_[index_redirect_chain_to_serve_];
 }
 
 const GURL& PrefetchContainer::Reader::GetCurrentURLToServe() const {
diff --git a/content/browser/preloading/prefetch/prefetch_container.h b/content/browser/preloading/prefetch/prefetch_container.h
index c8c99bd..274731c 100644
--- a/content/browser/preloading/prefetch/prefetch_container.h
+++ b/content/browser/preloading/prefetch/prefetch_container.h
@@ -7,6 +7,7 @@
 
 #include <utility>
 
+#include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "content/browser/preloading/prefetch/prefetch_probe_result.h"
@@ -306,7 +307,7 @@
 
     // Currently the lifetime of `Reader` and `PrefetchContainer` are the same
     // and thus this reference is always valid as long as `Reader` is valid.
-    PrefetchContainer& prefetch_container_;
+    const raw_ref<PrefetchContainer> prefetch_container_;
 
     // The index of the element in |prefetch_container_.redirect_chain_| that
     // can be served.
diff --git a/content/browser/renderer_host/navigation_controller_android.cc b/content/browser/renderer_host/navigation_controller_android.cc
index f3d19d1e..2cb16276 100644
--- a/content/browser/renderer_host/navigation_controller_android.cc
+++ b/content/browser/renderer_host/navigation_controller_android.cc
@@ -285,7 +285,6 @@
     blink::Impression impression;
     impression.attribution_src_token =
         GetAttributionSrcTokenFromJavaImpression(env, j_impression).value();
-    impression.nav_type = blink::mojom::AttributionNavigationType::kContextMenu;
     impression.runtime_features =
         GetAttributionRuntimeFeaturesFromJavaImpression(env, j_impression);
     params.impression = impression;
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index fd91b2f2..e987d93 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -1170,7 +1170,7 @@
   // An expiry time for resetting the pending_user_activation_timer_.
   static const base::TimeDelta kActivationNotificationExpireTime;
 
-  raw_ptr<FrameTree> frame_tree_;
+  raw_ptr<FrameTree, LeakedDanglingUntriaged> frame_tree_;
 
   // RenderWidgetHost are either:
   // - Owned by RenderViewHostImpl.
@@ -1212,7 +1212,8 @@
   // dynamically fetching it from `site_instance_group_` since its
   // value gets cleared early in `SiteInstanceGroup` via
   // RenderProcessHostDestroyed before this object is destroyed.
-  const raw_ref<AgentSchedulingGroupHost> agent_scheduling_group_;
+  const raw_ref<AgentSchedulingGroupHost, LeakedDanglingUntriaged>
+      agent_scheduling_group_;
 
   // The SiteInstanceGroup this RenderWidgetHost belongs to.
   // TODO(https://crbug.com/1420333) Turn this into base::SafeRef
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 4e74a049..5d467660 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -14,6 +14,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "components/url_formatter/elide_url.h"
+#include "components/url_formatter/url_formatter.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/devtools/devtools_instrumentation.h"
 #include "content/browser/renderer_host/render_frame_host_impl.h"
@@ -269,7 +270,8 @@
         GURL(url.scheme() + "://" + formatted_url_str),
         url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS));
   }
-  return GURL(url.scheme() + "://" + formatted_url_str).spec();
+  return base::UTF16ToUTF8(
+      url_formatter::FormatUrl(GURL(url.scheme() + "://" + formatted_url_str)));
 }
 
 std::string FormatOriginForDisplay(const url::Origin& origin) {
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 24cf889f..b2cc4ba 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -4246,4 +4246,40 @@
       FederatedAuthRequestImpl::ShouldMediateAuthz({"profile", "email"}));
 }
 
+class FederatedAuthRequestImplNewTabTest : public FederatedAuthRequestImplTest {
+ protected:
+  void SetUp() override {
+    RenderViewHostImplTestHarness::SetUp();
+    test_api_permission_delegate_ =
+        std::make_unique<TestApiPermissionDelegate>();
+    test_permission_delegate_ = std::make_unique<TestPermissionDelegate>();
+    test_auto_reauthn_permission_delegate_ =
+        std::make_unique<TestAutoReauthnPermissionDelegate>();
+    test_identity_registry_ = std::make_unique<TestIdentityRegistry>(
+        web_contents(), federated_auth_request_impl_,
+        url::Origin::Create(GURL(kIdpUrl)));
+
+    static_cast<TestWebContents*>(web_contents())
+        ->NavigateAndCommit(GURL("chrome://newtab/"), ui::PAGE_TRANSITION_LINK);
+
+    federated_auth_request_impl_ = &FederatedAuthRequestImpl::CreateForTesting(
+        *main_test_rfh(), test_api_permission_delegate_.get(),
+        test_auto_reauthn_permission_delegate_.get(),
+        test_permission_delegate_.get(), test_identity_registry_.get(),
+        request_remote_.BindNewPipeAndPassReceiver());
+
+    std::unique_ptr<TestIdpNetworkRequestManager> network_request_manager =
+        std::make_unique<TestIdpNetworkRequestManager>();
+    SetNetworkRequestManager(std::move(network_request_manager));
+
+    federated_auth_request_impl_->SetTokenRequestDelayForTests(
+        base::TimeDelta());
+  }
+};
+
+TEST_F(FederatedAuthRequestImplNewTabTest, SuccessfulFlow) {
+  RunAuthTest(kDefaultRequestParameters, kExpectationSuccess,
+              kConfigurationValid);
+}
+
 }  // namespace content
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.cc b/device/bluetooth/floss/bluetooth_adapter_floss.cc
index 695b074e..e5c698d 100644
--- a/device/bluetooth/floss/bluetooth_adapter_floss.cc
+++ b/device/bluetooth/floss/bluetooth_adapter_floss.cc
@@ -835,6 +835,32 @@
   BLUETOOTH_LOG(EVENT) << __func__ << device_cleared;
 }
 
+void BluetoothAdapterFloss::AdapterDevicePropertyChanged(
+    FlossAdapterClient::BtPropertyType prop_type,
+    const FlossDeviceId& device) {
+  DCHECK(FlossDBusManager::Get());
+  DCHECK(IsPresent());
+
+  BLUETOOTH_LOG(EVENT) << __func__ << device;
+
+  BluetoothDeviceFloss* device_ptr =
+      static_cast<BluetoothDeviceFloss*>(GetDevice(device.address));
+
+  if (!device_ptr) {
+    return;
+  }
+
+  switch (prop_type) {
+    case FlossAdapterClient::BtPropertyType::kBdName:
+      if (device.name.size() != 0) {
+        device_ptr->SetName(device.name);
+        NotifyDeviceChanged(device_ptr);
+      }
+      break;
+    default:;  // Do nothing for other property types for now
+  }
+}
+
 void BluetoothAdapterFloss::AdapterSspRequest(
     const FlossDeviceId& remote_device,
     uint32_t cod,
diff --git a/device/bluetooth/floss/bluetooth_adapter_floss.h b/device/bluetooth/floss/bluetooth_adapter_floss.h
index d13d410a8..5f93ad68 100644
--- a/device/bluetooth/floss/bluetooth_adapter_floss.h
+++ b/device/bluetooth/floss/bluetooth_adapter_floss.h
@@ -273,6 +273,9 @@
   void AdapterDiscoveringChanged(bool state) override;
   void AdapterFoundDevice(const FlossDeviceId& device_found) override;
   void AdapterClearedDevice(const FlossDeviceId& device_found) override;
+  void AdapterDevicePropertyChanged(
+      FlossAdapterClient::BtPropertyType prop_type,
+      const FlossDeviceId& device) override;
   void AdapterSspRequest(const FlossDeviceId& remote_device,
                          uint32_t cod,
                          FlossAdapterClient::BluetoothSspVariant variant,
diff --git a/device/bluetooth/floss/floss_adapter_client.cc b/device/bluetooth/floss/floss_adapter_client.cc
index 188c498..51bf9fa 100644
--- a/device/bluetooth/floss/floss_adapter_client.cc
+++ b/device/bluetooth/floss/floss_adapter_client.cc
@@ -239,6 +239,12 @@
       base::BindOnce(&HandleExported, adapter::kOnDeviceCleared));
 
   callbacks->ExportMethod(
+      adapter::kCallbackInterface, adapter::kOnDevicePropertiesChanged,
+      base::BindRepeating(&FlossAdapterClient::OnDevicePropertiesChanged,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&HandleExported, adapter::kOnDevicePropertiesChanged));
+
+  callbacks->ExportMethod(
       adapter::kCallbackInterface, adapter::kOnDiscoveringChanged,
       base::BindRepeating(&FlossAdapterClient::OnDiscoveringChanged,
                           weak_ptr_factory_.GetWeakPtr()),
@@ -435,6 +441,32 @@
   std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
 }
 
+void FlossAdapterClient::OnDevicePropertiesChanged(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+  FlossDeviceId device;
+  std::vector<uint32_t> props;
+
+  DVLOG(1) << __func__;
+
+  if (!ReadAllDBusParams(&reader, &device, &props)) {
+    std::move(response_sender)
+        .Run(dbus::ErrorResponse::FromMethodCall(
+            method_call, kErrorInvalidParameters, std::string()));
+    return;
+  }
+
+  for (auto& prop : props) {
+    BtPropertyType prop_type = static_cast<BtPropertyType>(prop);
+    for (auto& observer : observers_) {
+      observer.AdapterDevicePropertyChanged(prop_type, device);
+    }
+  }
+
+  std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
+}
+
 void FlossAdapterClient::OnSspRequest(
     dbus::MethodCall* method_call,
     dbus::ExportedObject::ResponseSender response_sender) {
diff --git a/device/bluetooth/floss/floss_adapter_client.h b/device/bluetooth/floss/floss_adapter_client.h
index f5e6898..3603804c 100644
--- a/device/bluetooth/floss/floss_adapter_client.h
+++ b/device/bluetooth/floss/floss_adapter_client.h
@@ -123,6 +123,10 @@
     // some amount of time ago).
     virtual void AdapterClearedDevice(const FlossDeviceId& device_cleared) {}
 
+    // Notification sent when a device property has changed.
+    virtual void AdapterDevicePropertyChanged(BtPropertyType prop_type,
+                                              const FlossDeviceId& device) {}
+
     // Notification sent for Simple Secure Pairing.
     virtual void AdapterSspRequest(const FlossDeviceId& remote_device,
                                    uint32_t cod,
@@ -342,6 +346,11 @@
   void OnDeviceCleared(dbus::MethodCall* method_call,
                        dbus::ExportedObject::ResponseSender response_sender);
 
+  // Handle callback |OnDevicePropertiesChanged| on exported object path.
+  void OnDevicePropertiesChanged(
+      dbus::MethodCall* method_call,
+      dbus::ExportedObject::ResponseSender response_sender);
+
   // Handle callback |OnSspRequest| on exported object path.
   void OnSspRequest(dbus::MethodCall* method_call,
                     dbus::ExportedObject::ResponseSender response_sender);
diff --git a/device/bluetooth/floss/floss_adapter_client_unittest.cc b/device/bluetooth/floss/floss_adapter_client_unittest.cc
index ec9f12f..fd0a3156 100644
--- a/device/bluetooth/floss/floss_adapter_client_unittest.cc
+++ b/device/bluetooth/floss/floss_adapter_client_unittest.cc
@@ -164,7 +164,7 @@
     // Exported callback methods that we don't need to invoke.  This will need
     // to be updated once new callbacks are added.
     // TODO(b/233124093): Reduce this count by 2 when SDP tests are added.
-    EXPECT_CALL(*exported_callbacks_.get(), ExportMethod).Times(12);
+    EXPECT_CALL(*exported_callbacks_.get(), ExportMethod).Times(13);
 
     // Save the method handlers of exported callbacks that we need to invoke in
     // test.
diff --git a/device/bluetooth/floss/floss_dbus_client.cc b/device/bluetooth/floss/floss_dbus_client.cc
index 962fe61..1d0fced 100644
--- a/device/bluetooth/floss/floss_dbus_client.cc
+++ b/device/bluetooth/floss/floss_dbus_client.cc
@@ -91,6 +91,7 @@
 const char kOnDiscoverableChanged[] = "OnDiscoverableChanged";
 const char kOnDeviceFound[] = "OnDeviceFound";
 const char kOnDeviceCleared[] = "OnDeviceCleared";
+const char kOnDevicePropertiesChanged[] = "OnDevicePropertiesChanged";
 const char kOnDiscoveringChanged[] = "OnDiscoveringChanged";
 const char kOnSspRequest[] = "OnSspRequest";
 const char kOnPinDisplay[] = "OnPinDisplay";
diff --git a/device/bluetooth/floss/floss_dbus_client.h b/device/bluetooth/floss/floss_dbus_client.h
index 2199ecb..f5ad0756 100644
--- a/device/bluetooth/floss/floss_dbus_client.h
+++ b/device/bluetooth/floss/floss_dbus_client.h
@@ -93,6 +93,7 @@
 extern DEVICE_BLUETOOTH_EXPORT const char kOnDiscoverableChanged[];
 extern DEVICE_BLUETOOTH_EXPORT const char kOnDeviceFound[];
 extern DEVICE_BLUETOOTH_EXPORT const char kOnDeviceCleared[];
+extern DEVICE_BLUETOOTH_EXPORT const char kOnDevicePropertiesChanged[];
 extern DEVICE_BLUETOOTH_EXPORT const char kOnDiscoveringChanged[];
 extern DEVICE_BLUETOOTH_EXPORT const char kOnSspRequest[];
 extern DEVICE_BLUETOOTH_EXPORT const char kOnPinDisplay[];
diff --git a/docs/mac_lld.md b/docs/mac_lld.md
index fd6e9fe..f37e18b 100644
--- a/docs/mac_lld.md
+++ b/docs/mac_lld.md
@@ -1,9 +1,6 @@
 # LLD for Mac builds
 
-Like on most other platforms, Chromium uses the LLD linker on iOS and macOS.
-This is a recent change. If things go well, it will ship in m95,
-but we might discover showstopper bugs that delay things a bit.
-
+Like on other platforms, Chromium uses the LLD linker on iOS and macOS.
 
 ## Background
 
@@ -13,9 +10,9 @@
 Chrome OS, Fuchsia), and it's faster than other COFF linkers (the executable
 file format on Windows).
 
-LLD is currently 3-4x as fast as ld64, the macOS system linker, at linking
+LLD is currently twice as fast as ld64, the macOS system linker, at linking
 Chromium Framework in symbol\_level=0 release builds, despite ld64 being already
-fast.
+fast. (Before Xcode 14.1, LLD was 6x as fast as ld64.)
 
 LLD has advantages unrelated to speed, too:
 
@@ -47,16 +44,9 @@
 
 ## Current status and known issues
 
-LLD is used by default in almost all build configurations. All
-tests on all bots are passing, both Intel and Arm.
-
-There are a few open issues:
-
-- LLD implements deduplication (aka "ICF"), but we don't yet
-  enable it in arm builds ([bug](https://crbug.com/1253924)).
-- LLD doesn't yet implement call graph profile sort.
-- LTO support in LLD/macOS isn't complete yet, and we don't use LTO
-  on macOS yet for that reason.
+LLD is used by default in all build configurations.
+All tests on all bots are passing, both Intel and Arm.
+Most things even work.
 
 ## Hacking on LLD
 
diff --git a/gin/interceptor.h b/gin/interceptor.h
index 8d2363d5..37f3258 100644
--- a/gin/interceptor.h
+++ b/gin/interceptor.h
@@ -37,7 +37,7 @@
       v8::Isolate* isolate);
 
  private:
-  raw_ptr<v8::Isolate> isolate_;
+  raw_ptr<v8::Isolate, LeakedDanglingUntriaged> isolate_;
   raw_ptr<WrappableBase> base_;
 };
 
@@ -59,7 +59,7 @@
       v8::Isolate* isolate);
 
  private:
-  raw_ptr<v8::Isolate> isolate_;
+  raw_ptr<v8::Isolate, LeakedDanglingUntriaged> isolate_;
   raw_ptr<WrappableBase> base_;
 };
 
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index d65f291b..3e2f4bc 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -252,8 +252,8 @@
   crash_key.Set(
       base::StringPrintf("%u", static_cast<uint32_t>(gr_context_type_)));
 
-  if (gpu_preferences.gr_context_type == GrContextType::kGraphiteDawn ||
-      gpu_preferences.gr_context_type == GrContextType::kGraphiteMetal) {
+  if (gr_context_type_ == GrContextType::kGraphiteDawn ||
+      gr_context_type_ == GrContextType::kGraphiteMetal) {
     return InitializeGraphite(gpu_preferences);
   }
 
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index e6c1d817..c1b21a2 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -56125,7 +56125,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -56215,7 +56215,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -56491,7 +56491,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -56581,7 +56581,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -56835,7 +56835,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57111,7 +57111,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57298,7 +57298,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57470,7 +57470,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57560,7 +57560,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57650,7 +57650,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57741,7 +57741,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57831,7 +57831,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -57921,7 +57921,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58011,7 +58011,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58192,7 +58192,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58282,7 +58282,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58372,7 +58372,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
@@ -58462,7 +58462,7 @@
       dimensions: "builderless:1"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-18.04"
+      dimensions: "os:Ubuntu-22.04"
       dimensions: "pool:luci.chromium.try"
       dimensions: "ssd:0"
       exe {
diff --git a/infra/config/lib/linux-default.json b/infra/config/lib/linux-default.json
index fdee148..99921aa 100644
--- a/infra/config/lib/linux-default.json
+++ b/infra/config/lib/linux-default.json
@@ -96,11 +96,29 @@
     "win11-wpt-content-shell-fyi-rel": "Ubuntu-22.04"
   },
   "try": {
+    "android-11-x86-rel": "Ubuntu-22.04",
+    "android-12-x64-dbg": "Ubuntu-22.04",
     "android-12-x64-rel": "Ubuntu-22.04",
     "android-12-x64-rel-compilator": "Ubuntu-22.04",
+    "android-12l-x64-dbg": "Ubuntu-22.04",
+    "android-13-x64-rel": "Ubuntu-22.04",
+    "android-arm64-all-targets-dbg": "Ubuntu-22.04",
     "android-arm64-rel": "Ubuntu-22.04",
     "android-arm64-rel-compilator": "Ubuntu-22.04",
+    "android-bfcache-rel": "Ubuntu-22.04",
     "android-binary-size": "Ubuntu-22.04",
+    "android-chrome-pie-x86-wpt-fyi-rel": "Ubuntu-22.04",
+    "android-code-coverage": "Ubuntu-22.04",
+    "android-code-coverage-native": "Ubuntu-22.04",
+    "android-cronet-arm-dbg": "Ubuntu-22.04",
+    "android-cronet-arm64-dbg": "Ubuntu-22.04",
+    "android-cronet-arm64-rel": "Ubuntu-22.04",
+    "android-cronet-asan-arm-rel": "Ubuntu-22.04",
+    "android-cronet-x86-dbg": "Ubuntu-22.04",
+    "android-cronet-x86-dbg-11-tests": "Ubuntu-22.04",
+    "android-cronet-x86-dbg-oreo-tests": "Ubuntu-22.04",
+    "android-cronet-x86-dbg-pie-tests": "Ubuntu-22.04",
+    "android-cronet-x86-rel": "Ubuntu-22.04",
     "android-nougat-x86-rel": "Ubuntu-22.04",
     "android-x64-cast": "Ubuntu-22.04",
     "android_compile_dbg": "Ubuntu-22.04",
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.mm b/ios/chrome/app/application_delegate/metrics_mediator.mm
index a9b565ca5..d78b141 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator.mm
@@ -317,30 +317,30 @@
 + (void)recordInactiveTabsSettingsAtStartup:(int)preference;
 // Logs the number of active tabs (based on the arm's definition of
 // active/inactive).
-+ (void)recordNumActiveTabAtStartup:(int)numTabs;
++ (void)recordStartupActiveTabCount:(int)tabCount;
 // Logs the number of inactive tabs (based on the arm's definition of
 // active/inactive).
-+ (void)recordNumInactiveTabAtStartup:(int)numTabs;
++ (void)recordStartupInactiveTabCount:(int)tabCount;
 // Logs the number of tabs older than 21 days.
-+ (void)recordNumAbsoluteInactiveTabAtStartup:(int)numTabs;
++ (void)recordStartupAbsoluteInactiveTabCount:(int)tabCount;
 // Logs the number of tabs with UMAHistogramCount100 and allows testing.
-+ (void)recordNumTabAtStartup:(int)numTabs;
++ (void)recordStartupTabCount:(int)tabCount;
 // Logs the number of tabs with UMAHistogramCount100 and allows testing.
-+ (void)recordNumTabAtResume:(int)numTabs;
++ (void)recordResumeTabCount:(int)tabCount;
 // Logs the number of NTP tabs with UMAHistogramCount100 and allows testing.
-+ (void)recordNumNTPTabAtStartup:(int)numTabs;
++ (void)recordStartupNTPTabCount:(int)tabCount;
 // Logs the number of NTP tabs with UMAHistogramCount100 and allows testing.
-+ (void)recordNumNTPTabAtResume:(int)numTabs;
++ (void)recordResumeNTPTabCount:(int)tabCount;
 // Logs the number of live NTP tabs with UMAHistogramCount100 and allows
 // testing.
-+ (void)recordNumLiveNTPTabAtResume:(int)numTabs;
++ (void)recordResumeLiveNTPTabCount:(int)tabCount;
 
 // Logs the number of old (inactive for more than 7 days) tabs with
 // UMAHistogramCount100 and allows testing.
-+ (void)recordNumOldTabAtStartup:(int)numTabs;
++ (void)recordStartupOldTabCount:(int)tabCount;
 // Logs the number of duplicated tabs with UMAHistogramCount100 and allows
 // testing.
-+ (void)recordNumDuplicatedTabAtStartup:(int)numTabs;
++ (void)recordStartupDuplicatedTabCount:(int)tabCount;
 // Logs the age (time elapsed since creation) of each tab  and allows testing.
 + (void)recordTabsAgeAtStartup:(const std::vector<base::TimeDelta>&)tabsAge;
 // Returns a corresponding TabAgeGroup for provided `timeSinceCreation` time.
@@ -412,14 +412,14 @@
                                connectedScenes:(NSArray<SceneState*>*)scenes {
   RecordAndResetUkmLogSizeOnSuccessCounter();
 
-  int numTabs = 0;
-  int numNTPTabs = 0;
-  int numLiveNTPTabs = 0;
-  int numOldTabs = 0;
-  int numDuplicatedTabs = 0;
-  int numActiveTabs = 0;
-  int numInactiveTabs = 0;
-  int numAbsoluteInactiveTabs = 0;
+  int tabCount = 0;
+  int NTPTabCount = 0;
+  int liveNTPTabCount = 0;
+  int oldTabCount = 0;
+  int duplicatedTabCount = 0;
+  int activeTabCount = 0;
+  int inactiveTabCount = 0;
+  int absoluteInactiveTabCount = 0;
 
   // Amount of time after which a tab is considered as old.
   constexpr base::TimeDelta kOldTabThreshold = base::Days(7);
@@ -448,11 +448,11 @@
     const int webStateListCount = webStateList->count();
     const int inactiveWebStateListCount = inactiveWebStateList->count();
 
-    numTabs += webStateListCount + inactiveWebStateListCount;
-    numActiveTabs += webStateListCount;
-    numInactiveTabs += inactiveWebStateListCount;
+    tabCount += webStateListCount + inactiveWebStateListCount;
+    activeTabCount += webStateListCount;
+    inactiveTabCount += inactiveWebStateListCount;
     // All inactive tabs are inactive since minimum 7 days or more.
-    numOldTabs += inactiveWebStateListCount;
+    oldTabCount += inactiveWebStateListCount;
 
     for (int i = 0; i < webStateListCount; i++) {
       web::WebState* webState = webStateList->GetWebStateAt(i);
@@ -461,13 +461,13 @@
 
       // Count NTPs.
       if (IsURLNewTabPage(URL)) {
-        numNTPTabs++;
+        NTPTabCount++;
       }
 
       // Count duplicate URLs.
       NSString* URLString = base::SysUTF8ToNSString(URL.GetWithoutRef().spec());
       if ([uniqueURLs containsObject:URLString]) {
-        numDuplicatedTabs++;
+        duplicatedTabCount++;
       } else {
         [uniqueURLs addObject:URLString];
       }
@@ -475,10 +475,10 @@
       // Count old tabs.
       base::TimeDelta inactiveTime = now - webState->GetLastActiveTime();
       if (inactiveTime > kOldTabThreshold) {
-        numOldTabs++;
+        oldTabCount++;
         // Count absolute inactive tabs.
         if (inactiveTime > kAbsoluteInactiveTabThreshold) {
-          numAbsoluteInactiveTabs++;
+          absoluteInactiveTabCount++;
         }
       }
 
@@ -500,7 +500,7 @@
       base::TimeDelta inactiveTime =
           base::Time::Now() - webState->GetLastActiveTime();
       if (inactiveTime > kAbsoluteInactiveTabThreshold) {
-        numAbsoluteInactiveTabs++;
+        absoluteInactiveTabCount++;
       }
     }
   }
@@ -509,19 +509,19 @@
     [self recordInactiveTabsSettingsAtStartup:
               GetApplicationContext()->GetLocalState()->GetInteger(
                   prefs::kInactiveTabsTimeThreshold)];
-    [self recordNumActiveTabAtStartup:numActiveTabs];
-    [self recordNumInactiveTabAtStartup:numInactiveTabs];
-    [self recordNumAbsoluteInactiveTabAtStartup:numAbsoluteInactiveTabs];
-    [self recordNumTabAtStartup:numTabs];
-    [self recordNumNTPTabAtStartup:numNTPTabs];
-    [self recordNumOldTabAtStartup:numOldTabs];
-    [self recordNumDuplicatedTabAtStartup:numDuplicatedTabs];
+    [self recordStartupActiveTabCount:activeTabCount];
+    [self recordStartupInactiveTabCount:inactiveTabCount];
+    [self recordStartupAbsoluteInactiveTabCount:absoluteInactiveTabCount];
+    [self recordStartupTabCount:tabCount];
+    [self recordStartupNTPTabCount:NTPTabCount];
+    [self recordStartupOldTabCount:oldTabCount];
+    [self recordStartupDuplicatedTabCount:duplicatedTabCount];
     [self recordTabsAgeAtStartup:timesSinceCreation];
   } else {
-    [self recordNumTabAtResume:numTabs];
-    [self recordNumNTPTabAtResume:numNTPTabs];
+    [self recordResumeTabCount:tabCount];
+    [self recordResumeNTPTabCount:NTPTabCount];
     // Only log at resume since there are likely no live NTPs on startup.
-    [self recordNumLiveNTPTabAtResume:numLiveNTPTabs];
+    [self recordResumeLiveNTPTabCount:liveNTPTabCount];
   }
 
   if (UIAccessibilityIsVoiceOverRunning()) {
@@ -729,44 +729,44 @@
                             InactiveTabsSettingFromPreference(preference));
 }
 
-+ (void)recordNumActiveTabAtStartup:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.ActiveCountAtStartup", numTabs);
++ (void)recordStartupActiveTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.ActiveCountAtStartup", tabCount);
 }
 
-+ (void)recordNumInactiveTabAtStartup:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.InactiveCountAtStartup", numTabs);
++ (void)recordStartupInactiveTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.InactiveCountAtStartup", tabCount);
 }
 
-+ (void)recordNumAbsoluteInactiveTabAtStartup:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.OldCountAtStartup", numTabs);
++ (void)recordStartupAbsoluteInactiveTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.OldCountAtStartup", tabCount);
 }
 
-+ (void)recordNumTabAtStartup:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.CountAtStartup", numTabs);
++ (void)recordStartupTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.CountAtStartup", tabCount);
 }
 
-+ (void)recordNumTabAtResume:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.CountAtResume", numTabs);
++ (void)recordResumeTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.CountAtResume", tabCount);
 }
 
-+ (void)recordNumNTPTabAtStartup:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.NTPCountAtStartup", numTabs);
++ (void)recordStartupNTPTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.NTPCountAtStartup", tabCount);
 }
 
-+ (void)recordNumNTPTabAtResume:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.NTPCountAtResume", numTabs);
++ (void)recordResumeNTPTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.NTPCountAtResume", tabCount);
 }
 
-+ (void)recordNumLiveNTPTabAtResume:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.LiveNTPCountAtResume", numTabs);
++ (void)recordResumeLiveNTPTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.LiveNTPCountAtResume", tabCount);
 }
 
-+ (void)recordNumOldTabAtStartup:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.UnusedCountAtStartup", numTabs);
++ (void)recordStartupOldTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.UnusedCountAtStartup", tabCount);
 }
 
-+ (void)recordNumDuplicatedTabAtStartup:(int)numTabs {
-  base::UmaHistogramCounts100("Tabs.DuplicatesCountAtStartup", numTabs);
++ (void)recordStartupDuplicatedTabCount:(int)tabCount {
+  base::UmaHistogramCounts100("Tabs.DuplicatesCountAtStartup", tabCount);
 }
 
 + (void)recordTabsAgeAtStartup:(const std::vector<base::TimeDelta>&)tabsAge {
diff --git a/ios/chrome/app/application_delegate/metrics_mediator_testing.h b/ios/chrome/app/application_delegate/metrics_mediator_testing.h
index 6bd97b5..9b56af25 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator_testing.h
+++ b/ios/chrome/app/application_delegate/metrics_mediator_testing.h
@@ -8,11 +8,11 @@
 #include "net/base/network_change_notifier.h"
 
 @interface MetricsMediator (TestingAddition)
-+ (void)recordNumTabAtStartup:(int)numTabs;
-+ (void)recordNumTabAtResume:(int)numTabs;
-+ (void)recordNumNTPTabAtStartup:(int)numTabs;
-+ (void)recordNumNTPTabAtResume:(int)numTabs;
-+ (void)recordNumLiveNTPTabAtResume:(int)numTabs;
++ (void)recordStartupTabCount:(int)tabCount;
++ (void)recordResumeTabCount:(int)tabCount;
++ (void)recordStartupNTPTabCount:(int)tabCount;
++ (void)recordResumeNTPTabCount:(int)tabCount;
++ (void)recordResumeLiveNTPTabCount:(int)tabCount;
 @end
 
 #endif  // IOS_CHROME_APP_APPLICATION_DELEGATE_METRICS_MEDIATOR_TESTING_H_
diff --git a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
index 98b1b09..7bbbc03 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator_unittest.mm
@@ -128,20 +128,20 @@
     } copy];
     if (coldStart) {
       tabs_uma_histogram_swizzler_.reset(new ScopedBlockSwizzler(
-          [MetricsMediator class], @selector(recordNumTabAtStartup:),
+          [MetricsMediator class], @selector(recordStartupTabCount:),
           num_tabs_swizzle_block_));
       ntp_tabs_uma_histogram_swizzler_.reset(new ScopedBlockSwizzler(
-          [MetricsMediator class], @selector(recordNumNTPTabAtStartup:),
+          [MetricsMediator class], @selector(recordStartupNTPTabCount:),
           num_ntp_tabs_swizzle_block_));
     } else {
       tabs_uma_histogram_swizzler_.reset(new ScopedBlockSwizzler(
-          [MetricsMediator class], @selector(recordNumTabAtResume:),
+          [MetricsMediator class], @selector(recordResumeTabCount:),
           num_tabs_swizzle_block_));
       ntp_tabs_uma_histogram_swizzler_.reset(new ScopedBlockSwizzler(
-          [MetricsMediator class], @selector(recordNumNTPTabAtResume:),
+          [MetricsMediator class], @selector(recordResumeNTPTabCount:),
           num_ntp_tabs_swizzle_block_));
       live_ntp_tabs_uma_histogram_swizzler_.reset(new ScopedBlockSwizzler(
-          [MetricsMediator class], @selector(recordNumLiveNTPTabAtResume:),
+          [MetricsMediator class], @selector(recordResumeLiveNTPTabCount:),
           num_live_ntp_tabs_swizzle_block_));
     }
   }
diff --git a/ios/chrome/browser/crash_report/crash_report_helper.mm b/ios/chrome/browser/crash_report/crash_report_helper.mm
index 4272182..4003504 100644
--- a/ios/chrome/browser/crash_report/crash_report_helper.mm
+++ b/ios/chrome/browser/crash_report/crash_report_helper.mm
@@ -160,19 +160,26 @@
 
 #pragma mark - WebStateListObserving protocol
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      [self removeTabId:replaceChange.replaced_web_state()
+                            ->GetStableIdentifier()];
+      break;
+    }
+  }
+}
+
 - (void)webStateList:(WebStateList*)webStateList
     didDetachWebState:(web::WebState*)webState
               atIndex:(int)atIndex {
   [self removeTabId:webState->GetStableIdentifier()];
 }
 
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
-  [self removeTabId:oldWebState->GetStableIdentifier()];
-}
-
 #pragma mark - CRWWebStateObserver protocol
 
 - (void)webState:(web::WebState*)webState
diff --git a/ios/chrome/browser/flags/BUILD.gn b/ios/chrome/browser/flags/BUILD.gn
index ca5638f..10569bd 100644
--- a/ios/chrome/browser/flags/BUILD.gn
+++ b/ios/chrome/browser/flags/BUILD.gn
@@ -85,6 +85,7 @@
     "//ios/chrome/browser/ui/post_restore_signin:features",
     "//ios/chrome/browser/ui/start_surface:feature_flags",
     "//ios/chrome/browser/ui/whats_new:feature_flags",
+    "//ios/chrome/browser/ui/whats_new:util",
     "//ios/chrome/browser/web:feature_flags",
     "//ios/components/security_interstitials/https_only_mode:feature",
     "//ios/public/provider/chrome/browser/app_utils:app_utils_api",
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 5e7462c..e1a5f696 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -98,6 +98,7 @@
 #import "ios/chrome/browser/ui/post_restore_signin/features.h"
 #import "ios/chrome/browser/ui/start_surface/start_surface_features.h"
 #import "ios/chrome/browser/ui/whats_new/feature_flags.h"
+#import "ios/chrome/browser/ui/whats_new/whats_new_util.h"
 #import "ios/chrome/browser/web/features.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/components/security_interstitials/https_only_mode/feature.h"
@@ -1238,6 +1239,9 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(kWhatsNewIOS,
                                     kWhatsNewLayoutVariations,
                                     "WhatsNewLayoutVariations")},
+    {"whats-new-ios-m116", flag_descriptions::kWhatsNewIOSM116Name,
+     flag_descriptions::kWhatsNewIOSM116Description, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kWhatsNewIOSM116)},
     {"ios-autofill-branding", flag_descriptions::kAutofillBrandingIOSName,
      flag_descriptions::kAutofillBrandingIOSDescription, flags_ui::kOsIos,
      FEATURE_WITH_PARAMS_VALUE_TYPE(autofill::features::kAutofillBrandingIOS,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 042b2b6..241bc89 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -1091,6 +1091,10 @@
 const char kWhatsNewIOSDescription[] =
     "When enabled, What's New will display new features and chrome tips.";
 
+const char kWhatsNewIOSM116Name[] = "Enable What's New M116.";
+const char kWhatsNewIOSM116Description[] =
+    "When enabled, What's New will display new features and a chrome tip.";
+
 // Please insert your name/description above in alphabetical order.
 
 }  // namespace flag_descriptions
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index ecafc21..9b12de4 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -956,6 +956,10 @@
 extern const char kWhatsNewIOSName[];
 extern const char kWhatsNewIOSDescription[];
 
+// Title and description for the flag to enable What's New version 2.
+extern const char kWhatsNewIOSM116Name[];
+extern const char kWhatsNewIOSM116Description[];
+
 // Please add names and descriptions above in alphabetical order.
 
 }  // namespace flag_descriptions
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn
index d6f615ca..3d6a9d02 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/BUILD.gn
@@ -34,9 +34,7 @@
     "//components/safe_browsing/core/common",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/sync_error",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate",
     "//ios/chrome/browser/overlays/public/infobar_banner",
     "//ios/chrome/browser/shared/model/browser",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm b/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm
index 6c1afa3..1308d2e 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/infobar_overlay_browser_agent_util.mm
@@ -10,13 +10,9 @@
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_banner_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile/save_address_profile_infobar_modal_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm/confirm_infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/sync_error/sync_error_infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_banner_interaction_handler.h"
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate/translate_infobar_modal_interaction_handler.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -27,10 +23,18 @@
   InfobarOverlayBrowserAgent* browser_agent =
       InfobarOverlayBrowserAgent::FromBrowser(browser);
 
+  if (base::FeatureList::IsEnabled(
+          safe_browsing::kTailoredSecurityIntegration)) {
+    browser_agent->AddDefaultInfobarInteractionHandlerForInfobarType(
+        InfobarType::kInfobarTypeTailoredSecurityService);
+  }
+
   browser_agent->AddDefaultInfobarInteractionHandlerForInfobarType(
       InfobarType::kInfobarTypePasswordSave);
   browser_agent->AddDefaultInfobarInteractionHandlerForInfobarType(
       InfobarType::kInfobarTypePasswordUpdate);
+  browser_agent->AddDefaultInfobarInteractionHandlerForInfobarType(
+      InfobarType::kInfobarTypeSaveCard);
 
   browser_agent->AddInfobarInteractionHandler(
       std::make_unique<InfobarInteractionHandler>(
@@ -42,11 +46,6 @@
           InfobarType::kInfobarTypeTranslate,
           std::make_unique<TranslateInfobarBannerInteractionHandler>(),
           std::make_unique<TranslateInfobarModalInteractionHandler>()));
-  browser_agent->AddInfobarInteractionHandler(
-      std::make_unique<InfobarInteractionHandler>(
-          InfobarType::kInfobarTypeSaveCard,
-          std::make_unique<SaveCardInfobarBannerInteractionHandler>(),
-          std::make_unique<SaveCardInfobarModalInteractionHandler>()));
   browser_agent->AddInfobarInteractionHandler(std::make_unique<
                                               InfobarInteractionHandler>(
       InfobarType::kInfobarTypeSaveAutofillAddressProfile,
@@ -59,17 +58,4 @@
           InfobarType::kInfobarTypeSyncError,
           std::make_unique<SyncErrorInfobarBannerInteractionHandler>(),
           /*modal_handler=*/nullptr));
-
-  if (base::FeatureList::IsEnabled(
-          safe_browsing::kTailoredSecurityIntegration)) {
-    const OverlayRequestSupport* support =
-        tailored_security_service_infobar_overlays::
-            TailoredSecurityServiceBannerRequestConfig::RequestSupport();
-    browser_agent->AddInfobarInteractionHandler(
-        std::make_unique<InfobarInteractionHandler>(
-            InfobarType::kInfobarTypeTailoredSecurityService,
-            std::make_unique<TailoredSecurityInfobarBannerInteractionHandler>(
-                support),
-            nil));
-  }
 }
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/BUILD.gn
deleted file mode 100644
index f3a1d30..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/BUILD.gn
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright 2020 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("save_card") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "save_card_infobar_banner_interaction_handler.h",
-    "save_card_infobar_banner_interaction_handler.mm",
-    "save_card_infobar_banner_overlay_request_callback_installer.h",
-    "save_card_infobar_banner_overlay_request_callback_installer.mm",
-    "save_card_infobar_modal_interaction_handler.h",
-    "save_card_infobar_modal_interaction_handler.mm",
-    "save_card_infobar_modal_overlay_request_callback_installer.h",
-    "save_card_infobar_modal_overlay_request_callback_installer.mm",
-  ]
-  deps = [
-    "//base",
-    "//components/autofill/core/browser",
-    "//ios/chrome/browser/infobars",
-    "//ios/chrome/browser/infobars:public",
-    "//ios/chrome/browser/infobars/overlays",
-    "//ios/chrome/browser/infobars/overlays:util",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common",
-    "//ios/chrome/browser/overlays",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/overlays/public/infobar_modal",
-    "//ios/chrome/browser/shared/model/browser",
-    "//ios/chrome/browser/shared/model/web_state_list",
-  ]
-}
-
-source_set("unit_tests") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  testonly = true
-  sources = [
-    "save_card_infobar_banner_interaction_handler_unittest.mm",
-    "save_card_infobar_banner_overlay_request_callback_installer_unittest.mm",
-    "save_card_infobar_modal_interaction_handler_unittest.mm",
-    "save_card_infobar_modal_overlay_request_callback_installer_unittest.mm",
-  ]
-  deps = [
-    ":save_card",
-    "//base/test:test_support",
-    "//components/autofill/core/browser:test_support",
-    "//components/prefs",
-    "//ios/chrome/browser/infobars",
-    "//ios/chrome/browser/infobars/overlays",
-    "//ios/chrome/browser/infobars/overlays:util",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test",
-    "//ios/chrome/browser/infobars/test",
-    "//ios/chrome/browser/overlays",
-    "//ios/chrome/browser/overlays/public/common",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/overlays/public/infobar_modal",
-    "//ios/chrome/browser/overlays/test",
-    "//ios/chrome/browser/shared/model/browser/test:test_support",
-    "//ios/chrome/browser/shared/model/web_state_list",
-    "//ios/chrome/browser/ui/infobars/test",
-    "//ios/web/public/test/fakes",
-    "//testing/gtest",
-  ]
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h
deleted file mode 100644
index 6a3bbaa..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_BANNER_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_BANNER_INTERACTION_HANDLER_H_
-
-#include <string.h>
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
-
-class InfobarBannerOverlayRequestCallbackInstaller;
-
-namespace autofill {
-class AutofillSaveCardInfoBarDelegateMobile;
-}
-
-// Helper object that updates the model layer for interaction events with the
-// SaveCard infobar banner UI.
-class SaveCardInfobarBannerInteractionHandler
-    : public InfobarBannerInteractionHandler {
- public:
-  SaveCardInfobarBannerInteractionHandler();
-  ~SaveCardInfobarBannerInteractionHandler() override;
-
-  // Instructs the handler to update the credentials with `cardholder_name`,
-  // `expiration_date_month`, and `expiration_date_year`. This replaces
-  // MainButtonTapped.
-  virtual void SaveCredentials(InfoBarIOS* infobar,
-                               std::u16string cardholder_name,
-                               std::u16string expiration_date_month,
-                               std::u16string expiration_date_year);
-
-  // Overrides InfobarBannerInteractionHandler implementation because a banner
-  // dismissal should not call InfoBarDismissed();
-  void BannerDismissedByUser(InfoBarIOS* infobar) override {}
-
- private:
-  // InfobarBannerInteractionHandler:
-  std::unique_ptr<InfobarBannerOverlayRequestCallbackInstaller>
-  CreateBannerInstaller() override;
-
-  // Returns the SaveCard delegate from `infobar`.
-  autofill::AutofillSaveCardInfoBarDelegateMobile* GetInfobarDelegate(
-      InfoBarIOS* infobar);
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_BANNER_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.mm
deleted file mode 100644
index d8ce553..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.mm
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h"
-
-#import "base/check.h"
-#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_cancel_handler.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using save_card_infobar_overlays::SaveCardBannerRequestConfig;
-
-#pragma mark - InfobarBannerInteractionHandler
-
-SaveCardInfobarBannerInteractionHandler::
-    SaveCardInfobarBannerInteractionHandler()
-    : InfobarBannerInteractionHandler(
-          SaveCardBannerRequestConfig::RequestSupport()) {}
-
-SaveCardInfobarBannerInteractionHandler::
-    ~SaveCardInfobarBannerInteractionHandler() = default;
-
-void SaveCardInfobarBannerInteractionHandler::SaveCredentials(
-    InfoBarIOS* infobar,
-    std::u16string cardholder_name,
-    std::u16string expiration_date_month,
-    std::u16string expiration_date_year) {
-  infobar->set_accepted(GetInfobarDelegate(infobar)->UpdateAndAccept(
-      cardholder_name, expiration_date_month, expiration_date_year));
-}
-
-#pragma mark - Private
-
-std::unique_ptr<InfobarBannerOverlayRequestCallbackInstaller>
-SaveCardInfobarBannerInteractionHandler::CreateBannerInstaller() {
-  return std::make_unique<SaveCardInfobarBannerOverlayRequestCallbackInstaller>(
-      this);
-}
-
-autofill::AutofillSaveCardInfoBarDelegateMobile*
-SaveCardInfobarBannerInteractionHandler::GetInfobarDelegate(
-    InfoBarIOS* infobar) {
-  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
-      autofill::AutofillSaveCardInfoBarDelegateMobile::FromInfobarDelegate(
-          infobar->delegate());
-  DCHECK(delegate);
-  return delegate;
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler_unittest.mm
deleted file mode 100644
index 8d23b919..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler_unittest.mm
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h"
-
-#import "base/strings/sys_string_conversions.h"
-#import "base/uuid.h"
-#import "components/autofill/core/browser/autofill_test_utils.h"
-#import "components/autofill/core/browser/data_model/credit_card.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
-#import "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// Test fixture for SaveCardInfobarBannerInteractionHandler.
-class SaveCardInfobarBannerInteractionHandlerTest : public PlatformTest {
- public:
-  SaveCardInfobarBannerInteractionHandlerTest()
-      : delegate_factory_(),
-        card_(base::Uuid::GenerateRandomV4().AsLowercaseString(),
-              "https://www.example.com/") {
-    infobar_ = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeSaveCard,
-        MockAutofillSaveCardInfoBarDelegateMobileFactory::
-            CreateMockAutofillSaveCardInfoBarDelegateMobileFactory(false,
-                                                                   card_));
-  }
-
-  MockAutofillSaveCardInfoBarDelegateMobile& mock_delegate() {
-    return *static_cast<MockAutofillSaveCardInfoBarDelegateMobile*>(
-        infobar_->delegate());
-  }
-
- protected:
-  SaveCardInfobarBannerInteractionHandler handler_;
-  MockAutofillSaveCardInfoBarDelegateMobileFactory delegate_factory_;
-  autofill::CreditCard card_;
-  std::unique_ptr<InfoBarIOS> infobar_;
-};
-
-TEST_F(SaveCardInfobarBannerInteractionHandlerTest, SaveCredentials) {
-  std::u16string cardholder_name = base::SysNSStringToUTF16(@"test name");
-  std::u16string expiration_date_month = base::SysNSStringToUTF16(@"06");
-  std::u16string expiration_date_year = base::SysNSStringToUTF16(@"2023");
-  EXPECT_CALL(mock_delegate(),
-              UpdateAndAccept(cardholder_name, expiration_date_month,
-                              expiration_date_year));
-  handler_.SaveCredentials(infobar_.get(), cardholder_name,
-                           expiration_date_month, expiration_date_year);
-}
-
-// Test that dismissing the banner does not call
-// InfobarDelegate::InfobarDismissed(), which is a behavior for the other
-// Infobars.
-TEST_F(SaveCardInfobarBannerInteractionHandlerTest, DismissalNoDelegateCall) {
-  EXPECT_CALL(mock_delegate(), InfoBarDismissed()).Times(0);
-  handler_.BannerDismissedByUser(infobar_.get());
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.h
deleted file mode 100644
index 06e42a7..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_BANNER_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_BANNER_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_overlay_request_callback_installer.h"
-
-class SaveCardInfobarBannerInteractionHandler;
-
-// Callback installer for SaveCard banner interaction events.
-class SaveCardInfobarBannerOverlayRequestCallbackInstaller
-    : public InfobarBannerOverlayRequestCallbackInstaller {
- public:
-  // Constructor for an instance that installs callbacks that forward
-  // interaction events to `interaction_handler`.
-  explicit SaveCardInfobarBannerOverlayRequestCallbackInstaller(
-      SaveCardInfobarBannerInteractionHandler* interaction_handler);
-  ~SaveCardInfobarBannerOverlayRequestCallbackInstaller() override;
-
- private:
-  // Used as a callback for OverlayResponses dispatched through `request`'s
-  // callback manager.  The OverlayDispatchCallback is created with an
-  // OverlayResponseSupport that guarantees that `response` is created with an
-  // save_card_infobar_modal_responses::SaveCardMainAction.
-  void SaveCredentialsCallback(OverlayRequest* request,
-                               OverlayResponse* response);
-
-  // OverlayRequestCallbackInstaller:
-  void InstallCallbacksInternal(OverlayRequest* request) override;
-
-  // The handler for received responses.
-  SaveCardInfobarBannerInteractionHandler* interaction_handler_ = nullptr;
-
-  base::WeakPtrFactory<SaveCardInfobarBannerOverlayRequestCallbackInstaller>
-      weak_factory_{this};
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_BANNER_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.mm
deleted file mode 100644
index b750a2f..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.mm
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.h"
-
-#import "base/check.h"
-#import "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using save_card_infobar_overlays::SaveCardMainAction;
-using save_card_infobar_overlays::SaveCardBannerRequestConfig;
-
-SaveCardInfobarBannerOverlayRequestCallbackInstaller::
-    SaveCardInfobarBannerOverlayRequestCallbackInstaller(
-        SaveCardInfobarBannerInteractionHandler* interaction_handler)
-    : InfobarBannerOverlayRequestCallbackInstaller(
-          SaveCardBannerRequestConfig::RequestSupport(),
-          interaction_handler),
-      interaction_handler_(interaction_handler) {
-  DCHECK(interaction_handler_);
-}
-
-SaveCardInfobarBannerOverlayRequestCallbackInstaller::
-    ~SaveCardInfobarBannerOverlayRequestCallbackInstaller() = default;
-
-#pragma mark - Private
-
-void SaveCardInfobarBannerOverlayRequestCallbackInstaller::
-    SaveCredentialsCallback(OverlayRequest* request,
-                            OverlayResponse* response) {
-  InfoBarIOS* infobar = GetOverlayRequestInfobar(request);
-  if (!infobar)
-    return;
-
-  SaveCardMainAction* info = response->GetInfo<SaveCardMainAction>();
-  interaction_handler_->SaveCredentials(
-      GetOverlayRequestInfobar(request),
-      base::SysNSStringToUTF16(info->cardholder_name()),
-      base::SysNSStringToUTF16(info->expiration_month()),
-      base::SysNSStringToUTF16(info->expiration_year()));
-}
-
-#pragma mark - OverlayRequestCallbackInstaller
-
-void SaveCardInfobarBannerOverlayRequestCallbackInstaller::
-    InstallCallbacksInternal(OverlayRequest* request) {
-  InfobarBannerOverlayRequestCallbackInstaller::InstallCallbacksInternal(
-      request);
-  OverlayCallbackManager* manager = request->GetCallbackManager();
-  manager->AddDispatchCallback(OverlayDispatchCallback(
-      base::BindRepeating(
-          &SaveCardInfobarBannerOverlayRequestCallbackInstaller::
-              SaveCredentialsCallback,
-          weak_factory_.GetWeakPtr(), request),
-      SaveCardMainAction::ResponseSupport()));
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer_unittest.mm
deleted file mode 100644
index f2d8f961..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer_unittest.mm
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_overlay_request_callback_installer.h"
-
-#import "base/strings/sys_string_conversions.h"
-#import "base/uuid.h"
-#import "components/autofill/core/browser/autofill_test_utils.h"
-#import "components/autofill/core/browser/data_model/credit_card.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#import "ios/chrome/browser/overlays/public/overlay_request.h"
-#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/web/public/test/fakes/fake_navigation_manager.h"
-#import "ios/web/public/test/fakes/fake_web_state.h"
-#import "testing/gmock/include/gmock/gmock.h"
-#import "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using save_card_infobar_overlays::SaveCardBannerRequestConfig;
-
-// Test fixture for SaveCardInfobarBannerOverlayRequestCallbackInstaller.
-class SaveCardInfobarBannerOverlayRequestCallbackInstallerTest
-    : public PlatformTest {
- public:
-  SaveCardInfobarBannerOverlayRequestCallbackInstallerTest()
-      : card_(base::Uuid::GenerateRandomV4().AsLowercaseString(),
-              "https://www.example.com/"),
-        installer_(&mock_handler_),
-        delegate_factory_() {
-    // Create the infobar and add it to the WebState's manager.
-    web_state_.SetNavigationManager(
-        std::make_unique<web::FakeNavigationManager>());
-    InfoBarManagerImpl::CreateForWebState(&web_state_);
-    std::unique_ptr<MockAutofillSaveCardInfoBarDelegateMobile> delegate =
-        delegate_factory_
-            .CreateMockAutofillSaveCardInfoBarDelegateMobileFactory(false,
-                                                                    card_);
-    delegate_ = delegate.get();
-    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeTranslate, std::move(delegate));
-
-    infobar_ = infobar.get();
-    manager()->AddInfoBar(std::move(infobar));
-    // Create the request and add it to the WebState's queue.
-    std::unique_ptr<OverlayRequest> added_request =
-        OverlayRequest::CreateWithConfig<SaveCardBannerRequestConfig>(infobar_);
-    request_ = added_request.get();
-    queue()->AddRequest(std::move(added_request));
-    // Install the callbacks on the added request.
-    installer_.InstallCallbacks(request_);
-  }
-
-  InfoBarManagerImpl* manager() {
-    return InfoBarManagerImpl::FromWebState(&web_state_);
-  }
-  OverlayRequestQueue* queue() {
-    return OverlayRequestQueue::FromWebState(&web_state_,
-                                             OverlayModality::kInfobarModal);
-  }
-
- protected:
-  autofill::CreditCard card_;
-  web::FakeWebState web_state_;
-  InfoBarIOS* infobar_ = nullptr;
-  OverlayRequest* request_ = nullptr;
-  MockSaveCardInfobarBannerInteractionHandler mock_handler_;
-  SaveCardInfobarBannerOverlayRequestCallbackInstaller installer_;
-  MockAutofillSaveCardInfoBarDelegateMobileFactory delegate_factory_;
-  MockAutofillSaveCardInfoBarDelegateMobile* delegate_;
-};
-
-TEST_F(SaveCardInfobarBannerOverlayRequestCallbackInstallerTest,
-       SaveCardCredentials) {
-  NSString* cardholder_name = @"test name";
-  NSString* expiration_date_month = @"06";
-  NSString* expiration_date_year = @"2023";
-  EXPECT_CALL(
-      mock_handler_,
-      SaveCredentials(infobar_, base::SysNSStringToUTF16(cardholder_name),
-                      base::SysNSStringToUTF16(expiration_date_month),
-                      base::SysNSStringToUTF16(expiration_date_year)));
-  request_->GetCallbackManager()->DispatchResponse(
-      OverlayResponse::CreateWithInfo<
-          save_card_infobar_overlays::SaveCardMainAction>(
-          cardholder_name, expiration_date_month, expiration_date_year));
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h
deleted file mode 100644
index 62a5d94..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_interaction_handler.h"
-
-class InfoBarIOS;
-class GURL;
-
-namespace autofill {
-class AutofillSaveCardInfoBarDelegateMobile;
-}
-
-// Helper object that updates the model layer for interaction events with the
-// SaveCard infobar modal UI.
-class SaveCardInfobarModalInteractionHandler
-    : public InfobarModalInteractionHandler {
- public:
-  SaveCardInfobarModalInteractionHandler();
-  ~SaveCardInfobarModalInteractionHandler() override;
-
-  // Instructs the handler to update the credentials with `cardholder_name`,
-  // `expiration_date_month`, and `expiration_date_year`. Replaces
-  // MainButtonTapped.
-  virtual void UpdateCredentials(InfoBarIOS* infobar,
-                                 std::u16string cardholder_name,
-                                 std::u16string expiration_date_month,
-                                 std::u16string expiration_date_year);
-
-  // Instructs the handler to load `url` through the delegate.
-  virtual void LoadURL(InfoBarIOS* infobar, GURL url);
-
-  // InfobarModalInteractionHandler:
-  void PerformMainAction(InfoBarIOS* infobar) override;
-  void InfobarVisibilityChanged(InfoBarIOS* infobar, bool visible) override {}
-
- private:
-  // InfobarModalInteractionHandler:
-  std::unique_ptr<InfobarModalOverlayRequestCallbackInstaller>
-  CreateModalInstaller() override;
-
-  // Returns the SaveCard delegate from `infobar`.
-  autofill::AutofillSaveCardInfoBarDelegateMobile* GetInfoBarDelegate(
-      InfoBarIOS* infobar);
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_MODAL_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.mm
deleted file mode 100644
index 41686863..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.mm
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h"
-
-#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.h"
-#import "ios/chrome/browser/shared/model/browser/browser.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-SaveCardInfobarModalInteractionHandler::
-    SaveCardInfobarModalInteractionHandler() = default;
-
-SaveCardInfobarModalInteractionHandler::
-    ~SaveCardInfobarModalInteractionHandler() = default;
-
-#pragma mark - Public
-
-void SaveCardInfobarModalInteractionHandler::UpdateCredentials(
-    InfoBarIOS* infobar,
-    std::u16string cardholder_name,
-    std::u16string expiration_date_month,
-    std::u16string expiration_date_year) {
-  infobar->set_accepted(GetInfoBarDelegate(infobar)->UpdateAndAccept(
-      cardholder_name, expiration_date_month, expiration_date_year));
-}
-
-void SaveCardInfobarModalInteractionHandler::LoadURL(InfoBarIOS* infobar,
-                                                     GURL url) {
-  GetInfoBarDelegate(infobar)->OnLegalMessageLinkClicked(url);
-}
-
-void SaveCardInfobarModalInteractionHandler::PerformMainAction(
-    InfoBarIOS* infobar) {
-  NOTREACHED() << "SaveCard does not use standard Infobar Accept action.";
-}
-
-#pragma mark - Private
-
-std::unique_ptr<InfobarModalOverlayRequestCallbackInstaller>
-SaveCardInfobarModalInteractionHandler::CreateModalInstaller() {
-  return std::make_unique<SaveCardInfobarModalOverlayRequestCallbackInstaller>(
-      this);
-}
-
-autofill::AutofillSaveCardInfoBarDelegateMobile*
-SaveCardInfobarModalInteractionHandler::GetInfoBarDelegate(
-    InfoBarIOS* infobar) {
-  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
-      autofill::AutofillSaveCardInfoBarDelegateMobile::FromInfobarDelegate(
-          infobar->delegate());
-  DCHECK(delegate);
-  return delegate;
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler_unittest.mm
deleted file mode 100644
index 65e6b8b..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler_unittest.mm
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h"
-
-#import <string>
-
-#import "base/strings/sys_string_conversions.h"
-#import "base/uuid.h"
-#import "components/autofill/core/browser/autofill_test_utils.h"
-#import "components/autofill/core/browser/data_model/credit_card.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
-#import "testing/platform_test.h"
-#import "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// Test fixture for SaveCardInfobarModalInteractionHandler.
-class SaveCardInfobarModalInteractionHandlerTest : public PlatformTest {
- public:
-  SaveCardInfobarModalInteractionHandlerTest()
-      : delegate_factory_(),
-        card_(base::Uuid::GenerateRandomV4().AsLowercaseString(),
-              "https://www.example.com/") {
-    infobar_ = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeSaveCard,
-        MockAutofillSaveCardInfoBarDelegateMobileFactory::
-            CreateMockAutofillSaveCardInfoBarDelegateMobileFactory(false,
-                                                                   card_));
-  }
-
-  MockAutofillSaveCardInfoBarDelegateMobile& mock_delegate() {
-    return *static_cast<MockAutofillSaveCardInfoBarDelegateMobile*>(
-        infobar_->delegate());
-  }
-
- protected:
-  SaveCardInfobarModalInteractionHandler handler_;
-  MockAutofillSaveCardInfoBarDelegateMobileFactory delegate_factory_;
-  autofill::CreditCard card_;
-  std::unique_ptr<InfoBarIOS> infobar_;
-};
-
-TEST_F(SaveCardInfobarModalInteractionHandlerTest, UpdateCredentials) {
-  std::u16string cardholder_name = base::SysNSStringToUTF16(@"test name");
-  std::u16string expiration_date_month = base::SysNSStringToUTF16(@"06");
-  std::u16string expiration_date_year = base::SysNSStringToUTF16(@"2023");
-  EXPECT_CALL(mock_delegate(),
-              UpdateAndAccept(cardholder_name, expiration_date_month,
-                              expiration_date_year));
-  handler_.UpdateCredentials(infobar_.get(), cardholder_name,
-                             expiration_date_month, expiration_date_year);
-}
-
-TEST_F(SaveCardInfobarModalInteractionHandlerTest, LoadURL) {
-  GURL url("https://test-example.com");
-  EXPECT_CALL(mock_delegate(), OnLegalMessageLinkClicked(url));
-  handler_.LoadURL(infobar_.get(), url);
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.h
deleted file mode 100644
index a26bb99..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_modal_overlay_request_callback_installer.h"
-
-#include "base/memory/weak_ptr.h"
-
-class SaveCardInfobarModalInteractionHandler;
-
-// Callback installer for SaveCard infobar modal interaction events.
-class SaveCardInfobarModalOverlayRequestCallbackInstaller
-    : public InfobarModalOverlayRequestCallbackInstaller {
- public:
-  // Constructor for an instance that installs callbacks that forward
-  // interaction events to `interaction_handler`.
-  explicit SaveCardInfobarModalOverlayRequestCallbackInstaller(
-      SaveCardInfobarModalInteractionHandler* interaction_handler);
-  ~SaveCardInfobarModalOverlayRequestCallbackInstaller() override;
-
- private:
-  // Used as a callback for OverlayResponses dispatched through `request`'s
-  // callback manager.  The OverlayDispatchCallback is created with an
-  // OverlayResponseSupport that guarantees that `response` is created with an
-  // save_card_infobar_modal_responses::SaveCardMainAction.
-  void SaveCardCredentialsCallback(OverlayRequest* request,
-                                   OverlayResponse* response);
-  // Used as a callback for OverlayResponses dispatched through `request`'s
-  // callback manager.  The OverlayDispatchCallback is created with an
-  // OverlayResponseSupport that guarantees that `response` is created with a
-  // save_card_infobar_modal_responses::SaveCardLoadURL.
-  void LoadURLCallback(OverlayRequest* request, OverlayResponse* response);
-
-  // OverlayRequestCallbackInstaller:
-  void InstallCallbacksInternal(OverlayRequest* request) override;
-
-  // The handler for received responses.
-  SaveCardInfobarModalInteractionHandler* interaction_handler_ = nullptr;
-
-  base::WeakPtrFactory<SaveCardInfobarModalOverlayRequestCallbackInstaller>
-      weak_factory_{this};
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_SAVE_CARD_SAVE_CARD_INFOBAR_MODAL_OVERLAY_REQUEST_CALLBACK_INSTALLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.mm
deleted file mode 100644
index f1414b0..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.mm
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.h"
-
-#import "base/check.h"
-#import "base/strings/sys_string_conversions.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using save_card_infobar_overlays::SaveCardMainAction;
-using save_card_infobar_overlays::SaveCardModalRequestConfig;
-using save_card_infobar_overlays::SaveCardLoadURL;
-
-SaveCardInfobarModalOverlayRequestCallbackInstaller::
-    SaveCardInfobarModalOverlayRequestCallbackInstaller(
-        SaveCardInfobarModalInteractionHandler* interaction_handler)
-    : InfobarModalOverlayRequestCallbackInstaller(
-          SaveCardModalRequestConfig::RequestSupport(),
-          interaction_handler),
-      interaction_handler_(interaction_handler) {
-  DCHECK(interaction_handler_);
-}
-
-SaveCardInfobarModalOverlayRequestCallbackInstaller::
-    ~SaveCardInfobarModalOverlayRequestCallbackInstaller() = default;
-
-#pragma mark - Private
-
-void SaveCardInfobarModalOverlayRequestCallbackInstaller::
-    SaveCardCredentialsCallback(OverlayRequest* request,
-                                OverlayResponse* response) {
-  InfoBarIOS* infobar = GetOverlayRequestInfobar(request);
-  if (!infobar)
-    return;
-
-  SaveCardMainAction* info = response->GetInfo<SaveCardMainAction>();
-  interaction_handler_->UpdateCredentials(
-      GetOverlayRequestInfobar(request),
-      base::SysNSStringToUTF16(info->cardholder_name()),
-      base::SysNSStringToUTF16(info->expiration_month()),
-      base::SysNSStringToUTF16(info->expiration_year()));
-}
-
-void SaveCardInfobarModalOverlayRequestCallbackInstaller::LoadURLCallback(
-    OverlayRequest* request,
-    OverlayResponse* response) {
-  SaveCardLoadURL* info = response->GetInfo<SaveCardLoadURL>();
-  interaction_handler_->LoadURL(GetOverlayRequestInfobar(request),
-                                info->link_url());
-}
-
-#pragma mark - OverlayRequestCallbackInstaller
-
-void SaveCardInfobarModalOverlayRequestCallbackInstaller::
-    InstallCallbacksInternal(OverlayRequest* request) {
-  InfobarModalOverlayRequestCallbackInstaller::InstallCallbacksInternal(
-      request);
-  OverlayCallbackManager* manager = request->GetCallbackManager();
-  manager->AddDispatchCallback(OverlayDispatchCallback(
-      base::BindRepeating(&SaveCardInfobarModalOverlayRequestCallbackInstaller::
-                              SaveCardCredentialsCallback,
-                          weak_factory_.GetWeakPtr(), request),
-      SaveCardMainAction::ResponseSupport()));
-  manager->AddDispatchCallback(OverlayDispatchCallback(
-      base::BindRepeating(
-          &SaveCardInfobarModalOverlayRequestCallbackInstaller::LoadURLCallback,
-          weak_factory_.GetWeakPtr(), request),
-      SaveCardLoadURL::ResponseSupport()));
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer_unittest.mm
deleted file mode 100644
index 0bda550..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer_unittest.mm
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_overlay_request_callback_installer.h"
-
-#import "base/strings/sys_string_conversions.h"
-#import "base/uuid.h"
-#import "components/autofill/core/browser/autofill_test_utils.h"
-#import "components/autofill/core/browser/data_model/credit_card.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#import "ios/chrome/browser/overlays/public/overlay_request.h"
-#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/web/public/test/fakes/fake_navigation_manager.h"
-#import "ios/web/public/test/fakes/fake_web_state.h"
-#import "testing/gmock/include/gmock/gmock.h"
-#import "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using save_card_infobar_overlays::SaveCardModalRequestConfig;
-
-// Test fixture for SaveCardInfobarModalOverlayRequestCallbackInstaller.
-class SaveCardInfobarModalOverlayRequestCallbackInstallerTest
-    : public PlatformTest {
- public:
-  SaveCardInfobarModalOverlayRequestCallbackInstallerTest()
-      : card_(base::Uuid::GenerateRandomV4().AsLowercaseString(),
-              "https://www.example.com/"),
-        installer_(&mock_handler_),
-        delegate_factory_() {
-    // Create the infobar and add it to the WebState's manager.
-    web_state_.SetNavigationManager(
-        std::make_unique<web::FakeNavigationManager>());
-    InfoBarManagerImpl::CreateForWebState(&web_state_);
-    std::unique_ptr<MockAutofillSaveCardInfoBarDelegateMobile> delegate =
-        delegate_factory_
-            .CreateMockAutofillSaveCardInfoBarDelegateMobileFactory(false,
-                                                                    card_);
-    delegate_ = delegate.get();
-    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeTranslate, std::move(delegate));
-
-    infobar_ = infobar.get();
-    manager()->AddInfoBar(std::move(infobar));
-    // Create the request and add it to the WebState's queue.
-    std::unique_ptr<OverlayRequest> added_request =
-        OverlayRequest::CreateWithConfig<SaveCardModalRequestConfig>(infobar_);
-    request_ = added_request.get();
-    queue()->AddRequest(std::move(added_request));
-    // Install the callbacks on the added request.
-    installer_.InstallCallbacks(request_);
-  }
-
-  InfoBarManagerImpl* manager() {
-    return InfoBarManagerImpl::FromWebState(&web_state_);
-  }
-  OverlayRequestQueue* queue() {
-    return OverlayRequestQueue::FromWebState(&web_state_,
-                                             OverlayModality::kInfobarModal);
-  }
-
- protected:
-  autofill::CreditCard card_;
-  web::FakeWebState web_state_;
-  InfoBarIOS* infobar_ = nullptr;
-  OverlayRequest* request_ = nullptr;
-  MockSaveCardInfobarModalInteractionHandler mock_handler_;
-  SaveCardInfobarModalOverlayRequestCallbackInstaller installer_;
-  MockAutofillSaveCardInfoBarDelegateMobileFactory delegate_factory_;
-  MockAutofillSaveCardInfoBarDelegateMobile* delegate_;
-};
-
-TEST_F(SaveCardInfobarModalOverlayRequestCallbackInstallerTest,
-       UpdateCredentials) {
-  NSString* cardholder_name = @"test name";
-  NSString* expiration_date_month = @"06";
-  NSString* expiration_date_year = @"2023";
-  EXPECT_CALL(
-      mock_handler_,
-      UpdateCredentials(infobar_, base::SysNSStringToUTF16(cardholder_name),
-                        base::SysNSStringToUTF16(expiration_date_month),
-                        base::SysNSStringToUTF16(expiration_date_year)));
-  request_->GetCallbackManager()->DispatchResponse(
-      OverlayResponse::CreateWithInfo<
-          save_card_infobar_overlays::SaveCardMainAction>(
-          cardholder_name, expiration_date_month, expiration_date_year));
-}
-
-TEST_F(SaveCardInfobarModalOverlayRequestCallbackInstallerTest, LoadURL) {
-  GURL url("https://test-example.com");
-  EXPECT_CALL(mock_handler_, LoadURL(infobar_, url));
-  request_->GetCallbackManager()->DispatchResponse(
-      OverlayResponse::CreateWithInfo<
-          save_card_infobar_overlays::SaveCardLoadURL>(url));
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/BUILD.gn
deleted file mode 100644
index 224f09b..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/BUILD.gn
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright 2022 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-source_set("tailored_security") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "tailored_security_infobar_banner_interaction_handler.h",
-    "tailored_security_infobar_banner_interaction_handler.mm",
-  ]
-  deps = [
-    "//base",
-    "//ios/chrome/browser/infobars",
-    "//ios/chrome/browser/infobars:public",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/safe_browsing/tailored_security:infobar_delegates",
-  ]
-}
-
-source_set("unit_tests") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  testonly = true
-  sources =
-      [ "tailored_security_infobar_banner_interaction_handler_unittest.mm" ]
-  deps = [
-    ":tailored_security",
-    "//base",
-    "//components/safe_browsing/core/common:safe_browsing_prefs",
-    "//components/sync_preferences",
-    "//components/sync_preferences:test_support",
-    "//ios/chrome/browser/infobars",
-    "//ios/chrome/browser/infobars/overlays",
-    "//ios/chrome/browser/infobars/test",
-    "//ios/chrome/browser/overlays/public/infobar_banner",
-    "//ios/chrome/browser/safe_browsing",
-    "//ios/chrome/browser/safe_browsing/tailored_security/test",
-    "//ios/chrome/browser/shared/model/browser_state:test_support",
-    "//ios/chrome/browser/shared/model/prefs:browser_prefs",
-    "//ios/components/security_interstitials/safe_browsing",
-    "//ios/web/public/test",
-    "//ios/web/public/test/fakes",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.h
deleted file mode 100644
index 712c459..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TAILORED_SECURITY_TAILORED_SECURITY_INFOBAR_BANNER_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TAILORED_SECURITY_TAILORED_SECURITY_INFOBAR_BANNER_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common/infobar_banner_interaction_handler.h"
-
-namespace safe_browsing {
-class TailoredSecurityServiceInfobarDelegate;
-}  // namespace safe_browsing
-
-// Helper object that updates the model layer for interaction events with the
-// tailored security infobar banner UI.
-class TailoredSecurityInfobarBannerInteractionHandler
-    : public InfobarBannerInteractionHandler {
- public:
-  TailoredSecurityInfobarBannerInteractionHandler(
-      const OverlayRequestSupport* request_support);
-  ~TailoredSecurityInfobarBannerInteractionHandler() override;
-
-  // InfobarBannerInteractionHandler:
-  void MainButtonTapped(InfoBarIOS* infobar) override;
-
- private:
-  // Returns the tailored security delegate from `infobar`.
-  safe_browsing::TailoredSecurityServiceInfobarDelegate* GetInfobarDelegate(
-      InfoBarIOS* infobar);
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TAILORED_SECURITY_TAILORED_SECURITY_INFOBAR_BANNER_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.mm
deleted file mode 100644
index 05dafff9..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.mm
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.h"
-
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/safe_browsing/tailored_security/tailored_security_service_infobar_delegate.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using safe_browsing::TailoredSecurityServiceInfobarDelegate;
-
-#pragma mark - InfobarBannerInteractionHandler
-
-TailoredSecurityInfobarBannerInteractionHandler::
-    TailoredSecurityInfobarBannerInteractionHandler(
-        const OverlayRequestSupport* request_support)
-    : InfobarBannerInteractionHandler(request_support) {}
-
-TailoredSecurityInfobarBannerInteractionHandler::
-    ~TailoredSecurityInfobarBannerInteractionHandler() = default;
-
-void TailoredSecurityInfobarBannerInteractionHandler::MainButtonTapped(
-    InfoBarIOS* infobar) {
-  infobar->set_accepted(GetInfobarDelegate(infobar)->Accept());
-}
-#pragma mark - Private
-
-TailoredSecurityServiceInfobarDelegate*
-TailoredSecurityInfobarBannerInteractionHandler::GetInfobarDelegate(
-    InfoBarIOS* infobar) {
-  TailoredSecurityServiceInfobarDelegate* delegate =
-      TailoredSecurityServiceInfobarDelegate::FromInfobarDelegate(
-          infobar->delegate());
-  DCHECK(delegate);
-  return delegate;
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler_unittest.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler_unittest.mm
deleted file mode 100644
index bbf0dd0..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler_unittest.mm
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security/tailored_security_infobar_banner_interaction_handler.h"
-
-#import "components/safe_browsing/core/common/safe_browsing_prefs.h"
-#import "components/sync_preferences/pref_service_mock_factory.h"
-#import "components/sync_preferences/pref_service_syncable.h"
-#import "ios/chrome/browser/infobars/infobar_manager_impl.h"
-#import "ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_request_inserter.h"
-#import "ios/chrome/browser/infobars/test/fake_infobar_ios.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/safe_browsing/safe_browsing_client_factory.h"
-#import "ios/chrome/browser/safe_browsing/tailored_security/test/mock_tailored_security_service_infobar_delegate.h"
-#import "ios/chrome/browser/shared/model/browser_state/test_chrome_browser_state.h"
-#import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
-#import "ios/components/security_interstitials/safe_browsing/safe_browsing_client.h"
-#import "ios/components/security_interstitials/safe_browsing/safe_browsing_query_manager.h"
-#import "ios/components/security_interstitials/safe_browsing/safe_browsing_tab_helper.h"
-#import "ios/web/public/test/fakes/fake_navigation_manager.h"
-#import "ios/web/public/test/fakes/fake_web_state.h"
-#import "ios/web/public/test/web_task_environment.h"
-#import "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-// Test fixture for TailoredSecurityInfobarBannerInteractionHandlerTest.
-class TailoredSecurityInfobarBannerInteractionHandlerTest
-    : public PlatformTest {
- public:
-  TailoredSecurityInfobarBannerInteractionHandlerTest()
-      : task_environment_(web::WebTaskEnvironment::IO_MAINLOOP),
-        handler_(
-            tailored_security_service_infobar_overlays::
-                TailoredSecurityServiceBannerRequestConfig::RequestSupport()) {
-    scoped_refptr<user_prefs::PrefRegistrySyncable> registry =
-        base::MakeRefCounted<user_prefs::PrefRegistrySyncable>();
-    RegisterBrowserStatePrefs(registry.get());
-    sync_preferences::PrefServiceMockFactory factory;
-
-    web_state_.SetNavigationManager(
-        std::make_unique<web::FakeNavigationManager>());
-    InfobarOverlayRequestInserter::CreateForWebState(
-        &web_state_, &DefaultInfobarOverlayRequestFactory);
-    InfoBarManagerImpl::CreateForWebState(&web_state_);
-
-    TestChromeBrowserState::Builder test_cbs_builder;
-    test_cbs_builder.SetPrefService(factory.CreateSyncable(registry.get()));
-    chrome_browser_state_ = test_cbs_builder.Build();
-    web_state_.SetBrowserState(chrome_browser_state_.get());
-
-    SafeBrowsingClient* client = SafeBrowsingClientFactory::GetForBrowserState(
-        chrome_browser_state_.get());
-    SafeBrowsingQueryManager::CreateForWebState(&web_state_, client);
-    SafeBrowsingTabHelper::CreateForWebState(&web_state_, client);
-  }
-
-  safe_browsing::MockTailoredSecurityServiceInfobarDelegate& mock_delegate() {
-    return *static_cast<
-        safe_browsing::MockTailoredSecurityServiceInfobarDelegate*>(
-        infobar_->delegate());
-  }
-
-  // Creates an infobar with a specific TailoredSecurityServiceMessageState.
-  void CreateInfobarWithMessageState(
-      safe_browsing::TailoredSecurityServiceMessageState message_state) {
-    std::unique_ptr<InfoBarIOS> infobar = std::make_unique<InfoBarIOS>(
-        InfobarType::kInfobarTypeTailoredSecurityService,
-        safe_browsing::MockTailoredSecurityServiceInfobarDelegate::Create(
-            message_state, &web_state_));
-    infobar_ = infobar.get();
-    InfoBarManagerImpl::FromWebState(&web_state_)
-        ->AddInfoBar(std::move(infobar));
-  }
-
- protected:
-  web::WebTaskEnvironment task_environment_;
-  TailoredSecurityInfobarBannerInteractionHandler handler_;
-  web::FakeWebState web_state_;
-  InfoBarIOS* infobar_;
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
-};
-
-// Tests MainButtonTapped() calls Accept() on the mock delegate and resets
-// the infobar to be accepted for consented message prompt.
-TEST_F(TailoredSecurityInfobarBannerInteractionHandlerTest,
-       ConsentedMessagePromptButtonTapped) {
-  CreateInfobarWithMessageState(
-      safe_browsing::TailoredSecurityServiceMessageState::
-          kConsentedAndFlowEnabled);
-  ASSERT_FALSE(infobar_->accepted());
-  handler_.MainButtonTapped(infobar_);
-  EXPECT_TRUE(infobar_->accepted());
-}
-
-// Tests MainButtonTapped() calls Accept() on the mock delegate and resets
-// the infobar to be accepted for unconsented message prompt.
-TEST_F(TailoredSecurityInfobarBannerInteractionHandlerTest,
-       UnconsentedMessagePromptButtonTapped) {
-  CreateInfobarWithMessageState(
-      safe_browsing::TailoredSecurityServiceMessageState::
-          kUnconsentedAndFlowEnabled);
-  ASSERT_FALSE(infobar_->accepted());
-  handler_.MainButtonTapped(infobar_);
-  EXPECT_TRUE(infobar_->accepted());
-  EXPECT_TRUE(
-      safe_browsing::GetSafeBrowsingState(*chrome_browser_state_->GetPrefs()) ==
-      safe_browsing::SafeBrowsingState::ENHANCED_PROTECTION);
-}
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
index 86b42a7..1c6f0b8 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/BUILD.gn
@@ -13,10 +13,6 @@
     "mock_infobar_interaction_handler.mm",
     "mock_save_address_profile_modal_infobar_interaction_handler.h",
     "mock_save_address_profile_modal_infobar_interaction_handler.mm",
-    "mock_save_card_banner_infobar_interaction_handler.h",
-    "mock_save_card_banner_infobar_interaction_handler.mm",
-    "mock_save_card_modal_infobar_interaction_handler.h",
-    "mock_save_card_modal_infobar_interaction_handler.mm",
     "mock_translate_infobar_interaction_handler.h",
     "mock_translate_infobar_interaction_handler.mm",
   ]
@@ -33,7 +29,6 @@
     "//ios/chrome/browser/infobars/overlays",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate",
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/common/infobars",
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.mm
index e376d3c..9e0cc7e 100644
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.mm
+++ b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.mm
@@ -4,15 +4,16 @@
 
 #import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
 
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
 #import "base/functional/bind.h"
 #import "base/uuid.h"
 #import "components/autofill/core/browser/autofill_test_utils.h"
+#import "components/autofill/core/browser/payments/test_legal_message_line.h"
 #import "components/signin/public/identity_manager/account_info.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 MockAutofillSaveCardInfoBarDelegateMobile::
     MockAutofillSaveCardInfoBarDelegateMobile(
         autofill::AutofillClient::SaveCreditCardOptions options,
@@ -56,5 +57,7 @@
   return std::make_unique<MockAutofillSaveCardInfoBarDelegateMobile>(
       autofill::AutofillClient::SaveCreditCardOptions(), card,
       upload ? Variant(std::move(upload_cb)) : Variant(std::move(local_cb)),
-      autofill::LegalMessageLines(), AccountInfo());
+      autofill::LegalMessageLines(
+          {autofill::TestLegalMessageLine("Test message")}),
+      AccountInfo());
 }
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.h
deleted file mode 100644
index 32e6246..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_SAVE_CARD_BANNER_INFOBAR_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_SAVE_CARD_BANNER_INFOBAR_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_banner_interaction_handler.h"
-
-#include <string.h>
-
-#include "testing/gmock/include/gmock/gmock.h"
-
-class InfoBarIOS;
-
-// Mock version of SaveCardInfobarBannerInteractionHandler for use in tests.
-class MockSaveCardInfobarBannerInteractionHandler
-    : public SaveCardInfobarBannerInteractionHandler {
- public:
-  MockSaveCardInfobarBannerInteractionHandler();
-  ~MockSaveCardInfobarBannerInteractionHandler() override;
-
-  MOCK_METHOD4(SaveCredentials,
-               void(InfoBarIOS* infobar,
-                    std::u16string cardholder_name,
-                    std::u16string expiration_date_month,
-                    std::u16string expiration_date_year));
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_SAVE_CARD_BANNER_INFOBAR_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.mm
deleted file mode 100644
index d0a38686..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.mm
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_banner_infobar_interaction_handler.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-MockSaveCardInfobarBannerInteractionHandler::
-    MockSaveCardInfobarBannerInteractionHandler() = default;
-
-MockSaveCardInfobarBannerInteractionHandler::
-    ~MockSaveCardInfobarBannerInteractionHandler() = default;
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.h b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.h
deleted file mode 100644
index ce444130..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_SAVE_CARD_MODAL_INFOBAR_INTERACTION_HANDLER_H_
-#define IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_SAVE_CARD_MODAL_INFOBAR_INTERACTION_HANDLER_H_
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card/save_card_infobar_modal_interaction_handler.h"
-
-#include "testing/gmock/include/gmock/gmock.h"
-#include "url/gurl.h"
-
-class InfoBarIOS;
-
-// Mock version of SaveCardInfobarModalInteractionHandler for use in tests.
-class MockSaveCardInfobarModalInteractionHandler
-    : public SaveCardInfobarModalInteractionHandler {
- public:
-  MockSaveCardInfobarModalInteractionHandler();
-  ~MockSaveCardInfobarModalInteractionHandler() override;
-
-  MOCK_METHOD4(UpdateCredentials,
-               void(InfoBarIOS* infobar,
-                    std::u16string cardholder_name,
-                    std::u16string expiration_date_month,
-                    std::u16string expiration_date_year));
-  MOCK_METHOD2(LoadURL, void(InfoBarIOS* infobar, GURL url));
-};
-
-#endif  // IOS_CHROME_BROWSER_INFOBARS_OVERLAYS_BROWSER_AGENT_INTERACTION_HANDLERS_TEST_MOCK_SAVE_CARD_MODAL_INFOBAR_INTERACTION_HANDLER_H_
diff --git a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.mm b/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.mm
deleted file mode 100644
index 9a7ac43..0000000
--- a/ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.mm
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_save_card_modal_infobar_interaction_handler.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-MockSaveCardInfobarModalInteractionHandler::
-    MockSaveCardInfobarModalInteractionHandler() = default;
-
-MockSaveCardInfobarModalInteractionHandler::
-    ~MockSaveCardInfobarModalInteractionHandler() = default;
diff --git a/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory.mm b/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory.mm
index 21d4e49..a5c697fd 100644
--- a/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory.mm
+++ b/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory.mm
@@ -11,12 +11,9 @@
 #import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/confirm_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/sync_error_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/translate_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/translate_infobar_modal_overlay_request_config.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -28,8 +25,11 @@
     InfobarOverlayType overlay_type) {
   DCHECK(infobar_ios);
   switch (infobar_ios->infobar_type()) {
+    case InfobarType::kInfobarTypeTailoredSecurityService:
     case InfobarType::kInfobarTypePasswordSave:
     case InfobarType::kInfobarTypePasswordUpdate:
+    case InfobarType::kInfobarTypePermissions:
+    case InfobarType::kInfobarTypeSaveCard:
       return OverlayRequest::CreateWithConfig<
           DefaultInfobarOverlayRequestConfig>(infobar_ios, overlay_type);
     case InfobarType::kInfobarTypeTranslate:
@@ -62,22 +62,6 @@
           return nullptr;
       }
 
-    case InfobarType::kInfobarTypeSaveCard:
-      switch (overlay_type) {
-        case InfobarOverlayType::kBanner:
-          return OverlayRequest::CreateWithConfig<
-              save_card_infobar_overlays::SaveCardBannerRequestConfig>(
-              infobar_ios);
-
-        case InfobarOverlayType::kModal:
-          return OverlayRequest::CreateWithConfig<
-              save_card_infobar_overlays::SaveCardModalRequestConfig>(
-              infobar_ios);
-
-        default:
-          return nullptr;
-      }
-
     case InfobarType::kInfobarTypeSaveAutofillAddressProfile:
       switch (overlay_type) {
         case InfobarOverlayType::kBanner:
@@ -94,16 +78,6 @@
           return nullptr;
       }
 
-    case InfobarType::kInfobarTypePermissions:
-      return OverlayRequest::CreateWithConfig<
-          DefaultInfobarOverlayRequestConfig>(infobar_ios, overlay_type);
-    case InfobarType::kInfobarTypeTailoredSecurityService:
-      if (overlay_type == InfobarOverlayType::kBanner) {
-        return OverlayRequest::CreateWithConfig<
-            tailored_security_service_infobar_overlays::
-                TailoredSecurityServiceBannerRequestConfig>(infobar_ios);
-      }
-      return nullptr;
     case InfobarType::kInfobarTypeSyncError:
       if (overlay_type == InfobarOverlayType::kBanner) {
         return OverlayRequest::CreateWithConfig<
diff --git a/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory_unittest.mm b/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory_unittest.mm
index e8a59049..c66662a 100644
--- a/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory_unittest.mm
+++ b/ios/chrome/browser/infobars/overlays/default_infobar_overlay_request_factory_unittest.mm
@@ -20,11 +20,8 @@
 #import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/confirm_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/save_address_profile_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/translate_infobar_banner_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/save_address_profile_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_modal/translate_infobar_modal_overlay_request_config.h"
 #import "ios/chrome/browser/passwords/ios_chrome_save_password_infobar_delegate.h"
 #import "ios/chrome/browser/passwords/test/mock_ios_chrome_save_passwords_infobar_delegate.h"
@@ -45,10 +42,6 @@
 using infobars::InfoBar;
 using infobars::InfoBarDelegate;
 using safe_browsing::TailoredSecurityServiceMessageState;
-using save_card_infobar_overlays::SaveCardBannerRequestConfig;
-using save_card_infobar_overlays::SaveCardModalRequestConfig;
-using tailored_security_service_infobar_overlays::
-    TailoredSecurityServiceBannerRequestConfig;
 using translate_infobar_overlays::TranslateBannerRequestConfig;
 using translate_infobar_overlays::TranslateModalRequestConfig;
 
@@ -128,12 +121,12 @@
   std::unique_ptr<OverlayRequest> banner_request =
       DefaultInfobarOverlayRequestFactory(&infobar,
                                           InfobarOverlayType::kBanner);
-  EXPECT_TRUE(banner_request->GetConfig<SaveCardBannerRequestConfig>());
+  EXPECT_TRUE(banner_request->GetConfig<DefaultInfobarOverlayRequestConfig>());
 
   // Test modal request creation.
   std::unique_ptr<OverlayRequest> modal_request =
       DefaultInfobarOverlayRequestFactory(&infobar, InfobarOverlayType::kModal);
-  EXPECT_TRUE(modal_request->GetConfig<SaveCardModalRequestConfig>());
+  EXPECT_TRUE(modal_request->GetConfig<DefaultInfobarOverlayRequestConfig>());
 }
 
 // Tests that the factory creates a translate request.
@@ -194,6 +187,5 @@
   std::unique_ptr<OverlayRequest> banner_request =
       DefaultInfobarOverlayRequestFactory(&infobar,
                                           InfobarOverlayType::kBanner);
-  EXPECT_TRUE(
-      banner_request->GetConfig<TailoredSecurityServiceBannerRequestConfig>());
+  EXPECT_TRUE(banner_request->GetConfig<DefaultInfobarOverlayRequestConfig>());
 }
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn b/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn
index cd86e4e..0e81be4 100644
--- a/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn
+++ b/ios/chrome/browser/overlays/public/infobar_banner/BUILD.gn
@@ -14,12 +14,8 @@
     "infobar_banner_placeholder_request_config.mm",
     "save_address_profile_infobar_banner_overlay_request_config.h",
     "save_address_profile_infobar_banner_overlay_request_config.mm",
-    "save_card_infobar_banner_overlay_request_config.h",
-    "save_card_infobar_banner_overlay_request_config.mm",
     "sync_error_infobar_banner_overlay_request_config.h",
     "sync_error_infobar_banner_overlay_request_config.mm",
-    "tailored_security_service_infobar_banner_overlay_request_config.h",
-    "tailored_security_service_infobar_banner_overlay_request_config.mm",
     "translate_infobar_banner_overlay_request_config.h",
     "translate_infobar_banner_overlay_request_config.mm",
   ]
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h b/ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h
deleted file mode 100644
index 19a9e42..0000000
--- a/ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_SAVE_CARD_INFOBAR_BANNER_OVERLAY_REQUEST_CONFIG_H_
-#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_SAVE_CARD_INFOBAR_BANNER_OVERLAY_REQUEST_CONFIG_H_
-
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <string>
-
-#include "ios/chrome/browser/overlays/public/overlay_request_config.h"
-#include "ios/chrome/browser/overlays/public/overlay_user_data.h"
-
-namespace infobars {
-class InfoBar;
-}
-
-namespace save_card_infobar_overlays {
-
-// Configuration object for OverlayRequests for the banner UI for an InfoBar
-// with a AutofillSaveCardInfoBarDelegateMobile.
-class SaveCardBannerRequestConfig
-    : public OverlayRequestConfig<SaveCardBannerRequestConfig> {
- public:
-  ~SaveCardBannerRequestConfig() override;
-
-  // The message text.
-  std::u16string message_text() const { return message_text_; }
-
-  // The label for the card.
-  std::u16string card_label() const { return card_label_; }
-
-  // The card holder name of the card.
-  std::u16string cardholder_name() const { return cardholder_name_; }
-
-  // The expiration month of the card.
-  std::u16string expiration_date_month() const {
-    return expiration_date_month_;
-  }
-
-  // The expiration year of the card.
-  std::u16string expiration_date_year() const { return expiration_date_year_; }
-
-  // The button label text.
-  std::u16string button_label_text() const { return button_label_text_; }
-
-  // Whether the action is an upload or a local save.
-  bool should_upload_credentials() const { return should_upload_credentials_; }
-
- private:
-  OVERLAY_USER_DATA_SETUP(SaveCardBannerRequestConfig);
-  explicit SaveCardBannerRequestConfig(infobars::InfoBar* infobar);
-
-  // OverlayUserData:
-  void CreateAuxiliaryData(base::SupportsUserData* user_data) override;
-
-  // The InfoBar causing this banner.
-  infobars::InfoBar* infobar_ = nullptr;
-  // Configuration data extracted from `infobar_`'s save card delegate.
-  std::u16string message_text_;
-  std::u16string card_label_;
-  std::u16string cardholder_name_;
-  std::u16string expiration_date_month_;
-  std::u16string expiration_date_year_;
-  std::u16string button_label_text_;
-  bool should_upload_credentials_ = false;
-};
-
-}  // namespace save_card_infobar_overlays
-
-#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_SAVE_CARD_INFOBAR_BANNER_OVERLAY_REQUEST_CONFIG_H_
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.mm b/ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.mm
deleted file mode 100644
index e5bf0d5..0000000
--- a/ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.mm
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-
-#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
-#import "components/infobars/core/infobar.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_type.h"
-#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
-#import "ios/chrome/grit/ios_strings.h"
-#import "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace save_card_infobar_overlays {
-
-OVERLAY_USER_DATA_SETUP_IMPL(SaveCardBannerRequestConfig);
-
-SaveCardBannerRequestConfig::SaveCardBannerRequestConfig(
-    infobars::InfoBar* infobar)
-    : infobar_(infobar) {
-  DCHECK(infobar_);
-  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
-      autofill::AutofillSaveCardInfoBarDelegateMobile::FromInfobarDelegate(
-          infobar_->delegate());
-  message_text_ = delegate->GetMessageText();
-  card_label_ = delegate->card_label();
-  cardholder_name_ = delegate->cardholder_name();
-  expiration_date_month_ = delegate->expiration_date_month();
-  expiration_date_year_ = delegate->expiration_date_year();
-  button_label_text_ =
-      delegate->is_for_upload()
-          ? l10n_util::GetStringUTF16(IDS_IOS_AUTOFILL_SAVE_ELLIPSIS)
-          : delegate->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_OK);
-  should_upload_credentials_ = delegate->is_for_upload();
-}
-
-SaveCardBannerRequestConfig::~SaveCardBannerRequestConfig() = default;
-
-void SaveCardBannerRequestConfig::CreateAuxiliaryData(
-    base::SupportsUserData* user_data) {
-  InfobarOverlayRequestConfig::CreateForUserData(
-      user_data, static_cast<InfoBarIOS*>(infobar_),
-      InfobarOverlayType::kBanner, false);
-}
-
-}  // namespace save_card_infobar_overlays
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h b/ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h
deleted file mode 100644
index 6d21129d..0000000
--- a/ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_TAILORED_SECURITY_SERVICE_INFOBAR_BANNER_OVERLAY_REQUEST_CONFIG_H_
-#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_TAILORED_SECURITY_SERVICE_INFOBAR_BANNER_OVERLAY_REQUEST_CONFIG_H_
-
-#import <CoreFoundation/CoreFoundation.h>
-
-#import <string>
-
-#import "ios/chrome/browser/overlays/public/overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/overlay_user_data.h"
-#import "ios/chrome/browser/safe_browsing/tailored_security/tailored_security_service_infobar_delegate.h"
-
-namespace infobars {
-class InfoBar;
-}
-
-namespace tailored_security_service_infobar_overlays {
-
-// Configuration object for OverlayRequests for the banner UI for an InfoBar
-// with a TailoredSecurityServiceInfobarDelegate.
-class TailoredSecurityServiceBannerRequestConfig
-    : public OverlayRequestConfig<TailoredSecurityServiceBannerRequestConfig> {
- public:
-  ~TailoredSecurityServiceBannerRequestConfig() override;
-
-  // The message text.
-  std::u16string message_text() const { return message_text_; }
-
-  // The button label text.
-  std::u16string button_label_text() const { return button_label_text_; }
-
-  // The description.
-  std::u16string description() const { return description_; }
-
-  // The badge of the infobar.
-  bool has_badge() const { return has_badge_; }
-
-  // The message state.
-  safe_browsing::TailoredSecurityServiceMessageState message_state() const {
-    return message_state_;
-  }
-
- private:
-  OVERLAY_USER_DATA_SETUP(TailoredSecurityServiceBannerRequestConfig);
-  explicit TailoredSecurityServiceBannerRequestConfig(
-      infobars::InfoBar* infobar);
-
-  // OverlayUserData:
-  void CreateAuxiliaryData(base::SupportsUserData* user_data) override;
-
-  // The InfoBar causing this banner.
-  infobars::InfoBar* infobar_ = nullptr;
-  // Configuration data extracted from `infobar_`'s tailored security delegate.
-  std::u16string message_text_;
-  std::u16string description_;
-  std::u16string button_label_text_;
-  safe_browsing::TailoredSecurityServiceMessageState message_state_;
-  // Determines if the banner should show the gear icon.
-  bool has_badge_ = false;
-};
-
-}  // namespace tailored_security_service_infobar_overlays
-
-#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_BANNER_TAILORED_SECURITY_SERVICE_INFOBAR_BANNER_OVERLAY_REQUEST_CONFIG_H_
diff --git a/ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.mm b/ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.mm
deleted file mode 100644
index f2cfcf7..0000000
--- a/ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.mm
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h"
-
-#import "components/infobars/core/infobar.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/infobars/overlays/infobar_overlay_type.h"
-#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
-#import "ios/chrome/grit/ios_strings.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace tailored_security_service_infobar_overlays {
-
-OVERLAY_USER_DATA_SETUP_IMPL(TailoredSecurityServiceBannerRequestConfig);
-
-TailoredSecurityServiceBannerRequestConfig::
-    TailoredSecurityServiceBannerRequestConfig(infobars::InfoBar* infobar)
-    : infobar_(infobar) {
-  DCHECK(infobar_);
-  safe_browsing::TailoredSecurityServiceInfobarDelegate* delegate =
-      safe_browsing::TailoredSecurityServiceInfobarDelegate::
-          FromInfobarDelegate(infobar_->delegate());
-  message_text_ = delegate->GetMessageText();
-  button_label_text_ = delegate->GetMessageActionText();
-  description_ = delegate->GetDescription();
-  message_state_ = delegate->message_state();
-}
-
-TailoredSecurityServiceBannerRequestConfig::
-    ~TailoredSecurityServiceBannerRequestConfig() = default;
-
-void TailoredSecurityServiceBannerRequestConfig::CreateAuxiliaryData(
-    base::SupportsUserData* user_data) {
-  InfobarOverlayRequestConfig::CreateForUserData(
-      user_data, static_cast<InfoBarIOS*>(infobar_),
-      InfobarOverlayType::kBanner, false);
-}
-
-}  // namespace tailored_security_service_infobar_overlays
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn b/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn
index 8e14eff..f4c8688 100644
--- a/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn
+++ b/ios/chrome/browser/overlays/public/infobar_modal/BUILD.gn
@@ -10,10 +10,6 @@
     "save_address_profile_infobar_modal_overlay_request_config.mm",
     "save_address_profile_infobar_modal_overlay_responses.h",
     "save_address_profile_infobar_modal_overlay_responses.mm",
-    "save_card_infobar_modal_overlay_request_config.h",
-    "save_card_infobar_modal_overlay_request_config.mm",
-    "save_card_infobar_modal_overlay_responses.h",
-    "save_card_infobar_modal_overlay_responses.mm",
     "translate_infobar_modal_overlay_request_config.h",
     "translate_infobar_modal_overlay_request_config.mm",
     "translate_infobar_modal_overlay_responses.h",
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h b/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h
deleted file mode 100644
index 89825d5..0000000
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_MODAL_SAVE_CARD_INFOBAR_MODAL_OVERLAY_REQUEST_CONFIG_H_
-#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_MODAL_SAVE_CARD_INFOBAR_MODAL_OVERLAY_REQUEST_CONFIG_H_
-
-#import <UIKit/UIKit.h>
-
-#include "ios/chrome/browser/overlays/public/overlay_request_config.h"
-
-#include "components/autofill/core/browser/payments/legal_message_line.h"
-#include "ui/gfx/image/image.h"
-
-class InfoBarIOS;
-@class SaveCardMessageWithLinks;
-
-namespace autofill {
-class AutofillSaveCardInfoBarDelegateMobile;
-}
-
-namespace save_card_infobar_overlays {
-
-// Configuration object for OverlayRequests for the modal UI for an infobar with
-// a AutofillSaveCardInfoBarDelegateMobile.
-class SaveCardModalRequestConfig
-    : public OverlayRequestConfig<SaveCardModalRequestConfig> {
- public:
-  ~SaveCardModalRequestConfig() override;
-
-  // The card holder name of the card.
-  const std::u16string& cardholder_name() const { return cardholder_name_; }
-
-  // The expiration month of the card.
-  const std::u16string& expiration_date_month() const {
-    return expiration_date_month_;
-  }
-
-  // The expiration year of the card.
-  const std::u16string& expiration_date_year() const {
-    return expiration_date_year_;
-  }
-
-  // The last four digits of the card.
-  const std::u16string& card_last_four_digits() const {
-    return card_last_four_digits_;
-  }
-
-  // The resource ID for the icon that identifies the issuer of the card.
-  int issuer_icon_id() const { return issuer_icon_id_; }
-
-  // The legal disclaimer shown at the bottom of the modal.
-  NSArray<SaveCardMessageWithLinks*>* legal_message_lines() const {
-    return legal_message_lines_;
-  }
-
-  // Whether the current card is already saved.
-  bool current_card_saved() const { return current_card_saved_; }
-
-  // Whether the action is an upload or a local save.
-  bool should_upload_credentials() const { return should_upload_credentials_; }
-
-  const std::u16string& displayed_target_account_email() const {
-    return displayed_target_account_email_;
-  }
-
-  const gfx::Image& displayed_target_account_avatar() const {
-    return displayed_target_account_avatar_;
-  }
-
- private:
-  OVERLAY_USER_DATA_SETUP(SaveCardModalRequestConfig);
-  explicit SaveCardModalRequestConfig(InfoBarIOS* infobar);
-
-  // OverlayUserData:
-  void CreateAuxiliaryData(base::SupportsUserData* user_data) override;
-
-  // Return an array of UI SaveCardMessageWithLinks model objects for
-  // `delegate`'s legal_message_lines_.
-  NSMutableArray<SaveCardMessageWithLinks*>* LegalMessagesForModal(
-      autofill::AutofillSaveCardInfoBarDelegateMobile* delegate);
-
-  // The InfoBar causing this modal.
-  InfoBarIOS* infobar_ = nullptr;
-  // Configuration data extracted from `infobar_`'s save card delegate.
-  std::u16string cardholder_name_;
-  std::u16string expiration_date_month_;
-  std::u16string expiration_date_year_;
-  std::u16string card_last_four_digits_;
-  int issuer_icon_id_;
-  NSArray<SaveCardMessageWithLinks*>* legal_message_lines_;
-  bool current_card_saved_ = false;
-  bool should_upload_credentials_ = false;
-  std::u16string displayed_target_account_email_;
-  gfx::Image displayed_target_account_avatar_;
-};
-
-}  // namespace save_card_infobar_overlays
-
-#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_MODAL_SAVE_CARD_INFOBAR_MODAL_OVERLAY_REQUEST_CONFIG_H_
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.mm b/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.mm
deleted file mode 100644
index 778736a..0000000
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.mm
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
-
-#import "base/check.h"
-#import "base/strings/sys_string_conversions.h"
-#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
-#import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
-#import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/overlays/public/common/infobars/infobar_overlay_request_config.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace save_card_infobar_overlays {
-
-OVERLAY_USER_DATA_SETUP_IMPL(SaveCardModalRequestConfig);
-
-SaveCardModalRequestConfig::SaveCardModalRequestConfig(InfoBarIOS* infobar)
-    : infobar_(infobar) {
-  DCHECK(infobar_);
-  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
-      static_cast<autofill::AutofillSaveCardInfoBarDelegateMobile*>(
-          infobar_->delegate());
-
-  cardholder_name_ = delegate->cardholder_name();
-  expiration_date_month_ = delegate->expiration_date_month();
-  expiration_date_year_ = delegate->expiration_date_year();
-  card_last_four_digits_ = delegate->card_last_four_digits();
-  issuer_icon_id_ = delegate->issuer_icon_id();
-  legal_message_lines_ = LegalMessagesForModal(delegate);
-  current_card_saved_ = infobar->accepted();
-  should_upload_credentials_ = delegate->is_for_upload();
-  displayed_target_account_email_ = delegate->displayed_target_account_email();
-  displayed_target_account_avatar_ =
-      delegate->displayed_target_account_avatar();
-}
-
-SaveCardModalRequestConfig::~SaveCardModalRequestConfig() = default;
-
-void SaveCardModalRequestConfig::CreateAuxiliaryData(
-    base::SupportsUserData* user_data) {
-  InfobarOverlayRequestConfig::CreateForUserData(
-      user_data, infobar_, InfobarOverlayType::kModal, false);
-}
-
-NSMutableArray<SaveCardMessageWithLinks*>*
-SaveCardModalRequestConfig::LegalMessagesForModal(
-    autofill::AutofillSaveCardInfoBarDelegateMobile* delegate) {
-  NSMutableArray<SaveCardMessageWithLinks*>* legalMessages =
-      [[NSMutableArray alloc] init];
-  // Only display legal Messages if the card is being uploaded and there are
-  // any.
-  if (delegate->is_for_upload() && !delegate->legal_message_lines().empty()) {
-    for (const auto& line : delegate->legal_message_lines()) {
-      SaveCardMessageWithLinks* message =
-          [[SaveCardMessageWithLinks alloc] init];
-      message.messageText = base::SysUTF16ToNSString(line.text());
-      NSMutableArray* linkRanges = [[NSMutableArray alloc] init];
-      std::vector<GURL> linkURLs;
-      for (const auto& link : line.links()) {
-        [linkRanges addObject:[NSValue valueWithRange:link.range.ToNSRange()]];
-        linkURLs.push_back(link.url);
-      }
-      message.linkRanges = linkRanges;
-      message.linkURLs = linkURLs;
-      [legalMessages addObject:message];
-    }
-  }
-  return legalMessages;
-}
-
-}  // namespace save_card_infobar_overlays
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h b/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h
deleted file mode 100644
index 9b37d481f..0000000
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_MODAL_SAVE_CARD_INFOBAR_MODAL_OVERLAY_RESPONSES_H_
-#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_MODAL_SAVE_CARD_INFOBAR_MODAL_OVERLAY_RESPONSES_H_
-
-#import <Foundation/Foundation.h>
-
-#include "ios/chrome/browser/overlays/public/overlay_response_info.h"
-#include "url/gurl.h"
-
-namespace save_card_infobar_overlays {
-
-// Main action response config for Save Card that passes the information of a
-// card to be saved.
-class SaveCardMainAction : public OverlayResponseInfo<SaveCardMainAction> {
- public:
-  ~SaveCardMainAction() override;
-
-  // The cardholder name of the card to be saved.
-  NSString* cardholder_name() const { return cardholder_name_; }
-  // The expiration month of the card to be saved.
-  NSString* expiration_month() const { return expiration_month_; }
-  // The expiration year of the card to be saved.
-  NSString* expiration_year() const { return expiration_year_; }
-
- private:
-  OVERLAY_USER_DATA_SETUP(SaveCardMainAction);
-  SaveCardMainAction(NSString* cardholder_name,
-                     NSString* expiration_month,
-                     NSString* expiration_year);
-
-  NSString* cardholder_name_ = nil;
-  NSString* expiration_month_ = nil;
-  NSString* expiration_year_ = nil;
-};
-
-// Response config to load a URL tapped in the modal.
-class SaveCardLoadURL : public OverlayResponseInfo<SaveCardLoadURL> {
- public:
-  ~SaveCardLoadURL() override;
-
-  const GURL& link_url() const { return link_url_; }
-
- private:
-  OVERLAY_USER_DATA_SETUP(SaveCardLoadURL);
-  SaveCardLoadURL(const GURL& link_url);
-
-  GURL link_url_;
-};
-
-}  // namespace save_card_infobar_overlays
-#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_INFOBAR_MODAL_SAVE_CARD_INFOBAR_MODAL_OVERLAY_RESPONSES_H_
diff --git a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.mm b/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.mm
deleted file mode 100644
index 0e90b31..0000000
--- a/ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.mm
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-
-#import "url/gurl.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace save_card_infobar_overlays {
-
-#pragma mark - SaveCardMainAction
-
-OVERLAY_USER_DATA_SETUP_IMPL(SaveCardMainAction);
-
-SaveCardMainAction::SaveCardMainAction(NSString* cardholder_name,
-                                       NSString* expiration_month,
-                                       NSString* expiration_year)
-    : cardholder_name_(cardholder_name),
-      expiration_month_(expiration_month),
-      expiration_year_(expiration_year) {}
-
-SaveCardMainAction::~SaveCardMainAction() = default;
-
-#pragma mark - SaveCardLoadURL
-
-OVERLAY_USER_DATA_SETUP_IMPL(SaveCardLoadURL);
-
-SaveCardLoadURL::SaveCardLoadURL(const GURL& link_url) : link_url_(link_url) {}
-
-SaveCardLoadURL::~SaveCardLoadURL() = default;
-
-}  // namespace save_card_infobar_overlays
diff --git a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
index 46656c2..2315626e 100644
--- a/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
+++ b/ios/chrome/browser/shared/model/prefs/browser_prefs.mm
@@ -404,6 +404,9 @@
   // Register pref used to determine if Browser Lockdown Mode is enabled.
   registry->RegisterBooleanPref(prefs::kBrowserLockdownModeEnabled, false);
 
+  // Register pref used to determine if OS Lockdown Mode is enabled.
+  registry->RegisterBooleanPref(prefs::kOSLockdownModeEnabled, false);
+
   ntp_snippets::prefs::RegisterProfilePrefsForMigrationApril2023(registry);
 
   registry->RegisterBooleanPref(kDeprecatedReadingListHasUnseenEntries, false);
diff --git a/ios/chrome/browser/shared/model/prefs/pref_names.cc b/ios/chrome/browser/shared/model/prefs/pref_names.cc
index f078e9b..1c5821a 100644
--- a/ios/chrome/browser/shared/model/prefs/pref_names.cc
+++ b/ios/chrome/browser/shared/model/prefs/pref_names.cc
@@ -241,6 +241,10 @@
 const char kDefaultFollowingFeedSortTypeChanged[] =
     "ios.ntp.following_feed_default_sort_type_changed";
 
+// Boolean that is true when OS Lockdown Mode is enabled for their entire device
+// through native iOS settings.
+const char kOSLockdownModeEnabled[] = "ios.os_lockdown_mode_enabled";
+
 // Dictionary preference which tracks day(s) a given destination is clicked from
 // the new overflow menu carousel.
 const char kOverflowMenuDestinationUsageHistory[] =
diff --git a/ios/chrome/browser/shared/model/prefs/pref_names.h b/ios/chrome/browser/shared/model/prefs/pref_names.h
index 5b4119e..32f30cb 100644
--- a/ios/chrome/browser/shared/model/prefs/pref_names.h
+++ b/ios/chrome/browser/shared/model/prefs/pref_names.h
@@ -60,6 +60,7 @@
 extern const char kNTPContentSuggestionsForSupervisedUserEnabled[];
 extern const char kNTPFollowingFeedSortType[];
 extern const char kDefaultFollowingFeedSortTypeChanged[];
+extern const char kOSLockdownModeEnabled[];
 extern const char kOverflowMenuDestinationUsageHistory[];
 extern const char kOverflowMenuNewDestinations[];
 extern const char kOverflowMenuDestinationsOrder[];
diff --git a/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h b/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h
index 60129a0..1927633 100644
--- a/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h
+++ b/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h
@@ -15,6 +15,11 @@
 
 @optional
 
+// Invoked after the WebStateList is updated.
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection;
+
 // Invoked after a new WebState has been added to the WebStateList at the
 // specified index. `activating` will be YES if the WebState will become
 // the new active WebState after the insertion.
@@ -29,13 +34,6 @@
            fromIndex:(int)fromIndex
              toIndex:(int)toIndex;
 
-// Invoked after the WebState at the specified index is replaced by another
-// WebState.
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex;
-
 // Invoked before the specified WebState is detached from the WebStateList.
 // The WebState is still valid and still in the WebStateList.
 - (void)webStateList:(WebStateList*)webStateList
diff --git a/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.mm b/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.mm
index 16bd15b..6fe069a 100644
--- a/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.mm
+++ b/ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.mm
@@ -18,26 +18,14 @@
     WebStateList* web_state_list,
     const WebStateListChange& change,
     const WebStateSelection& selection) {
-  switch (change.type()) {
-    case WebStateListChange::Type::kReplace: {
-      const WebStateListChangeReplace& replace_change =
-          change.As<WebStateListChangeReplace>();
-      const SEL selector = @selector(webStateList:
-                               didReplaceWebState:withWebState:atIndex:);
-      if (![observer_ respondsToSelector:selector]) {
-        return;
-      }
-
-      // TODO(crbug.com/1442546): Introduce -webStateList:didWebStateListChanged
-      // to WebStateListObserverBridge and replace
-      // -webStateList:didReplaceWebState with it.
-      [observer_ webStateList:web_state_list
-           didReplaceWebState:replace_change.replaced_web_state()
-                 withWebState:replace_change.inserted_web_state()
-                      atIndex:selection.index];
-      break;
-    }
+  const SEL selector = @selector(didChangeWebStateList:change:selection:);
+  if (![observer_ respondsToSelector:selector]) {
+    return;
   }
+
+  [observer_ didChangeWebStateList:web_state_list
+                            change:change
+                         selection:selection];
 }
 
 void WebStateListObserverBridge::WebStateInsertedAt(
diff --git a/ios/chrome/browser/shared/ui/util/pasteboard_util.h b/ios/chrome/browser/shared/ui/util/pasteboard_util.h
index 34b3c16..ee7e27a 100644
--- a/ios/chrome/browser/shared/ui/util/pasteboard_util.h
+++ b/ios/chrome/browser/shared/ui/util/pasteboard_util.h
@@ -19,9 +19,8 @@
 // Stores `url` in the pasteboard. `url` must be valid.
 void StoreURLInPasteboard(const GURL& url);
 
-// Stores `urls` in the pasteboard. `urls` must not be empty and each url
-// within `urls` must be valid. (Use `ClearPasteboard()` explicitly to clear
-// existing items.)
+// Stores `urls` in the pasteboard.
+// (Use `ClearPasteboard()` explicitly to clear existing items.)
 void StoreURLsInPasteboard(const std::vector<const GURL>& urls);
 
 // Stores `text` and `url` into the pasteboard.
diff --git a/ios/chrome/browser/shared/ui/util/pasteboard_util.mm b/ios/chrome/browser/shared/ui/util/pasteboard_util.mm
index a0bebaf..8bf5935 100644
--- a/ios/chrome/browser/shared/ui/util/pasteboard_util.mm
+++ b/ios/chrome/browser/shared/ui/util/pasteboard_util.mm
@@ -26,14 +26,11 @@
 }
 
 void StoreURLsInPasteboard(const std::vector<const GURL>& urls) {
-  DCHECK(!urls.empty());
-
   NSMutableArray* pasteboard_items = [[NSMutableArray alloc] init];
   for (const GURL& URL : urls) {
-    DCHECK(URL.is_valid());
-    // Although this breaks the API contract, invalid URLs arrive here in
-    // production. Prevent crashing by continuing and early returning below if
-    // no valid URLs were passed in `urls`. (crbug.com/880525)
+    // Invalid URLs arrive here in production. Prevent crashing by continuing
+    // and early returning below if no valid URLs were passed in `urls`.
+    // (crbug.com/880525)
     if (!URL.is_valid()) {
       continue;
     }
diff --git a/ios/chrome/browser/sync/BUILD.gn b/ios/chrome/browser/sync/BUILD.gn
index 2b5a77c..9394233f 100644
--- a/ios/chrome/browser/sync/BUILD.gn
+++ b/ios/chrome/browser/sync/BUILD.gn
@@ -74,6 +74,7 @@
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/sessions",
     "//ios/chrome/browser/shared/model/application_context",
+    "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/url:constants",
diff --git a/ios/chrome/browser/sync/session_sync_service_factory.mm b/ios/chrome/browser/sync/session_sync_service_factory.mm
index 0d3e96df..98499bb4 100644
--- a/ios/chrome/browser/sync/session_sync_service_factory.mm
+++ b/ios/chrome/browser/sync/session_sync_service_factory.mm
@@ -23,6 +23,7 @@
 #import "components/sync_sessions/synced_window_delegates_getter.h"
 #import "ios/chrome/browser/favicon/favicon_service_factory.h"
 #import "ios/chrome/browser/history/history_service_factory.h"
+#import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
 #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
 #import "ios/chrome/browser/sync/device_info_sync_service_factory.h"
@@ -60,13 +61,14 @@
 // might inherit from other interfaces with same methods.
 class SyncSessionsClientImpl : public sync_sessions::SyncSessionsClient {
  public:
-  explicit SyncSessionsClientImpl(ChromeBrowserState* browser_state)
+  SyncSessionsClientImpl(ChromeBrowserState* browser_state,
+                         BrowserList* browser_list)
       : browser_state_(browser_state),
         window_delegates_getter_(
             std::make_unique<IOSSyncedWindowDelegatesGetter>()),
         local_session_event_router_(
             std::make_unique<IOSChromeLocalSessionEventRouter>(
-                browser_state_,
+                browser_list,
                 this,
                 ios::sync_start_util::GetFlareForSyncableService(
                     browser_state_->GetStatePath()))),
@@ -162,6 +164,9 @@
     web::BrowserState* context) const {
   ChromeBrowserState* browser_state =
       ChromeBrowserState::FromBrowserState(context);
+  BrowserList* browser_list =
+      BrowserListFactory::GetForBrowserState(browser_state);
   return std::make_unique<sync_sessions::SessionSyncServiceImpl>(
-      ::GetChannel(), std::make_unique<SyncSessionsClientImpl>(browser_state));
+      ::GetChannel(),
+      std::make_unique<SyncSessionsClientImpl>(browser_state, browser_list));
 }
diff --git a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.h b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.h
index 2428af6..7f597ced 100644
--- a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.h
+++ b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.h
@@ -15,12 +15,12 @@
 #include "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer.h"
 #include "ios/web/public/web_state_observer.h"
 
-class ChromeBrowserState;
 class AllWebStateListObservationRegistrar;
+class BrowserList;
 
 namespace sync_sessions {
 class SyncSessionsClient;
-}
+}  // namespace sync_sessions
 
 // A LocalEventRouter that drives session sync via observation of
 // web::WebState-related events.
@@ -28,8 +28,7 @@
     : public sync_sessions::LocalSessionEventRouter {
  public:
   IOSChromeLocalSessionEventRouter(
-      // TODO(crbug.com/1450909): Pass a BrowserList directly instead.
-      ChromeBrowserState* browser_state,
+      BrowserList* browser_list,
       sync_sessions::SyncSessionsClient* sessions_client_,
       const syncer::SyncableService::StartSyncFlare& flare);
 
diff --git a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
index 364ab44..9a98993 100644
--- a/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
+++ b/ios/chrome/browser/sync/sessions/ios_chrome_local_session_event_router.mm
@@ -14,8 +14,7 @@
 #import "components/sync_sessions/synced_tab_delegate.h"
 #import "ios/chrome/browser/history/history_service_factory.h"
 #import "ios/chrome/browser/shared/model/browser/all_web_state_list_observation_registrar.h"
-#import "ios/chrome/browser/shared/model/browser/browser_list_factory.h"
-#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/shared/model/browser/browser_list.h"
 #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/sync/glue/sync_start_util.h"
 #import "ios/chrome/browser/sync/ios_chrome_synced_tab_delegate.h"
@@ -37,11 +36,11 @@
 }  // namespace
 
 IOSChromeLocalSessionEventRouter::IOSChromeLocalSessionEventRouter(
-    ChromeBrowserState* browser_state,
+    BrowserList* browser_list,
     sync_sessions::SyncSessionsClient* sessions_client,
     const syncer::SyncableService::StartSyncFlare& flare)
     : registrar_(std::make_unique<AllWebStateListObservationRegistrar>(
-          BrowserListFactory::GetForBrowserState(browser_state),
+          browser_list,
           std::make_unique<Observer>(this),
           AllWebStateListObservationRegistrar::Mode::REGULAR)),
       sessions_client_(sessions_client),
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
index 9127b22..cf99a2e 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h
@@ -21,6 +21,7 @@
 #include "components/autofill/core/browser/ui/payments/card_name_fix_flow_controller_impl.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_controller_impl.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_options.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #import "components/autofill/ios/browser/autofill_client_ios_bridge.h"
 #include "components/infobars/core/infobar_manager.h"
@@ -138,7 +139,7 @@
   void DidFillOrPreviewField(const std::u16string& autofilled_value,
                              const std::u16string& profile_full_name) override;
   bool IsContextSecure() const override;
-  void ExecuteCommand(Suggestion::FrontendId id) override;
+  void ExecuteCommand(PopupItemId popup_item_id) override;
   void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
   FormInteractionsFlowId GetCurrentFormInteractionsFlowId() override;
   LogManager* GetLogManager() const override;
diff --git a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
index 09125ab..02181ae5 100644
--- a/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
+++ b/ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.mm
@@ -24,6 +24,7 @@
 #import "components/autofill/core/browser/payments/credit_card_otp_authenticator.h"
 #import "components/autofill/core/browser/payments/payments_client.h"
 #import "components/autofill/core/browser/ui/payments/card_unmask_prompt_view.h"
+#import "components/autofill/core/browser/ui/popup_item_ids.h"
 #import "components/autofill/core/common/autofill_features.h"
 #import "components/autofill/core/common/autofill_prefs.h"
 #import "components/autofill/ios/browser/autofill_driver_ios.h"
@@ -490,7 +491,7 @@
   return IsContextSecureForWebState(web_state_);
 }
 
-void ChromeAutofillClientIOS::ExecuteCommand(Suggestion::FrontendId id) {
+void ChromeAutofillClientIOS::ExecuteCommand(PopupItemId popup_item_id) {
   NOTIMPLEMENTED();
 }
 
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.mm b/ios/chrome/browser/ui/badges/badge_mediator.mm
index 0bed93f..789560f 100644
--- a/ios/chrome/browser/ui/badges/badge_mediator.mm
+++ b/ios/chrome/browser/ui/badges/badge_mediator.mm
@@ -390,13 +390,20 @@
 
 #pragma mark - WebStateListObserver
 
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
-  DCHECK_EQ(self.webStateList, webStateList);
-  if (atIndex == webStateList->active_index())
-    self.webState = newWebState;
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      DCHECK_EQ(self.webStateList, webStateList);
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      if (selection.index == webStateList->active_index()) {
+        self.webState = replaceChange.inserted_web_state();
+      }
+      break;
+    }
+  }
 }
 
 - (void)webStateList:(WebStateList*)webStateList
diff --git a/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm b/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm
index 4f2f82fb..670705d 100644
--- a/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm
+++ b/ios/chrome/browser/ui/browser_view/tab_events_mediator.mm
@@ -124,6 +124,33 @@
 
 #pragma mark - WebStateListObserving methods
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      NewTabPageTabHelper* NTPTabHelper =
+          NewTabPageTabHelper::FromWebState(replaceChange.replaced_web_state());
+      if (NTPTabHelper->IsActive()) {
+        [self stopNTPIfNeeded];
+      }
+
+      web::WebState* currentWebState = _webStateList->GetActiveWebState();
+      web::WebState* newWebState = replaceChange.inserted_web_state();
+      // Add `newTab`'s view to the hierarchy if it's the current Tab.
+      if (currentWebState == newWebState) {
+        // Set this before triggering any of the possible page loads in
+        // displayTabViewIfActive.
+        newWebState->SetKeepRenderProcessAlive(true);
+        [self.consumer displayTabViewIfActive];
+      }
+      break;
+    }
+  }
+}
+
 - (void)webStateList:(WebStateList*)webStateList
     willDetachWebState:(web::WebState*)webState
                atIndex:(int)atIndex {
@@ -203,27 +230,6 @@
   }
 }
 
-// Observer method, WebState replaced in `webStateList`.
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
-  NewTabPageTabHelper* NTPTabHelper =
-      NewTabPageTabHelper::FromWebState(oldWebState);
-  if (NTPTabHelper->IsActive()) {
-    [self stopNTPIfNeeded];
-  }
-
-  web::WebState* currentWebState = _webStateList->GetActiveWebState();
-  // Add `newTab`'s view to the hierarchy if it's the current Tab.
-  if (currentWebState == newWebState) {
-    // Set this before triggering any of the possible page loads in
-    // displayTabViewIfActive.
-    newWebState->SetKeepRenderProcessAlive(true);
-    [self.consumer displayTabViewIfActive];
-  }
-}
-
 #pragma mark - WebStateListObserving helpers (Private)
 
 - (void)startNTPIfNeededForActiveWebState:(web::WebState*)webState {
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index c886dd8..478e279 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -358,7 +358,9 @@
     "//ios/chrome/browser/signin:system_identity_manager",
     "//ios/chrome/browser/ui/authentication:eg_test_support+eg2",
     "//ios/chrome/browser/ui/authentication/cells:constants",
+    "//ios/chrome/browser/ui/authentication/signin:constants",
     "//ios/chrome/browser/ui/content_suggestions/cells:constants",
+    "//ios/chrome/browser/ui/first_run:constants",
     "//ios/chrome/browser/ui/ntp:constants",
     "//ios/chrome/browser/ui/ntp:feature_flags",
     "//ios/chrome/browser/ui/popup_menu:constants",
@@ -366,6 +368,8 @@
     "//ios/chrome/browser/ui/start_surface:feature_flags",
     "//ios/chrome/browser/ui/toolbar/public:constants",
     "//ios/chrome/browser/ui/whats_new:feature_flags",
+    "//ios/chrome/common/ui/confirmation_alert:constants",
+    "//ios/chrome/common/ui/promo_style:constants",
     "//ios/chrome/test:eg_test_support+eg2",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
@@ -414,10 +418,13 @@
     "//components/keyed_service/ios",
     "//components/search_engines",
     "//ios/chrome/browser/flags:system_flags",
+    "//ios/chrome/browser/ntp:set_up_list_item_type",
+    "//ios/chrome/browser/ntp:set_up_list_prefs",
     "//ios/chrome/browser/search_engines",
     "//ios/chrome/browser/shared/model/application_context",
     "//ios/chrome/browser/shared/model/browser_state",
     "//ios/chrome/browser/shared/ui/util",
+    "//ios/chrome/browser/ui/content_suggestions/set_up_list",
     "//ios/chrome/browser/ui/ntp:constants",
     "//ios/chrome/test/app:test_support",
     "//ios/testing:block_swizzler",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
index a19d0f6..09d4f4a 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_egtest.mm
@@ -13,14 +13,22 @@
 #import "base/test/ios/wait_util.h"
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ntp/features.h"
+#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
+#import "ios/chrome/browser/signin/fake_system_identity.h"
+#import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
+#import "ios/chrome/browser/ui/first_run/first_run_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
+#import "ios/chrome/common/ui/confirmation_alert/constants.h"
+#import "ios/chrome/common/ui/promo_style/constants.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
@@ -56,6 +64,78 @@
   return std::move(http_response);
 }
 
+// Matcher for the SetUpList.
+id<GREYMatcher> SetUpList() {
+  return grey_allOf(grey_accessibilityID(@"kSetUpListAccessibilityID"),
+                    grey_sufficientlyVisible(), nil);
+}
+
+// Returns matcher for the secondary action button.
+id<GREYMatcher> SetUpListAllSet() {
+  return grey_accessibilityID(@"kSetUpListAllSetID");
+}
+
+// Scrolls to the SetUpList, if it is off-screen.
+void ScrollToSetUpList() {
+  [[[EarlGrey selectElementWithMatcher:SetUpList()]
+         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown, 200)
+      onElementWithMatcher:chrome_test_util::NTPCollectionView()]
+      assertWithMatcher:grey_notNil()];
+}
+
+// Taps the view with the given `accessibility_id`.
+void TapView(NSString* accessibility_id) {
+  id<GREYMatcher> matcher = grey_accessibilityID(accessibility_id);
+  [[EarlGrey selectElementWithMatcher:matcher] assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:matcher] performAction:grey_tap()];
+}
+
+// Tap the PromoStylePrimaryActionButton.
+void TapPromoStylePrimaryActionButton() {
+  id<GREYMatcher> button =
+      grey_accessibilityID(kPromoStylePrimaryActionAccessibilityIdentifier);
+  [[EarlGrey selectElementWithMatcher:button] assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:button] performAction:grey_tap()];
+}
+
+// Tap the PromoStyleSecondaryActionButton.
+void TapPromoStyleSecondaryActionButton() {
+  id<GREYMatcher> button =
+      grey_accessibilityID(kPromoStyleSecondaryActionAccessibilityIdentifier);
+  [[EarlGrey selectElementWithMatcher:button] assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:button] performAction:grey_tap()];
+}
+
+// Tap the ConfirmationAlertSecondaryAction Button.
+void TapSecondaryActionButton() {
+  id<GREYMatcher> button = grey_accessibilityID(
+      kConfirmationAlertSecondaryActionAccessibilityIdentifier);
+  [[EarlGrey selectElementWithMatcher:button] assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:button] performAction:grey_tap()];
+}
+
+// Tap the SetUpList button to expand the list.
+void TapSetUpListExpand() {
+  id<GREYMatcher> expandButton =
+      grey_accessibilityID(@"kSetUpListExpandButtonID");
+  [[EarlGrey selectElementWithMatcher:expandButton]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:expandButton] performAction:grey_tap()];
+}
+
+// Tap the "More" button if it is visible.
+void TapMoreButtonIfVisible() {
+  id<GREYInteraction> button =
+      [EarlGrey selectElementWithMatcher:
+                    grey_accessibilityID(
+                        @"PromoStyleReadMoreActionAccessibilityIdentifier")];
+  NSError* error;
+  [button assertWithMatcher:grey_notNil() error:&error];
+  if (!error) {
+    [button performAction:grey_tap()];
+  }
+}
+
 }  // namespace
 
 #pragma mark - TestCase
@@ -71,7 +151,9 @@
 
 - (AppLaunchConfiguration)appConfigurationForTestCase {
   AppLaunchConfiguration config;
+  config.features_disabled.push_back(kMagicStack);
   config.features_enabled.push_back(kEnableFeedAblation);
+  config.features_enabled.push_back(kIOSSetUpList);
   return config;
 }
 
@@ -96,6 +178,7 @@
 
 - (void)tearDown {
   [ChromeEarlGrey clearBrowsingHistory];
+  [ChromeEarlGreyAppInterface removeFirstRunSentinel];
   [super tearDown];
 }
 
@@ -228,8 +311,154 @@
       assertWithMatcher:grey_nil()];
 }
 
+#pragma mark - Set Up List tests
+
+// Tests that the SetUpList can be expanded and unexpanded by touching the
+// "expand" button at the bottom of the list.
+- (void)testSetUpListExpands {
+  [self prepareToTestSetUpList];
+
+  id<GREYMatcher> signinItem = grey_accessibilityID(@"kSetUpListItemSignInID");
+  id<GREYMatcher> defaultBrowserItem =
+      grey_accessibilityID(@"kSetUpListItemDefaultBrowserID");
+  id<GREYMatcher> autofillItem =
+      grey_accessibilityID(@"kSetUpListItemAutofillID");
+  [[EarlGrey selectElementWithMatcher:signinItem]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:defaultBrowserItem]
+      assertWithMatcher:grey_notNil()];
+  [[EarlGrey selectElementWithMatcher:autofillItem]
+      assertWithMatcher:grey_nil()];
+
+  TapSetUpListExpand();
+  ScrollToSetUpList();
+
+  // Autofill item should appear.
+  [[EarlGrey selectElementWithMatcher:autofillItem]
+      assertWithMatcher:grey_notNil()];
+
+  TapSetUpListExpand();
+
+  // Autofill item should disappear.
+  [[EarlGrey selectElementWithMatcher:autofillItem]
+      assertWithMatcher:grey_nil()];
+}
+
+// Tests that each item opens the appropriate UI flow and that dismissing that
+// UI marks the item complete. Also tests that the "All Set" view appears when
+// all items are complete.
+- (void)testSetUpListDismissItems {
+  [self prepareToTestSetUpList];
+
+  // Tap the signin item.
+  TapView(@"kSetUpListItemSignInID");
+  // Verify the signin screen appears and touch "Don't Sign In".
+  id<GREYMatcher> signinView = grey_accessibilityID(
+      first_run::kFirstRunSignInScreenAccessibilityIdentifier);
+  [[EarlGrey selectElementWithMatcher:signinView]
+      assertWithMatcher:grey_notNil()];
+  // Dismiss the signin view.
+  TapPromoStyleSecondaryActionButton();
+  // Verify the signin item is complete.
+  GREYAssertTrue([NewTabPageAppInterface setUpListItemSignInSyncIsComplete],
+                 @"SetUpList item SignIn not completed.");
+
+  // Tap the default browser item.
+  TapView(@"kSetUpListItemDefaultBrowserID");
+  // Ensure the Default Browser Promo is displayed.
+  id<GREYMatcher> defaultBrowserView = grey_accessibilityID(
+      first_run::kFirstRunDefaultBrowserScreenAccessibilityIdentifier);
+  [[EarlGrey selectElementWithMatcher:defaultBrowserView]
+      assertWithMatcher:grey_notNil()];
+  // Dismiss Default Browser Promo.
+  TapPromoStyleSecondaryActionButton();
+  // Verify the default browser item is complete.
+  GREYAssertTrue([NewTabPageAppInterface setUpListItemDefaultBrowserIsComplete],
+                 @"SetUpList item Default Browser not completed.");
+
+  TapSetUpListExpand();
+  ScrollToSetUpList();
+
+  // Tap the autofill item.
+  TapView(@"kSetUpListItemAutofillID");
+  // TODO - verify the CPE promo is displayed.
+  id<GREYMatcher> CPEPromoView =
+      grey_accessibilityID(@"kCredentialProviderPromoAccessibilityId");
+  [[EarlGrey selectElementWithMatcher:CPEPromoView]
+      assertWithMatcher:grey_notNil()];
+  // Dismiss the CPE promo.
+  TapSecondaryActionButton();
+  // Verify the Autofill item is complete.
+  GREYAssertTrue([NewTabPageAppInterface setUpListItemAutofillIsComplete],
+                 @"SetUpList item Autofill not completed.");
+
+  // Verify All Set view appears.
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:SetUpListAllSet()];
+
+  // Close NTP and reopen.
+  [ChromeEarlGrey closeAllTabs];
+  [ChromeEarlGrey openNewTab];
+  // SetUpList is still visible.
+  [[EarlGrey selectElementWithMatcher:SetUpList()]
+      assertWithMatcher:grey_notNil()];
+
+  // Close NTP and reopen. SetUpList should not be visible.
+  [ChromeEarlGrey closeAllTabs];
+  [ChromeEarlGrey openNewTab];
+  // SetUpList is not visible.
+  [[EarlGrey selectElementWithMatcher:SetUpList()]
+      assertWithMatcher:grey_nil()];
+}
+
+// Tests that the signin UI flow works and that the signin item is marked
+// complete when signin is completed.
+- (void)testSetUpListSignin {
+  [self prepareToTestSetUpList];
+  [SigninEarlGrey addFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+
+  // Tap the signin item.
+  TapView(@"kSetUpListItemSignInID");
+  // Verify the signin screen appears and touch "Continue as ...".
+  id<GREYMatcher> signinView = grey_accessibilityID(
+      first_run::kFirstRunSignInScreenAccessibilityIdentifier);
+  [[EarlGrey selectElementWithMatcher:signinView]
+      assertWithMatcher:grey_notNil()];
+  // Tap "Continue as ...".
+  TapPromoStylePrimaryActionButton();
+
+  // Verify the tangible sync screen appears.
+  id<GREYMatcher> syncView =
+      grey_accessibilityID(kTangibleSyncViewAccessibilityIdentifier);
+  [[EarlGrey selectElementWithMatcher:syncView]
+      assertWithMatcher:grey_notNil()];
+  // On small screens, we may need to tap the "More" button.
+  TapMoreButtonIfVisible();
+  // Tap "Yes, I'm in".
+  TapPromoStylePrimaryActionButton();
+
+  // Verify the signin item is complete.
+  GREYAssertTrue([NewTabPageAppInterface setUpListItemSignInSyncIsComplete],
+                 @"SetUpList item SignIn not completed.");
+}
+
 #pragma mark - Test utils
 
+// Sets up the test case to test SetUpList.
+- (void)prepareToTestSetUpList {
+  [ChromeEarlGreyAppInterface writeFirstRunSentinel];
+  [ChromeEarlGreyAppInterface clearDefaultBrowserPromoData];
+  [ChromeEarlGrey resetDataForLocalStatePref:
+                      prefs::kIosCredentialProviderPromoLastActionTaken];
+  [NewTabPageAppInterface resetSetUpListPrefs];
+  [ChromeEarlGrey closeAllTabs];
+  [ChromeEarlGrey openNewTab];
+  ScrollToSetUpList();
+
+  // SetUpList is visible
+  [[EarlGrey selectElementWithMatcher:SetUpList()]
+      assertWithMatcher:grey_notNil()];
+}
+
 // Setup a most visited tile, and open the context menu by long pressing on it.
 - (void)setupMostVisitedTileLongPress {
   self.testServer->RegisterRequestHandler(
diff --git a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
index 80ca1443..b5c34d7 100644
--- a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
+++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
@@ -29,6 +29,18 @@
 // Returns the Discover header label.
 + (UILabel*)discoverHeaderLabel;
 
+// Resets SetUpList prefs to clear any completed items.
++ (void)resetSetUpListPrefs;
+
+// Returns YES if the SetUpListItemView for SignInSync is complete.
++ (BOOL)setUpListItemSignInSyncIsComplete;
+
+// Returns YES if the SetUpListItemView for DefaultBrowser is complete.
++ (BOOL)setUpListItemDefaultBrowserIsComplete;
+
+// Returns YES if the SetUpListItemView for Autofill is complete.
++ (BOOL)setUpListItemAutofillIsComplete;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_NEW_TAB_PAGE_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
index 009ed47..2e1ee05 100644
--- a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
+++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
@@ -9,12 +9,15 @@
 #import "base/strings/utf_string_conversions.h"
 #import "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 #import "ios/chrome/browser/flags/system_flags.h"
+#import "ios/chrome/browser/ntp/set_up_list_item_type.h"
+#import "ios/chrome/browser/ntp/set_up_list_prefs.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h"
+#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "net/base/mac/url_conversions.h"
 
@@ -23,6 +26,7 @@
 #endif
 
 using content_suggestions::SearchFieldWidth;
+using set_up_list_prefs::SetUpListItemState;
 
 @implementation NewTabPageAppInterface
 
@@ -49,4 +53,33 @@
   return ntp_home::DiscoverHeaderLabel();
 }
 
++ (void)resetSetUpListPrefs {
+  PrefService* localState = GetApplicationContext()->GetLocalState();
+  SetUpListItemState unknown = SetUpListItemState::kUnknown;
+  set_up_list_prefs::SetItemState(localState, SetUpListItemType::kSignInSync,
+                                  unknown);
+  set_up_list_prefs::SetItemState(localState,
+                                  SetUpListItemType::kDefaultBrowser, unknown);
+  set_up_list_prefs::SetItemState(localState, SetUpListItemType::kAutofill,
+                                  unknown);
+}
+
++ (BOOL)setUpListItemSignInSyncIsComplete {
+  return ntp_home::SetUpListItemViewWithAccessibilityId(
+             @"kSetUpListItemSignInID")
+      .complete;
+}
+
++ (BOOL)setUpListItemDefaultBrowserIsComplete {
+  return ntp_home::SetUpListItemViewWithAccessibilityId(
+             @"kSetUpListItemDefaultBrowserID")
+      .complete;
+}
+
++ (BOOL)setUpListItemAutofillIsComplete {
+  return ntp_home::SetUpListItemViewWithAccessibilityId(
+             @"kSetUpListItemAutofillID")
+      .complete;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h
index c9dd4a7..be85d508 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.h
@@ -10,6 +10,7 @@
 #include "url/gurl.h"
 
 @protocol GREYMatcher;
+@class SetUpListItemView;
 
 namespace ntp_home {
 // Returns the primary collection view of the new tab page. Returns nil if it is
@@ -27,6 +28,11 @@
 // Returns the label corresponding to the Discover header label. Returns nil if
 // it is not in the view hierarchy.
 UILabel* DiscoverHeaderLabel();
+
+// Returns the SetUpListItemView with the given `accessibility_id`.
+SetUpListItemView* SetUpListItemViewWithAccessibilityId(
+    NSString* accessibility_id);
+
 }  // namespace ntp_home
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_NTP_HOME_TEST_UTILS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm
index a4403ccf..17d8d38 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_test_utils.mm
@@ -11,6 +11,8 @@
 #import "base/strings/utf_string_conversions.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
+#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_item_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/set_up_list/set_up_list_view.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/testing/earl_grey/earl_grey_app.h"
 #import "ios/web/common/uikit_ui_util.h"
@@ -39,6 +41,12 @@
   return nil;
 }
 
+// Returns the SetUpListView, if present.
+SetUpListView* GetSetUpListView() {
+  return base::mac::ObjCCast<SetUpListView>(SubviewWithAccessibilityIdentifier(
+      @"kSetUpListAccessibilityID", GetAnyKeyWindow()));
+}
+
 }  // namespace
 
 namespace ntp_home {
@@ -65,4 +73,10 @@
       DiscoverHeaderTitleAccessibilityID(), GetAnyKeyWindow()));
 }
 
+SetUpListItemView* SetUpListItemViewWithAccessibilityId(
+    NSString* accessibility_id) {
+  return base::mac::ObjCCast<SetUpListItemView>(
+      SubviewWithAccessibilityIdentifier(accessibility_id, GetSetUpListView()));
+}
+
 }  // namespace ntp_home
diff --git a/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn b/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
index 0da8db37..cdd5e505 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
+++ b/ios/chrome/browser/ui/infobars/coordinators/BUILD.gn
@@ -15,6 +15,7 @@
     "//components/infobars/core",
     "//ios/chrome/browser/infobars:public",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
+    "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/util",
diff --git a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
index ff666a03..0980d38 100644
--- a/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
+++ b/ios/chrome/browser/ui/infobars/coordinators/infobar_coordinator.mm
@@ -9,8 +9,10 @@
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
 #import "base/timer/timer.h"
+#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/ui/util/named_guide.h"
+#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
+#import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/ui/fullscreen/animated_scoped_fullscreen_disabler.h"
 #import "ios/chrome/browser/ui/fullscreen/fullscreen_controller.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_accessibility_util.h"
@@ -230,12 +232,10 @@
 #pragma mark InfobarBannerPositioner
 
 - (CGFloat)bannerYPosition {
-  NamedGuide* omniboxGuide =
-      [NamedGuide guideWithName:kOmniboxGuide
-                           view:self.baseViewController.view];
-  UIView* omniboxView = omniboxGuide.owningView;
-  CGRect omniboxFrame = [omniboxView convertRect:omniboxGuide.layoutFrame
-                                          toView:omniboxView.window];
+  LayoutGuideCenter* layoutGuideCenter =
+      LayoutGuideCenterForBrowser(self.browser);
+  UIView* omnibox = [layoutGuideCenter referencedViewUnderName:kOmniboxGuide];
+  CGRect omniboxFrame = [omnibox convertRect:omnibox.bounds toView:nil];
   return CGRectGetMaxY(omniboxFrame);
 }
 
diff --git a/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm b/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm
index cd3edbd4..0106de3 100644
--- a/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm
+++ b/ios/chrome/browser/ui/main_content/web_scroll_view_main_content_ui_forwarder.mm
@@ -179,12 +179,20 @@
 
 #pragma mark - WebStateListObserving
 
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
-  if (newWebState == webStateList->GetActiveWebState())
-    self.webState = newWebState;
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      web::WebState* insertedWebState = replaceChange.inserted_web_state();
+      if (insertedWebState == webStateList->GetActiveWebState()) {
+        self.webState = insertedWebState;
+      }
+      break;
+    }
+  }
 }
 
 - (void)webStateList:(WebStateList*)webStateList
diff --git a/ios/chrome/browser/ui/ntp/metrics/BUILD.gn b/ios/chrome/browser/ui/ntp/metrics/BUILD.gn
index cd7bd60..bd2047e0 100644
--- a/ios/chrome/browser/ui/ntp/metrics/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/metrics/BUILD.gn
@@ -7,6 +7,8 @@
   sources = [
     "feed_metrics_constants.h",
     "feed_metrics_constants.mm",
+    "feed_metrics_provider.h",
+    "feed_metrics_provider.mm",
     "feed_metrics_recorder+testing.h",
     "feed_metrics_recorder.h",
     "feed_metrics_recorder.mm",
@@ -25,6 +27,7 @@
     "//base",
     "//components/favicon_base",
     "//components/feed/core/v2/public:common",
+    "//components/metrics",
     "//components/ntp_tiles",
     "//ios/chrome/browser/discover_feed:constants",
     "//ios/chrome/browser/discover_feed:discover_feed_refresher",
@@ -44,6 +47,7 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
+    "feed_metrics_provider_unittest.mm",
     "feed_metrics_recorder_unittest.mm",
     "feed_session_recorder_unittest.mm",
   ]
diff --git a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.h b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.h
index 69ca250..74dd1980 100644
--- a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.h
+++ b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.h
@@ -250,6 +250,9 @@
 // Histogram name for the feed activity bucket metric.
 extern const char kAllFeedsActivityBucketsHistogram[];
 
+// Histogram name for the feed activity bucket filter.
+extern const char kAllFeedsActivityBucketsByProviderHistogram[];
+
 // Histogram name for a Discover feed card shown at index.
 extern const char kDiscoverFeedCardShownAtIndex[];
 
diff --git a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.mm b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.mm
index e4ecd9e..b4dc58f 100644
--- a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.mm
+++ b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.mm
@@ -57,6 +57,8 @@
     "ContentSuggestions.Feed.WebFeed.Shown";
 const char kAllFeedsActivityBucketsHistogram[] =
     "ContentSuggestions.Feed.AllFeeds.Activity";
+const char kAllFeedsActivityBucketsByProviderHistogram[] =
+    "ContentSuggestions.Feed.AllFeeds.Activity.ByProvider";
 const char kDiscoverFeedNoticeCardFulfilled[] =
     "ContentSuggestions.Feed.NoticeCardFulfilled2";
 const char kDiscoverFeedArticlesFetchNetworkDurationSuccess[] =
diff --git a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.h b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.h
new file mode 100644
index 0000000..ce80aaf
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.h
@@ -0,0 +1,23 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_NTP_METRICS_FEED_METRICS_PROVIDER_H_
+#define IOS_CHROME_BROWSER_UI_NTP_METRICS_FEED_METRICS_PROVIDER_H_
+
+#import "components/metrics/metrics_provider.h"
+
+class FeedMetricsProvider : public metrics::MetricsProvider {
+ public:
+  explicit FeedMetricsProvider();
+  FeedMetricsProvider(const FeedMetricsProvider&) = delete;
+  FeedMetricsProvider& operator=(const FeedMetricsProvider&) = delete;
+
+  ~FeedMetricsProvider() override;
+
+  // metrics::MetricsProvider
+  void ProvideCurrentSessionData(
+      metrics::ChromeUserMetricsExtension* uma_proto) override;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_NTP_METRICS_FEED_METRICS_PROVIDER_H_
diff --git a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.mm b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.mm
new file mode 100644
index 0000000..c62d77d
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.mm
@@ -0,0 +1,25 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.h"
+
+#import "base/metrics/histogram_functions.h"
+#import "ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+FeedMetricsProvider::FeedMetricsProvider() {}
+
+FeedMetricsProvider::~FeedMetricsProvider() {}
+
+void FeedMetricsProvider::ProvideCurrentSessionData(
+    metrics::ChromeUserMetricsExtension* uma_proto) {
+  // Retrieve activity bucket from storage.
+  FeedActivityBucket activityBucket = (FeedActivityBucket)
+      [[NSUserDefaults standardUserDefaults] integerForKey:kActivityBucketKey];
+  base::UmaHistogramEnumeration(kAllFeedsActivityBucketsByProviderHistogram,
+                                activityBucket);
+}
diff --git a/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider_unittest.mm b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider_unittest.mm
new file mode 100644
index 0000000..d7154fa7
--- /dev/null
+++ b/ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider_unittest.mm
@@ -0,0 +1,37 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/ntp/metrics/feed_metrics_provider.h"
+
+#import "base/test/metrics/histogram_tester.h"
+#import "ios/chrome/browser/ui/ntp/metrics/feed_metrics_constants.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Tests metrics that are recorded and uploaded by
+// FeedMetricsProvider.
+class FeedMetricsProviderTest : public PlatformTest {
+ public:
+  FeedMetricsProviderTest() {
+    histogram_tester_.reset(new base::HistogramTester());
+  }
+
+ protected:
+  void TearDown() override { PlatformTest::TearDown(); }
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+};
+
+// Tests the implementation of ProvideCurrentSessionData
+TEST_F(FeedMetricsProviderTest, ProvideCurrentSessionData) {
+  FeedMetricsProvider provider;
+  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+  [defaults setInteger:1 forKey:kActivityBucketKey];
+  provider.ProvideCurrentSessionData(nullptr /* uma_proto */);
+  histogram_tester_->ExpectBucketCount(
+      kAllFeedsActivityBucketsByProviderHistogram, 1, 1);
+}
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm
index 1ba3b92..5360c30 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_view_controller.mm
@@ -21,7 +21,6 @@
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
-#import "ios/chrome/browser/shared/ui/util/named_guide.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
@@ -145,11 +144,10 @@
 
   self.fakeOmniboxWidthConstraint.constant = self.headerView.bounds.size.width;
   [self.headerView layoutIfNeeded];
-  NamedGuide* omniboxGuide = [NamedGuide guideWithName:kOmniboxGuide
-                                                  view:self.headerView];
-  CGRect omniboxFrameInFakebox =
-      [[omniboxGuide owningView] convertRect:[omniboxGuide layoutFrame]
-                                      toView:self.fakeOmnibox];
+  UIView* omnibox =
+      [self.layoutGuideCenter referencedViewUnderName:kOmniboxGuide];
+  CGRect omniboxFrameInFakebox = [omnibox convertRect:omnibox.bounds
+                                               toView:self.fakeOmnibox];
   self.headerView.fakeLocationBarLeadingConstraint.constant =
       omniboxFrameInFakebox.origin.x;
   self.headerView.fakeLocationBarTrailingConstraint.constant =
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
index 3a2c8eb..783a657 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_coordinator.mm
@@ -168,6 +168,7 @@
   self.mediator.presenter = [[OmniboxPopupPresenter alloc]
       initWithPopupPresenterDelegate:self.presenterDelegate
                  popupViewController:self.popupViewController
+                   layoutGuideCenter:LayoutGuideCenterForBrowser(self.browser)
                            incognito:isIncognito];
 
   _popupView->SetMediator(self.mediator);
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h
index 038fdf7..02ad132 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 @protocol ContentProviding;
+@class LayoutGuideCenter;
 @class OmniboxPopupPresenter;
 
 @protocol OmniboxPopupPresenterDelegate
@@ -43,12 +44,13 @@
 /// Tells the presenter to update, following a trait collection change.
 - (void)updatePopupAfterTraitCollectionChange;
 
-- (instancetype)initWithPopupPresenterDelegate:
-                    (id<OmniboxPopupPresenterDelegate>)presenterDelegate
-                           popupViewController:
-                               (UIViewController<ContentProviding>*)
-                                   viewController
-                                     incognito:(BOOL)incognito;
+- (instancetype)
+    initWithPopupPresenterDelegate:
+        (id<OmniboxPopupPresenterDelegate>)presenterDelegate
+               popupViewController:
+                   (UIViewController<ContentProviding>*)viewController
+                 layoutGuideCenter:(LayoutGuideCenter*)layoutGuideCenter
+                         incognito:(BOOL)incognito;
 
 @end
 
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
index 047bcc872..8e72ba1 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.mm
@@ -5,8 +5,9 @@
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_presenter.h"
 
 #import "ios/chrome/browser/shared/public/features/features.h"
-#import "ios/chrome/browser/shared/ui/util/named_guide.h"
+#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_ui_features.h"
 #import "ios/chrome/browser/ui/omnibox/popup/content_providing.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_container_view.h"
@@ -39,6 +40,9 @@
 /// Separator for the bottom edge of the popup on iPad.
 @property(nonatomic, strong) UIView* bottomSeparator;
 
+// The layout guide center to use to refer to the omnibox.
+@property(nonatomic, strong) LayoutGuideCenter* layoutGuideCenter;
+
 @end
 
 @implementation OmniboxPopupPresenter
@@ -47,11 +51,13 @@
     initWithPopupPresenterDelegate:(id<OmniboxPopupPresenterDelegate>)delegate
                popupViewController:
                    (UIViewController<ContentProviding>*)viewController
+                 layoutGuideCenter:(LayoutGuideCenter*)layoutGuideCenter
                          incognito:(BOOL)incognito {
   self = [super init];
   if (self) {
     _delegate = delegate;
     _viewController = viewController;
+    _layoutGuideCenter = layoutGuideCenter;
 
     // Popup uses same colors as the toolbar, so the ToolbarConfiguration is
     // used to get the style.
@@ -199,13 +205,14 @@
       constraintGreaterThanOrEqualToAnchor:popup.bottomAnchor
                                   constant:kPopupBottomPaddingTablet];
 
-  // Position the top anchor of the popup relatively to the layout guide
-  // positioned on the omnibox.
-  UILayoutGuide* omniboxGuide = [NamedGuide guideWithName:kOmniboxGuide
-                                                     view:popup];
+  // Install in the superview the guide tracking the omnibox.
+  UILayoutGuide* omniboxGuide =
+      [self.layoutGuideCenter makeLayoutGuideNamed:kOmniboxGuide];
+  [[popup superview] addLayoutGuide:omniboxGuide];
+  // Position the top anchor of the popup relatively to that layout guide.
   NSLayoutConstraint* topConstraint =
-      [popup.topAnchor constraintEqualToAnchor:omniboxGuide.bottomAnchor];
-  topConstraint.constant = kVerticalOffset;
+      [popup.topAnchor constraintEqualToAnchor:omniboxGuide.bottomAnchor
+                                      constant:kVerticalOffset];
 
   NSMutableArray<NSLayoutConstraint*>* constraintsToActivate =
       [NSMutableArray arrayWithObject:topConstraint];
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index f49921e6..25d3e89 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -16,8 +16,8 @@
 #import "ios/chrome/browser/shared/ui/elements/self_sizing_table_view.h"
 #import "ios/chrome/browser/shared/ui/util/keyboard_observer_helper.h"
 #import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
-#import "ios/chrome/browser/shared/ui/util/named_guide.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_layout_util.h"
 #import "ios/chrome/browser/ui/favicon/favicon_attributes_provider.h"
 #import "ios/chrome/browser/ui/favicon/favicon_attributes_with_payload.h"
@@ -130,10 +130,17 @@
 /// content inset.
 @property(nonatomic, assign) CGFloat cachedContentHeight;
 
+/// Layout guide that tracks the position of the omnibox.
+/// This is useful to add constraints to, or to derive manual layout values off
+/// of.
+@property(nonatomic, readonly) UILayoutGuide* omniboxGuide;
+
 @end
 
 @implementation OmniboxPopupViewController
 
+@synthesize omniboxGuide = _omniboxGuide;
+
 - (instancetype)init {
   if (self = [super initWithNibName:nil bundle:nil]) {
     _forwardsScrollEvents = YES;
@@ -316,15 +323,11 @@
 }
 
 - (void)adjustMarginsToMatchOmniboxWidth {
-  NamedGuide* layoutGuide = [NamedGuide guideWithName:kOmniboxGuide
-                                                 view:self.view];
-  if (!layoutGuide) {
+  if (!self.omniboxGuide) {
     return;
   }
 
-  CGRect omniboxFrame = [layoutGuide.constrainedView
-      convertRect:layoutGuide.constrainedView.bounds
-           toView:self.view];
+  CGRect omniboxFrame = self.omniboxGuide.layoutFrame;
   CGFloat leftMargin =
       IsRegularXRegularSizeClass(self) ? omniboxFrame.origin.x : 0;
   CGFloat rightMargin = IsRegularXRegularSizeClass(self)
@@ -767,13 +770,8 @@
   // Inset the header to match the omnibox width, similar to
   // `adjustMarginsToMatchOmniboxWidth` method.
   if (IsRegularXRegularSizeClass(self)) {
-    NamedGuide* layoutGuide = [NamedGuide guideWithName:kOmniboxGuide
-                                                   view:self.view];
-    if (layoutGuide) {
-      CGRect omniboxFrame = [layoutGuide.constrainedView
-          convertRect:layoutGuide.constrainedView.bounds
-               toView:self.view];
-      CGFloat leftMargin = omniboxFrame.origin.x;
+    if (self.omniboxGuide) {
+      CGFloat leftMargin = CGRectGetMinX(self.omniboxGuide.layoutFrame);
 
       contentConfiguration.directionalLayoutMargins =
           NSDirectionalEdgeInsetsMake(kHeaderTopPadding,
@@ -1099,4 +1097,12 @@
                    completion:nil];
 }
 
+- (UILayoutGuide*)omniboxGuide {
+  if (!_omniboxGuide) {
+    _omniboxGuide = [self.layoutGuideCenter makeLayoutGuideNamed:kOmniboxGuide];
+    [self.view addLayoutGuide:_omniboxGuide];
+  }
+  return _omniboxGuide;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn
index f738434..29e85fe 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/BUILD.gn
@@ -18,6 +18,7 @@
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/common/infobars",
     "//ios/chrome/browser/overlays/public/default",
+    "//ios/chrome/browser/shared/coordinator/layout_guide",
     "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/ui/infobars:constants",
     "//ios/chrome/browser/ui/infobars/banners",
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
index 46b5b267..8a6eb995 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_coordinator.mm
@@ -13,7 +13,9 @@
 #import "ios/chrome/browser/overlays/public/overlay_request.h"
 #import "ios/chrome/browser/overlays/public/overlay_request_support.h"
 #import "ios/chrome/browser/overlays/public/overlay_response.h"
-#import "ios/chrome/browser/shared/ui/util/named_guide.h"
+#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
+#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
+#import "ios/chrome/browser/shared/ui/util/util_swift.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_accessibility_util.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_view_controller.h"
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
@@ -76,12 +78,10 @@
 #pragma mark - InfobarBannerPositioner
 
 - (CGFloat)bannerYPosition {
-  NamedGuide* omniboxGuide =
-      [NamedGuide guideWithName:kOmniboxGuide
-                           view:self.baseViewController.view];
-  UIView* owningView = omniboxGuide.owningView;
-  CGRect omniboxFrame = [owningView convertRect:omniboxGuide.layoutFrame
-                                         toView:owningView.window];
+  LayoutGuideCenter* layoutGuideCenter =
+      LayoutGuideCenterForBrowser(self.browser);
+  UIView* omnibox = [layoutGuideCenter referencedViewUnderName:kOmniboxGuide];
+  CGRect omniboxFrame = [omnibox convertRect:omnibox.bounds toView:nil];
   return CGRectGetMaxY(omniboxFrame);
 }
 
@@ -211,6 +211,12 @@
     case InfobarType::kInfobarTypePermissions:
       mediatorClass = [PermissionsBannerOverlayMediator class];
       break;
+    case InfobarType::kInfobarTypeTailoredSecurityService:
+      mediatorClass = [TailoredSecurityInfobarBannerOverlayMediator class];
+      break;
+    case InfobarType::kInfobarTypeSaveCard:
+      mediatorClass = [SaveCardInfobarBannerOverlayMediator class];
+      break;
     default:
       NOTREACHED_NORETURN() << "Received unsupported infobarType.";
   }
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/BUILD.gn
index ed91004..63618b5 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/BUILD.gn
@@ -12,7 +12,11 @@
 
   deps = [
     "//base",
+    "//components/autofill/core/browser",
+    "//ios/chrome/app/strings",
+    "//ios/chrome/browser/infobars/overlays:util",
     "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/default",
     "//ios/chrome/browser/overlays/public/infobar_banner",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/shared/ui/symbols",
@@ -38,16 +42,21 @@
     "//components/prefs",
     "//components/signin/public/identity_manager",
     "//ios/chrome/browser/infobars",
+    "//ios/chrome/browser/infobars:public",
+    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test",
     "//ios/chrome/browser/infobars/test",
     "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/default",
     "//ios/chrome/browser/overlays/public/infobar_banner",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/overlays/test",
+    "//ios/chrome/browser/ui/infobars/banners",
     "//ios/chrome/browser/ui/infobars/banners/test",
     "//ios/chrome/browser/ui/infobars/test",
     "//ios/chrome/browser/ui/overlays/test",
     "//testing/gmock",
     "//testing/gtest",
+    "//third_party/ocmock",
     "//ui/base",
   ]
 }
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm
index 2dc56e4..92c72b83 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.mm
@@ -5,63 +5,69 @@
 #import "ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator.h"
 
 #import "base/strings/sys_string_conversions.h"
+#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
+#import "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
+#import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h"
 #import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator+consumer_support.h"
 #import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h"
 #import "ios/chrome/browser/ui/overlays/overlay_request_mediator+subclassing.h"
+#import "ios/chrome/grit/ios_strings.h"
 #import "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-using save_card_infobar_overlays::SaveCardBannerRequestConfig;
-using save_card_infobar_overlays::SaveCardMainAction;
-
 @interface SaveCardInfobarBannerOverlayMediator ()
+
 // The save card banner config from the request.
-@property(nonatomic, readonly) SaveCardBannerRequestConfig* config;
+@property(nonatomic, readonly) DefaultInfobarOverlayRequestConfig* config;
+
 @end
 
 @implementation SaveCardInfobarBannerOverlayMediator
 
 #pragma mark - Accessors
 
-- (SaveCardBannerRequestConfig*)config {
-  return self.request ? self.request->GetConfig<SaveCardBannerRequestConfig>()
-                      : nullptr;
+- (DefaultInfobarOverlayRequestConfig*)config {
+  return self.request
+             ? self.request->GetConfig<DefaultInfobarOverlayRequestConfig>()
+             : nullptr;
+}
+
+// Returns the delegate attached to the config.
+- (autofill::AutofillSaveCardInfoBarDelegateMobile*)saveCardDelegate {
+  return static_cast<autofill::AutofillSaveCardInfoBarDelegateMobile*>(
+      self.config->delegate());
 }
 
 #pragma mark - OverlayRequestMediator
 
 + (const OverlayRequestSupport*)requestSupport {
-  return SaveCardBannerRequestConfig::RequestSupport();
+  return DefaultInfobarOverlayRequestConfig::RequestSupport();
 }
 
 #pragma mark - InfobarOverlayRequestMediator
 
 - (void)bannerInfobarButtonWasPressed:(UIButton*)sender {
+  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
+      self.saveCardDelegate;
+
   // Display the modal (thus the ToS) if the card will be uploaded, this is a
   // legal requirement and shouldn't be changed.
-  if (self.config->should_upload_credentials()) {
-    [self dispatchResponse:OverlayResponse::CreateWithInfo<
-                               InfobarBannerShowModalResponse>()];
+  if (delegate->is_for_upload()) {
+    [self presentInfobarModalFromBanner];
     return;
   }
-  // Notify the model layer to perform the infobar's main action before
-  // dismissing the banner.
-  [self dispatchResponse:OverlayResponse::CreateWithInfo<SaveCardMainAction>(
-                             base::SysUTF16ToNSString(
-                                 self.config->cardholder_name()),
-                             base::SysUTF16ToNSString(
-                                 self.config->expiration_date_month()),
-                             base::SysUTF16ToNSString(
-                                 self.config->expiration_date_year()))];
+
+  InfoBarIOS* infobar = GetOverlayRequestInfobar(self.request);
+  infobar->set_accepted(delegate->UpdateAndAccept(
+      delegate->cardholder_name(), delegate->expiration_date_month(),
+      delegate->expiration_date_year()));
+
   [self dismissOverlay];
 }
 
@@ -70,19 +76,30 @@
 @implementation SaveCardInfobarBannerOverlayMediator (ConsumerSupport)
 
 - (void)configureConsumer {
-  SaveCardBannerRequestConfig* config = self.config;
+  DefaultInfobarOverlayRequestConfig* config = self.config;
   if (!self.consumer || !config)
     return;
 
-  [self.consumer
-      setButtonText:base::SysUTF16ToNSString(self.config->button_label_text())];
+  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
+      self.saveCardDelegate;
+  if (!delegate) {
+    return;
+  }
+
+  std::u16string buttonLabelText =
+      delegate->is_for_upload()
+          ? l10n_util::GetStringUTF16(IDS_IOS_AUTOFILL_SAVE_ELLIPSIS)
+          : delegate->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_OK);
+  [self.consumer setButtonText:base::SysUTF16ToNSString(buttonLabelText)];
+
   UIImage* iconImage = DefaultSymbolTemplateWithPointSize(
       kCreditCardSymbol, kInfobarSymbolPointSize);
   [self.consumer setIconImage:iconImage];
+
   [self.consumer
-      setTitleText:base::SysUTF16ToNSString(self.config->message_text())];
+      setTitleText:base::SysUTF16ToNSString(delegate->GetMessageText())];
   [self.consumer
-      setSubtitleText:base::SysUTF16ToNSString(self.config->card_label())];
+      setSubtitleText:base::SysUTF16ToNSString(delegate->card_label())];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm
index 5074d8a3..316cf14e 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/save_card/save_card_infobar_banner_overlay_mediator_unittest.mm
@@ -11,133 +11,86 @@
 #import "components/autofill/core/browser/autofill_client.h"
 #import "components/autofill/core/browser/autofill_test_utils.h"
 #import "components/autofill/core/browser/data_model/credit_card.h"
-#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
 #import "components/signin/public/identity_manager/account_info.h"
 #import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/infobar_banner_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/save_card_infobar_banner_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/test/fake_overlay_request_callback_installer.h"
+#import "ios/chrome/browser/infobars/infobar_type.h"
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
+#import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
+#import "ios/chrome/browser/ui/infobars/banners/infobar_banner_delegate.h"
 #import "ios/chrome/browser/ui/infobars/banners/test/fake_infobar_banner_consumer.h"
 #import "testing/gtest_mac.h"
 #import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-using save_card_infobar_overlays::SaveCardBannerRequestConfig;
-using save_card_infobar_overlays::SaveCardMainAction;
-
 // Test fixture for SaveCardInfobarBannerOverlayMediator.
 class SaveCardInfobarBannerOverlayMediatorTest : public PlatformTest {
  public:
-  SaveCardInfobarBannerOverlayMediatorTest()
-      : callback_installer_(&callback_receiver_,
-                            {InfobarBannerShowModalResponse::ResponseSupport(),
-                             SaveCardMainAction::ResponseSupport()}) {
+  SaveCardInfobarBannerOverlayMediatorTest() {}
+
+  void InitInfobar(const bool upload) {
+    autofill::CreditCard credit_card(
+        base::Uuid::GenerateRandomV4().AsLowercaseString(),
+        "https://www.example.com/");
+    ;
+    std::unique_ptr<MockAutofillSaveCardInfoBarDelegateMobile> delegate =
+        MockAutofillSaveCardInfoBarDelegateMobileFactory::
+            CreateMockAutofillSaveCardInfoBarDelegateMobileFactory(upload,
+                                                                   credit_card);
+    delegate_ = delegate.get();
+    infobar_ = std::make_unique<InfoBarIOS>(InfobarType::kInfobarTypeSaveCard,
+                                            std::move(delegate));
+    request_ =
+        OverlayRequest::CreateWithConfig<DefaultInfobarOverlayRequestConfig>(
+            infobar_.get(), InfobarOverlayType::kBanner);
+    consumer_ = [[FakeInfobarBannerConsumer alloc] init];
+    mediator_ = [[SaveCardInfobarBannerOverlayMediator alloc]
+        initWithRequest:request_.get()];
+    ;
+    mediator_.consumer = consumer_;
   }
 
  protected:
-  MockOverlayRequestCallbackReceiver callback_receiver_;
-  FakeOverlayRequestCallbackInstaller callback_installer_;
+  std::unique_ptr<InfoBarIOS> infobar_;
+  std::unique_ptr<OverlayRequest> request_;
+  MockAutofillSaveCardInfoBarDelegateMobile* delegate_ = nil;
+  FakeInfobarBannerConsumer* consumer_ = nil;
+  SaveCardInfobarBannerOverlayMediator* mediator_ = nil;
 };
 
-// Tests that a SaveCardInfobarBannerOverlayMediator correctly sets up its
-// consumer.
 TEST_F(SaveCardInfobarBannerOverlayMediatorTest, SetUpConsumer) {
-  autofill::CreditCard credit_card(
-      base::Uuid::GenerateRandomV4().AsLowercaseString(),
-      "https://www.example.com/");
-  std::unique_ptr<autofill::AutofillSaveCardInfoBarDelegateMobile>
-      passed_delegate =
-          autofill::AutofillSaveCardInfoBarDelegateMobile::CreateForLocalSave(
-              autofill::AutofillClient::SaveCreditCardOptions(), credit_card,
-              base::DoNothing());
-  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
-      passed_delegate.get();
-  InfoBarIOS infobar(InfobarType::kInfobarTypeSaveCard,
-                     std::move(passed_delegate));
+  InitInfobar(false);
 
-  // Package the infobar into an OverlayRequest, then create a mediator that
-  // uses this request in order to set up a fake consumer.
-  std::unique_ptr<OverlayRequest> request =
-      OverlayRequest::CreateWithConfig<SaveCardBannerRequestConfig>(&infobar);
-  SaveCardInfobarBannerOverlayMediator* mediator =
-      [[SaveCardInfobarBannerOverlayMediator alloc]
-          initWithRequest:request.get()];
-  FakeInfobarBannerConsumer* consumer =
-      [[FakeInfobarBannerConsumer alloc] init];
-  mediator.consumer = consumer;
   // Verify that the infobar was set up properly.
-  NSString* title = base::SysUTF16ToNSString(delegate->GetMessageText());
-  EXPECT_NSEQ(title, consumer.titleText);
+  NSString* title = base::SysUTF16ToNSString(delegate_->GetMessageText());
+  EXPECT_NSEQ(title, consumer_.titleText);
   EXPECT_NSEQ(base::SysUTF16ToNSString(
-                  delegate->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_OK)),
-              consumer.buttonText);
-  EXPECT_NSEQ(base::SysUTF16ToNSString(delegate->card_label()),
-              consumer.subtitleText);
+                  delegate_->GetButtonLabel(ConfirmInfoBarDelegate::BUTTON_OK)),
+              consumer_.buttonText);
+  EXPECT_NSEQ(base::SysUTF16ToNSString(delegate_->card_label()),
+              consumer_.subtitleText);
 }
 
 // Tests that when upload is turned on, tapping on the banner action button
 // presents the modal.
 TEST_F(SaveCardInfobarBannerOverlayMediatorTest, PresentModalWhenUploadOn) {
-  // Create an InfoBarIOS with a ConfirmInfoBarDelegate.
-  autofill::CreditCard credit_card(
-      base::Uuid::GenerateRandomV4().AsLowercaseString(),
-      "https://www.example.com/");
-  std::unique_ptr<autofill::AutofillSaveCardInfoBarDelegateMobile>
-      passed_delegate =
-          autofill::AutofillSaveCardInfoBarDelegateMobile::CreateForUploadSave(
-              autofill::AutofillClient::SaveCreditCardOptions(), credit_card,
-              base::DoNothing(), autofill::LegalMessageLines(), AccountInfo());
+  InitInfobar(true);
 
-  InfoBarIOS infobar(InfobarType::kInfobarTypeSaveCard,
-                     std::move(passed_delegate));
-
-  // Package the infobar into an OverlayRequest, then create a mediator that
-  // uses this request in order to set up a fake consumer.
-  std::unique_ptr<OverlayRequest> request =
-      OverlayRequest::CreateWithConfig<SaveCardBannerRequestConfig>(&infobar);
-  callback_installer_.InstallCallbacks(request.get());
-  SaveCardInfobarBannerOverlayMediator* mediator =
-      [[SaveCardInfobarBannerOverlayMediator alloc]
-          initWithRequest:request.get()];
-
-  EXPECT_CALL(
-      callback_receiver_,
-      DispatchCallback(request.get(),
-                       InfobarBannerShowModalResponse::ResponseSupport()));
-  [mediator bannerInfobarButtonWasPressed:nil];
+  OCMExpect([mediator_ presentInfobarModalFromBanner]);
+  [mediator_ bannerInfobarButtonWasPressed:nil];
 }
 
 // Tests that when upload is turned off, tapping on the banner action button
 // does not present the modal.
 TEST_F(SaveCardInfobarBannerOverlayMediatorTest, PresentModalWhenUploadOff) {
-  // Create an InfoBarIOS with a ConfirmInfoBarDelegate.
-  autofill::CreditCard credit_card(
-      base::Uuid::GenerateRandomV4().AsLowercaseString(),
-      "https://www.example.com/");
-  std::unique_ptr<autofill::AutofillSaveCardInfoBarDelegateMobile>
-      passed_delegate =
-          autofill::AutofillSaveCardInfoBarDelegateMobile::CreateForLocalSave(
-              autofill::AutofillClient::SaveCreditCardOptions(), credit_card,
-              base::DoNothing());
+  InitInfobar(false);
 
-  InfoBarIOS infobar(InfobarType::kInfobarTypeSaveCard,
-                     std::move(passed_delegate));
-
-  // Package the infobar into an OverlayRequest, then create a mediator that
-  // uses this request in order to set up a fake consumer.
-  std::unique_ptr<OverlayRequest> request =
-      OverlayRequest::CreateWithConfig<SaveCardBannerRequestConfig>(&infobar);
-  callback_installer_.InstallCallbacks(request.get());
-  SaveCardInfobarBannerOverlayMediator* mediator =
-      [[SaveCardInfobarBannerOverlayMediator alloc]
-          initWithRequest:request.get()];
-
-  EXPECT_CALL(
-      callback_receiver_,
-      DispatchCallback(request.get(), SaveCardMainAction::ResponseSupport()));
-  [mediator bannerInfobarButtonWasPressed:nil];
+  EXPECT_CALL(*delegate_, UpdateAndAccept(delegate_->cardholder_name(),
+                                          delegate_->expiration_date_month(),
+                                          delegate_->expiration_date_year()));
+  [mediator_ bannerInfobarButtonWasPressed:nil];
 }
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/BUILD.gn
index 9e9a824..4e5d1a58 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/BUILD.gn
@@ -11,11 +11,14 @@
   deps = [
     "//base",
     "//components/infobars/core",
+    "//ios/chrome/browser/infobars/overlays:util",
+    "//ios/chrome/browser/overlays/public/default",
     "//ios/chrome/browser/overlays/public/infobar_banner",
     "//ios/chrome/browser/safe_browsing/tailored_security:infobar_delegates",
     "//ios/chrome/browser/shared/ui/symbols",
     "//ios/chrome/browser/shared/ui/symbols:buildflags",
     "//ios/chrome/browser/ui/infobars/banners",
+    "//ios/chrome/browser/ui/overlays:coordinators",
     "//ios/chrome/browser/ui/overlays/infobar_banner:mediators",
   ]
 }
@@ -28,6 +31,7 @@
     ":tailored_security",
     "//ios/chrome/browser/infobars",
     "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/default",
     "//ios/chrome/browser/overlays/public/infobar_banner",
     "//ios/chrome/browser/safe_browsing/tailored_security/test",
     "//ios/chrome/browser/shared/ui/symbols",
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator.mm
index 6128a0f..7d31bd8 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator.mm
@@ -6,13 +6,15 @@
 
 #import "base/strings/sys_string_conversions.h"
 #import "components/infobars/core/confirm_infobar_delegate.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h"
+#import "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
+#import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/safe_browsing/tailored_security/tailored_security_service_infobar_delegate.h"
 #import "ios/chrome/browser/shared/ui/symbols/buildflags.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/ui/infobars/banners/infobar_banner_consumer.h"
 #import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator+consumer_support.h"
 #import "ios/chrome/browser/ui/overlays/infobar_banner/infobar_banner_overlay_mediator.h"
+#import "ios/chrome/browser/ui/overlays/overlay_request_mediator+subclassing.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -20,9 +22,6 @@
 
 namespace {
 
-// The size of the symbol image.
-CGFloat kSymbolImagePointSize = 18.;
-
 // Returns the branded version of the Google shield symbol.
 NSString* GetBrandedGoogleShieldSymbol() {
 #if BUILDFLAG(IOS_USE_BRANDED_SYMBOLS)
@@ -34,30 +33,40 @@
 
 }  // namespace
 
-using tailored_security_service_infobar_overlays::
-    TailoredSecurityServiceBannerRequestConfig;
-
 @interface TailoredSecurityInfobarBannerOverlayMediator ()
 // The tailored security banner config from the request.
-@property(nonatomic, readonly)
-    TailoredSecurityServiceBannerRequestConfig* config;
+@property(nonatomic, readonly) DefaultInfobarOverlayRequestConfig* config;
 @end
 
 @implementation TailoredSecurityInfobarBannerOverlayMediator
 
 #pragma mark - Accessors
 
-- (TailoredSecurityServiceBannerRequestConfig*)config {
+- (DefaultInfobarOverlayRequestConfig*)config {
   return self.request
-             ? self.request
-                   ->GetConfig<TailoredSecurityServiceBannerRequestConfig>()
+             ? self.request->GetConfig<DefaultInfobarOverlayRequestConfig>()
              : nullptr;
 }
 
+// Returns the delegate attached to the config.
+- (safe_browsing::TailoredSecurityServiceInfobarDelegate*)tailoredDelegate {
+  return static_cast<safe_browsing::TailoredSecurityServiceInfobarDelegate*>(
+      self.config->delegate());
+}
+
 #pragma mark - OverlayRequestMediator
 
 + (const OverlayRequestSupport*)requestSupport {
-  return TailoredSecurityServiceBannerRequestConfig::RequestSupport();
+  return DefaultInfobarOverlayRequestConfig::RequestSupport();
+}
+
+#pragma mark - InfobarOverlayRequestMediator
+
+- (void)bannerInfobarButtonWasPressed:(UIButton*)sender {
+  InfoBarIOS* infobar = GetOverlayRequestInfobar(self.request);
+  infobar->set_accepted(self.tailoredDelegate->Accept());
+
+  [self dismissOverlay];
 }
 
 @end
@@ -65,33 +74,40 @@
 @implementation TailoredSecurityInfobarBannerOverlayMediator (ConsumerSupport)
 
 - (void)configureConsumer {
-  TailoredSecurityServiceBannerRequestConfig* config = self.config;
-  if (!self.consumer || !config)
+  DefaultInfobarOverlayRequestConfig* config = self.config;
+  if (!self.consumer || !config) {
     return;
+  }
 
-  NSString* title = base::SysUTF16ToNSString(config->message_text());
-  NSString* subtitle = base::SysUTF16ToNSString(config->description());
-  NSString* buttonText = base::SysUTF16ToNSString(config->button_label_text());
+  safe_browsing::TailoredSecurityServiceInfobarDelegate* delegate =
+      self.tailoredDelegate;
+  if (!delegate) {
+    return;
+  }
+
+  NSString* title = base::SysUTF16ToNSString(delegate->GetMessageText());
+  NSString* subtitle = base::SysUTF16ToNSString(delegate->GetDescription());
+  NSString* buttonText =
+      base::SysUTF16ToNSString(delegate->GetMessageActionText());
   NSString* bannerAccessibilityLabel =
       [NSString stringWithFormat:@"%@,%@", title, subtitle];
-  NSString* icon_image_name = [self chooseIconImageName:config];
+  NSString* iconImageName =
+      [self iconImageNameForState:delegate->message_state()];
+
   [self.consumer setBannerAccessibilityLabel:bannerAccessibilityLabel];
   [self.consumer setButtonText:buttonText];
-  [self.consumer setIconImage:CustomSymbolWithPointSize(icon_image_name,
-                                                        kSymbolImagePointSize)];
-  [self.consumer setPresentsModal:config->has_badge()];
+  [self.consumer setIconImage:CustomSymbolWithPointSize(
+                                  iconImageName, kInfobarSymbolPointSize)];
   [self.consumer setTitleText:title];
   [self.consumer setSubtitleText:subtitle];
 }
 
 #pragma mark - Private methods
 
-// Conversion function to choose NSString needed for UIImage.
-- (NSString*)chooseIconImageName:
-    (TailoredSecurityServiceBannerRequestConfig*)config {
-  safe_browsing::TailoredSecurityServiceMessageState message_state =
-      config->message_state();
-  switch (message_state) {
+// Returns the icon image name corresponding to the given `state`.
+- (NSString*)iconImageNameForState:
+    (safe_browsing::TailoredSecurityServiceMessageState)state {
+  switch (state) {
     case safe_browsing::TailoredSecurityServiceMessageState::
         kConsentedAndFlowEnabled:
     case safe_browsing::TailoredSecurityServiceMessageState::
diff --git a/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator_unittest.mm
index 768ea58..e0e7429 100644
--- a/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_banner/tailored_security/tailored_security_infobar_banner_overlay_mediator_unittest.mm
@@ -6,7 +6,7 @@
 
 #import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/overlays/public/infobar_banner/tailored_security_service_infobar_banner_overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/public/overlay_request.h"
 #import "ios/chrome/browser/safe_browsing/tailored_security/test/mock_tailored_security_service_infobar_delegate.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
@@ -37,100 +37,79 @@
 using safe_browsing::MockTailoredSecurityServiceInfobarDelegate;
 using safe_browsing::TailoredSecurityServiceInfobarDelegate;
 using safe_browsing::TailoredSecurityServiceMessageState;
-using tailored_security_service_infobar_overlays::
-    TailoredSecurityServiceBannerRequestConfig;
 
 // Test fixture for TailoredSecurityInfobarBannerOverlayMediator.
-using TailoredSecurityInfobarBannerOverlayMediatorTest = PlatformTest;
+class TailoredSecurityInfobarBannerOverlayMediatorTest : public PlatformTest {
+ public:
+  TailoredSecurityInfobarBannerOverlayMediatorTest() {}
+
+  void InitInfobar(const TailoredSecurityServiceMessageState state) {
+    std::unique_ptr<TailoredSecurityServiceInfobarDelegate> delegate =
+        MockTailoredSecurityServiceInfobarDelegate::Create(state, nullptr);
+    delegate_ = delegate.get();
+    infobar_ = std::make_unique<InfoBarIOS>(
+        InfobarType::kInfobarTypeTailoredSecurityService, std::move(delegate));
+    request_ =
+        OverlayRequest::CreateWithConfig<DefaultInfobarOverlayRequestConfig>(
+            infobar_.get(), InfobarOverlayType::kBanner);
+    consumer_ = [[FakeInfobarBannerConsumer alloc] init];
+    mediator_ = [[TailoredSecurityInfobarBannerOverlayMediator alloc]
+        initWithRequest:request_.get()];
+    ;
+    mediator_.consumer = consumer_;
+  }
+
+ protected:
+  std::unique_ptr<InfoBarIOS> infobar_;
+  std::unique_ptr<OverlayRequest> request_;
+  TailoredSecurityServiceInfobarDelegate* delegate_ = nil;
+  FakeInfobarBannerConsumer* consumer_ = nil;
+  TailoredSecurityInfobarBannerOverlayMediator* mediator_ = nil;
+};
 
 // Tests that a TailoredSecurityInfobarBannerOverlayMediatorTest correctly sets
 // up its consumer.
 TEST_F(TailoredSecurityInfobarBannerOverlayMediatorTest, SetUpConsumer) {
-  // Create an InfoBarIOS with a TailoredSecurityServiceInfobarDelegate.
-  std::unique_ptr<TailoredSecurityServiceInfobarDelegate> passed_delegate =
-      MockTailoredSecurityServiceInfobarDelegate::Create(
-          /*message_state*/ TailoredSecurityServiceMessageState::
-              kConsentedAndFlowEnabled,
-          nullptr);
-  TailoredSecurityServiceInfobarDelegate* delegate = passed_delegate.get();
-  InfoBarIOS infobar(InfobarType::kInfobarTypeTailoredSecurityService,
-                     std::move(passed_delegate));
-  // Package the infobar into an OverlayRequest, then create a mediator that
-  // uses this request in order to set up a fake consumer.
-  std::unique_ptr<OverlayRequest> request = OverlayRequest::CreateWithConfig<
-      TailoredSecurityServiceBannerRequestConfig>(&infobar);
-  TailoredSecurityInfobarBannerOverlayMediator* mediator =
-      [[TailoredSecurityInfobarBannerOverlayMediator alloc]
-          initWithRequest:request.get()];
-  FakeInfobarBannerConsumer* consumer =
-      [[FakeInfobarBannerConsumer alloc] init];
-  mediator.consumer = consumer;
+  InitInfobar(TailoredSecurityServiceMessageState::kConsentedAndFlowEnabled);
+
   // Verify that the infobar was set up properly.
-  NSString* title = base::SysUTF16ToNSString(delegate->GetMessageText());
-  NSString* subtitle = base::SysUTF16ToNSString(delegate->GetDescription());
+  NSString* title = base::SysUTF16ToNSString(delegate_->GetMessageText());
+  NSString* subtitle = base::SysUTF16ToNSString(delegate_->GetDescription());
   NSString* buttonText =
-      base::SysUTF16ToNSString(delegate->GetMessageActionText());
+      base::SysUTF16ToNSString(delegate_->GetMessageActionText());
   NSString* bannerAccessibilityLabel =
       [NSString stringWithFormat:@"%@,%@", title, subtitle];
-  EXPECT_NSEQ(bannerAccessibilityLabel, consumer.bannerAccessibilityLabel);
-  EXPECT_NSEQ(buttonText, consumer.buttonText);
-  EXPECT_NSEQ(title, consumer.titleText);
-  EXPECT_NSEQ(subtitle, consumer.subtitleText);
-  EXPECT_NSEQ(GetBrandedGoogleShieldSymbol(), consumer.iconImage);
+  EXPECT_NSEQ(bannerAccessibilityLabel, consumer_.bannerAccessibilityLabel);
+  EXPECT_NSEQ(buttonText, consumer_.buttonText);
+  EXPECT_NSEQ(title, consumer_.titleText);
+  EXPECT_NSEQ(subtitle, consumer_.subtitleText);
+  EXPECT_NSEQ(GetBrandedGoogleShieldSymbol(), consumer_.iconImage);
   EXPECT_TRUE(TailoredSecurityServiceMessageState::kConsentedAndFlowEnabled ==
-              delegate->message_state());
-  EXPECT_FALSE(consumer.presentsModal);
+              delegate_->message_state());
+  EXPECT_FALSE(consumer_.presentsModal);
 }
 
 // Tests that a TailoredSecurityInfobarBannerOverlayMediatorTest correctly
 // creates a consented and flow disabled message prompt.
 TEST_F(TailoredSecurityInfobarBannerOverlayMediatorTest,
        CheckConsentedAndFlowDisabledMessagePrompt) {
-  std::unique_ptr<TailoredSecurityServiceInfobarDelegate> passed_delegate =
-      MockTailoredSecurityServiceInfobarDelegate::Create(
-          /*message_state*/ TailoredSecurityServiceMessageState::
-              kConsentedAndFlowDisabled,
-          nullptr);
-  TailoredSecurityServiceInfobarDelegate* delegate = passed_delegate.get();
-  InfoBarIOS infobar(InfobarType::kInfobarTypeTailoredSecurityService,
-                     std::move(passed_delegate));
-  std::unique_ptr<OverlayRequest> request = OverlayRequest::CreateWithConfig<
-      TailoredSecurityServiceBannerRequestConfig>(&infobar);
-  TailoredSecurityInfobarBannerOverlayMediator* mediator =
-      [[TailoredSecurityInfobarBannerOverlayMediator alloc]
-          initWithRequest:request.get()];
-  FakeInfobarBannerConsumer* consumer =
-      [[FakeInfobarBannerConsumer alloc] init];
-  mediator.consumer = consumer;
+  InitInfobar(TailoredSecurityServiceMessageState::kConsentedAndFlowDisabled);
+
   // Verify that the infobar was set up properly.
   EXPECT_NSEQ(CustomSymbolWithPointSize(kShieldSymbol, kSymbolImagePointSize),
-              consumer.iconImage);
+              consumer_.iconImage);
   EXPECT_TRUE(TailoredSecurityServiceMessageState::kConsentedAndFlowDisabled ==
-              delegate->message_state());
+              delegate_->message_state());
 }
 
 // Tests that a TailoredSecurityInfobarBannerOverlayMediatorTest correctly
 // creates an unconsented and flow enabled message prompt.
 TEST_F(TailoredSecurityInfobarBannerOverlayMediatorTest,
        CheckUnconsentedAndFlowEnabledMessagePrompt) {
-  std::unique_ptr<TailoredSecurityServiceInfobarDelegate> passed_delegate =
-      MockTailoredSecurityServiceInfobarDelegate::Create(
-          /*message_state*/ TailoredSecurityServiceMessageState::
-              kUnconsentedAndFlowEnabled,
-          nullptr);
-  TailoredSecurityServiceInfobarDelegate* delegate = passed_delegate.get();
-  InfoBarIOS infobar(InfobarType::kInfobarTypeTailoredSecurityService,
-                     std::move(passed_delegate));
-  std::unique_ptr<OverlayRequest> request = OverlayRequest::CreateWithConfig<
-      TailoredSecurityServiceBannerRequestConfig>(&infobar);
-  TailoredSecurityInfobarBannerOverlayMediator* mediator =
-      [[TailoredSecurityInfobarBannerOverlayMediator alloc]
-          initWithRequest:request.get()];
-  FakeInfobarBannerConsumer* consumer =
-      [[FakeInfobarBannerConsumer alloc] init];
-  mediator.consumer = consumer;
+  InitInfobar(TailoredSecurityServiceMessageState::kUnconsentedAndFlowEnabled);
+
   // Verify that the infobar was set up properly.
-  EXPECT_NSEQ(GetBrandedGoogleShieldSymbol(), consumer.iconImage);
+  EXPECT_NSEQ(GetBrandedGoogleShieldSymbol(), consumer_.iconImage);
   EXPECT_TRUE(TailoredSecurityServiceMessageState::kUnconsentedAndFlowEnabled ==
-              delegate->message_state());
+              delegate_->message_state());
 }
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
index e24ef58..cc7bcab 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/BUILD.gn
@@ -15,11 +15,14 @@
 
   deps = [
     "//base",
+    "//components/autofill/core/browser",
     "//components/autofill/core/common",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/autofill/message",
+    "//ios/chrome/browser/infobars/overlays:util",
     "//ios/chrome/browser/overlays",
     "//ios/chrome/browser/overlays/public/common/infobars",
+    "//ios/chrome/browser/overlays/public/default",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/browser/ui/infobars/modals",
@@ -46,8 +49,11 @@
     "//components/signin/public/identity_manager",
     "//ios/chrome/browser/autofill/message",
     "//ios/chrome/browser/infobars",
+    "//ios/chrome/browser/infobars:public",
+    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test",
     "//ios/chrome/browser/infobars/test",
     "//ios/chrome/browser/overlays",
+    "//ios/chrome/browser/overlays/public/default",
     "//ios/chrome/browser/overlays/public/infobar_modal",
     "//ios/chrome/browser/overlays/test",
     "//ios/chrome/browser/ui/infobars/modals",
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_coordinator.mm b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_coordinator.mm
index 994fe787..bb1380d 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_coordinator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_coordinator.mm
@@ -5,10 +5,8 @@
 #import "ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_coordinator.h"
 
 #import "base/check.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
-#import "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
-#import "ios/chrome/browser/overlays/public/overlay_response.h"
+#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
+#import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_table_view_controller.h"
 #import "ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_coordinator+modal_configuration.h"
 #import "ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator.h"
@@ -19,15 +17,13 @@
 #error "This file requires ARC support."
 #endif
 
-using save_card_infobar_overlays::SaveCardModalRequestConfig;
-using save_card_infobar_overlays::SaveCardLoadURL;
-
 @interface SaveCardInfobarModalOverlayCoordinator ()
 // Redefine ModalConfiguration properties as readwrite.
 @property(nonatomic, strong, readwrite) OverlayRequestMediator* modalMediator;
 @property(nonatomic, strong, readwrite) UIViewController* modalViewController;
 // The request's config.
-@property(nonatomic, assign, readonly) SaveCardModalRequestConfig* config;
+@property(nonatomic, assign, readonly)
+    DefaultInfobarOverlayRequestConfig* config;
 // URL to load when the modal UI finishes dismissing.
 @property(nonatomic, assign) GURL pendingURLToLoad;
 @end
@@ -36,15 +32,16 @@
 
 #pragma mark - Accessors
 
-- (SaveCardModalRequestConfig*)config {
-  return self.request ? self.request->GetConfig<SaveCardModalRequestConfig>()
-                      : nullptr;
+- (DefaultInfobarOverlayRequestConfig*)config {
+  return self.request
+             ? self.request->GetConfig<DefaultInfobarOverlayRequestConfig>()
+             : nullptr;
 }
 
 #pragma mark - Public
 
 + (const OverlayRequestSupport*)requestSupport {
-  return SaveCardModalRequestConfig::RequestSupport();
+  return DefaultInfobarOverlayRequestConfig::RequestSupport();
 }
 
 #pragma mark - SaveCardInfobarModalOverlayMediatorDelegate
@@ -79,9 +76,12 @@
   DCHECK(self.modalMediator);
   DCHECK(self.modalViewController);
   if (!self.pendingURLToLoad.is_empty() && self.request) {
-    self.request->GetCallbackManager()->DispatchResponse(
-        OverlayResponse::CreateWithInfo<SaveCardLoadURL>(
-            self.pendingURLToLoad));
+    autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
+        static_cast<autofill::AutofillSaveCardInfoBarDelegateMobile*>(
+            self.config->delegate());
+    if (delegate) {
+      delegate->OnLegalMessageLinkClicked(self.pendingURLToLoad);
+    }
   }
   self.modalMediator = nil;
   self.modalViewController = nil;
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator.mm b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator.mm
index 59980dc5..edd15b5 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator.mm
@@ -5,9 +5,11 @@
 #import "ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator.h"
 
 #import "base/strings/sys_string_conversions.h"
+#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
 #import "components/autofill/core/common/autofill_payments_features.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
+#import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
+#import "ios/chrome/browser/infobars/overlays/infobar_overlay_util.h"
+#import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_modal_consumer.h"
 #import "ios/chrome/browser/ui/overlays/infobar_modal/infobar_modal_overlay_coordinator+modal_configuration.h"
@@ -19,61 +21,72 @@
 #error "This file requires ARC support."
 #endif
 
-using save_card_infobar_overlays::SaveCardModalRequestConfig;
-using save_card_infobar_overlays::SaveCardMainAction;
-
 @interface SaveCardInfobarModalOverlayMediator ()
 // The save card modal config from the request.
-@property(nonatomic, assign, readonly) SaveCardModalRequestConfig* config;
+@property(nonatomic, assign, readonly)
+    DefaultInfobarOverlayRequestConfig* config;
 @end
 
 @implementation SaveCardInfobarModalOverlayMediator
 
 #pragma mark - Accessors
 
-- (SaveCardModalRequestConfig*)config {
-  return self.request ? self.request->GetConfig<SaveCardModalRequestConfig>()
-                      : nullptr;
+- (DefaultInfobarOverlayRequestConfig*)config {
+  return self.request
+             ? self.request->GetConfig<DefaultInfobarOverlayRequestConfig>()
+             : nullptr;
+}
+
+// Returns the delegate attached to the config.
+- (autofill::AutofillSaveCardInfoBarDelegateMobile*)saveCardDelegate {
+  return static_cast<autofill::AutofillSaveCardInfoBarDelegateMobile*>(
+      self.config->delegate());
 }
 
 - (void)setConsumer:(id<InfobarSaveCardModalConsumer>)consumer {
   if (_consumer == consumer)
     return;
-
   _consumer = consumer;
 
-  SaveCardModalRequestConfig* config = self.config;
+  DefaultInfobarOverlayRequestConfig* config = self.config;
   if (!_consumer || !config)
     return;
 
+  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
+      self.saveCardDelegate;
+  if (!delegate) {
+    return;
+  }
+
+  InfoBarIOS* infobar = GetOverlayRequestInfobar(self.request);
   NSString* cardNumber = [NSString
       stringWithFormat:@"•••• %@", base::SysUTF16ToNSString(
-                                       config->card_last_four_digits())];
+                                       delegate->card_last_four_digits())];
+
   // Only allow editing if the card will be uploaded and it hasn't been
   // previously saved.
-  BOOL supportsEditing =
-      config->should_upload_credentials() && !config->current_card_saved();
+  BOOL supportsEditing = delegate->is_for_upload() && !infobar->accepted();
 
   // Convert gfx::Image to UIImage. The NSDictionary below doesn't support nil,
   // so NSNull must be used.
-  const gfx::Image& avatar_gfx = config->displayed_target_account_avatar();
+  const gfx::Image& avatarGfx = delegate->displayed_target_account_avatar();
   NSObject* avatar =
-      avatar_gfx.IsEmpty() ? [NSNull null] : avatar_gfx.ToUIImage();
+      avatarGfx.IsEmpty() ? [NSNull null] : avatarGfx.ToUIImage();
 
   NSDictionary* prefs = @{
     kCardholderNamePrefKey :
-        base::SysUTF16ToNSString(config->cardholder_name()),
-    kCardIssuerIconNamePrefKey : NativeImage(config->issuer_icon_id()),
+        base::SysUTF16ToNSString(delegate->cardholder_name()),
+    kCardIssuerIconNamePrefKey : NativeImage(delegate->issuer_icon_id()),
     kCardNumberPrefKey : cardNumber,
     kExpirationMonthPrefKey :
-        base::SysUTF16ToNSString(config->expiration_date_month()),
+        base::SysUTF16ToNSString(delegate->expiration_date_month()),
     kExpirationYearPrefKey :
-        base::SysUTF16ToNSString(config->expiration_date_year()),
-    kLegalMessagesPrefKey : config->legal_message_lines(),
-    kCurrentCardSavedPrefKey : @(config->current_card_saved()),
+        base::SysUTF16ToNSString(delegate->expiration_date_year()),
+    kLegalMessagesPrefKey : [self legalMessages],
+    kCurrentCardSavedPrefKey : @(infobar->accepted()),
     kSupportsEditingPrefKey : @(supportsEditing),
     kDisplayedTargetAccountEmailPrefKey :
-        base::SysUTF16ToNSString(config->displayed_target_account_email()),
+        base::SysUTF16ToNSString(delegate->displayed_target_account_email()),
     kDisplayedTargetAccountAvatarPrefKey : avatar,
   };
   [_consumer setupModalViewControllerWithPrefs:prefs];
@@ -82,7 +95,7 @@
 #pragma mark - OverlayRequestMediator
 
 + (const OverlayRequestSupport*)requestSupport {
-  return SaveCardModalRequestConfig::RequestSupport();
+  return DefaultInfobarOverlayRequestConfig::RequestSupport();
 }
 
 #pragma mark - InfobarSaveCardModalDelegate
@@ -90,8 +103,13 @@
 - (void)saveCardWithCardholderName:(NSString*)cardholderName
                    expirationMonth:(NSString*)month
                     expirationYear:(NSString*)year {
-  [self dispatchResponse:OverlayResponse::CreateWithInfo<SaveCardMainAction>(
-                             cardholderName, month, year)];
+  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
+      self.saveCardDelegate;
+  InfoBarIOS* infobar = GetOverlayRequestInfobar(self.request);
+  infobar->set_accepted(delegate->UpdateAndAccept(
+      base::SysNSStringToUTF16(cardholderName), base::SysNSStringToUTF16(month),
+      base::SysNSStringToUTF16(year)));
+
   [self dismissOverlay];
 }
 
@@ -100,4 +118,34 @@
   [self dismissOverlay];
 }
 
+#pragma mark - Private
+
+// Returns an array of UI SaveCardMessageWithLinks model objects.
+- (NSMutableArray<SaveCardMessageWithLinks*>*)legalMessages {
+  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate =
+      self.saveCardDelegate;
+
+  NSMutableArray<SaveCardMessageWithLinks*>* legalMessages =
+      [[NSMutableArray alloc] init];
+  // Only display legal Messages if the card is being uploaded and there are
+  // any.
+  if (delegate->is_for_upload() && !delegate->legal_message_lines().empty()) {
+    for (const auto& line : delegate->legal_message_lines()) {
+      SaveCardMessageWithLinks* message =
+          [[SaveCardMessageWithLinks alloc] init];
+      message.messageText = base::SysUTF16ToNSString(line.text());
+      NSMutableArray* linkRanges = [[NSMutableArray alloc] init];
+      std::vector<GURL> linkURLs;
+      for (const auto& link : line.links()) {
+        [linkRanges addObject:[NSValue valueWithRange:link.range.ToNSRange()]];
+        linkURLs.push_back(link.url);
+      }
+      message.linkRanges = linkRanges;
+      message.linkURLs = linkURLs;
+      [legalMessages addObject:message];
+    }
+  }
+  return legalMessages;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
index a860e9b..7e4bd4e5 100644
--- a/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_unittest.mm
@@ -11,13 +11,13 @@
 #import "components/autofill/core/browser/autofill_client.h"
 #import "components/autofill/core/browser/autofill_test_utils.h"
 #import "components/autofill/core/browser/data_model/credit_card.h"
-#import "components/autofill/core/browser/payments/autofill_save_card_infobar_delegate_mobile.h"
 #import "components/autofill/core/browser/payments/test_legal_message_line.h"
 #import "components/signin/public/identity_manager/account_info.h"
 #import "ios/chrome/browser/autofill/message/save_card_message_with_links.h"
 #import "ios/chrome/browser/infobars/infobar_ios.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_request_config.h"
-#import "ios/chrome/browser/overlays/public/infobar_modal/save_card_infobar_modal_overlay_responses.h"
+#import "ios/chrome/browser/infobars/infobar_type.h"
+#import "ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/test/mock_autofill_save_card_infobar_delegate_mobile.h"
+#import "ios/chrome/browser/overlays/public/default/default_infobar_overlay_request_config.h"
 #import "ios/chrome/browser/overlays/test/fake_overlay_request_callback_installer.h"
 #import "ios/chrome/browser/ui/infobars/modals/infobar_save_card_modal_consumer.h"
 #import "ios/chrome/browser/ui/overlays/infobar_modal/save_card/save_card_infobar_modal_overlay_mediator_delegate.h"
@@ -30,9 +30,6 @@
 #error "This file requires ARC support."
 #endif
 
-using save_card_infobar_overlays::SaveCardModalRequestConfig;
-using save_card_infobar_overlays::SaveCardMainAction;
-
 @interface FakeSaveCardMediatorDelegate
     : NSObject <SaveCardInfobarModalOverlayMediatorDelegate>
 @property(nonatomic, assign) GURL pendingURLToLoad;
@@ -74,44 +71,35 @@
 class SaveCardInfobarModalOverlayMediatorTest : public PlatformTest {
  public:
   SaveCardInfobarModalOverlayMediatorTest()
-      : callback_installer_(&callback_receiver_,
-                            {SaveCardMainAction::ResponseSupport()}),
-        mediator_delegate_(
+      : mediator_delegate_(
             OCMStrictProtocolMock(@protocol(OverlayRequestMediatorDelegate))) {
-    autofill::LegalMessageLines legal_message_lines =
-        autofill::LegalMessageLines(
-            {autofill::TestLegalMessageLine("Test message")});
-    std::unique_ptr<autofill::AutofillSaveCardInfoBarDelegateMobile> delegate =
-        autofill::AutofillSaveCardInfoBarDelegateMobile::CreateForUploadSave(
-            autofill::AutofillClient::SaveCreditCardOptions(),
-            autofill::CreditCard(
-                base::Uuid::GenerateRandomV4().AsLowercaseString(),
-                "https://www.example.com/"),
-            base::DoNothing(), legal_message_lines, AccountInfo());
+    autofill::CreditCard credit_card(
+        base::Uuid::GenerateRandomV4().AsLowercaseString(),
+        "https://www.example.com/");
+    std::unique_ptr<MockAutofillSaveCardInfoBarDelegateMobile> delegate =
+        MockAutofillSaveCardInfoBarDelegateMobileFactory::
+            CreateMockAutofillSaveCardInfoBarDelegateMobileFactory(true,
+                                                                   credit_card);
     delegate_ = delegate.get();
     infobar_ = std::make_unique<InfoBarIOS>(InfobarType::kInfobarTypeSaveCard,
                                             std::move(delegate));
 
-    request_ = OverlayRequest::CreateWithConfig<SaveCardModalRequestConfig>(
-        infobar_.get());
-    callback_installer_.InstallCallbacks(request_.get());
-
+    request_ =
+        OverlayRequest::CreateWithConfig<DefaultInfobarOverlayRequestConfig>(
+            infobar_.get(), InfobarOverlayType::kModal);
     mediator_ = [[SaveCardInfobarModalOverlayMediator alloc]
         initWithRequest:request_.get()];
     mediator_.delegate = mediator_delegate_;
   }
 
   ~SaveCardInfobarModalOverlayMediatorTest() override {
-    EXPECT_CALL(callback_receiver_, CompletionCallback(request_.get()));
     EXPECT_OCMOCK_VERIFY(mediator_delegate_);
   }
 
  protected:
-  autofill::AutofillSaveCardInfoBarDelegateMobile* delegate_;
   std::unique_ptr<InfoBarIOS> infobar_;
-  MockOverlayRequestCallbackReceiver callback_receiver_;
-  FakeOverlayRequestCallbackInstaller callback_installer_;
   std::unique_ptr<OverlayRequest> request_;
+  MockAutofillSaveCardInfoBarDelegateMobile* delegate_ = nil;
   SaveCardInfobarModalOverlayMediator* mediator_ = nil;
   id<OverlayRequestMediatorDelegate> mediator_delegate_ = nil;
 };
@@ -141,15 +129,20 @@
 }
 
 // Tests that calling saveCardWithCardholderName:expirationMonth:expirationYear:
-// triggers a SaveCardMainAction response.
+// calls UpdateAndAccept().
 TEST_F(SaveCardInfobarModalOverlayMediatorTest, MainAction) {
-  EXPECT_CALL(
-      callback_receiver_,
-      DispatchCallback(request_.get(), SaveCardMainAction::ResponseSupport()));
+  NSString* cardholderName = @"name";
+  NSString* month = @"3";
+  NSString* year = @"23";
+
+  EXPECT_CALL(*delegate_,
+              UpdateAndAccept(base::SysNSStringToUTF16(cardholderName),
+                              base::SysNSStringToUTF16(month),
+                              base::SysNSStringToUTF16(year)));
   OCMExpect([mediator_delegate_ stopOverlayForMediator:mediator_]);
-  [mediator_ saveCardWithCardholderName:@"name"
-                        expirationMonth:@"3"
-                         expirationYear:@"23"];
+  [mediator_ saveCardWithCardholderName:cardholderName
+                        expirationMonth:month
+                         expirationYear:year];
 }
 
 // Tests that calling dismissModalAndOpenURL: sends the passed URL to the
diff --git a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
index 172e2c3f..30a0fd9 100644
--- a/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
+++ b/ios/chrome/browser/ui/overlays/overlay_coordinator_factory.mm
@@ -87,10 +87,6 @@
       }
       return [InfobarBannerOverlayCoordinator class];
     case OverlayModality::kInfobarModal:
-      if ([SaveCardInfobarModalOverlayCoordinator requestSupport]
-              ->IsRequestSupported(request)) {
-        return [SaveCardInfobarModalOverlayCoordinator class];
-      }
       if ([SaveAddressProfileInfobarModalOverlayCoordinator requestSupport]
               ->IsRequestSupported(request)) {
         return [SaveAddressProfileInfobarModalOverlayCoordinator class];
@@ -125,6 +121,8 @@
           return [PasswordInfobarModalOverlayCoordinator class];
         case InfobarType::kInfobarTypePermissions:
           return [PermissionsInfobarModalOverlayCoordinator class];
+        case InfobarType::kInfobarTypeSaveCard:
+          return [SaveCardInfobarModalOverlayCoordinator class];
         default:
           break;
       }
diff --git a/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm b/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm
index 9e0970d..5cd8b64 100644
--- a/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm
@@ -297,14 +297,18 @@
 
 #pragma mark - WebStateListObserving
 
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
   DCHECK_EQ(_webStateList, webStateList);
-  if (atIndex == webStateList->active_index()) {
-    [self disableRefocus];
-    [self.consumer dismiss];
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      if (selection.index == webStateList->active_index()) {
+        [self disableRefocus];
+        [self.consumer dismiss];
+      }
+      break;
+    }
   }
 }
 
diff --git a/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_view_controller.mm b/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_view_controller.mm
index 299046fd..180d016 100644
--- a/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_view_controller.mm
+++ b/ios/chrome/browser/ui/passwords/bottom_sheet/password_suggestion_bottom_sheet_view_controller.mm
@@ -58,9 +58,6 @@
     UIGestureRecognizerDelegate,
     UITableViewDataSource,
     UITableViewDelegate> {
-  // Row in the table of suggestions of the use selectesd suggestion.
-  NSInteger _row;
-
   // If YES: the table view is currently showing a single suggestion
   // If NO: the table view is currently showing all suggestions
   BOOL _tableViewIsMinimized;
@@ -195,10 +192,14 @@
 
 - (void)tableView:(UITableView*)tableView
     didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
-  _row = indexPath.row;
+  if (_suggestions.count <= 1) {
+    return;
+  }
 
   if (_tableViewIsMinimized) {
     _tableViewIsMinimized = NO;
+    [_tableView cellForRowAtIndexPath:indexPath].accessoryView = nil;
+    [self addSuggestionsToTableView];
 
     // Update table view height.
     __weak __typeof(self) weakSelf = self;
@@ -210,8 +211,14 @@
     [self expand];
   }
 
-  // Refresh cells to show the checkmark icon next to the selected suggestion.
-  [_tableView reloadData];
+  [_tableView cellForRowAtIndexPath:indexPath].accessoryType =
+      UITableViewCellAccessoryCheckmark;
+}
+
+- (void)tableView:(UITableView*)tableView
+    didDeselectRowAtIndexPath:(NSIndexPath*)indexPath {
+  [_tableView cellForRowAtIndexPath:indexPath].accessoryType =
+      UITableViewCellAccessoryNone;
 }
 
 // Long press open context menu.
@@ -286,17 +293,6 @@
         initWithImage:DefaultSymbolTemplateWithPointSize(
                           kChevronDownSymbol, kSymbolAccessoryPointSize)];
     cell.accessoryView.tintColor = [UIColor colorNamed:kTextQuaternaryColor];
-  } else if (_row == indexPath.row) {
-    // The table view is showing all suggestions, and this cell contains the
-    // currently selected suggestion, so we display a checkmark on this cell.
-    cell.accessoryView = [[UIImageView alloc]
-        initWithImage:DefaultSymbolTemplateWithPointSize(
-                          kCheckmarkSymbol, kSymbolAccessoryPointSize)];
-    cell.accessoryView.tintColor = [UIColor colorNamed:kBlueColor];
-  } else {
-    // The table view is showing all suggestions, and this cell does not contain
-    // the currently selected suggestion.
-    cell.accessoryView = nil;
   }
   [self loadFaviconAtIndexPath:indexPath forCell:cell];
   return cell;
@@ -403,6 +399,10 @@
 
   _tableView.translatesAutoresizingMaskIntoConstraints = NO;
 
+  [_tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
+                          animated:NO
+                    scrollPosition:UITableViewScrollPositionNone];
+
   return _tableView;
 }
 
@@ -450,7 +450,7 @@
 
 // Notifies the delegate that a password suggestion was selected by the user.
 - (void)didSelectSuggestion {
-  [self.delegate didSelectSuggestion:_row];
+  [self.delegate didSelectSuggestion:_tableView.indexPathForSelectedRow.row];
 }
 
 // Returns whether the provided index path points to the last row of the table
@@ -548,6 +548,20 @@
   }
 }
 
+// Starting with a table view containing a single suggestion, add all other
+// suggestions to the table view.
+- (void)addSuggestionsToTableView {
+  [_tableView beginUpdates];
+  NSMutableArray<NSIndexPath*>* indexPaths =
+      [NSMutableArray arrayWithCapacity:_suggestions.count - 1];
+  for (NSUInteger i = 1; i < _suggestions.count; i++) {
+    [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
+  }
+  [_tableView insertRowsAtIndexPaths:indexPaths
+                    withRowAnimation:UITableViewRowAnimationNone];
+  [_tableView endUpdates];
+}
+
 // Creates the UI action used to open the password manager.
 - (UIAction*)openPasswordManagerAction {
   __weak __typeof(self) weakSelf = self;
diff --git a/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm b/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm
index f77cc1ff2..945e939 100644
--- a/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm
+++ b/ios/chrome/browser/ui/safe_browsing/safe_browsing_coordinator.mm
@@ -65,6 +65,20 @@
 
 #pragma mark - WebStateListObserving
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      SafeBrowsingTabHelper::FromWebState(replaceChange.inserted_web_state())
+          ->SetDelegate(self);
+      break;
+    }
+  }
+}
+
 - (void)webStateList:(WebStateList*)webStateList
     didInsertWebState:(web::WebState*)webState
               atIndex:(int)index
@@ -73,14 +87,6 @@
 }
 
 - (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
-  DCHECK(newWebState);
-  SafeBrowsingTabHelper::FromWebState(newWebState)->SetDelegate(self);
-}
-
-- (void)webStateList:(WebStateList*)webStateList
     didDetachWebState:(web::WebState*)webState
               atIndex:(int)atIndex {
   SafeBrowsingTabHelper::FromWebState(webState)->RemoveDelegate();
diff --git a/ios/chrome/browser/ui/screen_time/screen_time_mediator.mm b/ios/chrome/browser/ui/screen_time/screen_time_mediator.mm
index 183738f..ccfd40d7 100644
--- a/ios/chrome/browser/ui/screen_time/screen_time_mediator.mm
+++ b/ios/chrome/browser/ui/screen_time/screen_time_mediator.mm
@@ -73,12 +73,15 @@
 
 #pragma mark - WebStateListObserver
 
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
   DCHECK_EQ(self.webStateList, webStateList);
-  [self updateConsumer];
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace:
+      [self updateConsumer];
+      break;
+  }
 }
 
 - (void)webStateList:(WebStateList*)webStateList
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h
index fd48210..6ea8d23 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.h
@@ -185,6 +185,18 @@
 // TabGridModeSelection.
 - (void)deselectAllItemsForEditing;
 
+// Notifies the grid that all items will be closed.
+- (void)willCloseAll;
+
+// Notifies the grid that all items have been closed.
+- (void)didCloseAll;
+
+// Notifies the grid that all closed items will be restored.
+- (void)willUndoCloseAll;
+
+// Notifies the grid that all closed items have been restored.
+- (void)didUndoCloseAll;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_GRID_GRID_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
index 9969d8c..023a81d 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/grid/grid_view_controller.mm
@@ -193,6 +193,9 @@
 @property(nonatomic) BOOL localDragActionInProgress;
 // Tracks if the Inactive Tabs button is being animated out.
 @property(nonatomic) BOOL inactiveTabsHeaderHideAnimationInProgress;
+// Tracks if the items are in a batch action, which are the "Close All" or
+// "Undo" the close all.
+@property(nonatomic) BOOL isClosingAllOrUndoRunning;
 @end
 
 @implementation GridViewController {
@@ -553,6 +556,34 @@
   self.currentLayout.animatesItemUpdates = NO;
 }
 
+- (void)willCloseAll {
+  self.isClosingAllOrUndoRunning = YES;
+}
+
+- (void)didCloseAll {
+  self.isClosingAllOrUndoRunning = NO;
+}
+
+- (void)willUndoCloseAll {
+  self.isClosingAllOrUndoRunning = YES;
+}
+
+- (void)didUndoCloseAll {
+  self.isClosingAllOrUndoRunning = NO;
+
+  // Reload the button and ensure it is not hidden, as this is the only flow
+  // where the button can dynamically reappear when the app is running and the
+  // reappearance is not managed by default.
+  [self reloadInactiveTabsButtonHeader];
+  NSIndexPath* indexPath = [NSIndexPath indexPathForItem:0
+                                               inSection:kOpenTabsSectionIndex];
+  InactiveTabsButtonHeader* header =
+      base::mac::ObjCCast<InactiveTabsButtonHeader>([self.collectionView
+          supplementaryViewForElementKind:UICollectionElementKindSectionHeader
+                              atIndexPath:indexPath]);
+  header.hidden = NO;
+}
+
 #pragma mark - UICollectionViewDataSource
 
 - (NSInteger)numberOfSectionsInCollectionView:
@@ -743,6 +774,9 @@
   // `collectionViewLayout` should always be a flow layout.
   DCHECK(
       [collectionViewLayout isKindOfClass:[UICollectionViewFlowLayout class]]);
+  if (self.isClosingAllOrUndoRunning) {
+    return CGSizeZero;
+  }
   UICollectionViewFlowLayout* layout =
       (UICollectionViewFlowLayout*)collectionViewLayout;
   CGSize itemSize = layout.itemSize;
@@ -770,6 +804,9 @@
       if (!IsInactiveTabsAvailable()) {
         return CGSizeZero;
       }
+      if (self.isClosingAllOrUndoRunning) {
+        return CGSizeZero;
+      }
       if (self.inactiveTabsHeaderHideAnimationInProgress) {
         // The header is animated out to a height of 0.1.
         return CGSizeMake(collectionView.bounds.size.width, 0.1);
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button_mediator.mm
index 4e77dbf..dabddd0 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_button_mediator.mm
@@ -101,6 +101,15 @@
 
 #pragma mark - WebStateListObserving
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace:
+      NOTREACHED_NORETURN();
+  }
+}
+
 - (void)webStateList:(WebStateList*)webStateList
     didInsertWebState:(web::WebState*)webState
               atIndex:(int)index
@@ -120,13 +129,6 @@
 }
 
 - (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)index {
-  NOTREACHED();
-}
-
-- (void)webStateList:(WebStateList*)webStateList
     willDetachWebState:(web::WebState*)webState
                atIndex:(int)index {
   // No-op. `-webStateList:didDetachWebState:atIndex` will soon be called and
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm
index 45251ddf..a1f83d6 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/inactive_tabs/inactive_tabs_mediator.mm
@@ -263,6 +263,15 @@
 
 #pragma mark - WebStateListObserving
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace:
+      NOTREACHED_NORETURN();
+  }
+}
+
 - (void)webStateList:(WebStateList*)webStateList
     didInsertWebState:(web::WebState*)webState
               atIndex:(int)index
@@ -292,13 +301,6 @@
 }
 
 - (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)index {
-  NOTREACHED_NORETURN();
-}
-
-- (void)webStateList:(WebStateList*)webStateList
     willDetachWebState:(web::WebState*)webState
                atIndex:(int)index {
   DCHECK_EQ(_webStateList, webStateList);
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_mediator.mm
index ddc8227..082011d 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/pinned_tabs/pinned_tabs_mediator.mm
@@ -125,6 +125,36 @@
 
 #pragma mark - WebStateListObserving
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  DCHECK_EQ(_webStateList, webStateList);
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      if (webStateList->IsBatchInProgress()) {
+        return;
+      }
+
+      if (!webStateList->IsWebStatePinnedAt(selection.index)) {
+        return;
+      }
+
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      web::WebState* replacedWebState = replaceChange.replaced_web_state();
+      web::WebState* insertedWebState = replaceChange.inserted_web_state();
+      TabSwitcherItem* newItem = [[PinnedWebStateTabSwitcherItem alloc]
+          initWithWebState:insertedWebState];
+      [self.consumer replaceItemID:replacedWebState->GetStableIdentifier()
+                          withItem:newItem];
+
+      _scopedWebStateObservation->RemoveObservation(replacedWebState);
+      _scopedWebStateObservation->AddObservation(insertedWebState);
+      break;
+    }
+  }
+}
+
 - (void)webStateList:(WebStateList*)webStateList
     didInsertWebState:(web::WebState*)webState
               atIndex:(int)index
@@ -177,29 +207,6 @@
 }
 
 - (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)index {
-  DCHECK_EQ(_webStateList, webStateList);
-
-  if (webStateList->IsBatchInProgress()) {
-    return;
-  }
-
-  if (!webStateList->IsWebStatePinnedAt(index)) {
-    return;
-  }
-
-  TabSwitcherItem* newItem =
-      [[PinnedWebStateTabSwitcherItem alloc] initWithWebState:newWebState];
-  [self.consumer replaceItemID:oldWebState->GetStableIdentifier()
-                      withItem:newItem];
-
-  _scopedWebStateObservation->RemoveObservation(oldWebState);
-  _scopedWebStateObservation->AddObservation(newWebState);
-}
-
-- (void)webStateList:(WebStateList*)webStateList
     willDetachWebState:(web::WebState*)webState
                atIndex:(int)index {
   DCHECK_EQ(_webStateList, webStateList);
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
index 19486c6..076faa8 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
@@ -522,7 +522,7 @@
   bool thumbStripEnabled = self.isThumbStripEnabled;
   DCHECK(viewController || (thumbStripEnabled && self.bvcContainer));
 
-  if (shouldCloseTabGrid) {
+  if (shouldCloseTabGrid && !self.tabGridEnterTime.is_null()) {
     // Record when the tab switcher is dismissed.
     base::RecordAction(base::UserMetricsAction("MobileTabGridExited"));
 
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
index 0b18b32..2ecd035 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
@@ -250,6 +250,37 @@
 
 #pragma mark - WebStateListObserving
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  DCHECK_EQ(_webStateList, webStateList);
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      if (webStateList->IsBatchInProgress()) {
+        return;
+      }
+
+      if (IsPinnedTabsEnabled() &&
+          webStateList->IsWebStatePinnedAt(selection.index)) {
+        return;
+      }
+
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      web::WebState* replacedWebState = replaceChange.replaced_web_state();
+      web::WebState* insertedWebState = replaceChange.inserted_web_state();
+      TabSwitcherItem* newItem =
+          [[WebStateTabSwitcherItem alloc] initWithWebState:insertedWebState];
+      [self.consumer replaceItemID:replacedWebState->GetStableIdentifier()
+                          withItem:newItem];
+
+      _scopedWebStateObservation->RemoveObservation(replacedWebState);
+      _scopedWebStateObservation->AddObservation(insertedWebState);
+      break;
+    }
+  }
+}
+
 - (void)webStateList:(WebStateList*)webStateList
     didInsertWebState:(web::WebState*)webState
               atIndex:(int)index
@@ -302,28 +333,6 @@
 }
 
 - (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)index {
-  DCHECK_EQ(_webStateList, webStateList);
-  if (webStateList->IsBatchInProgress()) {
-    return;
-  }
-
-  if (IsPinnedTabsEnabled() && webStateList->IsWebStatePinnedAt(index)) {
-    return;
-  }
-
-  TabSwitcherItem* newItem =
-      [[WebStateTabSwitcherItem alloc] initWithWebState:newWebState];
-  [self.consumer replaceItemID:oldWebState->GetStableIdentifier()
-                      withItem:newItem];
-
-  _scopedWebStateObservation->RemoveObservation(oldWebState);
-  _scopedWebStateObservation->AddObservation(newWebState);
-}
-
-- (void)webStateList:(WebStateList*)webStateList
     willDetachWebState:(web::WebState*)webState
                atIndex:(int)index {
   DCHECK_EQ(_webStateList, webStateList);
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
index ce1edbc9..004af74 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
@@ -2800,23 +2800,37 @@
 }
 
 - (void)undoCloseAllItemsForRegularTabs {
+  GridViewController* regularViewController =
+      [self gridViewControllerForPage:TabGridPageRegularTabs];
+
+  [regularViewController willUndoCloseAll];
+
   // This was saved as a stack: first save the inactive tabs, then the active
   // tabs. So undo in the reverse order: first undo the active tabs, then the
   // inactive tabs.
   [self.regularTabsDelegate undoCloseAllItems];
   [self.inactiveTabsDelegate undoCloseAllItems];
 
+  [regularViewController didUndoCloseAll];
+
   self.undoCloseAllAvailable = NO;
   [self configureCloseAllButtonForCurrentPageAndUndoAvailability];
 }
 
 - (void)saveAndCloseAllItemsForRegularTabs {
+  GridViewController* regularViewController =
+      [self gridViewControllerForPage:TabGridPageRegularTabs];
+
+  [regularViewController willCloseAll];
+
   // This was saved as a stack: first save the inactive tabs, then the active
   // tabs. So undo in the reverse order: first undo the active tabs, then the
   // inactive tabs.
   [self.inactiveTabsDelegate saveAndCloseAllItems];
   [self.regularTabsDelegate saveAndCloseAllItems];
 
+  [regularViewController didCloseAll];
+
   self.undoCloseAllAvailable = YES;
   [self configureCloseAllButtonForCurrentPageAndUndoAvailability];
 }
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
index 72623ae..10dfa061 100644
--- a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
+++ b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
@@ -1166,6 +1166,21 @@
 
 #pragma mark - WebStateListObserving methods
 
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace: {
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      web::WebState* insertedWebState = replaceChange.inserted_web_state();
+      TabView* view = [self tabViewForWebState:insertedWebState];
+      [self updateTabView:view withWebState:insertedWebState];
+      break;
+    }
+  }
+}
+
 // Observer method, active WebState changed.
 - (void)webStateList:(WebStateList*)webStateList
     didChangeActiveWebState:(web::WebState*)newWebState
@@ -1261,15 +1276,6 @@
   [self updateContentOffsetForWebStateIndex:index isNewWebState:YES];
 }
 
-// Observer method, WebState replaced in `webStateList`.
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
-  TabView* view = [self tabViewForWebState:newWebState];
-  [self updateTabView:view withWebState:newWebState];
-}
-
 #pragma mark - WebStateFaviconDriverObserver
 
 // Observer method. `webState` got a favicon update.
diff --git a/ios/chrome/browser/ui/text_zoom/text_zoom_mediator.mm b/ios/chrome/browser/ui/text_zoom/text_zoom_mediator.mm
index e1032243..e6ec741 100644
--- a/ios/chrome/browser/ui/text_zoom/text_zoom_mediator.mm
+++ b/ios/chrome/browser/ui/text_zoom/text_zoom_mediator.mm
@@ -85,14 +85,19 @@
 
 #pragma mark - WebStateListObserver
 
-- (void)webStateList:(WebStateList*)webStateList
-    didReplaceWebState:(web::WebState*)oldWebState
-          withWebState:(web::WebState*)newWebState
-               atIndex:(int)atIndex {
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                    selection:(const WebStateSelection&)selection {
   DCHECK_EQ(_webStateList, webStateList);
-  if (atIndex == webStateList->active_index()) {
-    [self setActiveWebState:newWebState];
-    [_commandHandler closeTextZoom];
+  switch (change.type()) {
+    case WebStateListChange::Type::kReplace:
+      const WebStateListChangeReplace& replaceChange =
+          change.As<WebStateListChangeReplace>();
+      if (selection.index == webStateList->active_index()) {
+        [self setActiveWebState:replaceChange.inserted_web_state()];
+        [_commandHandler closeTextZoom];
+      }
+      break;
   }
 }
 
diff --git a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
index 2fdf2c3..31bcec8 100644
--- a/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.mm
@@ -166,12 +166,16 @@
   // set to topLayoutGuide after the view creation on iOS 10.
   [self.view setUp];
 
+  [self.layoutGuideCenter referenceView:self.view.locationBarContainer
+                              underName:kOmniboxGuide];
   self.view.locationBarBottomConstraint.constant =
       [self verticalMarginForLocationBarForFullscreenProgress:1];
 }
 
 - (void)didMoveToParentViewController:(UIViewController*)parent {
   [super didMoveToParentViewController:parent];
+  // TODO(crbug.com/1450530): Remove once all usage of kOmniboxGuide has moved
+  // to LayoutGuideCenter.
   UIView* omniboxView = self.view.locationBarContainer;
   [NamedGuide guideWithName:kOmniboxGuide view:omniboxView].constrainedView =
       omniboxView;
diff --git a/ios/chrome/browser/ui/whats_new/whats_new_util.h b/ios/chrome/browser/ui/whats_new/whats_new_util.h
index bb64c2f..f253765 100644
--- a/ios/chrome/browser/ui/whats_new/whats_new_util.h
+++ b/ios/chrome/browser/ui/whats_new/whats_new_util.h
@@ -6,9 +6,13 @@
 #define IOS_CHROME_BROWSER_UI_WHATS_NEW_WHATS_NEW_UTIL_H_
 
 #import <Foundation/Foundation.h>
+#include "base/feature_list.h"
 
 class PromosManager;
 
+// Feature flag that enables version 2 of What's New.
+BASE_DECLARE_FEATURE(kWhatsNewIOSM116);
+
 // Key to store whether the What's New promo has been register.
 extern NSString* const kWhatsNewPromoRegistrationKey;
 
@@ -40,4 +44,7 @@
 // more than once.
 bool ShouldRegisterWhatsNewPromo();
 
+// Returns whether What's New M116 is enabled.
+bool IsWhatsNewM116Enabled();
+
 #endif  // IOS_CHROME_BROWSER_UI_WHATS_NEW_WHATS_NEW_UTIL_H_
diff --git a/ios/chrome/browser/ui/whats_new/whats_new_util.mm b/ios/chrome/browser/ui/whats_new/whats_new_util.mm
index 6738277a..e815544 100644
--- a/ios/chrome/browser/ui/whats_new/whats_new_util.mm
+++ b/ios/chrome/browser/ui/whats_new/whats_new_util.mm
@@ -66,6 +66,10 @@
 
 }  // namespace
 
+BASE_FEATURE(kWhatsNewIOSM116,
+             "WhatsNewIOSM116",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 bool WasWhatsNewUsed() {
   return
       [[NSUserDefaults standardUserDefaults] boolForKey:kWhatsNewUsageEntryKey];
@@ -97,3 +101,7 @@
   return !IsWhatsNewPromoRegistered() &&
          (IsSixLaunchAfterFre() || IsSixDaysAfterFre());
 }
+
+bool IsWhatsNewM116Enabled() {
+  return base::FeatureList::IsEnabled(kWhatsNewIOSM116);
+}
diff --git a/ios/chrome/browser/web/chrome_web_client.h b/ios/chrome/browser/web/chrome_web_client.h
index e0bace8..a6ca168 100644
--- a/ios/chrome/browser/web/chrome_web_client.h
+++ b/ios/chrome/browser/web/chrome_web_client.h
@@ -67,6 +67,8 @@
   bool IsMixedContentAutoupgradeEnabled(
       web::BrowserState* browser_state) const override;
   bool IsBrowserLockdownModeEnabled(web::BrowserState* browser_state) override;
+  void SetOSLockdownModeEnabled(web::BrowserState* browser_state,
+                                bool enabled) override;
 
  private:
   // Reference to a view that is attached to a window.
diff --git a/ios/chrome/browser/web/chrome_web_client.mm b/ios/chrome/browser/web/chrome_web_client.mm
index 7fa87b3e..ea0aa8e 100644
--- a/ios/chrome/browser/web/chrome_web_client.mm
+++ b/ios/chrome/browser/web/chrome_web_client.mm
@@ -531,3 +531,13 @@
   }
   return false;
 }
+
+void ChromeWebClient::SetOSLockdownModeEnabled(web::BrowserState* browser_state,
+                                               bool enabled) {
+  if (base::FeatureList::IsEnabled(web::kBrowserLockdownModeAvailable)) {
+    ChromeBrowserState* chrome_browser_state =
+        ChromeBrowserState::FromBrowserState(browser_state);
+    PrefService* prefs = chrome_browser_state->GetPrefs();
+    prefs->SetBoolean(prefs::kOSLockdownModeEnabled, enabled);
+  }
+}
diff --git a/ios/chrome/browser/web/session_state/web_session_state_cache_factory.mm b/ios/chrome/browser/web/session_state/web_session_state_cache_factory.mm
index ba3655a..bc767bbd 100644
--- a/ios/chrome/browser/web/session_state/web_session_state_cache_factory.mm
+++ b/ios/chrome/browser/web/session_state/web_session_state_cache_factory.mm
@@ -27,9 +27,8 @@
 // allowing it bind it to an ChromeBrowserState as a KeyedService.
 class WebSessionStateCacheWrapper : public KeyedService {
  public:
-  explicit WebSessionStateCacheWrapper(
-      BrowserList* browser_list,
-      WebSessionStateCache* web_session_state_cache);
+  WebSessionStateCacheWrapper(BrowserList* browser_list,
+                              WebSessionStateCache* web_session_state_cache);
 
   WebSessionStateCacheWrapper(const WebSessionStateCacheWrapper&) = delete;
   WebSessionStateCacheWrapper& operator=(const WebSessionStateCacheWrapper&) =
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn
index 76fe9b5c..bf1e432 100644
--- a/ios/chrome/test/BUILD.gn
+++ b/ios/chrome/test/BUILD.gn
@@ -227,9 +227,7 @@
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/autofill_address_profile:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/common:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/confirm:unit_tests",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/save_card:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/sync_error:unit_tests",
-    "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/tailored_security:unit_tests",
     "//ios/chrome/browser/infobars/overlays/browser_agent/interaction_handlers/translate:unit_tests",
     "//ios/chrome/browser/itunes_urls:unit_tests",
     "//ios/chrome/browser/json_parser:unit_tests",
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 02360381..4a370a13 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -88,6 +88,7 @@
     "//ios/chrome/browser/device_sharing:eg_app_support+eg2",
     "//ios/chrome/browser/find_in_page",
     "//ios/chrome/browser/find_in_page:eg_app_support+eg2",
+    "//ios/chrome/browser/first_run",
     "//ios/chrome/browser/https_upgrades:eg_app_support+eg2",
     "//ios/chrome/browser/metrics:eg_app_support+eg2",
     "//ios/chrome/browser/ntp:features",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index 5d759e9..df4a645 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -682,6 +682,15 @@
 // message.
 + (void)disableDefaultBrowserPromo;
 
+#pragma mark - First Run Utilities
+
+// Writes the First Run Sentinel file, used to record that First Run has
+// completed.
++ (void)writeFirstRunSentinel;
+
+// Remove the FirstRun sentinel file.
++ (void)removeFirstRunSentinel;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index 3de781ca..cdd94f8e 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -8,6 +8,7 @@
 
 #import "base/command_line.h"
 #import "base/containers/contains.h"
+#import "base/files/file.h"
 #import "base/files/file_util.h"
 #import "base/ios/ios_util.h"
 #import "base/json/json_string_value_serializer.h"
@@ -35,6 +36,7 @@
 #import "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
 #import "ios/chrome/browser/default_browser/utils.h"
 #import "ios/chrome/browser/default_browser/utils_test_support.h"
+#import "ios/chrome/browser/first_run/first_run.h"
 #import "ios/chrome/browser/ntp/features.h"
 #import "ios/chrome/browser/search_engines/search_engines_util.h"
 #import "ios/chrome/browser/search_engines/template_url_service_factory.h"
@@ -1455,4 +1457,21 @@
   LogUserInteractionWithFullscreenPromo();
 }
 
+#pragma mark - First Run Utilities
+
++ (void)writeFirstRunSentinel {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  FirstRun::RemoveSentinel();
+  base::File::Error fileError;
+  FirstRun::CreateSentinel(&fileError);
+  FirstRun::LoadSentinelInfo();
+}
+
++ (void)removeFirstRunSentinel {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  if (FirstRun::RemoveSentinel()) {
+    FirstRun::LoadSentinelInfo();
+  }
+}
+
 @end
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index e7b9265..22351569 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -167,8 +167,19 @@
                         preferences:(WKWebpagePreferences*)preferences
                     decisionHandler:(void (^)(WKNavigationActionPolicy,
                                               WKWebpagePreferences*))handler {
-  GURL requestURL = net::GURLWithNSURL(action.request.URL);
+  // Check if OS lockdown mode is enabled and update the preference value.
+  if (!self.beingDestroyed) {
+    static dispatch_once_t onceToken;
+    web::BrowserState* browser_state = self.webStateImpl->GetBrowserState();
+    dispatch_once(&onceToken, ^{
+      if (@available(iOS 16.0, *)) {
+        web::GetWebClient()->SetOSLockdownModeEnabled(
+            browser_state, preferences.lockdownModeEnabled);
+      }
+    });
+  }
 
+  GURL requestURL = net::GURLWithNSURL(action.request.URL);
   const web::UserAgentType userAgentType =
       [self userAgentForNavigationAction:action webView:webView];
 
@@ -223,10 +234,11 @@
                               preferences.lockdownModeEnabled);
       }
 
-      if (self.webStateImpl) {
+      if (!self.beingDestroyed) {
         web::BrowserState* browser_state = self.webStateImpl->GetBrowserState();
-        if (web::GetWebClient()->IsBrowserLockdownModeEnabled(browser_state))
+        if (web::GetWebClient()->IsBrowserLockdownModeEnabled(browser_state)) {
           preferences.lockdownModeEnabled = true;
+        }
       }
     }
 #endif  // defined (__IPHONE_16_0)
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
index 7d53211b..2f43c3a 100644
--- a/ios/web/navigation/navigation_manager_impl.mm
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -549,10 +549,9 @@
                                       NavigationInitiationType initiation_type,
                                       bool has_user_gesture) {
   if (index < 0 || index >= GetItemCount()) {
-    // There are bugs in WKWebView where the back/forward list can fall out
-    // of sync with reality. In these situations, a navigation item that
-    // appears in the back or forward list might not actually exist. See
-    // crbug.com/1407244.
+    // Button actions are executed asynchronously, so it is possible for the
+    // client to call this with an invalid index if the user quickly taps the
+    // back or foward button mulitple times. See crbug.com/1407244.
     return;
   }
 
diff --git a/ios/web/public/web_client.h b/ios/web/public/web_client.h
index 77d8a1f..0fc27a7 100644
--- a/ios/web/public/web_client.h
+++ b/ios/web/public/web_client.h
@@ -212,6 +212,11 @@
   // Returns true if browser lockdown mode is enabled. Default return value is
   // false.
   virtual bool IsBrowserLockdownModeEnabled(web::BrowserState* browser_state);
+
+  // Sets OS lockdown mode preference value. By default, no preference value is
+  // set.
+  virtual void SetOSLockdownModeEnabled(web::BrowserState* browser_state,
+                                        bool enabled);
 };
 
 }  // namespace web
diff --git a/ios/web/web_client.mm b/ios/web/web_client.mm
index 98eba05e1..6835ef3 100644
--- a/ios/web/web_client.mm
+++ b/ios/web/web_client.mm
@@ -150,4 +150,7 @@
   return false;
 }
 
+void WebClient::SetOSLockdownModeEnabled(web::BrowserState* browser_state,
+                                         bool enabled) {}
+
 }  // namespace web
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index 518e404..075599f 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -323,11 +323,13 @@
                     delegate {
   // We only want Autofill suggestions.
   std::vector<autofill::Suggestion> filtered_suggestions;
-  base::ranges::copy_if(
-      suggestions, std::back_inserter(filtered_suggestions),
-      [](const autofill::Suggestion& suggestion) {
-        return suggestion.frontend_id.is_an_address_or_card_popup_item_id();
-      });
+  base::ranges::copy_if(suggestions, std::back_inserter(filtered_suggestions),
+                        [](const autofill::Suggestion& suggestion) {
+                          return suggestion.popup_item_id ==
+                                     autofill::PopupItemId::kAddressEntry ||
+                                 suggestion.popup_item_id ==
+                                     autofill::PopupItemId::kCreditCardEntry;
+                        });
   [_autofillAgent showAutofillPopup:filtered_suggestions
                       popupDelegate:delegate];
 }
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
index be6b3290..3704cca7 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.h
@@ -131,7 +131,7 @@
   void DidFillOrPreviewField(const std::u16string& autofilled_value,
                              const std::u16string& profile_full_name) override;
   bool IsContextSecure() const override;
-  void ExecuteCommand(Suggestion::FrontendId id) override;
+  void ExecuteCommand(PopupItemId popup_item_id) override;
   void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
   autofill::FormInteractionsFlowId GetCurrentFormInteractionsFlowId() override;
   bool IsLastQueriedField(FieldGlobalId field_id) override;
diff --git a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
index 8045495a..df8e5b8 100644
--- a/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
+++ b/ios/web_view/internal/autofill/web_view_autofill_client_ios.mm
@@ -7,31 +7,32 @@
 #import <utility>
 #import <vector>
 
-#include "base/check.h"
-#include "base/functional/bind.h"
-#include "base/functional/callback.h"
-#include "base/memory/ptr_util.h"
-#include "base/notreached.h"
-#include "components/autofill/core/browser/form_data_importer.h"
-#include "components/autofill/core/browser/logging/log_router.h"
+#import "base/check.h"
+#import "base/functional/bind.h"
+#import "base/functional/callback.h"
+#import "base/memory/ptr_util.h"
+#import "base/notreached.h"
+#import "components/autofill/core/browser/form_data_importer.h"
+#import "components/autofill/core/browser/logging/log_router.h"
 #import "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h"
-#include "components/autofill/core/browser/payments/payments_client.h"
-#include "components/autofill/core/common/autofill_prefs.h"
+#import "components/autofill/core/browser/payments/payments_client.h"
+#import "components/autofill/core/browser/ui/popup_item_ids.h"
+#import "components/autofill/core/common/autofill_prefs.h"
 #import "components/autofill/ios/browser/autofill_driver_ios.h"
-#include "components/autofill/ios/browser/autofill_util.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/security_state/ios/security_state_utils.h"
-#include "ios/web/public/browser_state.h"
+#import "components/autofill/ios/browser/autofill_util.h"
+#import "components/password_manager/core/common/password_manager_pref_names.h"
+#import "components/security_state/ios/security_state_utils.h"
+#import "ios/web/public/browser_state.h"
 #import "ios/web/public/web_state.h"
-#include "ios/web_view/internal/app/application_context.h"
-#include "ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h"
+#import "ios/web_view/internal/app/application_context.h"
+#import "ios/web_view/internal/autofill/web_view_autocomplete_history_manager_factory.h"
 #import "ios/web_view/internal/autofill/web_view_autofill_log_router_factory.h"
-#include "ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h"
-#include "ios/web_view/internal/autofill/web_view_strike_database_factory.h"
-#include "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
+#import "ios/web_view/internal/autofill/web_view_personal_data_manager_factory.h"
+#import "ios/web_view/internal/autofill/web_view_strike_database_factory.h"
+#import "ios/web_view/internal/signin/web_view_identity_manager_factory.h"
 #import "ios/web_view/internal/sync/web_view_sync_service_factory.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+#import "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -360,7 +361,7 @@
   return IsContextSecureForWebState(web_state_);
 }
 
-void WebViewAutofillClientIOS::ExecuteCommand(Suggestion::FrontendId id) {
+void WebViewAutofillClientIOS::ExecuteCommand(PopupItemId popup_item_id) {
   NOTIMPLEMENTED();
 }
 
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index 075f9927..40e663c1 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -38,6 +38,7 @@
 #include "media/base/media_switches.h"
 #include "media/base/win/mf_initializer.h"
 #include "media/capture/capture_switches.h"
+#include "media/capture/video/video_capture_metrics.h"
 #include "media/capture/video/win/metrics.h"
 #include "media/capture/video/win/video_capture_device_mf_win.h"
 #include "media/capture/video/win/video_capture_device_win.h"
@@ -418,6 +419,7 @@
           DVLOG(1) << " MediaFoundation Device: "
                    << device_descriptor.display_name();
           if (device->Init()) {
+            LogCaptureDeviceHashedModelId(device_descriptor);
             return VideoCaptureErrorOrDevice(std::move(device));
           }
           return VideoCaptureErrorOrDevice(
@@ -443,8 +445,10 @@
       auto device = std::make_unique<VideoCaptureDeviceWin>(
           device_descriptor, std::move(capture_filter));
       DVLOG(1) << " DirectShow Device: " << device_descriptor.display_name();
-      if (device->Init())
+      if (device->Init()) {
+        LogCaptureDeviceHashedModelId(device_descriptor);
         return VideoCaptureErrorOrDevice(std::move(device));
+      }
       return VideoCaptureErrorOrDevice(
           VideoCaptureError::kWinDirectShowDeviceInitializationFailed);
     }
diff --git a/media/gpu/windows/d3d11_video_decoder.cc b/media/gpu/windows/d3d11_video_decoder.cc
index 8205f5e7..2ebefdf 100644
--- a/media/gpu/windows/d3d11_video_decoder.cc
+++ b/media/gpu/windows/d3d11_video_decoder.cc
@@ -209,8 +209,11 @@
   }
 
   if (set_accelerator_decoder_wrapper_cb_) {
-    set_accelerator_decoder_wrapper_cb_.Run(
-        CreateD3D11VideoDecoderWrapper(video_decoder));
+    auto wrapper = CreateD3D11VideoDecoderWrapper(video_decoder);
+    if (!wrapper) {
+      return E_FAIL;
+    }
+    set_accelerator_decoder_wrapper_cb_.Run(std::move(wrapper));
   }
 
   // Provide the initial video decoder object.
@@ -693,9 +696,12 @@
         return NotifyError(std::move(video_decoder_or_error).error());
       }
       auto video_decoder = std::move(video_decoder_or_error).value();
+      auto wrapper = CreateD3D11VideoDecoderWrapper(video_decoder);
+      if (!wrapper) {
+        return NotifyError(D3D11StatusCode::kDecoderCreationFailed);
+      }
       if (set_accelerator_decoder_wrapper_cb_) {
-        set_accelerator_decoder_wrapper_cb_.Run(
-            CreateD3D11VideoDecoderWrapper(video_decoder));
+        set_accelerator_decoder_wrapper_cb_.Run(std::move(wrapper));
       }
       DCHECK(set_accelerator_decoder_cb_);
       set_accelerator_decoder_cb_.Run(video_decoder);
diff --git a/media/gpu/windows/d3d_accelerator.cc b/media/gpu/windows/d3d_accelerator.cc
index b78e627..504f12f 100644
--- a/media/gpu/windows/d3d_accelerator.cc
+++ b/media/gpu/windows/d3d_accelerator.cc
@@ -49,6 +49,7 @@
 
 void D3DAccelerator::SetVideoDecoderWrapper(
     std::unique_ptr<D3DVideoDecoderWrapper> video_decoder_wrapper) {
+  CHECK(video_decoder_wrapper);
   video_decoder_wrapper_ = std::move(video_decoder_wrapper);
 }
 
diff --git a/media/mojo/clients/mojo_video_decoder.h b/media/mojo/clients/mojo_video_decoder.h
index 87fdd87..ecf7c02 100644
--- a/media/mojo/clients/mojo_video_decoder.h
+++ b/media/mojo/clients/mojo_video_decoder.h
@@ -120,7 +120,8 @@
   // Manages VideoFrame destruction callbacks.
   scoped_refptr<MojoVideoFrameHandleReleaser> mojo_video_frame_handle_releaser_;
 
-  raw_ptr<GpuVideoAcceleratorFactories> gpu_factories_ = nullptr;
+  raw_ptr<GpuVideoAcceleratorFactories, LeakedDanglingUntriaged>
+      gpu_factories_ = nullptr;
 
   // Raw pointer is safe since both `this` and the `media_log` are owned by
   // WebMediaPlayerImpl with the correct declaration order.
diff --git a/media/mojo/mojom/speech_recognition.mojom b/media/mojo/mojom/speech_recognition.mojom
index 452cf30..11d70525 100644
--- a/media/mojo/mojom/speech_recognition.mojom
+++ b/media/mojo/mojom/speech_recognition.mojom
@@ -9,6 +9,8 @@
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "ui/gfx/geometry/mojom/geometry.mojom";
 
+// Next MinVersion: 2
+
 // Corresponds to the LangIdEvent.ConfidenceInterval defined in
 // http://google3/speech/soda/public/soda_event.proto.
 [Stable, Extensible]
@@ -19,6 +21,16 @@
   kHighlyConfident,
 };
 
+// Corresponds to the LangIdEvent.AsrSwitchResult defined in
+// http://google3/speech/soda/public/soda_event.proto.
+[Stable, Extensible]
+enum AsrSwitchResult {
+  [Default] kDefaultNoSwitch,
+  kSwitchSucceeded,
+  kSwitchFailed,
+  kSwitchSkipedNoLp,
+};
+
 // The main interface a renderer client uses to interact with a speech
 // recognition service process. In web Live Caption, every renderer can own one
 // or more Remote<SpeechRecognitionContext>, with the receiver bound through the
@@ -142,6 +154,10 @@
 
   // The confidence interval.
   ConfidenceLevel confidence_level;
+
+  // If multilang is enabled, describes the actions Automatic Speech Recognition
+  // took as a result of this event.
+  [MinVersion=1] AsrSwitchResult? asr_switch_result;
 };
 
 // The interface used to notify the speech recognition client of events
diff --git a/media/mojo/services/mojo_video_decoder_service.h b/media/mojo/services/mojo_video_decoder_service.h
index 9db66c61..ddc28db 100644
--- a/media/mojo/services/mojo_video_decoder_service.h
+++ b/media/mojo/services/mojo_video_decoder_service.h
@@ -101,10 +101,11 @@
   std::string codec_string_;
 
   // Decoder factory.
-  raw_ptr<MojoMediaClient> mojo_media_client_;
+  raw_ptr<MojoMediaClient, LeakedDanglingUntriaged> mojo_media_client_;
 
   // A helper object required to get the CDM from a CDM ID.
-  const raw_ptr<MojoCdmServiceContext> mojo_cdm_service_context_ = nullptr;
+  const raw_ptr<MojoCdmServiceContext, LeakedDanglingUntriaged>
+      mojo_cdm_service_context_ = nullptr;
 
   // Channel for sending async messages to the client.
   mojo::AssociatedRemote<mojom::VideoDecoderClient> client_;
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index b452b47..dea05810 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -1011,6 +1011,7 @@
     algorithm_->Reset(
         VideoRendererAlgorithm::ResetFlag::kPreserveNextFrameEstimates);
     painted_first_frame_ = false;
+    paint_first_frame_cb_.Cancel();
 
     // It's possible in the background rendering case for us to expire enough
     // frames that we need to transition from HAVE_ENOUGH => HAVE_NOTHING. Just
@@ -1064,11 +1065,12 @@
 
 void VideoRendererImpl::PaintFirstFrame() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
-  DCHECK(algorithm_->frames_queued());
   if (painted_first_frame_ || sink_started_) {
     return;
   }
 
+  DCHECK(algorithm_->frames_queued());
+
   auto first_frame =
       algorithm_->Render(base::TimeTicks(), base::TimeTicks(), nullptr);
   DCHECK(first_frame);
diff --git a/net/dns/host_resolver_cache.cc b/net/dns/host_resolver_cache.cc
index 40bb9530..3ffcb2dd 100644
--- a/net/dns/host_resolver_cache.cc
+++ b/net/dns/host_resolver_cache.cc
@@ -226,8 +226,8 @@
     lookup_name = canonicalized;
   }
 
-  auto range =
-      entries_.equal_range(KeyRef{lookup_name, network_anonymization_key});
+  auto range = entries_.equal_range(
+      KeyRef{lookup_name, raw_ref(network_anonymization_key)});
   if (range.first == entries_.cend() || range.second == entries_.cbegin()) {
     return matches;
   }
diff --git a/net/dns/host_resolver_cache.h b/net/dns/host_resolver_cache.h
index 50030bb..5c7e9e89 100644
--- a/net/dns/host_resolver_cache.h
+++ b/net/dns/host_resolver_cache.h
@@ -122,7 +122,7 @@
 
   struct KeyRef {
     base::StringPiece domain_name;
-    const NetworkAnonymizationKey& network_anonymization_key;
+    const raw_ref<const NetworkAnonymizationKey> network_anonymization_key;
   };
 
   // Allow comparing Key to KeyRef to allow refs for entry lookup.
@@ -136,11 +136,11 @@
 
     bool operator()(const Key& lhs, const KeyRef& rhs) const {
       return std::tie(lhs.domain_name, lhs.network_anonymization_key) <
-             std::tie(rhs.domain_name, rhs.network_anonymization_key);
+             std::tie(rhs.domain_name, *rhs.network_anonymization_key);
     }
 
     bool operator()(const KeyRef& lhs, const Key& rhs) const {
-      return std::tie(lhs.domain_name, lhs.network_anonymization_key) <
+      return std::tie(lhs.domain_name, *lhs.network_anonymization_key) <
              std::tie(rhs.domain_name, rhs.network_anonymization_key);
     }
   };
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index 00dcaa5..6957eb5 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -179,8 +179,9 @@
   // Owned by the DeliveryAgent and GarbageCollector, respectively, but
   // referenced here to preserve type:
 
-  raw_ptr<base::MockOneShotTimer> delivery_timer_;
-  raw_ptr<base::MockOneShotTimer> garbage_collection_timer_;
+  raw_ptr<base::MockOneShotTimer, LeakedDanglingUntriaged> delivery_timer_;
+  raw_ptr<base::MockOneShotTimer, LeakedDanglingUntriaged>
+      garbage_collection_timer_;
 };
 
 // A unit test base class that provides a TestReportingContext and shorthand
diff --git a/pdf/pdfium/pdfium_document.cc b/pdf/pdfium/pdfium_document.cc
index 8616b31..5b61e721 100644
--- a/pdf/pdfium/pdfium_document.cc
+++ b/pdf/pdfium/pdfium_document.cc
@@ -31,7 +31,7 @@
     return file_avail->doc_loader_->IsDataAvailable(offset, size);
   }
 
-  raw_ptr<DocumentLoader> doc_loader_;
+  raw_ptr<DocumentLoader, LeakedDanglingUntriaged> doc_loader_;
 };
 
 class DownloadHints : public FX_DOWNLOADHINTS {
@@ -51,7 +51,7 @@
     return download_hints->doc_loader_->RequestData(offset, size);
   }
 
-  raw_ptr<DocumentLoader> doc_loader_;
+  raw_ptr<DocumentLoader, LeakedDanglingUntriaged> doc_loader_;
 };
 
 class FileAccess : public FPDF_FILEACCESS {
@@ -73,7 +73,7 @@
     return file_access->doc_loader_->GetBlock(position, size, buffer);
   }
 
-  raw_ptr<DocumentLoader> doc_loader_;
+  raw_ptr<DocumentLoader, LeakedDanglingUntriaged> doc_loader_;
 };
 
 }  // namespace
diff --git a/sandbox/policy/features.cc b/sandbox/policy/features.cc
index 2b6bbc83..5d9678d 100644
--- a/sandbox/policy/features.cc
+++ b/sandbox/policy/features.cc
@@ -72,6 +72,11 @@
              "WinSboxHighRendererJobMemoryLimits",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Emergency "off switch" for closing the KsecDD handle in cryptbase.dll just
+// before sandbox lockdown in renderers.
+BASE_FEATURE(kWinSboxRendererCloseKsecDD,
+             "WinSboxRendererCloseKsecDD",
+             base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/sandbox/policy/features.h b/sandbox/policy/features.h
index 585f3ffd..e4abbc6b 100644
--- a/sandbox/policy/features.h
+++ b/sandbox/policy/features.h
@@ -32,6 +32,7 @@
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kRendererAppContainer);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxAllowSystemFonts);
 SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxHighRendererJobMemoryLimits);
+SANDBOX_POLICY_EXPORT BASE_DECLARE_FEATURE(kWinSboxRendererCloseKsecDD);
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 4d566d8..490ecf0 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -608,9 +608,17 @@
     return result;
 
   if (process_type == switches::kRendererProcess) {
+    if (base::FeatureList::IsEnabled(features::kWinSboxRendererCloseKsecDD)) {
+      // TODO(crbug.com/74242) Remove if we can avoid loading cryptbase.dll.
+      result = config->AddKernelObjectToClose(L"File", L"\\Device\\KsecDD");
+      if (result != SBOX_ALL_OK) {
+        return result;
+      }
+    }
     result = SandboxWin::AddWin32kLockdownPolicy(config);
-    if (result != SBOX_ALL_OK)
+    if (result != SBOX_ALL_OK) {
       return result;
+    }
   }
 
   if (!delegate->DisableDefaultPolicy()) {
diff --git a/services/network/public/cpp/server/web_socket.h b/services/network/public/cpp/server/web_socket.h
index c173b02..1a6285a 100644
--- a/services/network/public/cpp/server/web_socket.h
+++ b/services/network/public/cpp/server/web_socket.h
@@ -56,7 +56,7 @@
       const std::string& message,
       const net::NetworkTrafficAnnotationTag traffic_annotation);
 
-  const raw_ptr<HttpServer> server_;
+  const raw_ptr<HttpServer, LeakedDanglingUntriaged> server_;
   const raw_ptr<HttpConnection> connection_;
   std::unique_ptr<WebSocketEncoder> encoder_;
   bool closed_;
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index d46da4d2..9684424 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -2486,7 +2486,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2546,7 +2546,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2607,7 +2607,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2666,7 +2666,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2718,7 +2718,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2768,7 +2768,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2827,7 +2827,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2886,7 +2886,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -2945,7 +2945,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3005,7 +3005,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3065,7 +3065,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3124,7 +3124,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3184,7 +3184,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3243,7 +3243,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3303,7 +3303,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3362,7 +3362,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3432,7 +3432,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3500,7 +3500,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3565,7 +3565,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3625,7 +3625,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3686,7 +3686,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3748,7 +3748,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3810,7 +3810,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3870,7 +3870,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3931,7 +3931,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -3990,7 +3990,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4049,7 +4049,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4108,7 +4108,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4168,7 +4168,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4227,7 +4227,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4286,7 +4286,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4345,7 +4345,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4404,7 +4404,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4466,7 +4466,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4525,7 +4525,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4584,7 +4584,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4643,7 +4643,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4702,7 +4702,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4761,7 +4761,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4820,7 +4820,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4880,7 +4880,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4939,7 +4939,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -4998,7 +4998,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5058,7 +5058,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5117,7 +5117,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5176,7 +5176,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5235,7 +5235,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5294,7 +5294,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5354,7 +5354,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5414,7 +5414,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5473,7 +5473,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5532,7 +5532,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5591,7 +5591,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5650,7 +5650,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5709,7 +5709,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5768,7 +5768,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5827,7 +5827,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5886,7 +5886,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -5945,7 +5945,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6004,7 +6004,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6064,7 +6064,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6123,7 +6123,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6182,7 +6182,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6241,7 +6241,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6301,7 +6301,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6369,7 +6369,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6441,7 +6441,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6501,7 +6501,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -6560,7 +6560,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11142,7 +11142,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11202,7 +11202,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11263,7 +11263,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11322,7 +11322,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11374,7 +11374,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11425,7 +11425,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11484,7 +11484,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11543,7 +11543,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11602,7 +11602,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11662,7 +11662,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11722,7 +11722,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11781,7 +11781,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11841,7 +11841,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11900,7 +11900,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -11959,7 +11959,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12018,7 +12018,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12085,7 +12085,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12153,7 +12153,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12219,7 +12219,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12279,7 +12279,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12338,7 +12338,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12399,7 +12399,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12460,7 +12460,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12520,7 +12520,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12581,7 +12581,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12640,7 +12640,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12700,7 +12700,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12759,7 +12759,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12819,7 +12819,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12878,7 +12878,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12937,7 +12937,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -12996,7 +12996,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13055,7 +13055,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13117,7 +13117,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13176,7 +13176,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13235,7 +13235,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13294,7 +13294,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13353,7 +13353,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13412,7 +13412,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13471,7 +13471,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13531,7 +13531,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13590,7 +13590,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13649,7 +13649,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13709,7 +13709,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13768,7 +13768,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13827,7 +13827,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13886,7 +13886,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -13945,7 +13945,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14006,7 +14006,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14065,7 +14065,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14124,7 +14124,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14183,7 +14183,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14242,7 +14242,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14301,7 +14301,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14360,7 +14360,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14419,7 +14419,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14478,7 +14478,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14537,7 +14537,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14596,7 +14596,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14655,7 +14655,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14714,7 +14714,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14774,7 +14774,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14833,7 +14833,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14892,7 +14892,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -14951,7 +14951,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15011,7 +15011,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15071,7 +15071,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15130,7 +15130,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15189,7 +15189,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15252,7 +15252,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15312,7 +15312,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15373,7 +15373,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15432,7 +15432,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15484,7 +15484,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15535,7 +15535,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15594,7 +15594,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15653,7 +15653,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15712,7 +15712,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15772,7 +15772,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15832,7 +15832,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15891,7 +15891,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -15951,7 +15951,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16010,7 +16010,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16069,7 +16069,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16128,7 +16128,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16195,7 +16195,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16263,7 +16263,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16329,7 +16329,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16389,7 +16389,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16448,7 +16448,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16509,7 +16509,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16570,7 +16570,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16630,7 +16630,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16691,7 +16691,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16750,7 +16750,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16810,7 +16810,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16869,7 +16869,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16929,7 +16929,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -16988,7 +16988,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17047,7 +17047,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17106,7 +17106,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17165,7 +17165,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17227,7 +17227,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17286,7 +17286,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17345,7 +17345,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17404,7 +17404,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17463,7 +17463,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17522,7 +17522,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17581,7 +17581,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17641,7 +17641,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17700,7 +17700,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17759,7 +17759,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17819,7 +17819,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17878,7 +17878,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17937,7 +17937,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -17996,7 +17996,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18055,7 +18055,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18116,7 +18116,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18175,7 +18175,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18234,7 +18234,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18293,7 +18293,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18352,7 +18352,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18411,7 +18411,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18470,7 +18470,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18529,7 +18529,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18588,7 +18588,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18647,7 +18647,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18706,7 +18706,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18765,7 +18765,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18824,7 +18824,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18884,7 +18884,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -18943,7 +18943,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -19003,7 +19003,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -19071,7 +19071,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -19143,7 +19143,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -19213,7 +19213,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -19273,7 +19273,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -19332,7 +19332,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -19391,7 +19391,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -22707,7 +22707,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -22776,7 +22776,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -22844,7 +22844,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -22906,7 +22906,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -22969,7 +22969,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23030,7 +23030,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23093,7 +23093,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23693,7 +23693,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23752,7 +23752,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23811,7 +23811,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23870,7 +23870,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23929,7 +23929,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -23988,7 +23988,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24048,7 +24048,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24112,7 +24112,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24171,7 +24171,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24230,7 +24230,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24289,7 +24289,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24348,7 +24348,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24407,7 +24407,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24466,7 +24466,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24530,7 +24530,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24589,7 +24589,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24648,7 +24648,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24707,7 +24707,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24766,7 +24766,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24825,7 +24825,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24885,7 +24885,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -24949,7 +24949,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25008,7 +25008,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25067,7 +25067,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25126,7 +25126,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25185,7 +25185,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25244,7 +25244,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25303,7 +25303,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25367,7 +25367,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25426,7 +25426,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25485,7 +25485,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25544,7 +25544,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25603,7 +25603,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25662,7 +25662,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25722,7 +25722,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25786,7 +25786,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25845,7 +25845,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25904,7 +25904,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -25963,7 +25963,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26022,7 +26022,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26081,7 +26081,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26141,7 +26141,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26265,7 +26265,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26324,7 +26324,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26383,7 +26383,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26442,7 +26442,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26501,7 +26501,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26560,7 +26560,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -26620,7 +26620,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33321,7 +33321,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33381,7 +33381,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33442,7 +33442,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33501,7 +33501,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33553,7 +33553,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33603,7 +33603,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33662,7 +33662,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33721,7 +33721,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33780,7 +33780,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33840,7 +33840,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33900,7 +33900,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -33959,7 +33959,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34019,7 +34019,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34078,7 +34078,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34138,7 +34138,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34197,7 +34197,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34256,7 +34256,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34323,7 +34323,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34391,7 +34391,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34456,7 +34456,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34516,7 +34516,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34575,7 +34575,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34636,7 +34636,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34697,7 +34697,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34757,7 +34757,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34817,7 +34817,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34876,7 +34876,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34935,7 +34935,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -34994,7 +34994,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35054,7 +35054,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35113,7 +35113,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35172,7 +35172,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35231,7 +35231,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35290,7 +35290,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35352,7 +35352,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35411,7 +35411,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35470,7 +35470,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35529,7 +35529,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35588,7 +35588,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35647,7 +35647,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35706,7 +35706,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35766,7 +35766,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35825,7 +35825,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35884,7 +35884,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -35943,7 +35943,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36002,7 +36002,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36061,7 +36061,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36120,7 +36120,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36179,7 +36179,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36238,7 +36238,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36298,7 +36298,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36359,7 +36359,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36418,7 +36418,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36477,7 +36477,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36536,7 +36536,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36595,7 +36595,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36654,7 +36654,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36713,7 +36713,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36772,7 +36772,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36831,7 +36831,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36890,7 +36890,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -36949,7 +36949,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37009,7 +37009,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37068,7 +37068,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37127,7 +37127,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37186,7 +37186,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37252,7 +37252,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37313,7 +37313,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37373,7 +37373,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37432,7 +37432,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37491,7 +37491,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37576,7 +37576,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
@@ -37648,7 +37648,7 @@
               "cpu": "x86-64",
               "device_os": null,
               "device_type": null,
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.avd"
             }
           ],
diff --git a/testing/buildbot/chromium.coverage.json b/testing/buildbot/chromium.coverage.json
index 0fa5966c24..7eefa60 100644
--- a/testing/buildbot/chromium.coverage.json
+++ b/testing/buildbot/chromium.coverage.json
@@ -8847,7 +8847,8 @@
               "os": "Ubuntu-18.04"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 16
         },
         "test": "browser_tests",
         "test_id_prefix": "ninja://chrome/test:browser_tests/"
diff --git a/testing/buildbot/chromium.dawn.json b/testing/buildbot/chromium.dawn.json
index 95ea115..bbee691c 100644
--- a/testing/buildbot/chromium.dawn.json
+++ b/testing/buildbot/chromium.dawn.json
@@ -4430,7 +4430,6 @@
           "--use-webgpu-power-preference=default-high-performance",
           "--jobs=4"
         ],
-        "ci_only": true,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -4583,7 +4582,6 @@
           "--use-webgpu-power-preference=default-high-performance",
           "--jobs=4"
         ],
-        "ci_only": true,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -5594,7 +5592,6 @@
           "--use-webgpu-power-preference=default-high-performance",
           "--jobs=4"
         ],
-        "ci_only": true,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
@@ -5747,7 +5744,6 @@
           "--use-webgpu-power-preference=default-high-performance",
           "--jobs=4"
         ],
-        "ci_only": true,
         "isolate_name": "telemetry_gpu_integration_test",
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 3892d0e4..bf8a41e7 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -3276,7 +3276,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.finch"
             }
           ],
@@ -3342,7 +3342,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.finch"
             }
           ],
@@ -3401,7 +3401,7 @@
           "dimension_sets": [
             {
               "cpu": "x86-64",
-              "os": "Ubuntu-18.04",
+              "os": "Ubuntu-22.04",
               "pool": "chromium.tests.finch"
             }
           ],
diff --git a/testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter b/testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter
index d463b06..7db7cc46 100644
--- a/testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter
+++ b/testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter
@@ -10,3 +10,6 @@
 # ash-chrome < 116 checks the policy AttestationEnabledForUser when building a
 # response for a Verified Access challenge which leads to a different error.
 -KeystoreServiceLacrosBrowserTest.WrongFormattingUser
+
+# crbug.com/1451234
+-WebAppIntegration*
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 704d376..82f3067 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -4726,17 +4726,10 @@
       'Dawn Linux x64 Release (Intel UHD 630)': {
         'ci_only': True,
       },
-      'Dawn Mac x64 DEPS Release (AMD)': {
-        'ci_only': True,
-      },
       # crbug.com/1450207 corrupt Metal shader cache
       'Dawn Mac x64 DEPS Release (Intel)': {
         'ci_only': True,
       },
-      'Dawn Mac x64 Release (AMD)': {
-        'ci_only': True,
-      },
-      # crbug.com/1450207 corrupt Metal shader cache
       'Dawn Mac x64 Release (Intel)': {
         'ci_only': True,
       },
@@ -4758,12 +4751,6 @@
       'Dawn Linux x64 Release (Intel UHD 630)': {
         'ci_only': True,
       },
-      'Dawn Mac x64 DEPS Release (AMD)': {
-        'ci_only': True,
-      },
-      'Dawn Mac x64 Release (AMD)': {
-        'ci_only': True,
-      },
     },
   },
   'webkit_unit_tests': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index cede321c..4252f45 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -715,6 +715,9 @@
     'chromeos_js_code_coverage_browser_tests' : {
       'chromeos_js_code_coverage_browser_tests': {
         'args': [],
+        'swarming': {
+          'shards': 16,
+        },
         'test': 'browser_tests',
       }
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 24e25f88..3494788 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -704,7 +704,7 @@
           '11-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -735,7 +735,7 @@
           '12l-x64-emulator',
           'emulator-8-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -748,7 +748,7 @@
           '13-x64-emulator',
           'emulator-8-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -776,7 +776,7 @@
           'has_native_resultdb_integration',
           'nougat-x86-emulator',
           'emulator-8-cores',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -876,7 +876,7 @@
           '10-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -889,7 +889,7 @@
           '11-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -902,7 +902,7 @@
           'lollipop-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -915,7 +915,7 @@
           'marshmallow-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -928,7 +928,7 @@
           'oreo-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -941,7 +941,7 @@
           'pie-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -970,7 +970,7 @@
           'kitkat-x86-emulator',
           'emulator-4-cores',
           'has_native_resultdb_integration',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -1035,7 +1035,7 @@
           'has_native_resultdb_integration',
           'pie-x86-emulator',
           'emulator-4-cores',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -1049,7 +1049,7 @@
           'has_native_resultdb_integration',
           '10-x86-emulator',
           'emulator-4-cores',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
@@ -3264,7 +3264,7 @@
           'has_native_resultdb_integration',
           'finch-chromium-swarming-pool',
           'pie-x86-emulator',
-          'linux-bionic',
+          'linux-jammy',
           'x86-64',
         ],
         'os_type': 'android',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d456680c..6d22cbc 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3087,22 +3087,6 @@
             ]
         }
     ],
-    "ChurnPrivateComputingCheckIn": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "DeviceActiveClientChurnCohortCheckIn",
-                        "DeviceActiveClientChurnObservationCheckIn"
-                    ]
-                }
-            ]
-        }
-    ],
     "ClientSideDetectionModelOptimizationGuide": [
         {
             "platforms": [
@@ -10328,10 +10312,10 @@
             ],
             "experiments": [
                 {
-                    "name": "XRequestedWithDeprecation_20230302",
+                    "name": "XRequestedWithDeprecation_20230605",
                     "enable_features": [
                         "PersistentOriginTrials",
-                        "WebViewXRequestedWithDeprecation"
+                        "WebViewXRequestedWithHeaderControl"
                     ]
                 }
             ]
diff --git a/third_party/androidx/build.gradle.template b/third_party/androidx/build.gradle.template
index 75e19de..25c7356 100644
--- a/third_party/androidx/build.gradle.template
+++ b/third_party/androidx/build.gradle.template
@@ -29,6 +29,8 @@
     compile 'androidx.annotation:annotation-jvm:{{androidx_dependency_version}}'
     compile 'androidx.appcompat:appcompat:{{androidx_dependency_version}}'
     compile 'androidx.appcompat:appcompat-resources:{{androidx_dependency_version}}'
+    compile 'androidx.appsearch:appsearch:{{androidx_dependency_version}}'
+    compile 'androidx.appsearch:appsearch-platform-storage:{{androidx_dependency_version}}'
     compile 'androidx.asynclayoutinflater:asynclayoutinflater:{{androidx_dependency_version}}'
     compile 'androidx.asynclayoutinflater:asynclayoutinflater-appcompat:{{androidx_dependency_version}}'
     compile 'androidx.biometric:biometric:{{androidx_dependency_version}}'
diff --git a/third_party/blink/common/navigation/impression_mojom_traits.cc b/third_party/blink/common/navigation/impression_mojom_traits.cc
index 39e5d427..46d8b4e 100644
--- a/third_party/blink/common/navigation/impression_mojom_traits.cc
+++ b/third_party/blink/common/navigation/impression_mojom_traits.cc
@@ -13,7 +13,6 @@
     blink::mojom::ImpressionDataView data,
     blink::Impression* out) {
   return data.ReadAttributionSrcToken(&out->attribution_src_token) &&
-         data.ReadNavType(&out->nav_type) &&
          data.ReadRuntimeFeatures(&out->runtime_features);
 }
 
diff --git a/third_party/blink/public/common/navigation/impression.h b/third_party/blink/public/common/navigation/impression.h
index bfe1eb7c..36ea1c2d 100644
--- a/third_party/blink/public/common/navigation/impression.h
+++ b/third_party/blink/public/common/navigation/impression.h
@@ -8,7 +8,6 @@
 #include "services/network/public/cpp/attribution_reporting_runtime_features.h"
 #include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom.h"
 
 namespace blink {
 
@@ -22,7 +21,6 @@
   // Indicates the attributionsrc request associated with `this`.
   // Data parameters will be used from the attributionsrc response.
   AttributionSrcToken attribution_src_token;
-  blink::mojom::AttributionNavigationType nav_type;
   // TODO(crbug.com/1443561): Get rid of this when Runtime Feature State fully
   // supports runtime feature access from the browser process.
   network::AttributionReportingRuntimeFeatures runtime_features;
diff --git a/third_party/blink/public/common/navigation/impression_mojom_traits.h b/third_party/blink/public/common/navigation/impression_mojom_traits.h
index 0b73f6c..7c56ec78 100644
--- a/third_party/blink/public/common/navigation/impression_mojom_traits.h
+++ b/third_party/blink/public/common/navigation/impression_mojom_traits.h
@@ -10,7 +10,6 @@
 #include "third_party/blink/public/common/common_export.h"
 #include "third_party/blink/public/common/navigation/impression.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom.h"
 #include "third_party/blink/public/mojom/conversions/conversions.mojom.h"
 
 namespace mojo {
@@ -22,10 +21,6 @@
       const blink::Impression& r) {
     return r.attribution_src_token;
   }
-  static blink::mojom::AttributionNavigationType nav_type(
-      const blink::Impression& r) {
-    return r.nav_type;
-  }
   static const network::AttributionReportingRuntimeFeatures& runtime_features(
       const blink::Impression& r) {
     return r.runtime_features;
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index da44fc9..dea710d4 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -47,7 +47,6 @@
     "content_index/content_index.mojom",
     "context_menu/context_menu.mojom",
     "conversions/attribution_data_host.mojom",
-    "conversions/attribution_reporting.mojom",
     "conversions/conversions.mojom",
     "cookie_manager/cookie_manager_automation.mojom",
     "cookie_store/cookie_store.mojom",
diff --git a/third_party/blink/public/mojom/conversions/attribution_reporting.mojom b/third_party/blink/public/mojom/conversions/attribution_reporting.mojom
deleted file mode 100644
index 3543f64..0000000
--- a/third_party/blink/public/mojom/conversions/attribution_reporting.mojom
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum AttributionNavigationType {
-  kAnchor = 0,
-  kWindowOpen = 1,
-  kContextMenu = 2,
-};
diff --git a/third_party/blink/public/mojom/conversions/conversions.mojom b/third_party/blink/public/mojom/conversions/conversions.mojom
index effd6f64..f49d69a4 100644
--- a/third_party/blink/public/mojom/conversions/conversions.mojom
+++ b/third_party/blink/public/mojom/conversions/conversions.mojom
@@ -6,7 +6,6 @@
 
 import "components/attribution_reporting/registration_type.mojom";
 import "third_party/blink/public/mojom/conversions/attribution_data_host.mojom";
-import "third_party/blink/public/mojom/conversions/attribution_reporting.mojom";
 import "third_party/blink/public/mojom/tokens/tokens.mojom";
 import "services/network/public/mojom/attribution.mojom";
 
@@ -16,7 +15,6 @@
   // API parameters will be used from the the `AttributionDataHost`
   // that is associated with `attribution_src_token`.
   AttributionSrcToken attribution_src_token;
-  AttributionNavigationType nav_type;
   // Indicates whether Attribution Reporting API related runtime features are
   // enabled.
   network.mojom.AttributionReportingRuntimeFeatures runtime_features;
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 1954d6f..187e8fe 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -3918,6 +3918,7 @@
   kLongAnimationFrameRequested = 4578,
   kFedCmLoginHint = 4579,
   kFedCmRpContext = 4580,
+  kEventTimingArtificialPointerupOrClick = 4581,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
index d8af88c9..9fd98d49 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/container_query_evaluator_test.cc
@@ -21,7 +21,6 @@
 #include "third_party/blink/renderer/core/dom/dom_token_list.h"
 #include "third_party/blink/renderer/core/dom/element.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
-#include "third_party/blink/renderer/core/dom/parent_node.h"
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -748,10 +747,10 @@
 
   UpdateAllLifecyclePhasesForTest();
 
-  Element* outer_size = ParentNode::firstElementChild(*GetDocument().body());
-  Element* outer = ParentNode::firstElementChild(*outer_size);
-  Element* inner_size = ParentNode::firstElementChild(*outer);
-  Element* inner = ParentNode::firstElementChild(*inner_size);
+  Element* outer_size = GetDocument().body()->firstElementChild();
+  Element* outer = outer_size->firstElementChild();
+  Element* inner_size = outer->firstElementChild();
+  Element* inner = inner_size->firstElementChild();
 
   EXPECT_EQ(ContainerQueryEvaluator::FindContainer(
                 inner, ParseContainer("style(--foo: bar)")->Selector(),
@@ -792,11 +791,11 @@
 
   UpdateAllLifecyclePhasesForTest();
 
-  Element* sticky_size = ParentNode::firstElementChild(*GetDocument().body());
-  Element* outer_sticky = ParentNode::firstElementChild(*sticky_size);
-  Element* outer = ParentNode::firstElementChild(*outer_sticky);
-  Element* inner_sticky = ParentNode::firstElementChild(*outer);
-  Element* inner = ParentNode::firstElementChild(*inner_sticky);
+  Element* sticky_size = GetDocument().body()->firstElementChild();
+  Element* outer_sticky = sticky_size->firstElementChild();
+  Element* outer = outer_sticky->firstElementChild();
+  Element* inner_sticky = outer->firstElementChild();
+  Element* inner = inner_sticky->firstElementChild();
 
   EXPECT_EQ(
       ContainerQueryEvaluator::FindContainer(
diff --git a/third_party/blink/renderer/core/dom/build.gni b/third_party/blink/renderer/core/dom/build.gni
index 38a5eb2..abee6a4 100644
--- a/third_party/blink/renderer/core/dom/build.gni
+++ b/third_party/blink/renderer/core/dom/build.gni
@@ -50,7 +50,6 @@
   "child_frame_disconnector.h",
   "child_list_mutation_scope.cc",
   "child_list_mutation_scope.h",
-  "child_node.h",
   "child_node_list.cc",
   "child_node_list.h",
   "class_collection.cc",
@@ -220,11 +219,8 @@
   "node_traversal.h",
   "node_traversal_strategy.h",
   "node_with_index.h",
-  "non_document_type_child_node.h",
-  "non_element_parent_node.h",
   "nth_index_cache.cc",
   "nth_index_cache.h",
-  "parent_node.h",
   "parser_content_policy.h",
   "popover_data.h",
   "presentation_attribute_style.cc",
diff --git a/third_party/blink/renderer/core/dom/child_node.h b/third_party/blink/renderer/core/dom/child_node.h
deleted file mode 100644
index 7e50b90..0000000
--- a/third_party/blink/renderer/core/dom/child_node.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2014 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CHILD_NODE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CHILD_NODE_H_
-
-#include "third_party/blink/renderer/core/dom/node.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class ChildNode {
-  STATIC_ONLY(ChildNode);
-
- public:
-  static void before(
-      Node& node,
-      const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
-      ExceptionState& exception_state) {
-    return node.Before(nodes, exception_state);
-  }
-
-  static void after(
-      Node& node,
-      const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
-      ExceptionState& exception_state) {
-    return node.After(nodes, exception_state);
-  }
-
-  static void replaceWith(
-      Node& node,
-      const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
-      ExceptionState& exception_state) {
-    return node.ReplaceWith(nodes, exception_state);
-  }
-
-  static void remove(Node& node, ExceptionState& exception_state) {
-    return node.remove(exception_state);
-  }
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CHILD_NODE_H_
diff --git a/third_party/blink/renderer/core/dom/child_node.idl b/third_party/blink/renderer/core/dom/child_node.idl
index fd58ee2..b5fc1d1 100644
--- a/third_party/blink/renderer/core/dom/child_node.idl
+++ b/third_party/blink/renderer/core/dom/child_node.idl
@@ -21,12 +21,9 @@
  */
 
 // https://dom.spec.whatwg.org/#interface-childnode
-
-[
-    LegacyTreatAsPartialInterface
-] interface mixin ChildNode {
-    [Unscopable, RaisesException, CEReactions] void before((Node or DOMString or TrustedScript) ... nodes);
-    [Unscopable, RaisesException, CEReactions] void after((Node or DOMString or TrustedScript)... nodes);
-    [Unscopable, RaisesException, CEReactions] void replaceWith((Node or DOMString or TrustedScript)... nodes);
-    [Unscopable, RaisesException, CEReactions] void remove();
+interface mixin ChildNode {
+  [Unscopable, RaisesException, CEReactions] void before((Node or DOMString or TrustedScript) ... nodes);
+  [Unscopable, RaisesException, CEReactions] void after((Node or DOMString or TrustedScript)... nodes);
+  [Unscopable, RaisesException, CEReactions] void replaceWith((Node or DOMString or TrustedScript)... nodes);
+  [Unscopable, RaisesException, CEReactions] void remove();
 };
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 8b364ec..95c727e3 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -1258,10 +1258,38 @@
     this_element->PseudoStateChanged(CSSSelector::kPseudoDrag);
 }
 
-HTMLCollection* ContainerNode::Children() {
+HTMLCollection* ContainerNode::children() {
   return EnsureCachedCollection<HTMLCollection>(kNodeChildren);
 }
 
+Element* ContainerNode::firstElementChild() {
+  return ElementTraversal::FirstChild(*this);
+}
+
+Element* ContainerNode::lastElementChild() {
+  return ElementTraversal::LastChild(*this);
+}
+
+unsigned ContainerNode::childElementCount() {
+  unsigned count = 0;
+  for (Element* child = ElementTraversal::FirstChild(*this); child;
+       child = ElementTraversal::NextSibling(*child)) {
+    ++count;
+  }
+  return count;
+}
+
+Element* ContainerNode::querySelector(const AtomicString& selectors,
+                                      ExceptionState& exception_state) {
+  return QuerySelector(selectors, exception_state);
+}
+
+StaticElementList* ContainerNode::querySelectorAll(
+    const AtomicString& selectors,
+    ExceptionState& exception_state) {
+  return QuerySelectorAll(selectors, exception_state);
+}
+
 unsigned ContainerNode::CountChildren() const {
   unsigned count = 0;
   for (Node* node = firstChild(); node; node = node->nextSibling())
diff --git a/third_party/blink/renderer/core/dom/container_node.h b/third_party/blink/renderer/core/dom/container_node.h
index d840283..74003e3 100644
--- a/third_party/blink/renderer/core/dom/container_node.h
+++ b/third_party/blink/renderer/core/dom/container_node.h
@@ -83,15 +83,25 @@
 const int kInitialNodeVectorSize = 11;
 using NodeVector = HeapVector<Member<Node>, kInitialNodeVectorSize>;
 
-// Note: while ContainerNode itself isn't web-exposed, a number of methods it
-// implements (such as firstChild, lastChild) use web-style naming to shadow
-// the corresponding methods on Node. This is a performance optimization, as it
-// avoids a virtual dispatch if the type is statically known to be
-// ContainerNode.
+// ContainerNode itself isn't web-exposed exactly, but it maps closely to the
+// ParentNode mixin interface. A number of methods it implements (such as
+// firstChild, lastChild) use web-style naming to shadow the corresponding
+// methods on Node. This is a performance optimization, as it avoids a virtual
+// dispatch if the type is statically known to be ContainerNode.
 class CORE_EXPORT ContainerNode : public Node {
  public:
   ~ContainerNode() override;
 
+  // ParentNode web-exposed:
+  // Note that some of the ParentNode interface is implemented in Node.
+  HTMLCollection* children();
+  Element* firstElementChild();
+  Element* lastElementChild();
+  unsigned childElementCount();
+  Element* querySelector(const AtomicString& selectors, ExceptionState&);
+  StaticElementList* querySelectorAll(const AtomicString& selectors,
+                                      ExceptionState&);
+
   Node* firstChild() const { return first_child_; }
   Node* lastChild() const { return last_child_; }
   bool hasChildren() const { return first_child_; }
@@ -105,8 +115,6 @@
   }
   bool HasChildCount(unsigned) const;
 
-  HTMLCollection* Children();
-
   unsigned CountChildren() const;
 
   Element* QuerySelector(const AtomicString& selectors, ExceptionState&);
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 3d11917..2e11bb19 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -848,7 +848,7 @@
   return fragment;
 }
 
-void Node::Prepend(
+void Node::prepend(
     const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
     ExceptionState& exception_state) {
   auto* this_node = DynamicTo<ContainerNode>(this);
@@ -864,7 +864,7 @@
     this_node->InsertBefore(node, this_node->firstChild(), exception_state);
 }
 
-void Node::Append(
+void Node::append(
     const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
     ExceptionState& exception_state) {
   auto* this_node = DynamicTo<ContainerNode>(this);
@@ -880,7 +880,7 @@
     this_node->AppendChild(node, exception_state);
 }
 
-void Node::Before(
+void Node::before(
     const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
     ExceptionState& exception_state) {
   ContainerNode* parent = parentNode();
@@ -897,7 +897,7 @@
   }
 }
 
-void Node::After(
+void Node::after(
     const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
     ExceptionState& exception_state) {
   ContainerNode* parent = parentNode();
@@ -909,7 +909,7 @@
     parent->InsertBefore(node, viable_next_sibling, exception_state);
 }
 
-void Node::ReplaceWith(
+void Node::replaceWith(
     const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
     ExceptionState& exception_state) {
   ContainerNode* parent = parentNode();
@@ -927,7 +927,7 @@
 }
 
 // https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
-void Node::ReplaceChildren(
+void Node::replaceChildren(
     const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
     ExceptionState& exception_state) {
   auto* this_node = DynamicTo<ContainerNode>(this);
@@ -970,6 +970,14 @@
   remove(ASSERT_NO_EXCEPTION);
 }
 
+Element* Node::previousElementSibling() {
+  return ElementTraversal::PreviousSibling(*this);
+}
+
+Element* Node::nextElementSibling() {
+  return ElementTraversal::NextSibling(*this);
+}
+
 Node* Node::cloneNode(bool deep, ExceptionState& exception_state) const {
   // https://dom.spec.whatwg.org/#dom-node-clonenode
 
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index 6922be6..eee3dbb 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -240,27 +240,38 @@
   // https://dom.spec.whatwg.org/#concept-closed-shadow-hidden
   bool IsClosedShadowHiddenFrom(const Node&) const;
 
-  void Prepend(
+  // ParentNode interface. These functions are only actually web-exposed on
+  // interfaces that include ParentNode in their idl.
+  void prepend(
       const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
       ExceptionState& exception_state);
-  void Append(
+  void append(
       const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
       ExceptionState& exception_state);
-  void Before(
+  void replaceChildren(
       const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
       ExceptionState& exception_state);
-  void After(
+
+  // ChildNode interface. These functions are only actually web-exposed on
+  // interfaces that include ChildNode in their idl.
+  void before(
       const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
       ExceptionState& exception_state);
-  void ReplaceWith(
+  void after(
       const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
       ExceptionState& exception_state);
-  void ReplaceChildren(
+  void replaceWith(
       const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
       ExceptionState& exception_state);
   void remove(ExceptionState&);
   void remove();
 
+  // NonDocumentTypeChildNode interface. These functions are only actually
+  // web-exposed on  interfaces that include NonDocumentTypeChildNode in their
+  // idl.
+  Element* previousElementSibling();
+  Element* nextElementSibling();
+
   Node* PseudoAwareNextSibling() const;
   Node* PseudoAwarePreviousSibling() const;
   Node* PseudoAwareFirstChild() const;
diff --git a/third_party/blink/renderer/core/dom/non_document_type_child_node.h b/third_party/blink/renderer/core/dom/non_document_type_child_node.h
deleted file mode 100644
index 47b8c369..0000000
--- a/third_party/blink/renderer/core/dom/non_document_type_child_node.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2014 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NON_DOCUMENT_TYPE_CHILD_NODE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NON_DOCUMENT_TYPE_CHILD_NODE_H_
-
-#include "third_party/blink/renderer/core/dom/element_traversal.h"
-#include "third_party/blink/renderer/core/dom/node.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class NonDocumentTypeChildNode {
-  STATIC_ONLY(NonDocumentTypeChildNode);
-
- public:
-  static Element* previousElementSibling(Node& node) {
-    return ElementTraversal::PreviousSibling(node);
-  }
-
-  static Element* nextElementSibling(Node& node) {
-    return ElementTraversal::NextSibling(node);
-  }
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NON_DOCUMENT_TYPE_CHILD_NODE_H_
diff --git a/third_party/blink/renderer/core/dom/non_document_type_child_node.idl b/third_party/blink/renderer/core/dom/non_document_type_child_node.idl
index f11bc019..0980b47 100644
--- a/third_party/blink/renderer/core/dom/non_document_type_child_node.idl
+++ b/third_party/blink/renderer/core/dom/non_document_type_child_node.idl
@@ -3,10 +3,7 @@
 // found in the LICENSE file.
 
 // https://dom.spec.whatwg.org/#interface-nondocumenttypechildnode
-
-[
-    LegacyTreatAsPartialInterface
-] interface mixin NonDocumentTypeChildNode {
-    [PerWorldBindings] readonly attribute Element? previousElementSibling;
-    [PerWorldBindings] readonly attribute Element? nextElementSibling;
+interface mixin NonDocumentTypeChildNode {
+  [PerWorldBindings] readonly attribute Element? previousElementSibling;
+  [PerWorldBindings] readonly attribute Element? nextElementSibling;
 };
diff --git a/third_party/blink/renderer/core/dom/non_element_parent_node.h b/third_party/blink/renderer/core/dom/non_element_parent_node.h
deleted file mode 100644
index 8e5ae868..0000000
--- a/third_party/blink/renderer/core/dom/non_element_parent_node.h
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2015 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NON_ELEMENT_PARENT_NODE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NON_ELEMENT_PARENT_NODE_H_
-
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/document_fragment.h"
-
-namespace blink {
-
-class NonElementParentNode {
- public:
-  static Element* getElementById(Document& document, const AtomicString& id) {
-    return document.getElementById(id);
-  }
-
-  static Element* getElementById(DocumentFragment& fragment,
-                                 const AtomicString& id) {
-    return fragment.getElementById(id);
-  }
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NON_ELEMENT_PARENT_NODE_H_
diff --git a/third_party/blink/renderer/core/dom/non_element_parent_node.idl b/third_party/blink/renderer/core/dom/non_element_parent_node.idl
index e3a13d27..37bf01d 100644
--- a/third_party/blink/renderer/core/dom/non_element_parent_node.idl
+++ b/third_party/blink/renderer/core/dom/non_element_parent_node.idl
@@ -3,9 +3,6 @@
 // found in the LICENSE file.
 
 // https://dom.spec.whatwg.org/#interface-nonelementparentnode
-
-[
-    LegacyTreatAsPartialInterface
-] interface mixin NonElementParentNode {
-    [PerWorldBindings] Element? getElementById(DOMString elementId);
+interface mixin NonElementParentNode {
+   [PerWorldBindings] Element? getElementById(DOMString elementId);
 };
diff --git a/third_party/blink/renderer/core/dom/parent_node.h b/third_party/blink/renderer/core/dom/parent_node.h
deleted file mode 100644
index 3c24faa..0000000
--- a/third_party/blink/renderer/core/dom/parent_node.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2013 Samsung Electronics. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_PARENT_NODE_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_PARENT_NODE_H_
-
-#include "third_party/blink/renderer/core/dom/container_node.h"
-#include "third_party/blink/renderer/core/dom/element_traversal.h"
-#include "third_party/blink/renderer/core/html/html_collection.h"
-#include "third_party/blink/renderer/platform/heap/collection_support/heap_vector.h"
-#include "third_party/blink/renderer/platform/heap/member.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
-
-namespace blink {
-
-class ParentNode {
-  STATIC_ONLY(ParentNode);
-
- public:
-  static HTMLCollection* children(ContainerNode& node) {
-    return node.Children();
-  }
-
-  static Element* firstElementChild(ContainerNode& node) {
-    return ElementTraversal::FirstChild(node);
-  }
-
-  static Element* lastElementChild(ContainerNode& node) {
-    return ElementTraversal::LastChild(node);
-  }
-
-  static unsigned childElementCount(ContainerNode& node) {
-    unsigned count = 0;
-    for (Element* child = ElementTraversal::FirstChild(node); child;
-         child = ElementTraversal::NextSibling(*child))
-      ++count;
-    return count;
-  }
-
-  static void prepend(
-      Node& node,
-      const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
-      ExceptionState& exception_state) {
-    return node.Prepend(nodes, exception_state);
-  }
-
-  static void append(
-      Node& node,
-      const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
-      ExceptionState& exception_state) {
-    return node.Append(nodes, exception_state);
-  }
-
-  static void replaceChildren(
-      Node& node,
-      const HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>>& nodes,
-      ExceptionState& exception_state) {
-    return node.ReplaceChildren(nodes, exception_state);
-  }
-
-  static Element* querySelector(ContainerNode& node,
-                                const AtomicString& selectors,
-                                ExceptionState& exception_state) {
-    return node.QuerySelector(selectors, exception_state);
-  }
-
-  static StaticElementList* querySelectorAll(ContainerNode& node,
-                                             const AtomicString& selectors,
-                                             ExceptionState& exception_state) {
-    return node.QuerySelectorAll(selectors, exception_state);
-  }
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_PARENT_NODE_H_
diff --git a/third_party/blink/renderer/core/dom/parent_node.idl b/third_party/blink/renderer/core/dom/parent_node.idl
index 2427e1ec..0ab7fa6 100644
--- a/third_party/blink/renderer/core/dom/parent_node.idl
+++ b/third_party/blink/renderer/core/dom/parent_node.idl
@@ -29,21 +29,16 @@
  */
 
 // https://dom.spec.whatwg.org/#interface-parentnode
+interface mixin ParentNode {
+  [SameObject, PerWorldBindings] readonly attribute HTMLCollection children;
+  [PerWorldBindings] readonly attribute Element? firstElementChild;
+  [PerWorldBindings] readonly attribute Element? lastElementChild;
+  readonly attribute unsigned long childElementCount;
 
-[
-    LegacyTreatAsPartialInterface
-] interface mixin ParentNode {
-    [SameObject, PerWorldBindings] readonly attribute HTMLCollection children;
-    [PerWorldBindings] readonly attribute Element? firstElementChild;
-    [PerWorldBindings] readonly attribute Element? lastElementChild;
-    readonly attribute unsigned long childElementCount;
+  [Unscopable, RaisesException, CEReactions] void prepend((Node or DOMString or TrustedScript)... nodes);
+  [Unscopable, RaisesException, CEReactions] void append((Node or DOMString or TrustedScript)... nodes);
+  [Unscopable, RaisesException, CEReactions] void replaceChildren((Node or DOMString or TrustedScript)... nodes);
 
-    [Unscopable, RaisesException, CEReactions] void prepend((Node or DOMString or TrustedScript)... nodes);
-    [Unscopable, RaisesException, CEReactions] void append((Node or DOMString or TrustedScript)... nodes);
-    [Unscopable, RaisesException, CEReactions] void replaceChildren((Node or DOMString or TrustedScript)... nodes);
-
-    // [Unscopable] Element? query(DOMString relativeSelectors);
-    // [NewObject, Unscopable] Elements queryAll(DOMString relativeSelectors);
-    [Affects=Nothing, RaisesException] Element? querySelector(DOMString selectors);
-    [Affects=Nothing, NewObject, RaisesException] NodeList querySelectorAll(DOMString selectors);
+  [Affects=Nothing, RaisesException] Element? querySelector(DOMString selectors);
+  [Affects=Nothing, NewObject, RaisesException] NodeList querySelectorAll(DOMString selectors);
 };
diff --git a/third_party/blink/renderer/core/editing/commands/insert_list_command_test.cc b/third_party/blink/renderer/core/editing/commands/insert_list_command_test.cc
index bb1706c..0a54b5a9 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_list_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_list_command_test.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/core/editing/commands/insert_list_command.h"
 
-#include "third_party/blink/renderer/core/dom/parent_node.h"
 #include "third_party/blink/renderer/core/dom/text.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/editing/selection_template.h"
diff --git a/third_party/blink/renderer/core/exported/web_image.cc b/third_party/blink/renderer/core/exported/web_image.cc
index da0e43e1..42c4b59e 100644
--- a/third_party/blink/renderer/core/exported/web_image.cc
+++ b/third_party/blink/renderer/core/exported/web_image.cc
@@ -78,8 +78,9 @@
   }
 
   ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(index);
-  if (!frame || decoder->Failed())
+  if (!frame || decoder->Failed() || frame->Bitmap().drawsNothing()) {
     return {};
+  }
 
   if (decoder->Orientation().Orientation() == ImageOrientationEnum::kDefault)
     return frame->Bitmap();
diff --git a/third_party/blink/renderer/core/frame/attribution_src_loader.cc b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
index 048857f..cd914936f 100644
--- a/third_party/blink/renderer/core/frame/attribution_src_loader.cc
+++ b/third_party/blink/renderer/core/frame/attribution_src_loader.cc
@@ -36,7 +36,6 @@
 #include "third_party/blink/public/common/navigation/impression.h"
 #include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/conversions/attribution_data_host.mojom-blink.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-blink.h"
 #include "third_party/blink/public/mojom/conversions/conversions.mojom-blink.h"
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
@@ -327,9 +326,6 @@
   // operations and DevTools issues.
 
   const Impression impression{
-      .nav_type = element
-                      ? mojom::blink::AttributionNavigationType::kAnchor
-                      : mojom::blink::AttributionNavigationType::kWindowOpen,
       .runtime_features = GetRuntimeFeatures(),
   };
 
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser.cc b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
index d4c06269..812927b 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser.cc
@@ -643,6 +643,10 @@
           ? task_runner_state_->GetDefaultBudget()
           : kInfiniteTokenizationBudget;
 
+  if (RuntimeEnabledFeatures::HTMLParserYieldAndDelayOftenForTestingEnabled()) {
+    budget = 2;
+  }
+
   base::TimeDelta timed_budget;
   if (TimedParserBudgetEnabled())
     timed_budget = GetTimedBudget(task_runner_state_->TimesYielded());
@@ -693,7 +697,9 @@
     ConstructTreeFromToken(atomic_html_token);
     if (!should_run_until_completion && !IsPaused()) {
       DCHECK_EQ(task_runner_state_->GetMode(), kAllowDeferredParsing);
-      if (TimedParserBudgetEnabled()) {
+      if (TimedParserBudgetEnabled() &&
+          !RuntimeEnabledFeatures::
+              HTMLParserYieldAndDelayOftenForTestingEnabled()) {
         if (CheckParserBudgetLessOften()) {
           int newly_consumed_characters =
               input_.Current().NumberOfCharactersConsumed() -
@@ -769,6 +775,7 @@
   CHECK(!should_run_until_completion || !should_yield);
   if (should_yield)
     task_runner_state_->MarkYield();
+
   return should_yield;
 }
 
@@ -781,11 +788,16 @@
     // If the parser is already scheduled, there's no need to do anything.
     return;
   }
-  loading_task_runner_->PostTask(
+  base::TimeDelta delay = base::Milliseconds(0);
+  if (RuntimeEnabledFeatures::HTMLParserYieldAndDelayOftenForTestingEnabled()) {
+    delay = base::Milliseconds(1000);
+  }
+  loading_task_runner_->PostDelayedTask(
       FROM_HERE,
       WTF::BindOnce(&HTMLDocumentParser::DeferredPumpTokenizerIfPossible,
                     WrapPersistent(this), from_finish_append,
-                    base::TimeTicks::Now()));
+                    base::TimeTicks::Now()),
+      delay);
   task_runner_state_->SetState(
       HTMLDocumentParserState::DeferredParserState::kScheduled);
 
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 35aa33d9..1650a5c 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -465,7 +465,6 @@
     : spanner_placeholder_(nullptr),
       // TODO(rego): We should store these based on physical direction.
       has_override_containing_block_content_logical_width_(false),
-      has_override_containing_block_content_logical_height_(false),
       has_previous_content_box_rect_(false),
       snap_container_(nullptr) {}
 
@@ -2088,9 +2087,7 @@
 LayoutUnit LayoutBox::OverrideContainingBlockContentLogicalHeight() const {
   NOT_DESTROYED();
   DCHECK(HasOverrideContainingBlockContentLogicalHeight());
-  if (extra_input_)
-    return extra_input_->containing_block_content_block_size;
-  return rare_data_->override_containing_block_content_logical_height_;
+  return extra_input_->containing_block_content_block_size;
 }
 
 // TODO (lajava) Shouldn't we implement these functions based on physical
@@ -2107,10 +2104,7 @@
 // direction ?.
 bool LayoutBox::HasOverrideContainingBlockContentLogicalHeight() const {
   NOT_DESTROYED();
-  if (extra_input_)
-    return true;
-  return rare_data_ &&
-         rare_data_->has_override_containing_block_content_logical_height_;
+  return extra_input_;
 }
 
 // TODO (lajava) Shouldn't we implement these functions based on physical
@@ -2127,26 +2121,12 @@
 
 // TODO (lajava) Shouldn't we implement these functions based on physical
 // direction ?.
-void LayoutBox::SetOverrideContainingBlockContentLogicalHeight(
-    LayoutUnit logical_height) {
-  NOT_DESTROYED();
-  DCHECK(!extra_input_);
-  DCHECK_GE(logical_height, LayoutUnit(-1));
-  EnsureRareData().override_containing_block_content_logical_height_ =
-      logical_height;
-  EnsureRareData().has_override_containing_block_content_logical_height_ = true;
-}
-
-// TODO (lajava) Shouldn't we implement these functions based on physical
-// direction ?.
 void LayoutBox::ClearOverrideContainingBlockContentSize() {
   NOT_DESTROYED();
   DCHECK(!extra_input_);
   if (!rare_data_)
     return;
   EnsureRareData().has_override_containing_block_content_logical_width_ = false;
-  EnsureRareData().has_override_containing_block_content_logical_height_ =
-      false;
 }
 
 LayoutUnit LayoutBox::OverrideAvailableInlineSize() const {
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index d4ec614..444c734 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -100,11 +100,9 @@
   Member<LayoutMultiColumnSpannerPlaceholder> spanner_placeholder_;
 
   bool has_override_containing_block_content_logical_width_ : 1;
-  bool has_override_containing_block_content_logical_height_ : 1;
   bool has_previous_content_box_rect_ : 1;
 
   LayoutUnit override_containing_block_content_logical_width_;
-  LayoutUnit override_containing_block_content_logical_height_;
 
   // For snap area, the owning snap container.
   Member<LayoutBox> snap_container_;
@@ -874,7 +872,6 @@
   bool HasOverrideContainingBlockContentLogicalWidth() const;
   bool HasOverrideContainingBlockContentLogicalHeight() const;
   void SetOverrideContainingBlockContentLogicalWidth(LayoutUnit);
-  void SetOverrideContainingBlockContentLogicalHeight(LayoutUnit);
   void ClearOverrideContainingBlockContentSize();
 
   // When an available inline size override has been set, we'll use that to fill
diff --git a/third_party/blink/renderer/core/layout/layout_inline.cc b/third_party/blink/renderer/core/layout/layout_inline.cc
index 2cb0e5e..ce56ead 100644
--- a/third_party/blink/renderer/core/layout/layout_inline.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline.cc
@@ -403,7 +403,12 @@
 void LayoutInline::CollectLineBoxRects(
     const PhysicalRectCollector& yield) const {
   NOT_DESTROYED();
-  DCHECK(IsInLayoutNGInlineFormattingContext());
+#if DCHECK_IS_ON()
+  if (!IsInLayoutNGInlineFormattingContext()) {
+    ShowLayoutTreeForThis();
+    DCHECK(IsInLayoutNGInlineFormattingContext());
+  }
+#endif
   NGInlineCursor cursor;
   cursor.MoveToIncludingCulledInline(*this);
   for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc
index 457a5c7..54ad9f1 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.cc
@@ -44,15 +44,6 @@
           (style.ResolvedIsRowFlexDirection() && is_wrap_reverse));
 }
 
-void LayoutNGFlexibleBox::UpdateBlockLayout() {
-  if (IsOutOfFlowPositioned()) {
-    UpdateOutOfFlowBlockLayout();
-    return;
-  }
-
-  UpdateInFlowBlockLayout();
-}
-
 namespace {
 
 void MergeAnonymousFlexItems(LayoutObject* remove_child) {
diff --git a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h
index 0d07e5a8..96927c12 100644
--- a/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h
+++ b/third_party/blink/renderer/core/layout/ng/flex/layout_ng_flexible_box.h
@@ -24,7 +24,10 @@
   bool HasTopOverflow() const override;
   bool HasLeftOverflow() const override;
 
-  void UpdateBlockLayout() override;
+  void UpdateBlockLayout() final {
+    NOT_DESTROYED();
+    NOTREACHED_NORETURN();
+  }
 
   bool IsFlexibleBoxIncludingNG() const final {
     NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.cc b/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.cc
index e30f0b2..bcc6fe84 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.cc
+++ b/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.cc
@@ -11,14 +11,6 @@
 LayoutNGGrid::LayoutNGGrid(Element* element)
     : LayoutNGMixin<LayoutBlock>(element) {}
 
-void LayoutNGGrid::UpdateBlockLayout() {
-  if (IsOutOfFlowPositioned()) {
-    UpdateOutOfFlowBlockLayout();
-    return;
-  }
-  UpdateInFlowBlockLayout();
-}
-
 void LayoutNGGrid::AddChild(LayoutObject* new_child,
                             LayoutObject* before_child) {
   NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h b/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h
index fc9eef77..9ff1699 100644
--- a/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h
+++ b/third_party/blink/renderer/core/layout/ng/grid/layout_ng_grid.h
@@ -16,7 +16,10 @@
  public:
   explicit LayoutNGGrid(Element*);
 
-  void UpdateBlockLayout() override;
+  void UpdateBlockLayout() final {
+    NOT_DESTROYED();
+    NOTREACHED_NORETURN();
+  }
 
   const char* GetName() const override {
     NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc b/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
index 7d7c0070..84e3499 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_box_utils.cc
@@ -72,103 +72,6 @@
   return box.ContainingBlockLogicalWidthForContent();
 }
 
-NGLogicalStaticPosition LayoutBoxUtils::ComputeStaticPositionFromLegacy(
-    const LayoutBox& box,
-    const NGBoxStrut& container_border_scrollbar,
-    const NGBoxFragmentBuilder* container_builder) {
-  const auto* css_container = To<LayoutBoxModelObject>(box.Container());
-  const TextDirection parent_direction = box.Parent()->StyleRef().Direction();
-
-  // These two values represent the available-size for the OOF-positioned
-  // descandant, in the *descendant's* writing mode.
-  LayoutUnit containing_block_logical_width =
-      box.ContainingBlockLogicalWidthForPositioned(css_container);
-  LayoutUnit containing_block_logical_height =
-      box.ContainingBlockLogicalHeightForPositioned(css_container);
-
-  Length logical_left;
-  Length logical_right;
-  Length logical_top;
-  Length logical_bottom;
-  box.ComputeInlineStaticDistance(logical_left, logical_right, &box,
-                                  css_container, containing_block_logical_width,
-                                  container_builder);
-  box.ComputeBlockStaticDistance(logical_top, logical_bottom, &box,
-                                 css_container, container_builder);
-
-  // Determine the static-position.
-  LayoutUnit static_line;
-  LayoutUnit static_block;
-  if (IsLtr(parent_direction)) {
-    if (!logical_left.IsAuto()) {
-      static_line =
-          MinimumValueForLength(logical_left, containing_block_logical_width);
-    }
-  } else {
-    if (!logical_right.IsAuto()) {
-      static_line =
-          MinimumValueForLength(logical_right, containing_block_logical_width);
-    }
-
-    // |logical_right| is an adjustment from the right edge, to keep this
-    // relative to the line-left edge account for the
-    // |containing_block_logical_width|.
-    static_line = containing_block_logical_width - static_line;
-  }
-  if (!logical_top.IsAuto()) {
-    static_block =
-        MinimumValueForLength(logical_top, containing_block_logical_height);
-  }
-
-  NGLogicalStaticPosition::BlockEdge block_edge =
-      box.Layer()->StaticBlockEdge();
-  NGLogicalStaticPosition::InlineEdge inline_edge =
-      box.Layer()->StaticInlineEdge();
-
-  if (!IsLtr(parent_direction)) {
-    if (inline_edge == NGLogicalStaticPosition::InlineEdge::kInlineStart)
-      inline_edge = NGLogicalStaticPosition::InlineEdge::kInlineEnd;
-    else if (inline_edge == NGLogicalStaticPosition::InlineEdge::kInlineEnd)
-      inline_edge = NGLogicalStaticPosition::InlineEdge::kInlineStart;
-  }
-
-  NGLogicalStaticPosition logical_static_position{
-      {static_line, static_block}, inline_edge, block_edge};
-
-  // Determine the physical available-size, remember that the available-size is
-  // currently in the *descendant's* writing-mode.
-  PhysicalSize container_size =
-      ToPhysicalSize(LogicalSize(containing_block_logical_width,
-                                 containing_block_logical_height),
-                     box.StyleRef().GetWritingMode());
-
-  const LayoutBox* container = css_container->IsBox()
-                                   ? To<LayoutBox>(css_container)
-                                   : box.ContainingBlock();
-  const WritingMode container_writing_mode =
-      container->StyleRef().GetWritingMode();
-  const TextDirection container_direction = container->StyleRef().Direction();
-
-  // We perform a logical-physical-logical conversion to convert the
-  // static-position into the correct writing-mode, and direction combination.
-  //
-  // At the moment the static-position is in line-relative coordinates which is
-  // why we use |TextDirection::kLtr| for the first conversion.
-  logical_static_position =
-      logical_static_position
-          .ConvertToPhysical(
-              {{container_writing_mode, TextDirection::kLtr}, container_size})
-          .ConvertToLogical(
-              {{container_writing_mode, container_direction}, container_size});
-
-  // Finally we shift the static-position from being relative to the
-  // padding-box, to the border-box.
-  logical_static_position.offset +=
-      LogicalOffset{container_border_scrollbar.inline_start,
-                    container_border_scrollbar.block_start};
-  return logical_static_position;
-}
-
 bool LayoutBoxUtils::SkipContainingBlockForPercentHeightCalculation(
     const LayoutBlock* cb) {
   return LayoutBox::SkipContainingBlockForPercentHeightCalculation(cb);
diff --git a/third_party/blink/renderer/core/layout/ng/layout_box_utils.h b/third_party/blink/renderer/core/layout/ng/layout_box_utils.h
index 9ec609f..bdc9c02 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_box_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_box_utils.h
@@ -13,10 +13,7 @@
 class LayoutBlock;
 class LayoutPoint;
 class NGBlockBreakToken;
-class NGBoxFragmentBuilder;
 class NGPhysicalBoxFragment;
-struct NGBoxStrut;
-struct NGLogicalStaticPosition;
 struct PhysicalOffset;
 
 // This static class should be used for querying information from a |LayoutBox|,
@@ -33,14 +30,6 @@
   static LayoutUnit AvailableLogicalHeight(const LayoutBox& box,
                                            const LayoutBlock* cb);
 
-  // Produces a |NGLogicalStaticPosition| for |box| from the layout-tree.
-  // |container_builder| is needed as not all the information from current NG
-  // layout is copied to the layout-tree yet.
-  static NGLogicalStaticPosition ComputeStaticPositionFromLegacy(
-      const LayoutBox& box,
-      const NGBoxStrut& container_border_scrollbar,
-      const NGBoxFragmentBuilder* container_builder = nullptr);
-
   static bool SkipContainingBlockForPercentHeightCalculation(
       const LayoutBlock* cb);
 
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
index 286d5a03..5dfae2a 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.cc
@@ -18,8 +18,4 @@
          LayoutNGMixin<LayoutBlockFlow>::IsOfType(type);
 }
 
-void LayoutNGBlockFlow::UpdateBlockLayout() {
-  UpdateNGBlockLayout();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h
index 8529c4c4..9dbfbdc 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h
@@ -23,7 +23,10 @@
   explicit LayoutNGBlockFlow(ContainerNode*);
   ~LayoutNGBlockFlow() override;
 
-  void UpdateBlockLayout() override;
+  void UpdateBlockLayout() final {
+    NOT_DESTROYED();
+    NOTREACHED_NORETURN();
+  }
 
   const char* GetName() const override {
     NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
index e9e020b..64a83e2 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
@@ -189,18 +189,6 @@
 }
 
 template <typename Base>
-void LayoutNGBlockFlowMixin<Base>::UpdateNGBlockLayout() {
-  Base::CheckIsNotDestroyed();
-
-  if (Base::IsOutOfFlowPositioned()) {
-    LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout();
-    return;
-  }
-
-  LayoutNGMixin<Base>::UpdateInFlowBlockLayout();
-}
-
-template <typename Base>
 void LayoutNGBlockFlowMixin<Base>::Trace(Visitor* visitor) const {
   visitor->Trace(ng_inline_node_data_);
   LayoutNGMixin<Base>::Trace(visitor);
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h
index e19cc50..bc788e2e 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h
@@ -65,10 +65,6 @@
   void DirtyLinesFromChangedChild(LayoutObject* child,
                                   MarkingBehavior marking_behavior) final;
 
-  // Intended to be called from UpdateLayout() for subclasses that want the same
-  // behavior as LayoutNGBlockFlow.
-  void UpdateNGBlockLayout();
-
   Member<NGInlineNodeData> ng_inline_node_data_;
 
   friend class NGBaseLayoutAlgorithmTest;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.cc
index 07c5768..4a521015 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.cc
@@ -43,13 +43,6 @@
   To<HTMLFrameSetElement>(GetNode())->DirtyEdgeInfoAndFullPaintInvalidation();
 }
 
-void LayoutNGFrameSet::UpdateBlockLayout() {
-  if (IsOutOfFlowPositioned())
-    UpdateOutOfFlowBlockLayout();
-  else
-    UpdateInFlowBlockLayout();
-}
-
 CursorDirective LayoutNGFrameSet::GetCursor(const PhysicalOffset& point,
                                             ui::Cursor& cursor) const {
   NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.h b/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.h
index 92f2b00..51d77a2 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_frame_set.h
@@ -19,7 +19,6 @@
   bool IsChildAllowed(LayoutObject* child, const ComputedStyle&) const override;
   void AddChild(LayoutObject* new_child, LayoutObject* before_child) override;
   void RemoveChild(LayoutObject* child) override;
-  void UpdateBlockLayout() override;
   CursorDirective GetCursor(const PhysicalOffset& point,
                             ui::Cursor& cursor) const override;
 };
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index 3e0438b..c402ff3 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -28,26 +28,6 @@
 
 namespace blink {
 
-namespace {
-
-bool CanUseConstraintSpaceForCaching(const NGLayoutResult* previous_result,
-                                     const LayoutBox& box) {
-  if (!previous_result)
-    return false;
-  const auto& space = previous_result->GetConstraintSpaceForCaching();
-  if (space.IsFixedInlineSize() && box.HasOverrideLogicalWidth()) {
-    if (space.AvailableSize().inline_size != box.OverrideLogicalWidth())
-      return false;
-  }
-  if (space.IsFixedBlockSize() && box.HasOverrideLogicalHeight()) {
-    if (space.AvailableSize().block_size != box.OverrideLogicalHeight())
-      return false;
-  }
-  return space.GetWritingMode() == box.StyleRef().GetWritingMode();
-}
-
-}  // namespace
-
 template <typename Base>
 LayoutNGMixin<Base>::LayoutNGMixin(ContainerNode* node) : Base(node) {
   Base::CheckIsNotDestroyed();
@@ -171,178 +151,6 @@
   return builder.ToConstraintSpace();
 }
 
-template <typename Base>
-void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() {
-  Base::CheckIsNotDestroyed();
-
-  auto* css_container = To<LayoutBoxModelObject>(Base::Container());
-  DCHECK(!css_container->IsBox() || css_container->IsLayoutBlock());
-  auto* container = DynamicTo<LayoutBlock>(css_container);
-  if (!container)
-    container = Base::ContainingBlock();
-  const ComputedStyle* container_style = container->Style();
-  NGConstraintSpace constraint_space =
-      NGConstraintSpace::CreateFromLayoutObject(*container);
-
-  // As this is part of the Legacy->NG bridge, the container_builder is used
-  // for indicating the resolved size of the OOF-positioned containing-block
-  // and not used for caching purposes.
-  // When we produce a layout result from it, we access its child fragments
-  // which must contain *at least* this node. We use the child fragments for
-  // copying back position information.
-  NGBlockNode container_node(container);
-  NGBoxFragmentBuilder container_builder(
-      container_node, scoped_refptr<const ComputedStyle>(container_style),
-      constraint_space, container_style->GetWritingDirection());
-  container_builder.SetIsNewFormattingContext(
-      container_node.CreatesNewFormattingContext());
-
-  NGFragmentGeometry fragment_geometry;
-  fragment_geometry.border = ComputeBorders(constraint_space, container_node);
-  fragment_geometry.scrollbar =
-      ComputeScrollbars(constraint_space, container_node);
-  fragment_geometry.padding =
-      ComputePadding(constraint_space, *container_style);
-
-  NGBoxStrut border_scrollbar =
-      fragment_geometry.border + fragment_geometry.scrollbar;
-
-  // Calculate the border-box size of the object that's the containing block of
-  // this out-of-flow positioned descendant. Note that this is not to be used as
-  // the containing block size to resolve sizes and positions for the
-  // descendant, since we're dealing with the border box here (not the padding
-  // box, which is where the containing block is established). These sizes are
-  // just used to do a fake/partial NG layout pass of the containing block (that
-  // object is really managed by legacy layout).
-  LayoutUnit container_border_box_logical_width;
-  LayoutUnit container_border_box_logical_height;
-  if (Base::HasOverrideContainingBlockContentLogicalWidth()) {
-    container_border_box_logical_width =
-        Base::OverrideContainingBlockContentLogicalWidth() +
-        border_scrollbar.InlineSum();
-  } else {
-    container_border_box_logical_width = container->LogicalWidth();
-  }
-  if (Base::HasOverrideContainingBlockContentLogicalHeight()) {
-    container_border_box_logical_height =
-        Base::OverrideContainingBlockContentLogicalHeight() +
-        border_scrollbar.BlockSum();
-  } else {
-    container_border_box_logical_height = container->LogicalHeight();
-  }
-
-  fragment_geometry.border_box_size = {container_border_box_logical_width,
-                                       container_border_box_logical_height};
-  container_builder.SetInitialFragmentGeometry(fragment_geometry);
-
-  // TODO(1229581): Remove this call to determine the static position.
-  NGLogicalStaticPosition static_position =
-      LayoutBoxUtils::ComputeStaticPositionFromLegacy(*this, border_scrollbar);
-
-  // Set correct container for inline containing blocks.
-  container_builder.AddOutOfFlowLegacyCandidate(
-      NGBlockNode(this), static_position,
-      DynamicTo<LayoutInline>(css_container));
-
-  absl::optional<LogicalSize> initial_containing_block_fixed_size =
-      NGOutOfFlowLayoutPart::InitialContainingBlockFixedSize(
-          NGBlockNode(container));
-  // We really only want to lay out ourselves here, so we pass |this| to
-  // Run(). Otherwise, NGOutOfFlowLayoutPart may also lay out other objects
-  // it discovers that are part of the same containing block, but those
-  // should get laid out by the actual containing block.
-  NGOutOfFlowLayoutPart(css_container->CanContainAbsolutePositionObjects(),
-                        css_container->CanContainFixedPositionObjects(),
-                        /* is_grid_container */ false, constraint_space,
-                        &container_builder, initial_containing_block_fixed_size)
-      .Run(/* only_layout */ this);
-  const NGLayoutResult* result = container_builder.ToBoxFragment();
-
-  const auto& fragment = result->PhysicalFragment();
-  DCHECK_GT(fragment.Children().size(), 0u);
-
-  // Handle the unpositioned OOF descendants of the current OOF block.
-  if (fragment.HasOutOfFlowPositionedDescendants()) {
-    LayoutBlock* oof_container =
-        LayoutObject::FindNonAnonymousContainingBlock(container);
-    for (const auto& descendant : fragment.OutOfFlowPositionedDescendants())
-      descendant.Node().InsertIntoLegacyPositionedObjectsOf(oof_container);
-  }
-
-  // Copy sizes of all child fragments to Legacy.
-  // There could be multiple fragments, when this node has descendants whose
-  // container is this node's container.
-  // Example: fixed descendant of fixed element.
-  for (auto& child : fragment.Children()) {
-    const NGPhysicalFragment* child_fragment = child.get();
-    DCHECK(child_fragment->GetLayoutObject()->IsBox());
-    auto* child_legacy_box =
-        To<LayoutBox>(child_fragment->GetMutableLayoutObject());
-    PhysicalOffset child_offset = child.Offset();
-    if (container_style->IsFlippedBlocksWritingMode()) {
-      child_offset.left = container_border_box_logical_height -
-                          child_offset.left - child_fragment->Size().width;
-    }
-    child_legacy_box->SetLocation(child_offset.ToLayoutPoint());
-  }
-  DCHECK_EQ(fragment.Children()[0]->GetLayoutObject(), this);
-}
-
-template <typename Base>
-const NGLayoutResult* LayoutNGMixin<Base>::UpdateInFlowBlockLayout() {
-  Base::CheckIsNotDestroyed();
-
-  // This is an entry-point for LayoutNG from the legacy engine. This means that
-  // we need to be at a formatting context boundary, since NG and legacy don't
-  // cooperate on e.g. margin collapsing.
-  DCHECK(this->CreatesNewFormattingContext());
-
-  const NGLayoutResult* previous_result = Base::GetSingleCachedLayoutResult();
-
-  // If we are a layout root, use the previous space if available. This will
-  // include any stretched sizes if applicable.
-  NGConstraintSpace constraint_space =
-      CanUseConstraintSpaceForCaching(previous_result, *this)
-          ? previous_result->GetConstraintSpaceForCaching()
-          : NGConstraintSpace::CreateFromLayoutObject(*this);
-
-  const NGLayoutResult* result = NGBlockNode(this).Layout(constraint_space);
-
-  const auto& physical_fragment =
-      To<NGPhysicalBoxFragment>(result->PhysicalFragment());
-
-  for (const auto& descendant :
-       physical_fragment.OutOfFlowPositionedDescendants()) {
-    descendant.Node().InsertIntoLegacyPositionedObjectsOf(
-        descendant.box->ContainingBlock());
-  }
-
-  // Even if we are a layout root, our baseline may have shifted. In this
-  // (rare) case, mark our containing-block for layout.
-  if (previous_result) {
-    if (To<NGPhysicalBoxFragment>(previous_result->PhysicalFragment())
-            .FirstBaseline() != physical_fragment.FirstBaseline()) {
-      if (auto* containing_block = Base::ContainingBlock()) {
-        // Baselines inside replaced elements don't affect other boxes.
-        bool is_in_replaced = false;
-        for (auto* parent = Base::Parent();
-             parent && parent != containing_block; parent = parent->Parent()) {
-          if (parent->IsLayoutReplaced()) {
-            is_in_replaced = true;
-            break;
-          }
-        }
-        if (!is_in_replaced) {
-          containing_block->SetNeedsLayout(
-              layout_invalidation_reason::kChildChanged, kMarkContainerChain);
-        }
-      }
-    }
-  }
-
-  return result;
-}
-
 template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutBlock>;
 template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutBlockFlow>;
 template class CORE_TEMPLATE_EXPORT LayoutNGMixin<LayoutSVGBlock>;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index ff4dd677..0897745a 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -44,9 +44,6 @@
  protected:
   MinMaxSizes ComputeIntrinsicLogicalWidths() const override;
   NGConstraintSpace ConstraintSpaceForMinMaxSizes() const;
-
-  void UpdateOutOfFlowBlockLayout();
-  const NGLayoutResult* UpdateInFlowBlockLayout();
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc b/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc
index 1b6dc5c..c298da4 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc
+++ b/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.cc
@@ -14,15 +14,6 @@
     : LayoutNGMixin<LayoutBlock>(element) {
 }
 
-void LayoutNGMathMLBlock::UpdateBlockLayout() {
-  if (IsOutOfFlowPositioned()) {
-    UpdateOutOfFlowBlockLayout();
-    return;
-  }
-
-  UpdateInFlowBlockLayout();
-}
-
 bool LayoutNGMathMLBlock::IsOfType(LayoutObjectType type) const {
   return type == kLayoutObjectMathML ||
          (type == kLayoutObjectMathMLRoot && GetNode() &&
diff --git a/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h b/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h
index b3e942a..d1da740b 100644
--- a/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h
+++ b/third_party/blink/renderer/core/layout/ng/mathml/layout_ng_mathml_block.h
@@ -19,7 +19,10 @@
   }
 
  private:
-  void UpdateBlockLayout() final;
+  void UpdateBlockLayout() final {
+    NOT_DESTROYED();
+    NOTREACHED_NORETURN();
+  }
 
   bool IsOfType(LayoutObjectType) const final;
   bool IsChildAllowed(LayoutObject*, const ComputedStyle&) const final;
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 9bdf982..1ffa247 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
@@ -273,16 +273,6 @@
   has_inflow_child_break_inside_ |= !is_in_parallel_flow;
 }
 
-void NGBoxFragmentBuilder::AddOutOfFlowLegacyCandidate(
-    NGBlockNode node,
-    const NGLogicalStaticPosition& static_position,
-    const LayoutInline* inline_container) {
-  oof_positioned_candidates_.emplace_back(
-      node, static_position,
-      NGInlineContainer<LogicalOffset>(inline_container,
-                                       /* relative_offset */ LogicalOffset()));
-}
-
 void NGBoxFragmentBuilder::PropagateSpaceShortage(
     absl::optional<LayoutUnit> space_shortage) {
   // Space shortage should only be reported when we already have a tentative
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
index 7f8920a..23d3c03 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -223,10 +223,6 @@
   // this break token is for content in the same flow as this parent.
   void AddBreakToken(const NGBreakToken*, bool is_in_parallel_flow = false);
 
-  void AddOutOfFlowLegacyCandidate(NGBlockNode,
-                                   const NGLogicalStaticPosition&,
-                                   const LayoutInline* inline_container);
-
   // Before layout we'll determine whether we can tell for sure that the node
   // (or what's left of it to lay out, in case we've already broken) will fit in
   // the current fragmentainer. If this is the case, we'll know that any
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
index 221fb7a..88f8380 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.cc
@@ -177,7 +177,7 @@
   default_containing_block_info_for_fixed_.rect.offset = container_offset;
 }
 
-void NGOutOfFlowLayoutPart::Run(const LayoutBox* only_layout) {
+void NGOutOfFlowLayoutPart::Run() {
   HandleFragmentation();
   const LayoutObject* current_container = container_builder_->GetLayoutObject();
   if (!container_builder_->HasOutOfFlowPositionedCandidates() &&
@@ -198,7 +198,7 @@
       clear_scope(&candidates);
   container_builder_->SwapOutOfFlowPositionedCandidates(&candidates);
 
-  LayoutCandidates(&candidates, only_layout);
+  LayoutCandidates(&candidates);
 }
 
 void NGOutOfFlowLayoutPart::HandleFragmentation(
@@ -682,8 +682,7 @@
 }
 
 void NGOutOfFlowLayoutPart::LayoutCandidates(
-    HeapVector<NGLogicalOutOfFlowPositionedNode>* candidates,
-    const LayoutBox* only_layout) {
+    HeapVector<NGLogicalOutOfFlowPositionedNode>* candidates) {
   const WritingModeConverter conainer_converter(
       container_builder_->GetWritingDirection(), container_builder_->Size());
   const NGFragmentItemsBuilder::ItemWithOffsetList* items = nullptr;
@@ -696,18 +695,14 @@
       LayoutBox* layout_box = candidate.box;
       if (!container_builder_->IsBlockFragmentationContextRoot())
         SaveStaticPositionOnPaintLayer(layout_box, candidate.static_position);
-      if (IsContainingBlockForCandidate(candidate) &&
-          (!only_layout || layout_box == only_layout)) {
+      if (IsContainingBlockForCandidate(candidate)) {
         if (has_block_fragmentation_) {
           container_builder_->SetHasOutOfFlowInFragmentainerSubtree(true);
           if (!container_builder_->IsInitialColumnBalancingPass()) {
             // As an optimization, only populate legacy positioned objects lists
             // when inside a fragmentation context root, since otherwise we can
             // just look at the children in the fragment tree.
-            if (layout_box != only_layout) {
-              container_builder_->InsertLegacyPositionedObject(
-                  candidate.Node());
-            }
+            container_builder_->InsertLegacyPositionedObject(candidate.Node());
             NGLogicalOOFNodeForFragmentation fragmentainer_descendant(
                 candidate);
             container_builder_->AdjustFragmentainerDescendant(
@@ -738,10 +733,9 @@
         NodeInfo node_info = SetupNodeInfo(candidate);
         NodeToLayout node_to_layout = {
             node_info,
-            CalculateOffset(node_info, only_layout, /* is_first_run */ false,
+            CalculateOffset(node_info, /* is_first_run */ false,
                             needs_anchor_queries ? &*anchor_queries : nullptr)};
-        const NGLayoutResult* result =
-            LayoutOOFNode(node_to_layout, only_layout);
+        const NGLayoutResult* result = LayoutOOFNode(node_to_layout);
         container_builder_->AddResult(
             *result, result->OutOfFlowPositionedOffset(),
             /* relative_offset */ absl::nullopt, &candidate.inline_container);
@@ -1214,9 +1208,8 @@
 
         NodeInfo node_info = SetupNodeInfo(descendant);
         NodeToLayout node_to_layout = {
-            node_info,
-            CalculateOffset(node_info, /* only_layout */ nullptr,
-                            /* is_first_run */ true, &stitched_anchor_queries)};
+            node_info, CalculateOffset(node_info, /* is_first_run */ true,
+                                       &stitched_anchor_queries)};
         node_to_layout.containing_block_fragment =
             descendant.containing_block.Fragment();
         node_to_layout.offset_info.original_offset =
@@ -1465,7 +1458,6 @@
 
 const NGLayoutResult* NGOutOfFlowLayoutPart::LayoutOOFNode(
     NodeToLayout& oof_node_to_layout,
-    const LayoutBox* only_layout,
     const NGConstraintSpace* fragmentainer_constraint_space,
     bool is_last_fragmentainer_so_far) {
   const NodeInfo& node_info = oof_node_to_layout.node_info;
@@ -1539,8 +1531,7 @@
         // token, causing major confusion everywhere.
         //
         // [1] https://drafts.csswg.org/css-break/#varying-size-boxes
-        offset_info = CalculateOffset(node_info, only_layout,
-                                      /* is_first_run */ false);
+        offset_info = CalculateOffset(node_info, /* is_first_run */ false);
       }
 
       layout_result = Layout(oof_node_to_layout, fragmentainer_constraint_space,
@@ -1557,7 +1548,6 @@
 
 NGOutOfFlowLayoutPart::OffsetInfo NGOutOfFlowLayoutPart::CalculateOffset(
     const NodeInfo& node_info,
-    const LayoutBox* only_layout,
     bool is_first_run,
     const NGLogicalAnchorQueryMap* anchor_queries) {
   const ComputedStyle* style = &node_info.node.Style();
@@ -1598,9 +1588,9 @@
 
     const bool try_fit_available_space = next_fallback_style;
     PhysicalScrollRange non_overflowing_range;
-    offset_info = TryCalculateOffset(
-        node_info, *style, only_layout, anchor_queries, implicit_anchor,
-        try_fit_available_space, is_first_run, &non_overflowing_range);
+    offset_info = TryCalculateOffset(node_info, *style, anchor_queries,
+                                     implicit_anchor, try_fit_available_space,
+                                     is_first_run, &non_overflowing_range);
 
     // Also check if it fits the containing block after applying scroll offset.
     if (offset_info && next_fallback_style) {
@@ -1630,7 +1620,6 @@
 NGOutOfFlowLayoutPart::TryCalculateOffset(
     const NodeInfo& node_info,
     const ComputedStyle& candidate_style,
-    const LayoutBox* only_layout,
     const NGLogicalAnchorQueryMap* anchor_queries,
     const LayoutObject* implicit_anchor,
     bool try_fit_available_space,
@@ -1781,7 +1770,7 @@
   offset_info.offset.inline_offset += inset.inline_start;
   offset_info.offset.block_offset += inset.block_start;
 
-  if (!only_layout && !container_builder_->IsBlockFragmentationContextRoot()) {
+  if (!container_builder_->IsBlockFragmentationContextRoot()) {
     // OOFs contained by an inline that's been split into continuations are
     // special, as their offset is relative to a fragment that's not the same as
     // their containing NG fragment; take a look inside
@@ -2063,9 +2052,8 @@
     bool* has_actual_break_inside,
     NGSimplifiedOOFLayoutAlgorithm* algorithm,
     HeapVector<NodeToLayout>* fragmented_descendants) {
-  const NGLayoutResult* result =
-      LayoutOOFNode(descendant, /* only_layout */ nullptr, fragmentainer_space,
-                    is_last_fragmentainer_so_far);
+  const NGLayoutResult* result = LayoutOOFNode(descendant, fragmentainer_space,
+                                               is_last_fragmentainer_so_far);
 
   if (result->Status() != NGLayoutResult::kSuccess) {
     DCHECK_EQ(result->Status(), NGLayoutResult::kOutOfFragmentainerSpace);
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
index 2cf5674..6e3b14a7 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h
@@ -61,13 +61,7 @@
       NGBoxFragmentBuilder* container_builder,
       absl::optional<LogicalSize> initial_containing_block_fixed_size);
 
-  // Normally this function lays out and positions all out-of-flow objects from
-  // the container_builder and additional ones it discovers through laying out
-  // those objects. However, if only_layout is specified, only that object will
-  // get laid out; any additional ones will be stored as out-of-flow
-  // descendants in the builder for use via
-  // LayoutResult::OutOfFlowPositionedDescendants.
-  void Run(const LayoutBox* only_layout = nullptr);
+  void Run();
 
   struct ColumnBalancingInfo {
     DISALLOW_NEW();
@@ -299,8 +293,7 @@
       bool adjust_for_fragmentation = false);
 
   void LayoutCandidates(
-      HeapVector<NGLogicalOutOfFlowPositionedNode>* candidates,
-      const LayoutBox* only_layout);
+      HeapVector<NGLogicalOutOfFlowPositionedNode>* candidates);
 
   void HandleMulticolsWithPendingOOFs(NGBoxFragmentBuilder* container_builder);
   void LayoutOOFsInMulticol(
@@ -320,7 +313,6 @@
 
   const NGLayoutResult* LayoutOOFNode(
       NodeToLayout& oof_node_to_layout,
-      const LayoutBox* only_layout,
       const NGConstraintSpace* fragmentainer_constraint_space = nullptr,
       bool is_last_fragmentainer_so_far = false);
 
@@ -328,7 +320,6 @@
   // changing this to a more accurate name.
   OffsetInfo CalculateOffset(
       const NodeInfo& node_info,
-      const LayoutBox* only_layout,
       bool is_first_run = true,
       const NGLogicalAnchorQueryMap* anchor_queries = nullptr);
   // Calculates offsets with the given ComputedStyle. Returns nullopt if
@@ -337,7 +328,6 @@
   absl::optional<OffsetInfo> TryCalculateOffset(
       const NodeInfo& node_info,
       const ComputedStyle& style,
-      const LayoutBox* only_layout,
       const NGLogicalAnchorQueryMap* anchor_queries,
       const LayoutObject* implicit_anchor,
       bool try_fit_available_space,
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
index 60d5c70..ada6ab3 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.cc
@@ -252,16 +252,6 @@
   return false;
 }
 
-void LayoutNGTable::UpdateBlockLayout() {
-  NOT_DESTROYED();
-
-  if (IsOutOfFlowPositioned()) {
-    UpdateOutOfFlowBlockLayout();
-    return;
-  }
-  UpdateInFlowBlockLayout();
-}
-
 void LayoutNGTable::AddChild(LayoutObject* child, LayoutObject* before_child) {
   NOT_DESTROYED();
   TableGridStructureChanged();
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
index 4bdddc33..6b91de3 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table.h
@@ -146,8 +146,6 @@
     return "LayoutNGTable";
   }
 
-  void UpdateBlockLayout() override;
-
   void AddChild(LayoutObject* child,
                 LayoutObject* before_child = nullptr) override;
 
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.cc
index 8b03809..3aaaba3 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.cc
@@ -19,48 +19,4 @@
 LayoutNGTableCaption::LayoutNGTableCaption(Element* element)
     : LayoutNGBlockFlow(element) {}
 
-// Legacy method.
-// TODO(1229581): Remove.
-void LayoutNGTableCaption::CalculateAndSetMargins(
-    const NGConstraintSpace& constraint_space,
-    const NGPhysicalFragment& physical_fragment) {
-  NOT_DESTROYED();
-  const ComputedStyle& containing_block_style = ContainingBlock()->StyleRef();
-
-  NGBoxFragment box_fragment(containing_block_style.GetWritingDirection(),
-                             To<NGPhysicalBoxFragment>(physical_fragment));
-
-  NGPhysicalBoxStrut physical_margins =
-      ComputePhysicalMargins(constraint_space, StyleRef());
-
-  NGBoxStrut logical_margins = physical_margins.ConvertToLogical(
-      containing_block_style.GetWritingDirection());
-
-  LayoutUnit caption_inline_size_in_cb_writing_mode = box_fragment.InlineSize();
-
-  LayoutUnit available_inline_size_in_cb_writing_mode =
-      ToPhysicalSize(constraint_space.AvailableSize(),
-                     constraint_space.GetWritingMode())
-          .ConvertToLogical(containing_block_style.GetWritingMode())
-          .inline_size;
-
-  ResolveInlineMargins(StyleRef(), containing_block_style,
-                       available_inline_size_in_cb_writing_mode,
-                       caption_inline_size_in_cb_writing_mode,
-                       &logical_margins);
-  SetMargin(logical_margins.ConvertToPhysical(
-      containing_block_style.GetWritingDirection()));
-}
-
-// TODO(1229581): Remove.
-void LayoutNGTableCaption::UpdateBlockLayout() {
-  NOT_DESTROYED();
-
-  DCHECK(!IsOutOfFlowPositioned()) << "Out of flow captions are blockified.";
-
-  const NGLayoutResult* result = UpdateInFlowBlockLayout();
-  CalculateAndSetMargins(result->GetConstraintSpaceForCaching(),
-                         result->PhysicalFragment());
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h
index d1eb0dec..ae7a23d 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_caption.h
@@ -10,14 +10,10 @@
 
 namespace blink {
 
-class NGPhysicalFragment;
-
 class CORE_EXPORT LayoutNGTableCaption final : public LayoutNGBlockFlow {
  public:
   explicit LayoutNGTableCaption(Element*);
 
-  void UpdateBlockLayout() override;
-
   const char* GetName() const override {
     NOT_DESTROYED();
     return "LayoutNGTableCaption";
@@ -33,11 +29,6 @@
     return type == kLayoutObjectTableCaption ||
            LayoutNGBlockFlow::IsOfType(type);
   }
-
- private:
-  // Legacy-only API.
-  void CalculateAndSetMargins(const NGConstraintSpace&,
-                              const NGPhysicalFragment&);
 };
 
 // wtf/casting.h helper.
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
index 9384e45..651b3427 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.cc
@@ -128,16 +128,6 @@
   return nullptr;
 }
 
-void LayoutNGTableCell::UpdateBlockLayout() {
-  NOT_DESTROYED();
-
-  if (IsOutOfFlowPositioned()) {
-    UpdateOutOfFlowBlockLayout();
-    return;
-  }
-  UpdateInFlowBlockLayout();
-}
-
 void LayoutNGTableCell::StyleDidChange(StyleDifference diff,
                                        const ComputedStyle* old_style) {
   NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h
index 625fdee..4dc0d49 100644
--- a/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h
+++ b/third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h
@@ -68,8 +68,6 @@
 
   // LayoutBlockFlow methods start.
 
-  void UpdateBlockLayout() override;
-
   void StyleDidChange(StyleDifference diff,
                       const ComputedStyle* old_style) final;
 
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 1b47060..61edf7e 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -39,7 +39,6 @@
 #include "third_party/blink/public/common/input/web_menu_source_type.h"
 #include "third_party/blink/public/common/navigation/impression.h"
 #include "third_party/blink/public/mojom/context_menu/context_menu.mojom-blink.h"
-#include "third_party/blink/public/mojom/conversions/attribution_reporting.mojom-blink.h"
 #include "third_party/blink/public/web/web_local_frame_client.h"
 #include "third_party/blink/public/web/web_plugin.h"
 #include "third_party/blink/public/web/web_text_check_client.h"
@@ -762,7 +761,6 @@
                                                 /*element=*/anchor,
                                                 /*request_id=*/absl::nullopt)) {
           data.impression = blink::Impression{
-              .nav_type = mojom::blink::AttributionNavigationType::kContextMenu,
               .runtime_features = attribution_src_loader->GetRuntimeFeatures(),
           };
         }
diff --git a/third_party/blink/renderer/core/page/context_menu_controller_test.cc b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
index a20dc60..cc9f900 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller_test.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller_test.cc
@@ -1950,10 +1950,6 @@
 
     EXPECT_EQ(context_menu_data.impression.has_value(),
               test_case.impression_expected);
-    if (context_menu_data.impression.has_value()) {
-      EXPECT_EQ(context_menu_data.impression->nav_type,
-                mojom::blink::AttributionNavigationType::kContextMenu);
-    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index 325af55..b9e80ac4 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -203,7 +203,7 @@
   HeapVector<Member<V8UnionNodeOrStringOrTrustedScript>> nodes;
   nodes.push_back(
       MakeGarbageCollected<V8UnionNodeOrStringOrTrustedScript>(iframe));
-  document->documentElement()->ReplaceWith(nodes, ASSERT_NO_EXCEPTION);
+  document->documentElement()->replaceWith(nodes, ASSERT_NO_EXCEPTION);
 
   UpdateAllLifecyclePhases(MainFrameView());
 
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index af87166..389e97f 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -572,8 +572,22 @@
   base::TimeDelta time_to_next_paint =
       base::Milliseconds(entry_end_time - entry->processingEnd());
 
-  if (last_visibility_change_timestamp_ > event_timestamp &&
-      last_visibility_change_timestamp_ < presentation_timestamp) {
+  const bool is_artificial_pointerup_or_click =
+      (entry->name() == event_type_names::kPointerup ||
+       entry->name() == event_type_names::kClick) &&
+      entry->startTime() == pending_pointer_down_start_time_;
+
+  if (is_artificial_pointerup_or_click) {
+    UseCounter::Count(GetExecutionContext(),
+                      WebFeature::kEventTimingArtificialPointerupOrClick);
+  }
+
+  if ((last_visibility_change_timestamp_ > event_timestamp &&
+       last_visibility_change_timestamp_ < presentation_timestamp)
+#if BUILDFLAG(IS_MAC)
+      || is_artificial_pointerup_or_click
+#endif  // BUILDFLAG(IS_MAC)
+  ) {
     // The page visibility was changed. Ignore the presentation_timestamp and
     // fallback to processingEnd (as if there was no next paint needed).
     entry_end_time = entry->processingEnd();
@@ -592,6 +606,7 @@
   entry->SetUnsafePresentationTimestamp(entry_presentation_timestamp);
 
   if (entry->name() == "pointerdown") {
+    pending_pointer_down_start_time_ = entry->startTime();
     pending_pointer_down_input_delay_ = input_delay;
     pending_pointer_down_processing_time_ = processing_time;
     pending_pointer_down_time_to_next_paint_ = time_to_next_paint;
diff --git a/third_party/blink/renderer/core/timing/window_performance.h b/third_party/blink/renderer/core/timing/window_performance.h
index 74c0689..2dd0bc6 100644
--- a/third_party/blink/renderer/core/timing/window_performance.h
+++ b/third_party/blink/renderer/core/timing/window_performance.h
@@ -262,6 +262,7 @@
   mutable Member<PerformanceNavigation> navigation_;
   mutable Member<PerformanceTiming> timing_;
   mutable Member<PerformanceTimingForReporting> timing_for_reporting_;
+  DOMHighResTimeStamp pending_pointer_down_start_time_;
   absl::optional<base::TimeDelta> pending_pointer_down_input_delay_;
   absl::optional<base::TimeDelta> pending_pointer_down_processing_time_;
   absl::optional<base::TimeDelta> pending_pointer_down_time_to_next_paint_;
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 8db636a..1ab47af7 100644
--- a/third_party/blink/renderer/core/timing/window_performance_test.cc
+++ b/third_party/blink/renderer/core/timing/window_performance_test.cc
@@ -1029,6 +1029,72 @@
   EXPECT_EQ(0u, entries.size());
 }
 
+#if BUILDFLAG(IS_MAC)
+//  Test artificial pointerup and click on MacOS fall back to use processingEnd
+//  as event duration ending time.
+//  See crbug.com/1321819
+TEST_P(WindowPerformanceTest, ArtificialPointerupOrClick) {
+  // Random keycode picked for testing
+  PointerId pointer_id = 4;
+
+  // Pointerdown
+  base::TimeTicks pointerdown_timestamp = GetTimeOrigin();
+  base::TimeTicks processing_start_pointerdown = GetTimeStamp(1);
+  base::TimeTicks processing_end_pointerdown = GetTimeStamp(2);
+  base::TimeTicks presentation_time_pointerdown = GetTimeStamp(3);
+  RegisterPointerEvent("pointerdown", pointerdown_timestamp,
+                       processing_start_pointerdown, processing_end_pointerdown,
+                       pointer_id);
+  SimulatePaintAndResolvePresentationPromise(presentation_time_pointerdown);
+  // Artificial Pointerup
+  base::TimeTicks pointerup_timestamp = pointerdown_timestamp;
+  base::TimeTicks processing_start_pointerup = GetTimeStamp(5);
+  base::TimeTicks processing_end_pointerup = GetTimeStamp(6);
+  base::TimeTicks presentation_time_pointerup = GetTimeStamp(10);
+  RegisterPointerEvent("pointerup", pointerup_timestamp,
+                       processing_start_pointerup, processing_end_pointerup,
+                       pointer_id);
+  SimulatePaintAndResolvePresentationPromise(presentation_time_pointerup);
+  // Artificial Click
+  base::TimeTicks click_timestamp = pointerup_timestamp;
+  base::TimeTicks processing_start_click = GetTimeStamp(11);
+  base::TimeTicks processing_end_click = GetTimeStamp(12);
+  base::TimeTicks presentation_time_click = GetTimeStamp(20);
+  RegisterPointerEvent("click", click_timestamp, processing_start_click,
+                       processing_end_click, pointer_id);
+  SimulatePaintAndResolvePresentationPromise(presentation_time_click);
+
+  // Flush UKM logging mojo request.
+  RunPendingTasks();
+
+  // Check UKM recording.
+  auto entries = GetUkmRecorder()->GetEntriesByName(
+      ukm::builders::Responsiveness_UserInteraction::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+  const ukm::mojom::UkmEntry* ukm_entry = entries[0];
+  GetUkmRecorder()->ExpectEntryMetric(
+      ukm_entry,
+      ukm::builders::Responsiveness_UserInteraction::kMaxEventDurationName, 12);
+  GetUkmRecorder()->ExpectEntryMetric(
+      ukm_entry,
+      ukm::builders::Responsiveness_UserInteraction::kTotalEventDurationName,
+      12);
+  GetUkmRecorder()->ExpectEntryMetric(
+      ukm_entry,
+      ukm::builders::Responsiveness_UserInteraction::kInteractionTypeName, 1);
+
+  // Check UMA recording.
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.AllTypes", 1);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Keyboard", 0);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.TapOrClick", 1);
+  GetHistogramTester().ExpectTotalCount(
+      "Blink.Responsiveness.UserInteraction.MaxEventDuration.Drag", 0);
+}
+#endif  // BUILDFLAG(IS_MAC)
+
 TEST_P(WindowPerformanceTest, ElementTimingTraceEvent) {
   using trace_analyzer::Query;
   trace_analyzer::Start("*");
diff --git a/third_party/blink/renderer/modules/websockets/dom_websocket.cc b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
index 556e5ae..47ce748 100644
--- a/third_party/blink/renderer/modules/websockets/dom_websocket.cc
+++ b/third_party/blink/renderer/modules/websockets/dom_websocket.cc
@@ -403,8 +403,9 @@
   // needs to fix the size of the File at this point. For this reason,
   // construct a new BlobDataHandle here with the size that this method
   // observed.
-  channel_->Send(
-      BlobDataHandle::Create(binary_data->Uuid(), binary_data->type(), size));
+  channel_->Send(BlobDataHandle::Create(binary_data->Uuid(),
+                                        binary_data->type(), size,
+                                        binary_data->AsMojoBlob()));
 }
 
 void DOMWebSocket::close(uint16_t code,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 5e189476..e5aba46 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1956,6 +1956,13 @@
       status: "stable",
     },
     {
+      // A flag, just for local testing to make the
+      // HTML parser yield more often and take longer to resume.
+      // This helps when debugging flaky tests caused by parser
+      // yielding heuristics.
+      name: "HTMLParserYieldAndDelayOftenForTesting",
+    },
+    {
       // A kill-switch for crbug.com/1412729
       // (enabling the `v` flag for `pattern` RegExps).
       name: "HTMLPatternRegExpUnicodeSets",
diff --git a/third_party/blink/tools/blinkpy/common/net/results_fetcher.py b/third_party/blink/tools/blinkpy/common/net/results_fetcher.py
index fd676845..f19cdae 100644
--- a/third_party/blink/tools/blinkpy/common/net/results_fetcher.py
+++ b/third_party/blink/tools/blinkpy/common/net/results_fetcher.py
@@ -88,7 +88,7 @@
     def gather_results(self,
                        build: Build,
                        step_name: str,
-                       exclude_exonerated: bool = True,
+                       exclude_exonerated: bool = False,
                        only_unexpected: bool = True) -> WebTestResults:
         """Gather all web test results on a given build step from ResultDB."""
         assert build.build_id, '%s must set a build ID' % build
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index c74fa804dd..156e3a6 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2845,6 +2845,11 @@
 # unblock roll wpt
 crbug.com/626703 external/wpt/service-workers/service-worker/worker-interception.https.html [ Failure ]
 
+# Disable certain requestIdleCallback tests pending spec or test changes.
+crbug.com/1316344 virtual/threaded/external/wpt/requestidlecallback/deadline-max-timeout-dynamic.html [ Failure ]
+crbug.com/1329296 virtual/threaded/external/wpt/requestidlecallback/deadline-max-rAF-dynamic.html [ Failure Pass ]
+crbug.com/1329290 virtual/threaded/external/wpt/requestidlecallback/deadline-max-rAF.html [ Failure Pass ]
+
 # ====== Test expectations added to unblock wpt-importer ======
 crbug.com/626703 external/wpt/fetch/api/crashtests/body-window-destroy.html [ Crash ]
 crbug.com/626703 external/wpt/fetch/metadata/generated/worker-dedicated-constructor.sub.html [ Failure ]
@@ -2852,7 +2857,6 @@
 crbug.com/626703 [ Linux ] external/wpt/web-bundle/subresource-loading/reuse-web-bundle-resource.https.tentative.html [ Failure ]
 crbug.com/626703 [ Linux ] virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/worker-interception.https.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/web-bundle/subresource-loading/reuse-web-bundle-resource.https.tentative.html [ Failure ]
-crbug.com/626703 virtual/threaded/external/wpt/requestidlecallback/deadline-max-timeout-dynamic.html [ Failure ]
 crbug.com/626703 external/wpt/service-workers/service-worker/navigation-timing-extended.https.html [ Failure ]
 crbug.com/626703 virtual/plz-dedicated-worker/external/wpt/service-workers/service-worker/navigation-timing-extended.https.html [ Failure ]
 crbug.com/626703 external/wpt/preload/preload-resource-match.https.html [ Failure ]
@@ -5670,7 +5674,6 @@
 crbug.com/1339291 [ Mac12-arm64 Release ] external/wpt/webaudio/the-audio-api/the-convolvernode-interface/realtime-conv.html [ Failure Pass ]
 crbug.com/1339293 [ Linux ] http/tests/devtools/network/network-initiator.js [ Failure Pass ]
 crbug.com/1339293 [ Linux ] external/wpt/html/semantics/embedded-content/the-img-element/invisible-image.html [ Failure Pass ]
-crbug.com/1339293 virtual/threaded/external/wpt/requestidlecallback/deadline-max-rAF-dynamic.html [ Failure Pass ]
 crbug.com/webrtc/14228 external/wpt/webrtc/protocol/h264-profile-levels.https.html [ Failure Pass ]
 
 external/wpt/html/semantics/embedded-content/media-elements/loading-the-media-resource/resource-selection-invoke-insert-source-networkState.html [ Crash Failure Pass Timeout ]
@@ -6590,9 +6593,6 @@
 # Sheriff 2023-04-21
 crbug.com/1426534 wpt_internal/content-security-policy/reporting-api/reporting-api-works-on-frame-ancestors.https.sub.html [ Failure Pass ]
 
-# Flake suppressor for Mac12 Tests (dbg) builder 2023-04-26
-crbug.com/1446992 [ Mac ] virtual/threaded/external/wpt/requestidlecallback/deadline-max-rAF.html [ Failure ]
-
 # Sheriff 2023-05-01
 crbug.com/1432145 [ Mac10.14 ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaStreamTrack-MediaElement-disabled-video-is-black.https.html [ Failure Timeout ]
 crbug.com/1441416 [ Linux ] external/wpt/js-self-profiling/max-buffer-size.window.html [ Crash Pass ]
@@ -6683,5 +6683,13 @@
 # Sheriff 2023-05-30
 crbug.com/1450020 [ Mac ] compositing/geometry/preserve-3d-switching.html [ Failure Pass ]
 
+# Re-enable after adding changes https://bit.ly/3w5hn7y
+crbug.com/1450344 http/tests/devtools/application-panel/resources-panel-on-navigation.js [ Failure Pass ]
+crbug.com/1450344 http/tests/devtools/application-panel/resources-panel-resource-preview.js [ Failure Pass ]
+crbug.com/1450344 http/tests/devtools/application-panel/resources-panel-selection-on-reload.js [ Failure Pass ]
+crbug.com/1450344 http/tests/devtools/application-panel/resources-panel-websql.js [ Failure Pass ]
+crbug.com/1450344 http/tests/devtools/bindings/suspendtarget-bindings.js [ Failure Pass ]
+crbug.com/1450344 http/tests/devtools/resource-tree/resource-tree-non-unique-url.js [ Failure Pass ]
+
 # Temporarily disabled to unblock https://crrev.com/c/4580489
-crbug.com/1422552 http/tests/devtools/tracing/timeline-misc/timeline-window-filter.js [ Failure Pass ]
\ No newline at end of file
+crbug.com/1422552 http/tests/devtools/tracing/timeline-misc/timeline-window-filter.js [ Failure Pass ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 992c1ed..a45d0d2 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -995,7 +995,7 @@
         "--enable-blink-features=PrivateStateTokens,PrivateStateTokensAlwaysAllowIssuance",
         "--additional-private-state-token-key-commitments={\"https://web-platform.test:8444\":{\"PrivateStateTokenV3VOPRF\":{\"protocol_version\":\"PrivateStateTokenV3VOPRF\",\"id\":1,\"batchsize\":1,\"keys\":{\"0\":{\"Y\":\"AAAAAASqh8oivosFN46xxx7zIK10bh07Younm5hZ90HgglQqOFUC8l2/VSlsOlReOHJ2CrfJ6CG1adnTkKJhZ0BtbSPWBwviQtdl64MWJc7sSg9HPvWfTjDigX5ihbzihG8V8aA=\",\"expiry\":\"253402300799000000\"}}}}}"
     ],
-    "expires": "Jul 1, 2023"
+    "expires": "Jul 1, 2024"
   },
   {
     "prefix": "offsetparent-old-behavior",
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 1349474..99e5fd3 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -273088,6 +273088,16 @@
    }
   },
   "support": {
+   ".cache": {
+    "gitignore2.json": [
+     "93a0a07250beb02c97bf584f5557fb82ceff850c",
+     []
+    ],
+    "mtime.json": [
+     "d4b901e30e68b23104c30c01dd073f227cb662fd",
+     []
+    ]
+   },
    ".gitignore": [
     "d93e645d547894b50149d3726de2654957b6e06f",
     []
@@ -314441,7 +314451,7 @@
       []
      ],
      "OWNERS": [
-      "1d83d3d366d28815eb47da2822550e2185d9cca8",
+      "62cd9f61ce8ef70159268b40d05237aa08ea504f",
       []
      ],
      "README": [
@@ -327523,7 +327533,7 @@
       []
      ],
      "css-transform-3d-transform-style.html.ini": [
-      "3584653fa3bc04d5ff272f19c4126e83ec83501c",
+      "c49ba92bf37c77522db19791b9fe95a2e87e3926",
       []
      ],
      "css-transform-animate-translate-implied-y-ref.html": [
@@ -341080,7 +341090,7 @@
      []
     ],
     "OWNERS": [
-     "167a625063bc31c6e7d58df13f8561ed28931b29",
+     "561efe79c015fe27b136facc852015d5336a63df",
      []
     ],
     "README.md": [
@@ -381390,7 +381400,7 @@
      []
     ],
     "modulepreload-as.html.ini": [
-     "2c0f0a40c06d1df7e019c94e337bb21e76e51943",
+     "5302dddde319b678136c25e8e0c067b233fdf3a1",
      []
     ],
     "modulepreload-expected.txt": [
@@ -388872,7 +388882,7 @@
      []
     ],
     "single-face-detection.https.html.ini": [
-     "7d0ccf1b6bacb32298c108cff5c4395744404958",
+     "1c255288e78c9b5f3ac3beafa3440cc425edde92",
      []
     ],
     "single-text-detection.https.html.ini": [
@@ -396158,7 +396168,7 @@
      []
     ],
     "full-cycle-test.https.any.js.ini": [
-     "32114a16db46455a5aaae321e722fb60c96c1783",
+     "78a68a53fb6812f1722c4c9aae69ce0e38d3a84c",
      []
     ],
     "h264.annexb": [
@@ -396194,7 +396204,7 @@
      []
     ],
     "reconfiguring-encoder.https.any.js.ini": [
-     "bdea86ee7cc56b7294b42ebb3c01d44e8abfd4ee",
+     "ef20bdfdc19d0af65500d157392a2b820a73fe9c",
      []
     ],
     "sfx-aac.mp4": [
@@ -557269,7 +557279,14 @@
         ]
        ],
        "pattern_attribute.html": [
-        "93cbd2caeca80c51a1b0627b85c7a0b0cc536748",
+        "ec093425f71e8298a9e139e568032b2e504601b6",
+        [
+         null,
+         {}
+        ]
+       ],
+       "pattern_attribute_v_flag.html": [
+        "5246421e8bd0fdb9c76316b9384b452239b8898c",
         [
          null,
          {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transforms/css-transform-3d-transform-style.html.ini b/third_party/blink/web_tests/external/wpt/css/css-transforms/css-transform-3d-transform-style.html.ini
index 3584653..c49ba92b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transforms/css-transform-3d-transform-style.html.ini
+++ b/third_party/blink/web_tests/external/wpt/css/css-transforms/css-transform-3d-transform-style.html.ini
@@ -1,3 +1,4 @@
 [css-transform-3d-transform-style.html]
   expected:
+    if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
     if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/pattern_attribute.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/pattern_attribute.html
index 93cbd2c..ec093425 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/pattern_attribute.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/pattern_attribute.html
@@ -16,40 +16,6 @@
   <input pattern="\p{ASCII_Hex_Digit}+" value="c0ff33" id="unicode-property">
 
   <input pattern="\p{RGI_Emoji}+" value="&#x1F618;&#x1F48B;" id="unicode-property-of-strings">
-  <input pattern="[\p{ASCII_Hex_Digit}--[Ff]]" value="0123456789abcdefABCDEF" id="set-difference">
-  <input pattern="[_\q{a|bc|def}]" value="q" id="string-literal">
-
-  <div class="breaking-changes-from-u-to-v">
-    <!-- Unescaped special characters in character classes. -->
-    <input pattern="[(]" value="foo">
-    <input pattern="[)]" value="foo">
-    <input pattern="[[]" value="foo">
-    <input pattern="[{]" value="foo">
-    <input pattern="[}]" value="foo">
-    <input pattern="[/]" value="foo">
-    <input pattern="[-]" value="foo">
-    <input pattern="[|]" value="foo">
-    <!-- Double punctuators in character classes. -->
-    <input pattern="[&&]" value="foo">
-    <input pattern="[!!]" value="foo">
-    <input pattern="[##]" value="foo">
-    <input pattern="[$$]" value="foo">
-    <input pattern="[%%]" value="foo">
-    <input pattern="[**]" value="foo">
-    <input pattern="[++]" value="foo">
-    <input pattern="[,,]" value="foo">
-    <input pattern="[..]" value="foo">
-    <input pattern="[::]" value="foo">
-    <input pattern="[;;]" value="foo">
-    <input pattern="[<<]" value="foo">
-    <input pattern="[==]" value="foo">
-    <input pattern="[>>]" value="foo">
-    <input pattern="[??]" value="foo">
-    <input pattern="[@@]" value="foo">
-    <input pattern="[``]" value="foo">
-    <input pattern="[~~]" value="foo">
-    <input pattern="[_^^]" value="foo">
-  </div>
 </div>
 <div id="log"></div>
 <script>
@@ -86,32 +52,4 @@
     assert_true(input.matches(":valid"));
     assert_false(input.validity.patternMismatch);
   }, "<input pattern> supports Unicode property escape syntax for properties of strings");
-
-  test(() => {
-    const input = document.querySelector("#set-difference");
-    assert_false(input.validity.valid);
-    assert_false(input.matches(":valid"));
-    assert_true(input.validity.patternMismatch);
-  }, "<input pattern> supports set difference syntax");
-
-  test(() => {
-    const input = document.querySelector("#string-literal");
-    assert_false(input.validity.valid);
-    assert_true(input.validity.patternMismatch);
-    assert_true(input.matches(":invalid"));
-  }, "<input pattern> supports string literal syntax");
-
-  test(() => {
-    const inputs = document.querySelectorAll(".breaking-changes-from-u-to-v input");
-    // These examples are all written such that they’re all `:invalid`
-    // when using `u`, but would become `:valid` when using `v` because
-    // the pattern would error, in turn resulting in
-    // `validity.valid === true`.
-    for (const input of inputs) {
-      const html = input.outerHTML;
-      assert_true(input.validity.valid, `${html} should be valid`);
-      assert_true(input.matches(":valid"), `${html} should match \`:valid\``);
-      assert_false(input.validity.patternMismatch, `${html} should not trigger a pattern mismatch`);
-    }
-  }, "<input pattern> enables the RegExp v flag");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/pattern_attribute_v_flag.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/pattern_attribute_v_flag.html
new file mode 100644
index 0000000..5246421
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/pattern_attribute_v_flag.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>pattern attribute: v flag</title>
+<meta name=viewport content="width=device-width">
+<link rel="author" title="Fabrice Clari" href="mailto:f.clari@inno-group.com">
+<link rel="author" title="Dimitri Bocquet" href="mailto:Dimitri.Bocquet@mosquito-fp7.eu">
+<link rel="author" title="Mathias Bynens" href="https://mathiasbynens.be/">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#attr-input-pattern">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h1><code>pattern</code> attribute: v flag</h1>
+<div style="display: none">
+  <input pattern="[\p{ASCII_Hex_Digit}--[Ff]]" value="0123456789abcdefABCDEF" id="set-difference">
+  <input pattern="[_\q{a|bc|def}]" value="q" id="string-literal">
+
+  <div class="breaking-changes-from-u-to-v">
+    <!-- Unescaped special characters in character classes. -->
+    <input pattern="[(]" value="foo">
+    <input pattern="[)]" value="foo">
+    <input pattern="[[]" value="foo">
+    <input pattern="[{]" value="foo">
+    <input pattern="[}]" value="foo">
+    <input pattern="[/]" value="foo">
+    <input pattern="[-]" value="foo">
+    <input pattern="[|]" value="foo">
+    <!-- Double punctuators in character classes. -->
+    <input pattern="[&&]" value="foo">
+    <input pattern="[!!]" value="foo">
+    <input pattern="[##]" value="foo">
+    <input pattern="[$$]" value="foo">
+    <input pattern="[%%]" value="foo">
+    <input pattern="[**]" value="foo">
+    <input pattern="[++]" value="foo">
+    <input pattern="[,,]" value="foo">
+    <input pattern="[..]" value="foo">
+    <input pattern="[::]" value="foo">
+    <input pattern="[;;]" value="foo">
+    <input pattern="[<<]" value="foo">
+    <input pattern="[==]" value="foo">
+    <input pattern="[>>]" value="foo">
+    <input pattern="[??]" value="foo">
+    <input pattern="[@@]" value="foo">
+    <input pattern="[``]" value="foo">
+    <input pattern="[~~]" value="foo">
+    <input pattern="[_^^]" value="foo">
+  </div>
+</div>
+<div id="log"></div>
+<script>
+  test(() => {
+    const input = document.querySelector("#set-difference");
+    assert_false(input.validity.valid);
+    assert_false(input.matches(":valid"));
+    assert_true(input.validity.patternMismatch);
+  }, "<input pattern> supports set difference syntax");
+
+  test(() => {
+    const input = document.querySelector("#string-literal");
+    assert_false(input.validity.valid);
+    assert_true(input.validity.patternMismatch);
+    assert_true(input.matches(":invalid"));
+  }, "<input pattern> supports string literal syntax");
+
+  test(() => {
+    const inputs = document.querySelectorAll(".breaking-changes-from-u-to-v input");
+    // These examples are all written such that they’re all `:invalid`
+    // when using `u`, but would become `:valid` when using `v` because
+    // the pattern would error, in turn resulting in
+    // `validity.valid === true`.
+    for (const input of inputs) {
+      const html = input.outerHTML;
+      assert_true(input.validity.valid, `${html} should be valid`);
+      assert_true(input.matches(":valid"), `${html} should match \`:valid\``);
+      assert_false(input.validity.patternMismatch, `${html} should not trigger a pattern mismatch`);
+    }
+  }, "<input pattern> enables the RegExp v flag");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/modulepreload-as.html.ini b/third_party/blink/web_tests/external/wpt/preload/modulepreload-as.html.ini
index 2c0f0a4..5302ddd 100644
--- a/third_party/blink/web_tests/external/wpt/preload/modulepreload-as.html.ini
+++ b/third_party/blink/web_tests/external/wpt/preload/modulepreload-as.html.ini
@@ -1,9 +1,9 @@
 [modulepreload-as.html]
   expected:
+    if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): TIMEOUT
     if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): OK
-    if (product == "content_shell") and (os == "mac") and (port == "mac12"): OK
     if (product == "content_shell") and (os == "mac") and (port == "mac11"): OK
-    if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): [TIMEOUT, ERROR]
+    if (product == "content_shell") and (os == "mac") and (port == "mac12"): OK
     if (product == "content_shell") and (os == "win"): TIMEOUT
     if product == "chrome": TIMEOUT
     ERROR
@@ -21,44 +21,41 @@
   [Modulepreload with as="document"]
     expected:
       if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): PASS
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if product == "chrome": NOTRUN
       FAIL
 
   [Modulepreload with as="embed"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "win"): PASS
       if product == "chrome": NOTRUN
       FAIL
 
   [Modulepreload with as="fetch"]
     expected:
+      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
       if product == "chrome": NOTRUN
 
   [Modulepreload with as="font"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "win") and (port == "win10.20h2"): PASS
+      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
       if product == "chrome": NOTRUN
       FAIL
 
   [Modulepreload with as="frame"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): PASS
+      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
       if (product == "content_shell") and (os == "win"): PASS
       if product == "chrome": NOTRUN
       FAIL
 
   [Modulepreload with as="iMaGe"]
     expected:
+      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
@@ -66,21 +63,21 @@
 
   [Modulepreload with as="iframe"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
+      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): FAIL
       if product == "chrome": NOTRUN
 
   [Modulepreload with as="image"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
-      if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): PASS
-      if (product == "content_shell") and (os == "win"): PASS
+      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
       if product == "chrome": NOTRUN
-      FAIL
 
   [Modulepreload with as="invalid-dest"]
     expected:
@@ -89,19 +86,19 @@
 
   [Modulepreload with as="manifest"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
       if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): PASS
       if (product == "content_shell") and (os == "win"): PASS
       if product == "chrome": NOTRUN
       FAIL
 
   [Modulepreload with as="object"]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): FAIL
       if product == "chrome": NOTRUN
 
   [Modulepreload with as="paintworklet"]
@@ -136,7 +133,6 @@
 
   [Modulepreload with as="style"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
       if (product == "content_shell") and (os == "win"): PASS
       if product == "chrome": NOTRUN
@@ -145,14 +141,12 @@
   [Modulepreload with as="track"]
     expected:
       if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "win"): PASS
       if product == "chrome": NOTRUN
       FAIL
 
   [Modulepreload with as="video"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
       if (product == "content_shell") and (os == "win"): PASS
       if product == "chrome": NOTRUN
@@ -160,11 +154,11 @@
 
   [Modulepreload with as="webidentity"]
     expected:
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [FAIL, PASS]
       if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
+      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): FAIL
       if product == "chrome": NOTRUN
 
   [Modulepreload with as="worker"]
@@ -174,9 +168,9 @@
 
   [Modulepreload with as="xslt"]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): PASS
-      if (product == "content_shell") and (os == "linux") and (flag_specific == ""): [PASS, FAIL]
-      if (product == "content_shell") and (os == "linux") and (flag_specific == "disable-site-isolation-trials"): PASS
-      if (product == "content_shell") and (os == "win"): PASS
+      if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
       if product == "chrome": NOTRUN
-      FAIL
diff --git a/third_party/blink/web_tests/external/wpt/shape-detection/single-face-detection.https.html.ini b/third_party/blink/web_tests/external/wpt/shape-detection/single-face-detection.https.html.ini
index 7d0ccf1b..1c25528 100644
--- a/third_party/blink/web_tests/external/wpt/shape-detection/single-face-detection.https.html.ini
+++ b/third_party/blink/web_tests/external/wpt/shape-detection/single-face-detection.https.html.ini
@@ -1,24 +1,21 @@
 [single-face-detection.https.html]
   expected:
-    if (product == "content_shell") and (os == "mac") and (port == "mac11"): [OK, TIMEOUT]
     if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): TIMEOUT
-    if (product == "content_shell") and (os == "mac") and (port == "mac13"): [OK, TIMEOUT]
-    if (product == "content_shell") and (os == "mac") and (port == "mac12"): [OK, TIMEOUT]
   [Blob]
     expected: FAIL
 
   [HTMLCanvasElement]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
-      if (product == "content_shell") and (os == "mac") and (port == "mac13"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): PASS
+      if (product == "content_shell") and (os == "mac") and (port == "mac13"): PASS
+      if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
       FAIL
 
   [HTMLImageElement]
     expected:
+      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac13"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
-      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): PASS
       FAIL
 
   [HTMLVideoElement]
@@ -26,25 +23,23 @@
 
   [ImageBitmap]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac12"): PASS
-      if (product == "content_shell") and (os == "mac") and (port == "mac13"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): PASS
+      if (product == "content_shell") and (os == "mac") and (port == "mac13"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
       FAIL
 
   [ImageData]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac13"): PASS
-      if (product == "content_shell") and (os == "mac") and (port == "mac12"): PASS
+      if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
       FAIL
 
   [OffscreenCanvas]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): PASS
       if (product == "content_shell") and (os == "mac") and (port == "mac13"): PASS
+      if (product == "content_shell") and (os == "mac") and (port == "mac11"): PASS
       FAIL
 
   [VideoFrame]
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/full-cycle-test.https.any.js.ini b/third_party/blink/web_tests/external/wpt/webcodecs/full-cycle-test.https.any.js.ini
index 32114a1..78a68a5 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/full-cycle-test.https.any.js.ini
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/full-cycle-test.https.any.js.ini
@@ -1,27 +1,24 @@
 [full-cycle-test.https.any.html?h264_annexb]
   expected:
+    if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): ERROR
+    if (product == "content_shell") and (os == "mac") and (port == "mac13"): ERROR
     if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): TIMEOUT
     if (product == "content_shell") and (os == "mac") and (port == "mac11"): ERROR
-    if (product == "content_shell") and (os == "mac") and (port == "mac13"): ERROR
-    if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): ERROR
     if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): ERROR
+    if (product == "content_shell") and (os == "mac") and (port == "mac12"): ERROR
   [Encoding and decoding cycle]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): FAIL
-      PRECONDITION_FAILED
+      if (product == "content_shell") and (os == "win"): PRECONDITION_FAILED
+      if (product == "content_shell") and (os == "linux"): PRECONDITION_FAILED
+      if product == "chrome": PRECONDITION_FAILED
+      FAIL
 
   [Encoding and decoding cycle w/ stripped color space]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac11"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac10.15"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): FAIL
-      if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): FAIL
-      PRECONDITION_FAILED
+      if (product == "content_shell") and (os == "win"): PRECONDITION_FAILED
+      if (product == "content_shell") and (os == "linux"): PRECONDITION_FAILED
+      if product == "chrome": PRECONDITION_FAILED
+      FAIL
 
 
 [full-cycle-test.https.any.html?h264_avc]
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/reconfiguring-encoder.https.any.js.ini b/third_party/blink/web_tests/external/wpt/webcodecs/reconfiguring-encoder.https.any.js.ini
index bdea86e..ef20bdf 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/reconfiguring-encoder.https.any.js.ini
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/reconfiguring-encoder.https.any.js.ini
@@ -18,11 +18,13 @@
     if (product == "content_shell") and (os == "mac") and (port == "mac13"): ERROR
     if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): ERROR
     if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): ERROR
+    if (product == "content_shell") and (os == "mac") and (port == "mac12"): ERROR
   [Reconfiguring encoder]
     expected:
-      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac12-arm64"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac12"): FAIL
       if (product == "content_shell") and (os == "mac") and (port == "mac13-arm64"): FAIL
+      if (product == "content_shell") and (os == "mac") and (port == "mac13"): FAIL
       PRECONDITION_FAILED
 
 
diff --git a/third_party/qcms/OWNERS b/third_party/qcms/OWNERS
index 5489c5f5..4eb0cb4 100644
--- a/third_party/qcms/OWNERS
+++ b/third_party/qcms/OWNERS
@@ -1,2 +1 @@
-noel@chromium.org
 ccameron@chromium.org
diff --git a/third_party/zlib/OWNERS b/third_party/zlib/OWNERS
index ecffb59..3a82157 100644
--- a/third_party/zlib/OWNERS
+++ b/third_party/zlib/OWNERS
@@ -1,5 +1,4 @@
 agl@chromium.org
 cavalcantii@chromium.org
 cblume@chromium.org
-noel@chromium.org
 scroggo@google.com
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index e78ac3a..75cf7f2 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -29808,6 +29808,25 @@
   </description>
 </action>
 
+<action name="Settings.PrivacySandbox.RestrictedNotice.ClosedNoInteraction"
+    not_user_triggered="true">
+  <owner>tommasin@chromium.org</owner>
+  <owner>kartoffel-core@google.com</owner>
+  <owner>kartoffel-core-eng@google.com</owner>
+  <description>
+    Restricted Notice closed with no interaction (e.g. browser shutdown).
+  </description>
+</action>
+
+<action name="Settings.PrivacySandbox.RestrictedNotice.MoreButtonClicked">
+  <owner>tommasin@chromium.org</owner>
+  <owner>kartoffel-core@google.com</owner>
+  <owner>kartoffel-core-eng@google.com</owner>
+  <description>
+    User clicked on the More button in the Privacy Sandbox Restricted Notice.
+  </description>
+</action>
+
 <action name="Settings.PrivacySandbox.RestrictedNotice.OpenedSettings">
   <owner>djmitche@chromium.org</owner>
   <owner>kartoffel-core@google.com</owner>
@@ -29817,6 +29836,14 @@
   </description>
 </action>
 
+<action name="Settings.PrivacySandbox.RestrictedNotice.Shown"
+    not_user_triggered="true">
+  <owner>tommasin@chromium.org</owner>
+  <owner>kartoffel-core@google.com</owner>
+  <owner>kartoffel-core-eng@google.com</owner>
+  <description>Restricted Privacy Sandbox notice is shown.</description>
+</action>
+
 <action name="Settings.PrivacySandbox.SpamFraud.Opened">
   <owner>dullweber@google.com</owner>
   <owner>sauski@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6ec96d7e..404d343 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -42686,6 +42686,7 @@
   <int value="4578" label="LongAnimationFrameRequested"/>
   <int value="4579" label="FedCmLoginHint"/>
   <int value="4580" label="FedCmRpContext"/>
+  <int value="4581" label="EventTimingArtificialPointerupOrClick"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -62485,6 +62486,7 @@
   <int value="-236433493" label="ArcKeyboardShortcutHelperIntegration:enabled"/>
   <int value="-236310717" label="AccessibilityDeprecateChromeVoxTabs:disabled"/>
   <int value="-236071174" label="NearbyShareVisibilityReminder:disabled"/>
+  <int value="-236058937" label="UseGpuSchedulerDfs:disabled"/>
   <int value="-235949168" label="DrawPredictedInkPoint:enabled"/>
   <int value="-235843754" label="OmniboxGroupingFrameworkForNonZPS:disabled"/>
   <int value="-234966279" label="PointerEvent:disabled"/>
@@ -66197,6 +66199,7 @@
       label="OmniboxUIExperimentBoldUserTextOnSearchSuggestions:enabled"/>
   <int value="1743151811" label="CommerceLocalPDPDetection:disabled"/>
   <int value="1743603853" label="CameraAppDocScanDlc:disabled"/>
+  <int value="1744034696" label="UseGpuSchedulerDfs:enabled"/>
   <int value="1745053254" label="ClickToCallOpenDialerDirectly:enabled"/>
   <int value="1747279677" label="disable-delegated-renderer"/>
   <int value="1748481830" label="AppManagement:enabled"/>
@@ -103069,6 +103072,7 @@
   <int value="2" label="Reauthentication failed"/>
   <int value="3" label="'Manage Passwords' was selected"/>
   <int value="4" label="Passkey was selected"/>
+  <int value="5" label="Hybrid passkey sign-in was selected"/>
 </enum>
 
 <enum name="TouchToFill.SubmissionReadiness">
@@ -103088,6 +103092,7 @@
   <int value="1" label="Dismissed"/>
   <int value="2" label="Selected Manage Passwords"/>
   <int value="3" label="Selected WebAuthn Credential"/>
+  <int value="4" label="Selected Hybrid"/>
 </enum>
 
 <enum name="TouchType">
@@ -111519,18 +111524,6 @@
   <int value="11" label="Delete all site entries for a FPS owner"/>
 </enum>
 
-<enum name="WebsiteSettingsDiscoverabilityAction">
-  <int value="0" label="Permission icon shown in omnibox"/>
-  <int value="1"
-      label="Page info opened while permission icon was shown in omnibox"/>
-  <int value="2" label="Permissions opened after click on permissions icon"/>
-  <int value="3" label="Permission changed after click on permissions icon"/>
-  <int value="4" label="Store icon shown in omnibox"/>
-  <int value="5"
-      label="Page info opened while store icon was shown in omnibox"/>
-  <int value="6" label="Store info opened after click on store icon"/>
-</enum>
-
 <enum name="WebSocketCloseCode">
   <int value="0" label="Normal Closure">1000</int>
   <int value="1" label="Going Away">1001</int>
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml
index e81b13d..a0954ca 100644
--- a/tools/metrics/histograms/metadata/accessibility/histograms.xml
+++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -1824,6 +1824,35 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.ReadAnything.MergedDistillationTime.{Result}"
+    units="ms" expires_after="2023-11-30">
+  <owner>abigailbklein@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Records the time taken to distill a web page using the merged algorithm that
+    combines results from the rules based algorithm and the ML model. Refer to
+    AXTreeDistiller::Distill for details.
+  </summary>
+  <token key="Result">
+    <variant name="Failure" summary="has failed"/>
+    <variant name="Success" summary="has succeeded"/>
+  </token>
+</histogram>
+
+<histogram name="Accessibility.ReadAnything.RulesDistillationTime.{Result}"
+    units="ms" expires_after="2023-11-30">
+  <owner>abigailbklein@google.com</owner>
+  <owner>chrome-a11y-core@google.com</owner>
+  <summary>
+    Records the time taken to distill a web page using the rules based
+    algorithm. Refer to AXTreeDistiller::DistillViaAlgorithm for details.
+  </summary>
+  <token key="Result">
+    <variant name="Failure" summary="has failed"/>
+    <variant name="Success" summary="has succeeded"/>
+  </token>
+</histogram>
+
 <histogram name="Accessibility.ReadAnything.ScrollEvent"
     enum="ReadAnythingScrollEvent" expires_after="2023-11-30">
   <owner>abigailbklein@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index 1c7dd2c..b11107d 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -2012,6 +2012,25 @@
   </token>
 </histogram>
 
+<histogram name="ContentSuggestions.{FeedType}.Activity.ByProvider"
+    enum="FeedActivityBucket" expires_after="2023-11-24">
+  <owner>guiperez@google.com</owner>
+  <owner>feed@chromium.org</owner>
+  <summary>
+    Tracks user activity buckets for the filter. Reported every time a metric is
+    reported through the MetricsProvider to allow filtering. The histogram used
+    for analysis is ContentSuggestions.{FeedType}.Activity, whilst this one will
+    only be used for the filter. Any changes to
+    ContentSuggestions.{FeedType}.Activity will also change how this metric is
+    reported.
+  </summary>
+  <token key="FeedType">
+    <variant name="Feed" summary="the For-You feed"/>
+    <variant name="Feed.AllFeeds" summary="All Feeds combined"/>
+    <variant name="Feed.WebFeed" summary="the Following/Web Feed"/>
+  </token>
+</histogram>
+
 <histogram name="ContentSuggestions.{FeedType}.CardIndexOnSwitch" units="index"
     expires_after="2023-04-21">
   <owner>adamta@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 4ce29de4..93fe119f 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -1735,7 +1735,7 @@
 </histogram>
 
 <histogram name="IOS.PostRestoreSignin.Choice"
-    enum="IOSPostRestoreSigninChoice" expires_after="2023-06-15">
+    enum="IOSPostRestoreSigninChoice" expires_after="2023-10-15">
   <owner>scottyoder@google.com</owner>
   <owner>bling-get-set-up@google.com</owner>
   <summary>
@@ -1744,7 +1744,7 @@
 </histogram>
 
 <histogram name="IOS.PostRestoreSignin.Displayed" enum="Boolean"
-    expires_after="2023-06-15">
+    expires_after="2023-10-15">
   <owner>scottyoder@google.com</owner>
   <owner>bling-get-set-up@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index bc81611..8b7d5a6 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -167,7 +167,7 @@
 </variants>
 
 <histogram name="OptimizationGuide.ApplyDecision.{OptimizationType}"
-    enum="OptimizationGuideOptimizationTypeDecision" expires_after="M117">
+    enum="OptimizationGuideOptimizationTypeDecision" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -178,7 +178,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.ApplyDecisionAsync.{OptimizationType}"
-    enum="OptimizationGuideOptimizationTypeDecision" expires_after="M117">
+    enum="OptimizationGuideOptimizationTypeDecision" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -192,7 +192,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.ClearFetchedHints.StoreAvailable"
-    enum="BooleanAvailable" expires_after="M117">
+    enum="BooleanAvailable" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -226,7 +226,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintCache.HintType.Loaded"
-    enum="HintCacheStoreEntryType" expires_after="M117">
+    enum="HintCacheStoreEntryType" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -258,7 +258,7 @@
 
 <histogram
     name="OptimizationGuide.HintsFetcher.GetHintsRequest.ActiveRequestCanceled.{RequestContext}"
-    units="counts" expires_after="M117">
+    units="counts" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -270,7 +270,7 @@
 
 <histogram
     name="OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedHosts.{RequestContext}"
-    units="counts" expires_after="M117">
+    units="counts" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -283,7 +283,7 @@
 
 <histogram
     name="OptimizationGuide.HintsFetcher.GetHintsRequest.DroppedUrls.{RequestContext}"
-    units="counts" expires_after="M117">
+    units="counts" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -296,7 +296,7 @@
 
 <histogram
     name="OptimizationGuide.HintsFetcher.GetHintsRequest.FetchLatency.{RequestContext}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -318,7 +318,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintsFetcher.GetHintsRequest.HostCount"
-    units="total host count" expires_after="M117">
+    units="total host count" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -358,7 +358,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.HintsFetcher.RequestStatus.{RequestContext}"
-    enum="OptimizationGuideHintsFetcherRequestStatus" expires_after="M117">
+    enum="OptimizationGuideHintsFetcherRequestStatus" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -406,7 +406,7 @@
 
 <histogram
     name="OptimizationGuide.HintsManager.PageNavigationHintsReturnedBeforeDataFlushed"
-    enum="BooleanStored" expires_after="M117">
+    enum="BooleanStored" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -430,7 +430,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.IsPredictionModelValid.{OptimizationTarget}"
-    enum="BooleanValid" expires_after="M117">
+    enum="BooleanValid" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -442,7 +442,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.MetadataFetchValidation.Result"
-    enum="Boolean" expires_after="M117">
+    enum="Boolean" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -453,7 +453,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.DidTimeout.{OptimizationTarget}"
-    enum="Boolean" expires_after="M117">
+    enum="Boolean" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -466,7 +466,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.ExecutionLatency.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -480,7 +480,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.ExecutionStatus.{OptimizationTarget}"
-    enum="OptimizationGuideExecutionStatus" expires_after="M117">
+    enum="OptimizationGuideExecutionStatus" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -494,7 +494,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.ExecutionThreadTime.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -510,7 +510,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.ExecutionThreadTimeMicroseconds.{OptimizationTarget}"
-    units="microseconds" expires_after="M117">
+    units="microseconds" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -524,7 +524,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.ModelAvailableToLoad.{OptimizationTarget}"
-    enum="BooleanAvailable" expires_after="M117">
+    enum="BooleanAvailable" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -541,7 +541,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.ModelLoadingDuration2.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -554,7 +554,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.TaskExecutionLatency.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -572,7 +572,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.TaskSchedulingLatency.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -586,7 +586,7 @@
 
 <histogram
     name="OptimizationGuide.ModelExecutor.TimeSincePreviousRun.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -598,7 +598,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.ModelFilesVerified.{OptimizationTarget}"
-    enum="BooleanValid" expires_after="M117">
+    enum="BooleanValid" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -613,7 +613,7 @@
 
 <histogram
     name="OptimizationGuide.ModelHandler.HandlerCreated.{OptimizationTarget}"
-    enum="BooleanCreated" expires_after="M117">
+    enum="BooleanCreated" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -626,7 +626,7 @@
 
 <histogram
     name="OptimizationGuide.ModelHandler.HandlerCreatedToModelAvailable.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -638,7 +638,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.OptimizationFilterStatus.{OptimizationType}"
-    enum="OptimizationGuideOptimizationFilterStatus" expires_after="M117">
+    enum="OptimizationGuideOptimizationFilterStatus" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -650,7 +650,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.OptimizationHintsComponent.MajorVersion"
-    units="major version number" expires_after="M117">
+    units="major version number" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -674,7 +674,7 @@
 
 <histogram
     name="OptimizationGuide.PageContentAnnotations.BatchRequestedSize.{AnnotationType}"
-    units="counts" expires_after="M117">
+    units="counts" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -686,7 +686,7 @@
 
 <histogram
     name="OptimizationGuide.PageContentAnnotations.BatchSuccess.{AnnotationType}"
-    enum="BooleanSuccess" expires_after="M117">
+    enum="BooleanSuccess" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -724,7 +724,7 @@
 
 <histogram
     name="OptimizationGuide.PageContentAnnotations.JobExecutionTime.{AnnotationType}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -736,7 +736,7 @@
 
 <histogram
     name="OptimizationGuide.PageContentAnnotations.JobScheduleTime.{AnnotationType}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -771,7 +771,7 @@
 
 <histogram
     name="OptimizationGuide.PageContentAnnotationsService.ContentAnnotated"
-    enum="BooleanAnnotated" expires_after="M117">
+    enum="BooleanAnnotated" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -801,7 +801,7 @@
 
 <histogram
     name="OptimizationGuide.PageContentAnnotationsService.ContentAnnotationsStorageMinMagnitudeForVisitNotFound.{PageContentAnnotationsStorageType}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <obsolete>
     Obsolete as of 04/2023.
   </obsolete>
@@ -835,7 +835,7 @@
 <histogram
     name="OptimizationGuide.PageContentAnnotationsService.ContentAnnotationsStorageStatus.{PageContentAnnotationsStorageType}"
     enum="OptimizationGuidePageContentAnnotationsStorageStatus"
-    expires_after="M117">
+    expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -984,7 +984,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PageTextDump.AbandonedRequests"
-    units="count" expires_after="M117">
+    units="count" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -996,7 +996,7 @@
 
 <histogram
     name="OptimizationGuide.PageTextDump.FrameDumpLength.{PageTextDumpEvent}"
-    units="bytes" expires_after="M117">
+    units="bytes" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1009,7 +1009,7 @@
 
 <histogram
     name="OptimizationGuide.PageTextDump.OutstandingRequests.DidFinishLoad"
-    units="count" expires_after="M117">
+    units="count" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1019,7 +1019,7 @@
 
 <histogram
     name="OptimizationGuide.PageTextDump.TimeUntilFrameDisconnected.{PageTextDumpEvent}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1032,7 +1032,7 @@
 
 <histogram
     name="OptimizationGuide.PageTextDump.TimeUntilFrameDumpCompleted.{PageTextDumpEvent}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1061,7 +1061,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PageTopicsOverrideList.GotFile"
-    enum="Boolean" expires_after="M117">
+    enum="Boolean" expires_after="M121">
   <obsolete>
     Removed in M115.
   </obsolete>
@@ -1099,7 +1099,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionManager.FirstModelFetchSinceServiceInit"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>rajendrant@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1125,7 +1125,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionManager.IsDownloadUrlValid.{OptimizationTarget}"
-    enum="BooleanValid" expires_after="M117">
+    enum="BooleanValid" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -1137,7 +1137,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionManager.ModelAvailableAtRegistration.{OptimizationTarget}"
-    enum="BooleanAvailable" expires_after="M117">
+    enum="BooleanAvailable" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -1151,7 +1151,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionManager.ModelDeliveryEvents.{OptimizationTarget}"
-    enum="OptimizationGuideModelDeliveryEvent" expires_after="M117">
+    enum="OptimizationGuideModelDeliveryEvent" expires_after="M121">
   <owner>rajendrant@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1181,7 +1181,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionManager.RegistrationTimeSinceServiceInit.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>rajendrant@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1195,7 +1195,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelDownloadManager.DownloadStartLatency.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>rajendrant@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1232,7 +1232,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelDownloadManager.ReplaceFileError.{OptimizationTarget}"
-    enum="PlatformFileError" expires_after="M117">
+    enum="PlatformFileError" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1246,7 +1246,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelDownloadManager.State.{OptimizationTarget}"
-    enum="OptimizationGuidePredictionModelDownloadState" expires_after="M117">
+    enum="OptimizationGuidePredictionModelDownloadState" expires_after="M121">
   <owner>rajendrant@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1257,7 +1257,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PredictionModelExpired.{OptimizationTarget}"
-    enum="BooleanExpired" expires_after="M117">
+    enum="BooleanExpired" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1271,7 +1271,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelExpiredVersion.{OptimizationTarget}"
-    units="version number" expires_after="M117">
+    units="version number" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1299,7 +1299,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelFetcher.GetModelsResponse.NetErrorCode.{OptimizationTarget}"
-    enum="NetErrorCodes" expires_after="M117">
+    enum="NetErrorCodes" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1329,7 +1329,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelFetcher.GetModelsResponse.Status.{OptimizationTarget}"
-    enum="HttpResponseCode" expires_after="M117">
+    enum="HttpResponseCode" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1345,7 +1345,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelLoadedVersion.{OptimizationTarget}"
-    units="version number" expires_after="M117">
+    units="version number" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1372,7 +1372,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelStore.ModelCount.{OptimizationTarget}"
-    units="counts" expires_after="M117">
+    units="counts" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>rajendrant@chromium.org</owner>
   <summary>
@@ -1395,7 +1395,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelStore.TotalDirectorySize.{OptimizationTarget}"
-    units="MB" expires_after="M117">
+    units="MB" expires_after="M121">
   <owner>sophiechang@chromium.org</owner>
   <owner>rajendrant@chromium.org</owner>
   <summary>
@@ -1407,7 +1407,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelUpdateVersion.{OptimizationTarget}"
-    units="version number" expires_after="M117">
+    units="version number" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1419,7 +1419,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelValidationLatency.{OptimizationTarget}"
-    units="ms" expires_after="M117">
+    units="ms" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1443,7 +1443,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.ProcessingComponentAtShutdown"
-    enum="BooleanYesNo" expires_after="M117">
+    enum="BooleanYesNo" expires_after="M121">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1453,7 +1453,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PushNotifications.CachedNotificationCount"
-    units="count" expires_after="M117">
+    units="count" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1465,7 +1465,7 @@
 
 <histogram
     name="OptimizationGuide.PushNotifications.CachedNotificationsHandledSuccessfully"
-    enum="BooleanSuccess" expires_after="M117">
+    enum="BooleanSuccess" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1475,7 +1475,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PushNotifications.DidOverflow"
-    enum="Boolean" expires_after="M117">
+    enum="Boolean" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1485,7 +1485,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PushNotifications.GotPushNotification"
-    enum="Boolean" expires_after="M117">
+    enum="Boolean" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>Records a true sample for every incoming push notification.</summary>
@@ -1493,7 +1493,7 @@
 
 <histogram
     name="OptimizationGuide.PushNotifications.PushNotificationHandledSuccessfully"
-    enum="BooleanSuccess" expires_after="M117">
+    enum="BooleanSuccess" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1503,7 +1503,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PushNotifications.ReadCacheResult"
-    enum="OptimizationGuideReadCacheResult" expires_after="M117">
+    enum="OptimizationGuideReadCacheResult" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1514,7 +1514,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PushNotifications.ReceivedNotificationType"
-    enum="OptimizationType" expires_after="M117">
+    enum="OptimizationType" expires_after="M121">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml
index 85cfb93..c71f098 100644
--- a/tools/metrics/histograms/metadata/permissions/histograms.xml
+++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -1486,55 +1486,6 @@
   </summary>
 </histogram>
 
-<histogram name="WebsiteSettings.Discoverability.Action"
-    enum="WebsiteSettingsDiscoverabilityAction" expires_after="2023-06-18">
-  <owner>eokoyomon@chromium.org</owner>
-  <owner>dullweber@chromium.org</owner>
-  <summary>
-    Tracks actions related to page info when discoverability has been activated.
-  </summary>
-</histogram>
-
-<histogram name="WebsiteSettings.Discoverability.TimeToClickHighlight"
-    units="ms" expires_after="M98">
-  <owner>eokoyomon@chromium.org</owner>
-  <owner>dullweber@chromium.org</owner>
-  <summary>
-    Records the amount of time between when the page info is opened and the
-    highlighted permissions section is clicked.
-  </summary>
-</histogram>
-
-<histogram name="WebsiteSettings.Discoverability.TimeToClickHighlightStoreInfo"
-    units="ms" expires_after="2022-12-01">
-  <owner>zhiyuancai@chromium.org</owner>
-  <owner>chrome-shopping@google.com</owner>
-  <summary>
-    Records the amount of time between when the page info is opened and the
-    highlighted store info section is clicked.
-  </summary>
-</histogram>
-
-<histogram name="WebsiteSettings.Discoverability.TimeToOpen" units="ms"
-    expires_after="2023-02-19">
-  <owner>eokoyomon@chromium.org</owner>
-  <owner>dullweber@chromium.org</owner>
-  <summary>
-    Records the amount of time the discoverability icon is shown in the omnibox
-    before the user clicks into page info.
-  </summary>
-</histogram>
-
-<histogram name="WebsiteSettings.Discoverability.TimeToOpenFromStoreIcon"
-    units="ms" expires_after="2022-12-01">
-  <owner>zhiyuancai@chromium.org</owner>
-  <owner>chrome-shopping@google.com</owner>
-  <summary>
-    Records the amount of time the store icon is shown in the omnibox before the
-    user clicks into page info.
-  </summary>
-</histogram>
-
 <histogram name="WebsiteSettings.GetAllSitesLoadTime" units="ms"
     expires_after="2023-10-22">
   <owner>olesiamarukhno@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index 1465d3f..2ed2c18 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -1160,6 +1160,26 @@
   </token>
 </histogram>
 
+<histogram name="Tabs.Active.AbsolutePosition" units="index"
+    expires_after="2023-12-31">
+  <owner>emshack@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <summary>
+    When a tab is activated, records the index of that tab in the tab strip,
+    starting at 1 (1-indexed).
+  </summary>
+</histogram>
+
+<histogram name="Tabs.Active.RelativePosition" units="%"
+    expires_after="2023-12-31">
+  <owner>emshack@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <summary>
+    When a tab is activated, records the index of that tab in the tab strip as a
+    percentage of the total number of tabs.
+  </summary>
+</histogram>
+
 <histogram name="Tabs.ActiveCountAtStartup" units="tabs"
     expires_after="2023-10-01">
   <owner>alionadangla@chromium.org</owner>
@@ -1352,6 +1372,25 @@
   </summary>
 </histogram>
 
+<histogram name="Tabs.PageLoad.TimeSinceActive" units="ms"
+    expires_after="2023-12-31">
+  <owner>emshack@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <summary>
+    On page load, records the time since last active for each tab in the tab
+    strip.
+  </summary>
+</histogram>
+
+<histogram name="Tabs.PageLoad.TimeSinceCreated" units="ms"
+    expires_after="2023-12-31">
+  <owner>emshack@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <summary>
+    On page load, records the time since creation for each tab in the tab strip.
+  </summary>
+</histogram>
+
 <histogram name="Tabs.PersistedTabData.Critical.Map.Success"
     enum="BooleanSuccess" expires_after="2024-01-08">
   <owner>davidjm@chromium.org</owner>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 6214062..cea5d5a 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -176,6 +176,65 @@
   </metric>
 </event>
 
+<event name="Accessibility.ReadAnything">
+  <owner>abigailbklein@google.com</owner>
+  <summary>
+    Records the time taken to distill a web page.
+  </summary>
+  <metric name="MergedDistillationTime.Failure">
+    <summary>
+      The amount of time (ms) required to distill a web page with the merged
+      algorithm if distillation failed.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+  <metric name="MergedDistillationTime.Success">
+    <summary>
+      The amount of time (ms) required to distill a web page with the merged
+      algorithm if distillation succeeded.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+  <metric name="RulesDistillationTime.Failure">
+    <summary>
+      The amount of time (ms) required to distill a web page with the rules
+      based algorithm if distillation failed.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+  <metric name="RulesDistillationTime.Success">
+    <summary>
+      The amount of time (ms) required to distill a web page with the rules
+      based algorithm if distillation succeeded.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <quantiles type="std-percentiles"/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+</event>
+
 <event name="Accessibility.Renderer">
   <owner>aleventhal@chromium.org</owner>
   <owner>nektar@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 124c69c..7ddccb2 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@
         },
         "win": {
             "hash": "7fc90812791d74914cbeb83830922c679e81110a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/957e728e6dc3cd807cbbb25fbd0a9fc739d72603/trace_processor_shell.exe"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/bcbe569710dbd97b0d7d9e95d87e93be7d1f8065/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "336a42cb9ec3c417e13a97816271fec10cdf67e5",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v34.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "1fb5ba5a9d703de86025743e3cb7222fe73e028e",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/bcbe569710dbd97b0d7d9e95d87e93be7d1f8065/trace_processor_shell"
+            "hash": "0eadb7dcd4b7a7845660666a66c756b4e00e14bb",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/55c4abdb8736d8618e0d6c1c0c6a7df5a41559a0/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 6aa9eb6..15616ccd 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -195,7 +195,7 @@
  <item id="reporting" added_in_milestone="62" content_hash_code="077eedd0" os_list="linux,windows,chromeos,android" file_path="net/reporting/reporting_uploader.cc" />
  <item id="rlz_ping" added_in_milestone="63" content_hash_code="06160e82" os_list="windows,chromeos" file_path="rlz/lib/financial_ping.cc" />
  <item id="safe_browsing_binary_upload_app" added_in_milestone="87" content_hash_code="03da20b9" os_list="linux,windows,chromeos" file_path="chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc" />
- <item id="safe_browsing_binary_upload_connector" added_in_milestone="87" content_hash_code="031b9017" os_list="linux,windows,chromeos" file_path="chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc" />
+ <item id="safe_browsing_binary_upload_connector" added_in_milestone="87" content_hash_code="03c0c06a" os_list="linux,windows,chromeos" file_path="chrome/browser/safe_browsing/cloud_content_scanning/cloud_binary_upload_service.cc" />
  <item id="safe_browsing_cache_collector" added_in_milestone="62" content_hash_code="03b69135" os_list="linux,windows,chromeos,android" file_path="components/safe_browsing/content/browser/threat_details_cache.cc" />
  <item id="safe_browsing_certificate_error_reporting" added_in_milestone="62" content_hash_code="018e6226" os_list="linux,windows,chromeos,android" file_path="chrome/browser/ssl/certificate_error_reporter.cc" />
  <item id="safe_browsing_client_side_phishing_detector" added_in_milestone="62" content_hash_code="018611e2" os_list="linux,windows,chromeos,android" file_path="components/safe_browsing/content/browser/client_side_detection_service.cc" />
diff --git a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
index 9356415..e653b69 100644
--- a/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
+++ b/ui/accelerated_widget_mac/ca_layer_tree_unittest_mac.mm
@@ -5,6 +5,7 @@
 #import <AVFoundation/AVFoundation.h>
 #include <memory>
 
+#include "base/test/scoped_feature_list.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
@@ -193,8 +194,8 @@
                 gfx::Rect([content_layer bounds]));
       EXPECT_EQ(kCALayerLeftEdge, [content_layer edgeAntialiasingMask]);
       EXPECT_EQ(properties.opacity, [content_layer opacity]);
-      EXPECT_NSEQ(kCAFilterLinear, [content_layer minificationFilter]);
-      EXPECT_NSEQ(kCAFilterLinear, [content_layer magnificationFilter]);
+      EXPECT_NSEQ(kCAFilterNearest, [content_layer minificationFilter]);
+      EXPECT_NSEQ(kCAFilterNearest, [content_layer magnificationFilter]);
       EXPECT_EQ(properties.scale_factor, [content_layer contentsScale]);
     }
 
@@ -758,6 +759,9 @@
 
 // Test updating each layer's properties.
 TEST_F(CALayerTreeTest, AVLayer) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures({ui::kFullscreenLowPowerBackdropMac}, {});
+
   CALayerProperties properties;
   properties.io_surface =
       CreateScopedIOSurface(gfx::Size(256, 256), gfx::BufferFormat::BGRA_8888);
@@ -862,6 +866,9 @@
 
 // Ensure that blocklisting AVSampleBufferDisplayLayer works.
 TEST_F(CALayerTreeTest, AVLayerBlocklist) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures({ui::kFullscreenLowPowerBackdropMac}, {});
+
   CALayerProperties properties;
   properties.io_surface = CreateScopedIOSurface(
       gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR);
@@ -919,6 +926,9 @@
 
 // Test fullscreen low power detection.
 TEST_F(CALayerTreeTest, FullscreenLowPower) {
+  base::test::ScopedFeatureList features;
+  features.InitWithFeatures({ui::kFullscreenLowPowerBackdropMac}, {});
+
   CALayerProperties properties;
   properties.io_surface = CreateScopedIOSurface(
       gfx::Size(256, 256), gfx::BufferFormat::YUV_420_BIPLANAR);
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
index 4dfdcb81..88b043a 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
@@ -13,6 +13,7 @@
 #include <memory>
 
 #include "base/containers/flat_map.h"
+#include "base/feature_list.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/memory/raw_ptr.h"
@@ -32,6 +33,10 @@
 
 namespace ui {
 
+ACCELERATED_WIDGET_MAC_EXPORT BASE_DECLARE_FEATURE(
+    kFullscreenLowPowerBackdropMac);
+ACCELERATED_WIDGET_MAC_EXPORT BASE_DECLARE_FEATURE(kCALayerTreeOptimization);
+
 struct CARendererLayerParams;
 
 enum class CALayerType {
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index bef1a3c9..59876c5 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -12,7 +12,6 @@
 #include <utility>
 
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_functions.h"
@@ -29,6 +28,17 @@
 
 namespace ui {
 
+// Transitioning between AVSampleBufferDisplayLayer and CALayer with IOSurface
+// contents can cause flickering.
+// https://crbug.com/1441762
+BASE_FEATURE(kFullscreenLowPowerBackdropMac,
+             "FullscreenLowPowerBackdropMac",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+BASE_FEATURE(kCALayerTreeOptimization,
+             "CALayerTreeOptimization",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 namespace {
 
 class ComparatorSkColor4f {
@@ -38,10 +48,6 @@
   }
 };
 
-BASE_FEATURE(kCALayerTreeOptimization,
-             "CALayerTreeOptimization",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 void RecordIOSurfaceHistograms(
     int changed_io_surfaces_during_commit,
     int unchanged_io_surfaces_during_commit,
@@ -606,6 +612,10 @@
 }
 
 bool CARendererLayerTree::RootLayer::WantsFullscreenLowPowerBackdrop() const {
+  if (!base::FeatureList::IsEnabled(kFullscreenLowPowerBackdropMac)) {
+    return false;
+  }
+
   bool found_video_layer = false;
   for (auto& clip_layer : clip_and_sorting_layers_) {
     for (auto& transform_layer : clip_layer.transform_layers_) {
diff --git a/ui/events/platform/wayland/wayland_event_watcher_glib.cc b/ui/events/platform/wayland/wayland_event_watcher_glib.cc
index a0275a3..278ac3d2f 100644
--- a/ui/events/platform/wayland/wayland_event_watcher_glib.cc
+++ b/ui/events/platform/wayland/wayland_event_watcher_glib.cc
@@ -20,8 +20,8 @@
 struct GLibWaylandSource : public GSource {
   // Note: The GLibWaylandSource is created and destroyed by GLib. So its
   // constructor/destructor may or may not get called.
-  raw_ptr<WaylandEventWatcherGlib> event_watcher;
-  raw_ptr<GPollFD> poll_fd;
+  raw_ptr<WaylandEventWatcherGlib, LeakedDanglingUntriaged> event_watcher;
+  raw_ptr<GPollFD, LeakedDanglingUntriaged> poll_fd;
 };
 
 gboolean WatchSourcePrepare(GSource* source, gint* timeout_ms) {
diff --git a/ui/events/platform/x11/x11_event_watcher_glib.cc b/ui/events/platform/x11/x11_event_watcher_glib.cc
index b0411f50..4b325566 100644
--- a/ui/events/platform/x11/x11_event_watcher_glib.cc
+++ b/ui/events/platform/x11/x11_event_watcher_glib.cc
@@ -15,8 +15,8 @@
 struct GLibX11Source : public GSource {
   // Note: The GLibX11Source is created and destroyed by GLib. So its
   // constructor/destructor may or may not get called.
-  raw_ptr<x11::Connection> connection;
-  raw_ptr<GPollFD> poll_fd;
+  raw_ptr<x11::Connection, LeakedDanglingUntriaged> connection;
+  raw_ptr<GPollFD, LeakedDanglingUntriaged> poll_fd;
 };
 
 gboolean XSourcePrepare(GSource* source, gint* timeout_ms) {
diff --git a/ui/views/controls/menu/menu_controller.cc b/ui/views/controls/menu/menu_controller.cc
index c7a50f5..e014534 100644
--- a/ui/views/controls/menu/menu_controller.cc
+++ b/ui/views/controls/menu/menu_controller.cc
@@ -916,6 +916,7 @@
                  SELECTION_OPEN_SUBMENU | SELECTION_UPDATE_IMMEDIATELY);
   }
   SendMouseCaptureLostToActiveView();
+  MaybeForwardToAnnotation(source, event);
 }
 
 void MenuController::OnMouseMoved(SubmenuView* source,
@@ -957,6 +958,8 @@
   // which may reset the current hot tracked button.
   if (new_hot_tracked_button)
     SetHotTrackedButton(new_hot_tracked_button);
+
+  MaybeForwardToAnnotation(source, event);
 }
 
 void MenuController::OnMouseEntered(SubmenuView* source,
@@ -1062,9 +1065,16 @@
   if (event->type() == ui::ET_TOUCH_PRESSED) {
     MenuPart part = GetMenuPart(source, event->location());
     if (part.type == MenuPartType::kNone) {
+      if (MaybeForwardToAnnotation(source, *event)) {
+        event->SetHandled();
+        return;
+      }
+
       RepostEventAndCancel(source, event);
       event->SetHandled();
     }
+  } else {
+    MaybeForwardToAnnotation(source, *event);
   }
 }
 
@@ -1542,6 +1552,11 @@
   if (part.type == MenuPartType::kNone ||
       (part.type == MenuPartType::kMenuItem && part.menu &&
        part.menu->GetRootMenuItem() != state_.item->GetRootMenuItem())) {
+    // See if this event was located within a menu annotation.
+    if (MaybeForwardToAnnotation(source, *event)) {
+      return;
+    }
+
     // Remember the time stamp of the current (press down) event. The owner can
     // then use this to figure out if this menu was finished with the same click
     // which is sent to it thereafter.
@@ -3492,6 +3507,30 @@
   }
 }
 
+MenuController::AnnotationCallbackHandle MenuController::SetAnnotationCallback(
+    AnnotationCallback callback) {
+  // TODO(dfried): change this so multiple annotations can be supported.
+  // This check avoids a potential race/UAF situation.
+  CHECK(!annotation_callback_)
+      << "Only one annotation callback allowed at a time.";
+  return AnnotationCallbackHandle(
+      AsWeakPtr(), &MenuController::annotation_callback_, callback);
+}
+
+bool MenuController::MaybeForwardToAnnotation(SubmenuView* source,
+                                              const ui::LocatedEvent& event) {
+  if (!annotation_callback_) {
+    return false;
+  }
+
+  const std::unique_ptr<ui::Event> cloned = event.Clone();
+  auto* located = static_cast<ui::LocatedEvent*>(cloned.get());
+  const gfx::Point screen_loc = View::ConvertPointToScreen(
+      source->GetScrollViewContainer(), event.location());
+  located->set_root_location(screen_loc);
+  return annotation_callback_.Run(*located);
+}
+
 bool MenuController::CanProcessInputEvents() const {
 #if BUILDFLAG(IS_MAC)
   return !menu_closure_animation_;
diff --git a/ui/views/controls/menu/menu_controller.h b/ui/views/controls/menu/menu_controller.h
index fa91e570..1e6a8dc 100644
--- a/ui/views/controls/menu/menu_controller.h
+++ b/ui/views/controls/menu/menu_controller.h
@@ -14,7 +14,9 @@
 #include <vector>
 
 #include "base/containers/flat_set.h"
+#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_auto_reset.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -99,6 +101,25 @@
     kReadonly,
   };
 
+  // Callback that is used to pass events to an "annotation" bubble or widget,
+  // such as a help bubble, that floats alongside the menu and acts as part of
+  // the menu for event-handling purposes. These require special handling
+  // because menus never actually become active, and activating any other widget
+  // causes the menu to close, so this handler should in most cases not activate
+  // the annotation widget or otherwise close the menu.
+  //
+  // Not all events will be forwarded, but mouse clicks, taps, and hover/mouse
+  // move events will be. `event.root_location()` will be the screen coordinates
+  // of the event; `event.location()` is relative to the menu and can safely be
+  // ignored.
+  //
+  // Returns true if `event` is handled by the annotation and should not be
+  // processed by the menu (except for purposes of e.g. hot-tracking).
+  using AnnotationCallback =
+      base::RepeatingCallback<bool(const ui::LocatedEvent& event)>;
+  using AnnotationCallbackHandle =
+      base::WeakAutoReset<MenuController, AnnotationCallback>;
+
   // If a menu is currently active, this returns the controller for it.
   static MenuController* GetActiveInstance();
 
@@ -262,6 +283,11 @@
   // Sets the customized rounded corners of the context menu.
   void SetMenuRoundedCorners(absl::optional<gfx::RoundedCornersF> corners);
 
+  // Sets the annotation event handler. The handle should be discarded when the
+  // calling code no longer wants to intercept events for the annotation. It is
+  // safe to discard the handle after the menu controller has been destroyed.
+  AnnotationCallbackHandle SetAnnotationCallback(AnnotationCallback callback);
+
  private:
   friend class internal::MenuRunnerImpl;
   friend class test::MenuControllerTest;
@@ -604,6 +630,11 @@
                                   const gfx::Point& item_loc,
                                   ui::OwnedWindowAnchor* anchor);
 
+  // Possibly forwards the specified `event` to an annotation callback, if one
+  // is present, and returns the result (default false if no callback is set).
+  bool MaybeForwardToAnnotation(SubmenuView* source,
+                                const ui::LocatedEvent& event);
+
   // The active instance.
   static MenuController* active_instance_;
 
@@ -767,6 +798,10 @@
 
   // The rounded corners of the context menu.
   absl::optional<gfx::RoundedCornersF> rounded_corners_ = absl::nullopt;
+
+  // The current annotation callback. Set if there is a menu annotation; see
+  // `AnnotationCallback` for more information.
+  AnnotationCallback annotation_callback_;
 };
 
 }  // namespace views
diff --git a/ui/views/controls/webview/webview.h b/ui/views/controls/webview/webview.h
index 2c69bd3f..2b7d528 100644
--- a/ui/views/controls/webview/webview.h
+++ b/ui/views/controls/webview/webview.h
@@ -201,7 +201,7 @@
   // Set to true when |holder_| is letterboxed (scaled to be smaller than this
   // view, to preserve its aspect ratio).
   bool is_letterboxing_ = false;
-  raw_ptr<content::BrowserContext> browser_context_;
+  raw_ptr<content::BrowserContext, LeakedDanglingUntriaged> browser_context_;
   bool allow_accelerators_ = false;
   raw_ptr<View, DanglingUntriaged> crashed_overlay_view_ = nullptr;
   bool is_primary_web_contents_for_window_ = false;
diff --git a/url/BUILD.gn b/url/BUILD.gn
index 8751cf3..63c5ebc 100644
--- a/url/BUILD.gn
+++ b/url/BUILD.gn
@@ -10,6 +10,7 @@
 import("//build/config/cronet/config.gni")
 
 if (is_android || is_robolectric) {
+  import("//build/config/android/jni.gni")
   import("//build/config/android/rules.gni")
 }
 
@@ -393,7 +394,8 @@
 if (is_robolectric) {
   # Use this in robolectric_binary() targets if you just need GURL and //base
   # functionality. Otherwise, define a custom shared_library().
-  shared_library("libgurl_robolectric") {
+  shared_library_with_jni("libgurl_robolectric") {
+    testonly = true
     sources = [ "android/robolectric_test_main.cc" ]
     deps = [
       "//base",
@@ -402,5 +404,7 @@
 
     # Make jni.h available.
     configs += [ "//third_party/jdk" ]
+
+    java_targets = [ "//chrome/android:chrome_junit_tests" ]
   }
 }
diff --git a/weblayer/browser/autofill_client_impl.cc b/weblayer/browser/autofill_client_impl.cc
index 0faeef2..d4847f1 100644
--- a/weblayer/browser/autofill_client_impl.cc
+++ b/weblayer/browser/autofill_client_impl.cc
@@ -10,6 +10,7 @@
 #include "components/android_autofill/browser/android_autofill_manager.h"
 #include "components/autofill/core/browser/autofill_download_manager.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "components/autofill/core/browser/ui/suggestion.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/navigation_entry.h"
@@ -361,7 +362,7 @@
   return false;
 }
 
-void AutofillClientImpl::ExecuteCommand(autofill::Suggestion::FrontendId id) {
+void AutofillClientImpl::ExecuteCommand(autofill::PopupItemId popup_item_id) {
   NOTREACHED();
 }
 
diff --git a/weblayer/browser/autofill_client_impl.h b/weblayer/browser/autofill_client_impl.h
index e4764f5d..1126eec8 100644
--- a/weblayer/browser/autofill_client_impl.h
+++ b/weblayer/browser/autofill_client_impl.h
@@ -8,6 +8,7 @@
 #include "base/compiler_specific.h"
 #include "build/build_config.h"
 #include "components/autofill/content/browser/content_autofill_client.h"
+#include "components/autofill/core/browser/ui/popup_item_ids.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
 
@@ -153,7 +154,7 @@
   void DidFillOrPreviewField(const std::u16string& autofilled_value,
                              const std::u16string& profile_full_name) override;
   bool IsContextSecure() const override;
-  void ExecuteCommand(autofill::Suggestion::FrontendId id) override;
+  void ExecuteCommand(autofill::PopupItemId popup_item_id) override;
   void OpenPromoCodeOfferDetailsURL(const GURL& url) override;
   autofill::FormInteractionsFlowId GetCurrentFormInteractionsFlowId() override;