diff --git a/DEPS b/DEPS
index c3a5ea31..9099928 100644
--- a/DEPS
+++ b/DEPS
@@ -303,15 +303,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '1161451d903b47647c31aa0b463623ff58071b56',
+  'skia_revision': '50ed51d9e5294acc90db06d904bb5f3a89ca1a75',
   # 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': '1610539c8018d743b4aeb2a24a12aa5a0190159f',
+  'v8_revision': 'f37982889df3b07bfea01fd35d891b9452cb791b',
   # 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': '99eb1ceb5895d57a8859447411f4ccf68c2038d9',
+  'angle_revision': '84e3b8f1bb235392502e2316f0045b941cd62c37',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -319,7 +319,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'c31ed43bee772633c28f7688eba2d6786f1a6034',
+  'pdfium_revision': 'c440145ffc24384f7260c7c8315fbabcf7163746',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -419,7 +419,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'a78978efe0b237a00be1d5879598cafa5d7bd4b4',
+  'dawn_revision': '1753adf84ae4320ae7aa6c921d4825cffe4888ef',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -499,7 +499,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.
-  'libcxxabi_revision':    '6cc4c9c768689b90b0fdc8c6c3c186589e6798d1',
+  'libcxxabi_revision':    'f2a7f2987f9dcdf8b04c2d8cd4dcb186641a7c3e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1495,7 +1495,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '72d9d8f0bcf477b826e8abd3986b2b24eff275ff',
+    '26de4f3045d4ffc355f172a228fce0a245976f37',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1524,7 +1524,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '91a227c283a463f49d0b55474f9686b67aa2d9da',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'a801760a5838d0ad87a1ca107efc3d447f8b867f',
       'condition': 'checkout_ios',
   },
 
@@ -1654,7 +1654,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'G89DVlKhY4pOqWg7hiMtXu9y20yZdUbM5VCmQ0YNMWwC',
+          'version': 'yNvqwu9a-clNE5SfE-D-4kpkCtqHrGy-TTZcDnupiBMC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -2272,7 +2272,7 @@
     Var('chromium_git') + '/external/libaddressinput.git' + '@' + '2610f7b1043d6784ada41392fc9392d1ea09ea07',
 
   'src/third_party/libaom/source/libaom':
-    Var('aomedia_git') + '/aom.git' + '@' +  '1983dea2323c761a09555791a81421a24d73cfe1',
+    Var('aomedia_git') + '/aom.git' + '@' +  'a23a4799ec2d7dd6e436c7b64a34553773014ed7',
 
   'src/third_party/crabbyavif/src':
     Var('chromium_git') + '/external/github.com/webmproject/CrabbyAvif.git' + '@' + Var('crabbyavif_revision'),
@@ -2535,7 +2535,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'b9acf7ff3553f8cf7545dc5e2bfb1b1e1a4a7081',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '451625d8a0a35cdcd34a9932d6d0def867cef936',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -3025,7 +3025,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_app/app',
-        'version': 'jU4QG3Inor_UzXvRcJLZ4fjvMTl4GrXu1j1eYqEfxiEC',
+        'version': 'MV97eC8eAlUGa1KiAs_lK0A-kwY4z-mof6w8txxsXDEC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4623,7 +4623,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '7d0a3c2435fe79279ba2a404cecb24a8b6015e4a',
+        'ec81c2567f8bfb29d84641673c86dbe24a335d94',
       'condition': 'checkout_src_internal',
   },
 
@@ -4689,7 +4689,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '09a455234a0af038336d80a2f116dc4602e49a5c',
+        '5077d2b6c74926b472eddb9c2cc581f485b75654',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 8ec325f4..8f064f5 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -683,6 +683,14 @@
     'bottombar': {
       'filepath': 'chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/',
     },
+    'bounce_tracking_mitigations': {
+      'filepath': 'chrome/browser/btm/' \
+                  '|content/browser/btm/' \
+                  '|content/public/common/btm_.*' \
+                  '|content/public/browser/btm_.*' \
+                  '|content/public/test/btm_.*' \
+                  '|content/test/data/btm/'
+    },
     'bound_session_credentials': {
       'filepath': 'chrome/renderer/bound_session_credentials/' \
                   '|chrome/browser/signin/bound_session_credentials/'
@@ -1106,9 +1114,6 @@
     'diagnostics_ui': {
       'filepath': 'ash/webui/diagnostics_ui/',
     },
-    'dips': {
-      'filepath': 'chrome/browser/dips/',
-    },
     'discardable_memory': {
         'filepath': 'components/discardable_memory/',
     },
@@ -2751,6 +2756,12 @@
     'borealis': ['borealis-reviews+watch@google.com'],
     'bottombar': ['donnd+watch@chromium.org',
                   'mdjones+watch@chromium.org'],
+    'bounce_tracking_mitigations': ['amaliev+watch-btm@chromium.org',
+                                    'jdh+watch-btm@chromium.org',
+                                    'liu+watch-btm@chromium.org',
+                                    'njeunje+watch-btm@chromium.org',
+                                    'ortuno+watch-btm@chromium.org',
+                                    'svend+watch-btm@chromium.org'],
     'bound_session_credentials': ['drubery+watch@chromium.org'],
     'browser_components': ['browser-components-watch@chromium.org'],
     'browser_compositor': ['vollick@chromium.org'],
@@ -2913,11 +2924,6 @@
                        'gavindodd+diagnostics-watch@google.com',
                        'ashleydp+diagnostics-watch@google.com',
                        'zhangwenyu+diagnostics-watch@google.com'],
-    'dips': ['jdh+watch-dips@chromium.org',
-             'njeunje+watch-dips@chromium.org',
-             'amaliev+watch-dips@chromium.org',
-             'rtarpine+watch-dips@chromium.org',
-             'wanderview+watch-dips@chromium.org'],
     'discardable_memory': ['thiabaud+watch-discardable-memory@google.com'],
     'disk_cache': ['gavinp+disk@chromium.org'],
     'display': ['zhangwenyu+watch@google.com'],
diff --git a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 882fcda..718e090 100644
--- a/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -8980,9 +8980,7 @@
     getter displayHeight
     getter displayWidth
     getter duration
-    getter flip
     getter format
-    getter rotation
     getter timestamp
     getter visibleRect
     method allocationSize
diff --git a/apps/launcher.cc b/apps/launcher.cc
index 0b169611..34e398d 100644
--- a/apps/launcher.cc
+++ b/apps/launcher.cc
@@ -381,7 +381,7 @@
     bool in_kiosk_mode = false;
 #if BUILDFLAG(IS_CHROMEOS)
     user_manager::UserManager* user_manager = user_manager::UserManager::Get();
-    in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
+    in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskChromeApp();
 #endif
     if (!in_kiosk_mode) {
       NOTREACHED() << "App with 'kiosk_only' attribute must be run in "
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index e9fc738..7457f9e4 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -6584,6 +6584,9 @@
       <message name="IDS_ASH_SUNFISH_CAPTURE_LABEL" desc="The capture label message which shows in the middle of the screen when in sunfish session." translateable="false">
         Drag to select an area to search
       </message>
+      <message name="IDS_ASH_SUNFISH_CAPTURE_LABEL_KEYBOARD_NAVIGATION" desc="The capture label message which shows in the middle of the screen when in sunfish session. Includes keyboard navigation instructions.">
+        Drag or press Space to select an area to search
+      </message>
       <message name="IDS_ASH_SCREEN_CAPTURE_LABEL_WINDOW_IMAGE_CAPTURE" desc="The capture label message which shows in the middle of the screen when in window image capture mode in tablet mode.">
         Select a window to capture
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_SUNFISH_CAPTURE_LABEL_KEYBOARD_NAVIGATION.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SUNFISH_CAPTURE_LABEL_KEYBOARD_NAVIGATION.png.sha1
new file mode 100644
index 0000000..3803c13
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_ASH_SUNFISH_CAPTURE_LABEL_KEYBOARD_NAVIGATION.png.sha1
@@ -0,0 +1 @@
+b4ef97cd844cd1eacda47e69de8f447402e94abf
\ No newline at end of file
diff --git a/ash/capture_mode/capture_label_view.cc b/ash/capture_mode/capture_label_view.cc
index 640d049..54e0ff42 100644
--- a/ash/capture_mode/capture_label_view.cc
+++ b/ash/capture_mode/capture_label_view.cc
@@ -265,7 +265,13 @@
   label_->SetVisible(label_visibility);
   if (label_visibility && (label_->GetText() != text)) {
     label_->SetText(text);
-    capture_mode_util::TriggerAccessibilityAlertSoon(base::UTF16ToUTF8(text));
+
+    // Don't trigger an accessibility alert if we are in a Sunfish session, as
+    // the sunfish label text differs from the session open alert.
+    if (!capture_mode_session_->active_behavior()
+             ->ShouldPaintSunfishCaptureRegion()) {
+      capture_mode_util::TriggerAccessibilityAlertSoon(base::UTF16ToUTF8(text));
+    }
   }
 }
 
diff --git a/ash/capture_mode/capture_mode_behavior.cc b/ash/capture_mode/capture_mode_behavior.cc
index abdd1841..3ff5d5c5 100644
--- a/ash/capture_mode/capture_mode_behavior.cc
+++ b/ash/capture_mode/capture_mode_behavior.cc
@@ -395,7 +395,8 @@
     return true;
   }
   const std::u16string GetCaptureLabelRegionText() const override {
-    return l10n_util::GetStringUTF16(IDS_ASH_SUNFISH_CAPTURE_LABEL);
+    return l10n_util::GetStringUTF16(
+        IDS_ASH_SUNFISH_CAPTURE_LABEL_KEYBOARD_NAVIGATION);
   }
   const std::u16string GetActionButtonContainerTitle() const override {
     return l10n_util::GetStringUTF16(
diff --git a/ash/capture_mode/sunfish_unittest.cc b/ash/capture_mode/sunfish_unittest.cc
index 8c75b21f..6683c2e 100644
--- a/ash/capture_mode/sunfish_unittest.cc
+++ b/ash/capture_mode/sunfish_unittest.cc
@@ -770,7 +770,8 @@
   // select a capture region phase.
   EXPECT_FALSE(capture_button->GetVisible());
   EXPECT_TRUE(capture_label->GetVisible());
-  EXPECT_EQ(u"Drag to select an area to search", capture_label->GetText());
+  EXPECT_EQ(u"Drag or press Space to select an area to search",
+            capture_label->GetText());
 
   // Tests it can drag and select a region.
   auto* event_generator = GetEventGenerator();
@@ -868,7 +869,8 @@
   CaptureModeSessionTestApi test_api(controller->capture_mode_session());
   auto* capture_label = test_api.GetCaptureLabelInternalView();
   EXPECT_TRUE(capture_label->GetVisible());
-  EXPECT_EQ(u"Drag to select an area to search", capture_label->GetText());
+  EXPECT_EQ(u"Drag or press Space to select an area to search",
+            capture_label->GetText());
 }
 
 // Tests the sunfish capture mode bar view.
@@ -1131,7 +1133,8 @@
   auto* capture_label = test_api.GetCaptureLabelInternalView();
   EXPECT_FALSE(capture_button->GetVisible());
   EXPECT_TRUE(capture_label->GetVisible());
-  EXPECT_EQ(u"Drag to select an area to search", capture_label->GetText());
+  EXPECT_EQ(u"Drag or press Space to select an area to search",
+            capture_label->GetText());
 
   // Test we can select a region and show the search results panel.
   SelectCaptureModeRegion(GetEventGenerator(), gfx::Rect(100, 100, 600, 500),
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 68e6d91b..e843c4b 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -282,6 +282,12 @@
              "BocaClientTypeForSpeechRecognition",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+// Enables or disables using a specific app name for speech recognition for Boca
+// on ChromeOS.
+BASE_FEATURE(kBocaAdjustCaptionBubbleOnExpand,
+             "BocaAdjustCaptionBubbleOnExpand",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Enables or disables enforcing sequential execution for Boca Session load.
 BASE_FEATURE(kBocaSequentialSessionLoad,
              "BocaSequentialSessionLoad",
@@ -3497,6 +3503,10 @@
   return base::FeatureList::IsEnabled(kBocaClientTypeForSpeechRecognition);
 }
 
+bool IsBocaAdjustCaptionBubbleOnExpandEnabled() {
+  return base::FeatureList::IsEnabled(kBocaAdjustCaptionBubbleOnExpand);
+}
+
 bool IsBocaSequentialSessionLoadEnabled() {
   return base::FeatureList::IsEnabled(kBocaSequentialSessionLoad);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index ce99ee75..9918002 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -101,6 +101,8 @@
 BASE_DECLARE_FEATURE(kBocaSequentialSessionLoad);
 COMPONENT_EXPORT(ASH_CONSTANTS)
 BASE_DECLARE_FEATURE(kBocaClientTypeForSpeechRecognition);
+COMPONENT_EXPORT(ASH_CONSTANTS)
+BASE_DECLARE_FEATURE(kBocaAdjustCaptionBubbleOnExpand);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCameraSuperResSupported);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCrosSwitcher);
 COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kBorealisBigGl);
@@ -1095,6 +1097,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsBocaClientTypeForSpeechRecognitionEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS)
+bool IsBocaAdjustCaptionBubbleOnExpandEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS)
 bool IsBocaSequentialSessionLoadEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsBrightnessControlInSettingsEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsCaptureModeEducationEnabled();
diff --git a/ash/display/screen_ash.cc b/ash/display/screen_ash.cc
index 230aa2d3..1e321b8b 100644
--- a/ash/display/screen_ash.cc
+++ b/ash/display/screen_ash.cc
@@ -152,13 +152,14 @@
     return GetPrimaryDisplay();
 
   const aura::Window* root_window = window->GetRootWindow();
-  if (!root_window)
+  if (!root_window || root_window->is_destroying()) {
     return GetPrimaryDisplay();
+  }
   const RootWindowSettings* rws = GetRootWindowSettings(root_window);
   CHECK(rws) << "Missing RootWindowSettings : window=" << window->GetName()
              << ", root=" << root_window->GetName();
   int64_t id = rws->display_id;
-  // if id is |kInvaildDisplayID|, it's being deleted.
+  // if id is |kInvalidDisplayId|, it's being deleted.
   if (id == display::kInvalidDisplayId)
     return GetPrimaryDisplay();
 
diff --git a/ash/glanceables/common/glanceables_time_management_bubble_view.cc b/ash/glanceables/common/glanceables_time_management_bubble_view.cc
index 834b012..947c5a2 100644
--- a/ash/glanceables/common/glanceables_time_management_bubble_view.cc
+++ b/ash/glanceables/common/glanceables_time_management_bubble_view.cc
@@ -135,7 +135,8 @@
 
 GlanceablesTimeManagementBubbleView::GlanceablesTimeManagementBubbleView(
     InitParams params)
-    : context_(params.context),
+    : views::AnimationDelegateViews(this),
+      context_(params.context),
       combobox_model_(std::move(params.combobox_model)) {
   GetViewAccessibility().SetRole(ax::mojom::Role::kGroup);
 
diff --git a/ash/glanceables/common/glanceables_time_management_bubble_view.h b/ash/glanceables/common/glanceables_time_management_bubble_view.h
index 339e797..ba982d2 100644
--- a/ash/glanceables/common/glanceables_time_management_bubble_view.h
+++ b/ash/glanceables/common/glanceables_time_management_bubble_view.h
@@ -14,6 +14,7 @@
 #include "base/functional/callback_forward.h"
 #include "ui/base/models/combobox_model.h"
 #include "ui/compositor/compositor_metrics_tracker.h"
+#include "ui/views/animation/animation_delegate_views.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/layout/flex_layout_view.h"
 #include "ui/views/view.h"
@@ -33,7 +34,7 @@
 // `GlanceableTrayChildBubble`.
 class ASH_EXPORT GlanceablesTimeManagementBubbleView
     : public views::FlexLayoutView,
-      public gfx::AnimationDelegate {
+      public views::AnimationDelegateViews {
   METADATA_HEADER(GlanceablesTimeManagementBubbleView, views::FlexLayoutView)
 
  public:
@@ -82,7 +83,7 @@
   gfx::Size CalculatePreferredSize(
       const views::SizeBounds& available_size) const override;
 
-  // gfx::AnimationDelegate:
+  // views::AnimationDelegateViews:
   void AnimationEnded(const gfx::Animation* animation) override;
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationCanceled(const gfx::Animation* animation) override;
diff --git a/ash/metrics/demo_session_metrics_recorder.h b/ash/metrics/demo_session_metrics_recorder.h
index 6934e039..05a5dce 100644
--- a/ash/metrics/demo_session_metrics_recorder.h
+++ b/ash/metrics/demo_session_metrics_recorder.h
@@ -115,7 +115,13 @@
             // policy not connected.
     kEmptyDMToken = 7,   // The DM Token on the device is empty.
     kEmptyClientID = 8,  // The Client ID on the device is empty.
-    kMaxValue = kEmptyClientID,
+
+    // TODO(crbugs.com/355727308): update xml enum.
+    kQuotaExhaustedRetriable =
+        9,  // Server quota exhausted, might be max QPS reached.
+    kQuotaExhaustedNotRetriable =
+        10,  // Server quota exhausted, device might be blocked.
+    kMaxValue = kQuotaExhaustedNotRetriable,
   };
 
   // Types of the current demo session.
diff --git a/ash/webui/boca_ui/DEPS b/ash/webui/boca_ui/DEPS
index e977148..f9d6a20 100644
--- a/ash/webui/boca_ui/DEPS
+++ b/ash/webui/boca_ui/DEPS
@@ -12,6 +12,7 @@
   "+components/content_settings/core/browser",
   "+components/content_settings/core/common",
   "+components/permissions/permission_decision_auto_blocker.h",
+  "+components/session_manager/core",
   "+components/sessions/core/session_id.h",
   "+components/signin/public/identity_manager",
   "+components/signin/public/base"
diff --git a/ash/webui/boca_ui/boca_app_page_handler.cc b/ash/webui/boca_ui/boca_app_page_handler.cc
index 4596c71..ba1327e 100644
--- a/ash/webui/boca_ui/boca_app_page_handler.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler.cc
@@ -24,7 +24,9 @@
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
 #include "base/logging.h"
+#include "base/sequence_checker.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "chromeos/ash/components/boca/boca_app_client.h"
@@ -273,6 +275,7 @@
 }
 
 BocaAppHandler::~BocaAppHandler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (is_producer_ &&
       producer_current_session_caption_config_->session_caption_enabled) {
     ::boca::CaptionsConfig caption_config;
@@ -691,6 +694,8 @@
 void BocaAppHandler::OnSpeechRecognitionInstallStateUpdated(
     mojom::SpeechRecognitionInstallState) {}
 
+void BocaAppHandler::OnSessionCaptionDisabled(bool is_error) {}
+
 void BocaAppHandler::OnSessionStarted(const std::string& session_id,
                                       const ::boca::UserIdentity& producer) {
   ResetProducerSessionCaptionConfig();
@@ -723,6 +728,7 @@
 }
 
 void BocaAppHandler::OnLocalCaptionClosed() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   remote_->OnLocalCaptionDisabled();
 }
 
@@ -730,6 +736,26 @@
   remote_->OnSpeechRecognitionInstallStateUpdated(GetMojomSodaState(status));
 }
 
+void BocaAppHandler::OnSessionCaptionClosed(bool is_error) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (!is_producer_) {
+    LOG(ERROR) << "Session caption closed called on consumer.";
+    return;
+  }
+  auto* session = GetSessionManager()->GetCurrentSession();
+  if (!session || session->session_state() != ::boca::Session::ACTIVE) {
+    return;
+  }
+  remote_->OnSessionCaptionDisabled(is_error);
+  producer_current_session_caption_config_->session_caption_enabled = false;
+  // Fire and forget captions update request, we don't need to handle the
+  // response and the session captions will be disabled locally either way.
+  UpdateCaptionConfigInternal(session->session_id(),
+                              producer_current_session_caption_config_->Clone(),
+                              /*callback=*/base::DoNothing(),
+                              /*can_proceed=*/true);
+}
+
 void BocaAppHandler::NotifyLocalCaptionConfigUpdate(
     mojom::CaptionConfigPtr config) {
   ::boca::CaptionsConfig local_caption_config;
diff --git a/ash/webui/boca_ui/boca_app_page_handler.h b/ash/webui/boca_ui/boca_app_page_handler.h
index e4e70ec..e3c1e84 100644
--- a/ash/webui/boca_ui/boca_app_page_handler.h
+++ b/ash/webui/boca_ui/boca_app_page_handler.h
@@ -119,6 +119,7 @@
   void OnLocalCaptionDisabled() override;
   void OnSpeechRecognitionInstallStateUpdated(
       mojom::SpeechRecognitionInstallState state) override;
+  void OnSessionCaptionDisabled(bool is_error) override;
 
   // BocaSessionManager::Observer
   void OnConsumerActivityUpdated(
@@ -136,6 +137,7 @@
   void OnSessionRosterUpdated(const ::boca::Roster& roster) override;
   void OnLocalCaptionClosed() override;
   void OnSodaStatusUpdate(BocaSessionManager::SodaStatus status) override;
+  void OnSessionCaptionClosed(bool is_error) override;
 
   void NotifyLocalCaptionConfigUpdate(mojom::CaptionConfigPtr config);
 
diff --git a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
index 427f55cc..3b186db 100644
--- a/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
+++ b/ash/webui/boca_ui/boca_app_page_handler_unittest.cc
@@ -48,6 +48,7 @@
 #include "components/content_settings/core/browser/content_settings_policy_provider.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/session_manager/core/session_manager.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/user_manager/fake_user_manager.h"
@@ -326,6 +327,11 @@
   void OnSpeechRecognitionInstallStateUpdated(
       mojom::SpeechRecognitionInstallState state) override {}
 
+  void SetSessionCaptionDisabledInterceptorCallback(
+      base::OnceCallback<void(bool)> session_caption_disabled_cb) {
+    session_caption_disabled_cb_ = std::move(session_caption_disabled_cb);
+  }
+
  private:
   // mojom::Page:
   void OnStudentActivityUpdated(
@@ -346,10 +352,16 @@
       std::move(local_caption_disabled_cb_).Run();
     }
   }
+  void OnSessionCaptionDisabled(bool is_error) override {
+    if (session_caption_disabled_cb_) {
+      std::move(session_caption_disabled_cb_).Run(is_error);
+    }
+  }
 
   ActivityInterceptorCallback student_activity_updated_cb_;
   SessionConfigInterceptorCallback session_config_updated_cb_;
   base::OnceClosure local_caption_disabled_cb_;
+  base::OnceCallback<void(bool)> session_caption_disabled_cb_;
 
   const mojo::Receiver<mojom::Page> receiver_;
 };
@@ -507,6 +519,7 @@
   content::BrowserTaskEnvironment task_environment_;
   sync_preferences::TestingPrefServiceSyncable pref_service_;
   TestingPrefServiceSimple local_state_;
+  session_manager::SessionManager device_session_manager_;
 
   user_manager::TypedScopedUserManager<user_manager::FakeUserManager>
       fake_user_manager_;
@@ -2348,6 +2361,122 @@
   EXPECT_TRUE(future.Wait());
 }
 
+TEST_F(BocaAppPageHandlerTest, NotifyWhenSessionCaptionClosedRequestSucceed) {
+  ::boca::CaptionsConfig request_captions_config;
+  ::boca::CaptionsConfig notify_captions_config;
+  ::boca::Session session =
+      GetCommonActiveSessionProto({.captions_enabled = true});
+  EXPECT_CALL(*session_manager(), GetCurrentSession)
+      .WillRepeatedly(Return(&session));
+  EXPECT_CALL(*session_manager(), UpdateCurrentSession).Times(1);
+  EXPECT_CALL(*session_client_impl(), UpdateSession)
+      .WillOnce([&request_captions_config](
+                    std::unique_ptr<UpdateSessionRequest> request) {
+        std::unique_ptr<::boca::Session> result =
+            std::make_unique<::boca::Session>(
+                GetCommonActiveSessionProto({.captions_enabled = false}));
+        request_captions_config = *request->captions_config();
+        request->callback().Run(std::move(result));
+      });
+  EXPECT_CALL(*session_manager(), NotifySessionCaptionProducerEvents)
+      .WillOnce([&notify_captions_config](
+                    const ::boca::CaptionsConfig& captions_config) {
+        notify_captions_config = captions_config;
+      });
+  base::test::TestFuture<bool> future;
+  fake_page()->SetSessionCaptionDisabledInterceptorCallback(
+      future.GetCallback());
+  boca_app_handler()->OnSessionCaptionClosed(/*is_error=*/false);
+
+  EXPECT_FALSE(request_captions_config.captions_enabled());
+  EXPECT_FALSE(notify_captions_config.captions_enabled());
+  EXPECT_FALSE(future.Get());
+}
+
+TEST_F(BocaAppPageHandlerTest, NotifyWhenSessionCaptionClosedRequestFailed) {
+  ::boca::CaptionsConfig request_captions_config;
+  ::boca::CaptionsConfig notify_captions_config;
+  ::boca::Session session =
+      GetCommonActiveSessionProto({.captions_enabled = true});
+  EXPECT_CALL(*session_manager(), GetCurrentSession)
+      .WillRepeatedly(Return(&session));
+  EXPECT_CALL(*session_manager(), UpdateCurrentSession).Times(0);
+  EXPECT_CALL(*session_client_impl(), UpdateSession)
+      .WillOnce([&request_captions_config](
+                    std::unique_ptr<UpdateSessionRequest> request) {
+        std::unique_ptr<::boca::Session> result =
+            std::make_unique<::boca::Session>(
+                GetCommonActiveSessionProto({.captions_enabled = false}));
+        request_captions_config = *request->captions_config();
+        request->callback().Run(
+            base::unexpected(google_apis::ApiErrorCode::HTTP_BAD_REQUEST));
+      });
+  EXPECT_CALL(*session_manager(), NotifySessionCaptionProducerEvents)
+      .WillOnce([&notify_captions_config](
+                    const ::boca::CaptionsConfig& captions_config) {
+        notify_captions_config = captions_config;
+      });
+  base::test::TestFuture<bool> future;
+  fake_page()->SetSessionCaptionDisabledInterceptorCallback(
+      future.GetCallback());
+  boca_app_handler()->OnSessionCaptionClosed(/*is_error=*/true);
+
+  EXPECT_FALSE(request_captions_config.captions_enabled());
+  EXPECT_FALSE(notify_captions_config.captions_enabled());
+  EXPECT_TRUE(future.Get());
+}
+
+TEST_F(BocaAppPageHandlerTest,
+       DoesNotNotifyWhenSessionCaptionClosedForConsumer) {
+  CreateBocaAppHandler(/*is_producer=*/false);
+  ::boca::Session session =
+      GetCommonActiveSessionProto({.captions_enabled = true});
+  EXPECT_CALL(*session_manager(), GetCurrentSession)
+      .WillRepeatedly(Return(&session));
+  EXPECT_CALL(*session_manager(), UpdateCurrentSession).Times(0);
+  EXPECT_CALL(*session_client_impl(), UpdateSession).Times(0);
+  EXPECT_CALL(*session_manager(), NotifySessionCaptionProducerEvents).Times(0);
+  base::test::TestFuture<bool> future;
+  fake_page()->SetSessionCaptionDisabledInterceptorCallback(
+      future.GetCallback());
+  boca_app_handler()->OnSessionCaptionClosed(/*is_error=*/true);
+
+  EXPECT_FALSE(future.IsReady());
+}
+
+TEST_F(BocaAppPageHandlerTest,
+       DoesNotNotifyWhenSessionCaptionClosedIfNullSession) {
+  EXPECT_CALL(*session_manager(), GetCurrentSession)
+      .WillRepeatedly(Return(nullptr));
+  EXPECT_CALL(*session_manager(), UpdateCurrentSession).Times(0);
+  EXPECT_CALL(*session_client_impl(), UpdateSession).Times(0);
+  EXPECT_CALL(*session_manager(), NotifySessionCaptionProducerEvents).Times(0);
+  base::test::TestFuture<bool> future;
+  fake_page()->SetSessionCaptionDisabledInterceptorCallback(
+      future.GetCallback());
+  boca_app_handler()->OnSessionCaptionClosed(/*is_error=*/true);
+
+  EXPECT_FALSE(future.IsReady());
+}
+
+TEST_F(BocaAppPageHandlerTest,
+       DoesNotNotifyWhenSessionCaptionClosedIfSessionInactive) {
+  ::boca::Session session =
+      GetCommonActiveSessionProto({.captions_enabled = true});
+  session.set_session_state(::boca::Session::PAST);
+  EXPECT_CALL(*session_manager(), GetCurrentSession)
+      .WillRepeatedly(Return(&session));
+  EXPECT_CALL(*session_manager(), UpdateCurrentSession).Times(0);
+  EXPECT_CALL(*session_client_impl(), UpdateSession).Times(0);
+  EXPECT_CALL(*session_manager(), NotifySessionCaptionProducerEvents).Times(0);
+  base::test::TestFuture<bool> future;
+  fake_page()->SetSessionCaptionDisabledInterceptorCallback(
+      future.GetCallback());
+  boca_app_handler()->OnSessionCaptionClosed(/*is_error=*/true);
+
+  EXPECT_FALSE(future.IsReady());
+}
+
 TEST_F(BocaAppPageHandlerTest, ProducerCaptionsOverrideGetSessionCaptions) {
   ::boca::Session response_session = GetCommonActiveSessionProto(
       {.captions_enabled = true, .translations_enabled = true});
diff --git a/ash/webui/boca_ui/mojom/boca.mojom b/ash/webui/boca_ui/mojom/boca.mojom
index 86066c3..86140966 100644
--- a/ash/webui/boca_ui/mojom/boca.mojom
+++ b/ash/webui/boca_ui/mojom/boca.mojom
@@ -385,6 +385,8 @@
   OnLocalCaptionDisabled();
   // Notifies when Soda Installation has succeeded or failed.
   OnSpeechRecognitionInstallStateUpdated(SpeechRecognitionInstallState state);
+  // Notifies app when session captions is turned off from chrome.
+  OnSessionCaptionDisabled(bool is_error);
 };
 
 // Implemented in browser process to set up the communication between the Page
diff --git a/ash/webui/boca_ui/resources/app/boca_app.ts b/ash/webui/boca_ui/resources/app/boca_app.ts
index 639abbe9..097910e 100644
--- a/ash/webui/boca_ui/resources/app/boca_app.ts
+++ b/ash/webui/boca_ui/resources/app/boca_app.ts
@@ -446,4 +446,10 @@
    */
   onSpeechRecognitionInstallStateUpdated(state: SpeechRecognitionInstallState):
       void;
+
+  /**
+   * Notify the app that the session captions has been turned off in chrome.
+   * This can be due to an error or because of an event such as device locked.
+   */
+  onSessionCaptionDisabled(isError: boolean): void;
 }
diff --git a/ash/webui/boca_ui/resources/app/receiver.ts b/ash/webui/boca_ui/resources/app/receiver.ts
index 6d32dd2..76c2a18 100644
--- a/ash/webui/boca_ui/resources/app/receiver.ts
+++ b/ash/webui/boca_ui/resources/app/receiver.ts
@@ -40,10 +40,14 @@
 
   callbackRouter.onLocalCaptionDisabled.addListener(
       () => app.onLocalCaptionDisabled());
+
   callbackRouter.onSpeechRecognitionInstallStateUpdated.addListener(
       (state: SpeechRecognitionInstallState) =>
           app.onSpeechRecognitionInstallStateUpdated(
               getSpeechRecognitionInstallStateMojomToUI(state)));
+
+  callbackRouter.onSessionCaptionDisabled.addListener(
+      (isError: boolean) => app.onSessionCaptionDisabled(isError));
 }
 
 /**
diff --git a/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.ts b/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.ts
index ea73a2f..c5308131 100644
--- a/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.ts
+++ b/ash/webui/demo_mode_app_ui/resources/demo_mode_metrics_service.ts
@@ -84,6 +84,22 @@
   // CBX:
   HELP_ME_READ = 'HelpMeRead',
   LIVE_TRANSLATE = 'LiveTranslate',
+
+  // New in 2025 Refresh
+  // CB Generic:
+  QUICK_INSERT = 'QuickInsert',
+  GEMINI_PWA = 'GeminiPWA',
+  SELECT_TO_SEARCH = 'SelectToSearch',
+  GEMINI_ADVANCE = 'GeminiAdvance',
+  DRIVE_INTEGRATION = 'DriveIntegration',
+  BETTER_TOGETHER = 'BetterTogether',
+  PHONE_HUB = 'PhoneHub',
+  WELCOM_RECAP = 'WelcomeRecap',
+  // CBX:
+  FILE_SYNC = 'FileSync',
+  MAGIC_EDITOR = 'MagicEditor',
+  // Enum shared between CB & CBX are: GEMINI_PWA, SELECT_TO_SEARCH,
+  // QUICK_INSERT, GEMINI_ADVANCE, BETTER_TOGETHER, and PHONE_HUB
 }
 
 /**
diff --git a/base/memory/platform_shared_memory_region.h b/base/memory/platform_shared_memory_region.h
index a5549d4..1491abd 100644
--- a/base/memory/platform_shared_memory_region.h
+++ b/base/memory/platform_shared_memory_region.h
@@ -223,7 +223,27 @@
 #endif
   );
 
-  static bool CheckPlatformHandlePermissionsCorrespondToMode(
+  enum class PermissionModeCheckResult {
+    kOk,
+    kExpectedReadOnlyButNot,
+    kExpectedWritableButNot,
+#if BUILDFLAG(IS_ANDROID)
+    kFailedToGetAshmemRegionProtectionMask,
+#endif
+#if BUILDFLAG(IS_APPLE)
+    kVmMapFailed,
+#endif
+#if BUILDFLAG(IS_FUCHSIA)
+    kNotVmo,
+#endif
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
+    kFcntlFailed,
+    kReadOnlyFdNotReadOnly,
+    kUnexpectedReadOnlyFd,
+#endif
+  };
+  static PermissionModeCheckResult
+  CheckPlatformHandlePermissionsCorrespondToMode(
       PlatformSharedMemoryHandle handle,
       Mode mode,
       size_t size);
diff --git a/base/memory/platform_shared_memory_region_android.cc b/base/memory/platform_shared_memory_region_android.cc
index eb071d4..9696530 100644
--- a/base/memory/platform_shared_memory_region_android.cc
+++ b/base/memory/platform_shared_memory_region_android.cc
@@ -7,6 +7,8 @@
 #include <sys/mman.h>
 
 #include "base/bits.h"
+#include "base/check_op.h"
+#include "base/debug/alias.h"
 #include "base/logging.h"
 #include "base/memory/page_size.h"
 #include "base/memory/shared_memory_tracker.h"
@@ -54,7 +56,10 @@
     return {};
   }
 
-  CHECK(CheckPlatformHandlePermissionsCorrespondToMode(fd.get(), mode, size));
+  PermissionModeCheckResult result =
+      CheckPlatformHandlePermissionsCorrespondToMode(fd.get(), mode, size);
+  base::debug::Alias(&result);
+  CHECK_EQ(PermissionModeCheckResult::kOk, result);
 
   return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid);
 }
@@ -160,27 +165,26 @@
   return PlatformSharedMemoryRegion(std::move(scoped_fd), mode, size, guid);
 }
 
-bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
+PlatformSharedMemoryRegion::PermissionModeCheckResult
+PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
     PlatformSharedMemoryHandle handle,
     Mode mode,
     size_t size) {
   int prot = GetAshmemRegionProtectionMask(handle);
   if (prot < 0) {
-    return false;
+    return PermissionModeCheckResult::kFailedToGetAshmemRegionProtectionMask;
   }
 
   bool is_read_only = (prot & PROT_WRITE) == 0;
   bool expected_read_only = mode == Mode::kReadOnly;
 
   if (is_read_only != expected_read_only) {
-    // TODO(crbug.com/40574272): convert to DLOG when bug fixed.
-    LOG(ERROR) << "Ashmem region has a wrong protection mask: it is"
-               << (is_read_only ? " " : " not ") << "read-only but it should"
-               << (expected_read_only ? " " : " not ") << "be";
-    return false;
+    return expected_read_only
+               ? PermissionModeCheckResult::kExpectedReadOnlyButNot
+               : PermissionModeCheckResult::kExpectedWritableButNot;
   }
 
-  return true;
+  return PermissionModeCheckResult::kOk;
 }
 
 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
diff --git a/base/memory/platform_shared_memory_region_apple.cc b/base/memory/platform_shared_memory_region_apple.cc
index 3bd054a..ce55308 100644
--- a/base/memory/platform_shared_memory_region_apple.cc
+++ b/base/memory/platform_shared_memory_region_apple.cc
@@ -8,6 +8,8 @@
 
 #include "base/apple/mach_logging.h"
 #include "base/apple/scoped_mach_vm.h"
+#include "base/check_op.h"
+#include "base/debug/alias.h"
 
 namespace base::subtle {
 
@@ -29,8 +31,10 @@
     return {};
   }
 
-  CHECK(
-      CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
+  PermissionModeCheckResult result =
+      CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size);
+  base::debug::Alias(&result);
+  CHECK_EQ(PermissionModeCheckResult::kOk, result);
 
   return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
 }
@@ -155,7 +159,8 @@
 }
 
 // static
-bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
+PlatformSharedMemoryRegion::PermissionModeCheckResult
+PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
     PlatformSharedMemoryHandle handle,
     Mode mode,
     size_t size) {
@@ -172,21 +177,19 @@
         << "vm_deallocate";
   } else if (kr != KERN_INVALID_RIGHT) {
     MACH_LOG(ERROR, kr) << "vm_map";
-    return false;
+    return PermissionModeCheckResult::kVmMapFailed;
   }
 
   bool is_read_only = kr == KERN_INVALID_RIGHT;
   bool expected_read_only = mode == Mode::kReadOnly;
 
   if (is_read_only != expected_read_only) {
-    // TODO(crbug.com/40574272): convert to DLOG when bug fixed.
-    LOG(ERROR) << "VM region has a wrong protection mask: it is"
-               << (is_read_only ? " " : " not ") << "read-only but it should"
-               << (expected_read_only ? " " : " not ") << "be";
-    return false;
+    return expected_read_only
+               ? PermissionModeCheckResult::kExpectedReadOnlyButNot
+               : PermissionModeCheckResult::kExpectedWritableButNot;
   }
 
-  return true;
+  return PermissionModeCheckResult::kOk;
 }
 
 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
diff --git a/base/memory/platform_shared_memory_region_fuchsia.cc b/base/memory/platform_shared_memory_region_fuchsia.cc
index daef86f4..63c2578 100644
--- a/base/memory/platform_shared_memory_region_fuchsia.cc
+++ b/base/memory/platform_shared_memory_region_fuchsia.cc
@@ -10,6 +10,7 @@
 
 #include "base/bits.h"
 #include "base/check_op.h"
+#include "base/debug/alias.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/memory/page_size.h"
 
@@ -38,8 +39,11 @@
     return {};
   }
 
-  CHECK(CheckPlatformHandlePermissionsCorrespondToMode(zx::unowned_vmo(handle),
-                                                       mode, size));
+  PermissionModeCheckResult result =
+      CheckPlatformHandlePermissionsCorrespondToMode(zx::unowned_vmo(handle),
+                                                     mode, size);
+  base::debug::Alias(&result);
+  CHECK_EQ(PermissionModeCheckResult::kOk, result);
 
   return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
 }
@@ -143,7 +147,8 @@
 }
 
 // static
-bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
+PlatformSharedMemoryRegion::PermissionModeCheckResult
+PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
     PlatformSharedMemoryHandle handle,
     Mode mode,
     size_t size) {
@@ -155,21 +160,19 @@
   if (basic.type != ZX_OBJ_TYPE_VMO) {
     // TODO(crbug.com/40574272): convert to DLOG when bug fixed.
     LOG(ERROR) << "Received zircon handle is not a VMO";
-    return false;
+    return PermissionModeCheckResult::kNotVmo;
   }
 
   bool is_read_only = (basic.rights & (ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE)) == 0;
   bool expected_read_only = mode == Mode::kReadOnly;
 
   if (is_read_only != expected_read_only) {
-    // TODO(crbug.com/40574272): convert to DLOG when bug fixed.
-    LOG(ERROR) << "VMO object has wrong access rights: it is"
-               << (is_read_only ? " " : " not ") << "read-only but it should"
-               << (expected_read_only ? " " : " not ") << "be";
-    return false;
+    return expected_read_only
+               ? PermissionModeCheckResult::kExpectedReadOnlyButNot
+               : PermissionModeCheckResult::kExpectedWritableButNot;
   }
 
-  return true;
+  return PermissionModeCheckResult::kOk;
 }
 
 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
diff --git a/base/memory/platform_shared_memory_region_posix.cc b/base/memory/platform_shared_memory_region_posix.cc
index 2bdd457..51c14c6 100644
--- a/base/memory/platform_shared_memory_region_posix.cc
+++ b/base/memory/platform_shared_memory_region_posix.cc
@@ -7,6 +7,10 @@
 #include <fcntl.h>
 #include <sys/mman.h>
 
+#include <optional>
+
+#include "base/check_op.h"
+#include "base/debug/alias.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -33,23 +37,25 @@
     ScopedGeneric<const FilePath*, ScopedPathUnlinkerTraits>;
 
 #if !BUILDFLAG(IS_NACL)
-bool CheckFDAccessMode(int fd, int expected_mode) {
+enum class FDAccessModeError {
+  kFcntlFailed,
+  kMismatch,
+};
+
+std::optional<FDAccessModeError> CheckFDAccessMode(int fd, int expected_mode) {
   int fd_status = fcntl(fd, F_GETFL);
   if (fd_status == -1) {
     // TODO(crbug.com/40574272): convert to DLOG when bug fixed.
     PLOG(ERROR) << "fcntl(" << fd << ", F_GETFL) failed";
-    return false;
+    return FDAccessModeError::kFcntlFailed;
   }
 
   int mode = fd_status & O_ACCMODE;
   if (mode != expected_mode) {
-    // TODO(crbug.com/40574272): convert to DLOG when bug fixed.
-    LOG(ERROR) << "Descriptor access mode (" << mode
-               << ") differs from expected (" << expected_mode << ")";
-    return false;
+    return FDAccessModeError::kMismatch;
   }
 
-  return true;
+  return std::nullopt;
 }
 #endif  // !BUILDFLAG(IS_NACL)
 
@@ -85,8 +91,10 @@
     return {};
   }
 
-  CHECK(
-      CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
+  PermissionModeCheckResult result =
+      CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size);
+  base::debug::Alias(&result);
+  CHECK_EQ(PermissionModeCheckResult::kOk, result);
 
   switch (mode) {
     case Mode::kReadOnly:
@@ -275,33 +283,49 @@
 #endif  // !BUILDFLAG(IS_NACL)
 }
 
-bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
+PlatformSharedMemoryRegion::PermissionModeCheckResult
+PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
     PlatformSharedMemoryHandle handle,
     Mode mode,
     size_t size) {
 #if !BUILDFLAG(IS_NACL)
-  if (!CheckFDAccessMode(handle.fd,
-                         mode == Mode::kReadOnly ? O_RDONLY : O_RDWR)) {
-    return false;
+  if (auto result = CheckFDAccessMode(
+          handle.fd, mode == Mode::kReadOnly ? O_RDONLY : O_RDWR);
+      result.has_value()) {
+    switch (*result) {
+      case FDAccessModeError::kFcntlFailed:
+        return PermissionModeCheckResult::kFcntlFailed;
+      case FDAccessModeError::kMismatch:
+        return mode == Mode::kReadOnly
+                   ? PermissionModeCheckResult::kExpectedReadOnlyButNot
+                   : PermissionModeCheckResult::kExpectedWritableButNot;
+    }
   }
 
   if (mode == Mode::kWritable) {
-    return CheckFDAccessMode(handle.readonly_fd, O_RDONLY);
+    if (auto result = CheckFDAccessMode(handle.readonly_fd, O_RDONLY);
+        result.has_value()) {
+      switch (*result) {
+        case FDAccessModeError::kFcntlFailed:
+          return PermissionModeCheckResult::kFcntlFailed;
+        case FDAccessModeError::kMismatch:
+          return PermissionModeCheckResult::kReadOnlyFdNotReadOnly;
+      }
+    }
+    return PermissionModeCheckResult::kOk;
   }
 
   // The second descriptor must be invalid in kReadOnly and kUnsafe modes.
   if (handle.readonly_fd != -1) {
-    // TODO(crbug.com/40574272): convert to DLOG when bug fixed.
-    LOG(ERROR) << "The second descriptor must be invalid";
-    return false;
+    return PermissionModeCheckResult::kUnexpectedReadOnlyFd;
   }
 
-  return true;
+  return PermissionModeCheckResult::kOk;
 #else
   // fcntl(_, F_GETFL) is not implemented on NaCl.
   // We also cannot try to mmap() a region as writable and look at the return
   // status because the plugin process crashes if system mmap() fails.
-  return true;
+  return PermissionModeCheckResult::kOk;
 #endif  // !BUILDFLAG(IS_NACL)
 }
 
diff --git a/base/memory/platform_shared_memory_region_unittest.cc b/base/memory/platform_shared_memory_region_unittest.cc
index 34dc53f..f141d79 100644
--- a/base/memory/platform_shared_memory_region_unittest.cc
+++ b/base/memory/platform_shared_memory_region_unittest.cc
@@ -333,25 +333,31 @@
             region.GetPlatformHandle(), mode, region.GetSize());
   };
 
+  using PermissionModeCheckResult =
+      PlatformSharedMemoryRegion::PermissionModeCheckResult;
   // Check kWritable region.
   PlatformSharedMemoryRegion region =
       PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
   ASSERT_TRUE(region.IsValid());
-  EXPECT_TRUE(check(region, Mode::kWritable));
-  EXPECT_FALSE(check(region, Mode::kReadOnly));
+  EXPECT_EQ(PermissionModeCheckResult::kOk, check(region, Mode::kWritable));
+  EXPECT_EQ(PermissionModeCheckResult::kExpectedReadOnlyButNot,
+            check(region, Mode::kReadOnly));
 
   // Check kReadOnly region.
   ASSERT_TRUE(region.ConvertToReadOnly());
-  EXPECT_TRUE(check(region, Mode::kReadOnly));
-  EXPECT_FALSE(check(region, Mode::kWritable));
-  EXPECT_FALSE(check(region, Mode::kUnsafe));
+  EXPECT_EQ(PermissionModeCheckResult::kOk, check(region, Mode::kReadOnly));
+  EXPECT_EQ(PermissionModeCheckResult::kExpectedWritableButNot,
+            check(region, Mode::kWritable));
+  EXPECT_EQ(PermissionModeCheckResult::kExpectedWritableButNot,
+            check(region, Mode::kUnsafe));
 
   // Check kUnsafe region.
   PlatformSharedMemoryRegion region2 =
       PlatformSharedMemoryRegion::CreateUnsafe(kRegionSize);
   ASSERT_TRUE(region2.IsValid());
-  EXPECT_TRUE(check(region2, Mode::kUnsafe));
-  EXPECT_FALSE(check(region2, Mode::kReadOnly));
+  EXPECT_EQ(PermissionModeCheckResult::kOk, check(region2, Mode::kUnsafe));
+  EXPECT_EQ(PermissionModeCheckResult::kExpectedReadOnlyButNot,
+            check(region2, Mode::kReadOnly));
 }
 
 // Tests that it's impossible to create read-only platform shared memory region.
diff --git a/base/memory/platform_shared_memory_region_win.cc b/base/memory/platform_shared_memory_region_win.cc
index 88132eec..2c660daf 100644
--- a/base/memory/platform_shared_memory_region_win.cc
+++ b/base/memory/platform_shared_memory_region_win.cc
@@ -9,6 +9,9 @@
 #include <stdint.h>
 
 #include "base/bits.h"
+#include "base/check.h"
+#include "base/check_op.h"
+#include "base/debug/alias.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -116,8 +119,10 @@
     return {};
   }
 
-  CHECK(
-      CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
+  PermissionModeCheckResult result =
+      CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size);
+  base::debug::Alias(&result);
+  CHECK_EQ(PermissionModeCheckResult::kOk, result);
 
   return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
 }
@@ -242,7 +247,8 @@
 }
 
 // static
-bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
+PlatformSharedMemoryRegion::PermissionModeCheckResult
+PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
     PlatformSharedMemoryHandle handle,
     Mode mode,
     size_t size) {
@@ -261,13 +267,12 @@
   bool expected_read_only = mode == Mode::kReadOnly;
 
   if (is_read_only != expected_read_only) {
-    DLOG(ERROR) << "File mapping handle has wrong access rights: it is"
-                << (is_read_only ? " " : " not ") << "read-only but it should"
-                << (expected_read_only ? " " : " not ") << "be";
-    return false;
+    return expected_read_only
+               ? PermissionModeCheckResult::kExpectedReadOnlyButNot
+               : PermissionModeCheckResult::kExpectedWritableButNot;
   }
 
-  return true;
+  return PermissionModeCheckResult::kOk;
 }
 
 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
diff --git a/base/metrics/histogram_snapshot_manager.cc b/base/metrics/histogram_snapshot_manager.cc
index 9f77c5d..84a2bcc 100644
--- a/base/metrics/histogram_snapshot_manager.cc
+++ b/base/metrics/histogram_snapshot_manager.cc
@@ -80,7 +80,7 @@
     for (size_t index = 0; index < ranges->size(); ++index) {
       ranges_string += base::StringPrintf("%d ", ranges->range(index));
     }
-    SCOPED_CRASH_KEY_STRING32("PrepareSamples", "ranges", ranges_string);
+    SCOPED_CRASH_KEY_STRING1024("PrepareSamples", "ranges", ranges_string);
 
     // The checksum should have caught this, so crash separately if it didn't.
     CHECK_NE(0U, HistogramBase::RANGE_CHECKSUM_ERROR & corruption);
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionWaiter.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionWaiter.java
index e0cd14b..46eda1c 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionWaiter.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ConditionWaiter.java
@@ -34,7 +34,6 @@
 /** Polls multiple {@link Condition}s in parallel. */
 @NullMarked
 public class ConditionWaiter {
-
     /**
      * The process of waiting for a {@link Condition} to be fulfilled.
      *
@@ -196,6 +195,7 @@
     protected @MonotonicNonNull List<ConditionWait> mWaits;
     protected @MonotonicNonNull Map<Condition, ElementFactory> mConditionsGuardingFactories;
     protected final Map<String, ConditionWait> mExitWaitsByElementId = new HashMap<>();
+    private boolean mPreCheckFulfilledConditions;
 
     ConditionWaiter(Transition transition) {
         mTransition = transition;
@@ -255,6 +255,12 @@
                             + createWaitConditionsSummary(
                                     mWaits, /* generateMainMessage= */ false));
         }
+
+        // If the preCheck already saw all Conditions fulfilled and there is no trigger which might
+        // cause state changes, avoid checking Conditions a second time.
+        if (mTransition.mTrigger == null && !anyCriteriaMissing) {
+            mPreCheckFulfilledConditions = true;
+        }
     }
 
     /**
@@ -266,20 +272,22 @@
     void waitFor() throws TravelException {
         assert isPreCheckDone();
 
-        TransitionOptions options = mTransition.getOptions();
-        long timeoutMs = options.mTimeoutMs != 0 ? options.mTimeoutMs : MAX_TIME_TO_POLL;
-        try {
-            CriteriaHelper.pollInstrumentationThread(
-                    new CheckConditionsOnce(), timeoutMs, POLLING_INTERVAL);
-        } catch (CriteriaHelper.TimeoutException timeoutException) {
-            // Unwrap the TimeoutException and CriteriaNotSatisfiedException parts of the stack to
-            // reduce the error message.
-            if (timeoutException.getCause()
-                    instanceof CriteriaNotSatisfiedException criteriaNotSatisfiedException) {
-                throw TravelException.newTravelException(
-                        criteriaNotSatisfiedException.getMessage());
-            } else {
-                throw timeoutException;
+        if (!mPreCheckFulfilledConditions) {
+            TransitionOptions options = mTransition.getOptions();
+            long timeoutMs = options.mTimeoutMs != 0 ? options.mTimeoutMs : MAX_TIME_TO_POLL;
+            try {
+                CriteriaHelper.pollInstrumentationThread(
+                        new CheckConditionsOnce(), timeoutMs, POLLING_INTERVAL);
+            } catch (CriteriaHelper.TimeoutException timeoutException) {
+                // Unwrap the TimeoutException and CriteriaNotSatisfiedException parts of the stack
+                // to reduce the error message.
+                if (timeoutException.getCause()
+                        instanceof CriteriaNotSatisfiedException criteriaNotSatisfiedException) {
+                    throw TravelException.newTravelException(
+                            criteriaNotSatisfiedException.getMessage());
+                } else {
+                    throw timeoutException;
+                }
             }
         }
 
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java
index 8c143a6..556d842 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java
@@ -123,11 +123,6 @@
         return getPerformTrigger(ViewActions.click());
     }
 
-    @Deprecated
-    public Transition.Trigger clickTrigger() {
-        return getClickTrigger();
-    }
-
     /**
      * Trigger an Espresso click on this View.
      *
diff --git a/base/tracing/perfetto_platform.cc b/base/tracing/perfetto_platform.cc
index 0a3ef838..225f506 100644
--- a/base/tracing/perfetto_platform.cc
+++ b/base/tracing/perfetto_platform.cc
@@ -27,7 +27,6 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner,
     Options options)
     : process_name_prefix_(std::move(options.process_name_prefix)),
-      defer_delayed_tasks_(options.defer_delayed_tasks),
       task_runner_(std::move(task_runner)),
       thread_local_object_([](void* object) {
         delete static_cast<ThreadLocalObject*>(object);
@@ -50,7 +49,7 @@
   // TODO(b/242965112): Add support for the builtin task runner
   DCHECK(!perfetto_task_runner_);
   auto perfetto_task_runner =
-      std::make_unique<PerfettoTaskRunner>(task_runner_, defer_delayed_tasks_);
+      std::make_unique<PerfettoTaskRunner>(task_runner_);
   perfetto_task_runner_ = perfetto_task_runner->GetWeakPtr();
   return perfetto_task_runner;
 }
diff --git a/base/tracing/perfetto_platform.h b/base/tracing/perfetto_platform.h
index b565ea7..5deb154 100644
--- a/base/tracing/perfetto_platform.h
+++ b/base/tracing/perfetto_platform.h
@@ -27,10 +27,6 @@
     // TraceConfig.DataSource.producer_name_filter).
     std::string process_name_prefix = "org.chromium-";
 
-    // Defer delayed tasks to PerfettoTaskRunner until task runner resets after
-    // sandbox entry.
-    bool defer_delayed_tasks = false;
-
     // Work around https://bugs.llvm.org/show_bug.cgi?id=36684
     static Options Default() { return {}; }
   };
@@ -54,7 +50,6 @@
 
  private:
   const std::string process_name_prefix_;
-  const bool defer_delayed_tasks_;
   WeakPtr<PerfettoTaskRunner> perfetto_task_runner_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   ThreadLocalStorage::Slot thread_local_object_;
diff --git a/base/tracing/perfetto_task_runner.cc b/base/tracing/perfetto_task_runner.cc
index 2a22e005..ebd0ad8 100644
--- a/base/tracing/perfetto_task_runner.cc
+++ b/base/tracing/perfetto_task_runner.cc
@@ -22,10 +22,8 @@
 namespace base::tracing {
 
 PerfettoTaskRunner::PerfettoTaskRunner(
-    scoped_refptr<base::SequencedTaskRunner> task_runner,
-    bool defer_delayed_tasks)
-    : task_runner_(std::move(task_runner)),
-      defer_delayed_tasks_(defer_delayed_tasks) {
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
+    : task_runner_(std::move(task_runner)) {
   CHECK(task_runner_);
 }
 
@@ -42,10 +40,6 @@
 
 void PerfettoTaskRunner::PostDelayedTask(std::function<void()> task,
                                          uint32_t delay_ms) {
-  if (defer_delayed_tasks_ && delay_ms) {
-    deferred_delayed_tasks_.emplace_back(task, delay_ms);
-    return;
-  }
   base::ScopedDeferTaskPosting::PostOrDefer(
       task_runner_, FROM_HERE,
       base::BindOnce(
@@ -123,11 +117,6 @@
 void PerfettoTaskRunner::ResetTaskRunner(
     scoped_refptr<base::SequencedTaskRunner> task_runner) {
   task_runner_ = std::move(task_runner);
-  defer_delayed_tasks_ = false;
-  for (auto& task : deferred_delayed_tasks_) {
-    PostDelayedTask(task.task, task.delay);
-  }
-  deferred_delayed_tasks_.clear();
 }
 
 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
@@ -138,12 +127,4 @@
     default;
 #endif  // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
 
-PerfettoTaskRunner::DeferredTask::DeferredTask(std::function<void()> task,
-                                               uint32_t delay)
-    : task(std::move(task)), delay(delay) {}
-
-PerfettoTaskRunner::DeferredTask::DeferredTask(DeferredTask&& task) = default;
-
-PerfettoTaskRunner::DeferredTask::~DeferredTask() = default;
-
 }  // namespace base::tracing
diff --git a/base/tracing/perfetto_task_runner.h b/base/tracing/perfetto_task_runner.h
index 1e539a5..52bfc02 100644
--- a/base/tracing/perfetto_task_runner.h
+++ b/base/tracing/perfetto_task_runner.h
@@ -5,8 +5,6 @@
 #ifndef BASE_TRACING_PERFETTO_TASK_RUNNER_H_
 #define BASE_TRACING_PERFETTO_TASK_RUNNER_H_
 
-#include <vector>
-
 #include "base/base_export.h"
 #include "base/cancelable_callback.h"
 #include "base/memory/weak_ptr.h"
@@ -31,8 +29,7 @@
 // to provide it to Perfetto.
 class BASE_EXPORT PerfettoTaskRunner : public perfetto::base::TaskRunner {
  public:
-  explicit PerfettoTaskRunner(scoped_refptr<base::SequencedTaskRunner>,
-                              bool defer_delayed_tasks = false);
+  explicit PerfettoTaskRunner(scoped_refptr<base::SequencedTaskRunner>);
   ~PerfettoTaskRunner() override;
   PerfettoTaskRunner(const PerfettoTaskRunner&) = delete;
   void operator=(const PerfettoTaskRunner&) = delete;
@@ -61,21 +58,6 @@
  private:
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
-  struct DeferredTask {
-    DeferredTask(std::function<void()> task, uint32_t delay);
-    DeferredTask(const DeferredTask&) = delete;
-    DeferredTask& operator=(const DeferredTask&) = delete;
-    DeferredTask(DeferredTask&& task);
-    ~DeferredTask();
-
-    std::function<void()> task;
-    uint32_t delay;
-  };
-
-  // Delayed tasks will be posted when `task_runner_` resets.
-  std::vector<DeferredTask> deferred_delayed_tasks_;
-  bool defer_delayed_tasks_;
-
 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
   // FDControllerAndCallback keeps track of the state of FD watching:
   // * |controller| has value: FD watching is added. |callback| is nullopt.
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index 27c1549f..27490f8 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -38,7 +38,6 @@
     "java/res/drawable/ic_close_tabs_24dp.xml",
     "java/res/drawable/ic_deselect_all_24dp.xml",
     "java/res/drawable/ic_group_add_16dp.xml",
-    "java/res/drawable/ic_open_new_tab_in_group_24dp.xml",
     "java/res/drawable/ic_rating_star_full.xml",
     "java/res/drawable/ic_rating_star_half.xml",
     "java/res/drawable/ic_rating_star_outline.xml",
diff --git a/chrome/android/features/tab_ui/java/res/drawable/ic_open_new_tab_in_group_24dp.xml b/chrome/android/features/tab_ui/java/res/drawable/ic_open_new_tab_in_group_24dp.xml
deleted file mode 100644
index 2119dc4c..0000000
--- a/chrome/android/features/tab_ui/java/res/drawable/ic_open_new_tab_in_group_24dp.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2024 The Chromium Authors
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:pathData="m13,14h2v-3h3v-2h-3v-3h-2v3h-3v2h3zM8,18c-0.55,0 -1.025,-0.192 -1.425,-0.575 -0.383,-0.4 -0.575,-0.875 -0.575,-1.425v-12c0,-0.55 0.192,-1.017 0.575,-1.4 0.4,-0.4 0.875,-0.6 1.425,-0.6h12c0.55,0 1.017,0.2 1.4,0.6 0.4,0.383 0.6,0.85 0.6,1.4v12c0,0.55 -0.2,1.025 -0.6,1.425 -0.383,0.383 -0.85,0.575 -1.4,0.575zM8,16h12v-12h-12zM4,22c-0.55,0 -1.025,-0.192 -1.425,-0.575 -0.383,-0.4 -0.575,-0.875 -0.575,-1.425v-14h2v14h14v2zM8,4v12z"
-      android:fillColor="@macro/default_icon_color"/>
-</vector>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java
index 0eec917..4d916b7 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediator.java
@@ -67,7 +67,7 @@
                 new TabGroupRowViewTitleData(
                         mTabGroupModelFilter.getTabGroupTitle(rootId),
                         numTabs,
-                        R.string.tab_group_bottom_sheet_row_accessibility_text);
+                        R.plurals.tab_group_bottom_sheet_row_accessibility_text);
         builder.with(TabGroupRowProperties.TITLE_DATA, titleData);
         builder.with(
                 TabGroupRowProperties.ROW_CLICK_RUNNABLE,
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java
index 786fc97..9fd0029 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/LocalTabGroupListBottomSheetRowMediatorUnitTest.java
@@ -93,7 +93,7 @@
         assertEquals(TEST_TITLE, titleData.title);
         assertEquals(1, titleData.numTabs);
         assertEquals(
-                R.string.tab_group_bottom_sheet_row_accessibility_text,
+                R.plurals.tab_group_bottom_sheet_row_accessibility_text,
                 titleData.rowAccessibilityTextResId);
         assertNull(model.get(TabGroupRowProperties.TIMESTAMP_EVENT));
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
index dad3030..ff7c611 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogCoordinator.java
@@ -190,7 +190,7 @@
                         TabUiThemeProvider.getTabGridDialogBackgroundColor(
                                 mDialogView.getContext(), /* isIncognito= */ false);
                 SharedImageTilesConfig config =
-                        new SharedImageTilesConfig.Builder(activity)
+                        SharedImageTilesConfig.Builder.createForButton(activity)
                                 .setBorderColor(backgroundColor)
                                 .build();
                 mSharedImageTilesCoordinator =
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java
index 0e7e7c29..88294a6 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProvider.java
@@ -224,7 +224,7 @@
         if (!shouldShowSharedImageTiles(groupSharedState)) return;
 
         mSharedImageTilesConfigBuilder =
-                SharedImageTilesConfig.Builder.createThumbnail(mContext, mColorId);
+                SharedImageTilesConfig.Builder.createForTabGroupColorContext(mContext, mColorId);
 
         mSharedImageTilesCoordinator =
                 new SharedImageTilesCoordinator(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java
index fdd9c72..3dbba5d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupColorViewProviderUnitTest.java
@@ -291,7 +291,9 @@
         assertEquals(stroke, params.topMargin);
 
         SharedImageTilesConfig config =
-                SharedImageTilesConfig.Builder.createThumbnail(mContext, currentColorId).build();
+                SharedImageTilesConfig.Builder.createForTabGroupColorContext(
+                                mContext, currentColorId)
+                        .build();
         final @Px int size = config.getBorderAndTotalIconSizes(res).second + 2 * stroke;
         assertEquals(size, colorView.getMinimumWidth());
         assertEquals(size, colorView.getMinimumHeight());
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java
index 4b1d086..facb2c2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediator.java
@@ -71,7 +71,7 @@
                 new TabGroupRowViewTitleData(
                         mSavedTabGroup.title,
                         numTabs,
-                        R.string.tab_group_bottom_sheet_row_accessibility_text);
+                        R.plurals.tab_group_bottom_sheet_row_accessibility_text);
         builder.with(TabGroupRowProperties.TITLE_DATA, titleData);
 
         builder.with(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediatorUnitTest.java
index d131ea9..559b86d 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListBottomSheetRowMediatorUnitTest.java
@@ -105,7 +105,7 @@
         assertEquals(TEST_TITLE, titleData.title);
         assertEquals(1, titleData.numTabs);
         assertEquals(
-                R.string.tab_group_bottom_sheet_row_accessibility_text,
+                R.plurals.tab_group_bottom_sheet_row_accessibility_text,
                 titleData.rowAccessibilityTextResId);
         assertNotNull(model.get(TabGroupRowProperties.TIMESTAMP_EVENT));
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
index 72fb3ee..ea8760fa 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListMediatorUnitTest.java
@@ -236,7 +236,8 @@
 
         PropertyModel model = mModelList.get(0).model;
         assertEquals(
-                new TabGroupRowViewTitleData("Title", 1, R.string.tab_group_row_accessibility_text),
+                new TabGroupRowViewTitleData(
+                        "Title", 1, R.plurals.tab_group_row_accessibility_text),
                 model.get(TITLE_DATA));
         assertEquals(TabGroupColorId.BLUE, model.get(COLOR_INDEX));
     }
@@ -260,13 +261,13 @@
 
         PropertyModel barModel = mModelList.get(0).model;
         assertEquals(
-                new TabGroupRowViewTitleData("Bar", 3, R.string.tab_group_row_accessibility_text),
+                new TabGroupRowViewTitleData("Bar", 3, R.plurals.tab_group_row_accessibility_text),
                 barModel.get(TITLE_DATA));
         assertEquals(TabGroupColorId.RED, barModel.get(COLOR_INDEX));
 
         PropertyModel fooModel = mModelList.get(1).model;
         assertEquals(
-                new TabGroupRowViewTitleData("Foo", 2, R.string.tab_group_row_accessibility_text),
+                new TabGroupRowViewTitleData("Foo", 2, R.plurals.tab_group_row_accessibility_text),
                 fooModel.get(TITLE_DATA));
         assertEquals(TabGroupColorId.BLUE, fooModel.get(COLOR_INDEX));
     }
@@ -342,12 +343,12 @@
         PropertyModel model1 = mModelList.get(0).model;
         assertEquals(
                 new TabGroupRowViewTitleData(
-                        "in current", 1, R.string.tab_group_row_accessibility_text),
+                        "in current", 1, R.plurals.tab_group_row_accessibility_text),
                 model1.get(TITLE_DATA));
         PropertyModel model2 = mModelList.get(1).model;
         assertEquals(
                 new TabGroupRowViewTitleData(
-                        "hidden", 1, R.string.tab_group_row_accessibility_text),
+                        "hidden", 1, R.plurals.tab_group_row_accessibility_text),
                 model2.get(TITLE_DATA));
     }
 
@@ -861,13 +862,13 @@
 
         PropertyModel barModel = mModelList.get(1).model;
         assertEquals(
-                new TabGroupRowViewTitleData("Bar", 3, R.string.tab_group_row_accessibility_text),
+                new TabGroupRowViewTitleData("Bar", 3, R.plurals.tab_group_row_accessibility_text),
                 barModel.get(TITLE_DATA));
         assertEquals(TabGroupColorId.RED, barModel.get(COLOR_INDEX));
 
         PropertyModel fooModel = mModelList.get(2).model;
         assertEquals(
-                new TabGroupRowViewTitleData("Foo", 2, R.string.tab_group_row_accessibility_text),
+                new TabGroupRowViewTitleData("Foo", 2, R.plurals.tab_group_row_accessibility_text),
                 fooModel.get(TITLE_DATA));
         assertEquals(TabGroupColorId.BLUE, fooModel.get(COLOR_INDEX));
     }
@@ -909,13 +910,13 @@
 
         PropertyModel barModel = mModelList.get(0).model;
         assertEquals(
-                new TabGroupRowViewTitleData("Bar", 3, R.string.tab_group_row_accessibility_text),
+                new TabGroupRowViewTitleData("Bar", 3, R.plurals.tab_group_row_accessibility_text),
                 barModel.get(TITLE_DATA));
         assertEquals(TabGroupColorId.RED, barModel.get(COLOR_INDEX));
 
         PropertyModel fooModel = mModelList.get(1).model;
         assertEquals(
-                new TabGroupRowViewTitleData("Foo", 2, R.string.tab_group_row_accessibility_text),
+                new TabGroupRowViewTitleData("Foo", 2, R.plurals.tab_group_row_accessibility_text),
                 fooModel.get(TITLE_DATA));
         assertEquals(TabGroupColorId.BLUE, fooModel.get(COLOR_INDEX));
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java
index 0457dd9..41fcb7f0 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowMediator.java
@@ -113,7 +113,7 @@
         String userTitle = savedTabGroup.title;
         TabGroupRowViewTitleData titleData =
                 new TabGroupRowViewTitleData(
-                        userTitle, numberOfTabs, R.string.tab_group_row_accessibility_text);
+                        userTitle, numberOfTabs, R.plurals.tab_group_row_accessibility_text);
         builder.with(TabGroupRowProperties.TITLE_DATA, titleData);
 
         builder.with(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowView.java
index 02cbb939..37071efe 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowView.java
@@ -19,6 +19,7 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.Nullable;
+import androidx.annotation.PluralsRes;
 import androidx.annotation.StringRes;
 
 import org.chromium.chrome.browser.data_sharing.ui.shared_image_tiles.SharedImageTilesView;
@@ -44,7 +45,7 @@
     public static class TabGroupRowViewTitleData {
         public final String title;
         public final int numTabs;
-        public final @StringRes int rowAccessibilityTextResId;
+        public final @PluralsRes int rowAccessibilityTextResId;
 
         /**
          * @param title The title string to display. If empty, a default title will be used.
@@ -53,7 +54,7 @@
          *     describes the row.
          */
         public TabGroupRowViewTitleData(
-                String title, int numTabs, @StringRes int rowAccessibilityTextResId) {
+                String title, int numTabs, @PluralsRes int rowAccessibilityTextResId) {
             this.title = title;
             this.numTabs = numTabs;
             this.rowAccessibilityTextResId = rowAccessibilityTextResId;
@@ -120,7 +121,11 @@
         // Note that the subtitle will also be read for the row, as it just loops over visible text
         // children.
         mTitleTextView.setContentDescription(
-                resources.getString(titleData.rowAccessibilityTextResId, title));
+                resources.getQuantityString(
+                        titleData.rowAccessibilityTextResId,
+                        titleData.numTabs,
+                        title,
+                        titleData.numTabs));
     }
 
     void setTimestampEvent(TabGroupTimeAgo event) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java
index 98087c9..6faebb2 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewRenderTest.java
@@ -149,7 +149,7 @@
                             new TabGroupRowViewTitleData(
                                     "Title",
                                     1,
-                                    R.string.tab_group_bottom_sheet_row_accessibility_text));
+                                    R.plurals.tab_group_bottom_sheet_row_accessibility_text));
                     builder.with(
                             TIMESTAMP_EVENT,
                             new TabGroupTimeAgo(
@@ -174,7 +174,7 @@
                             new TabGroupRowViewTitleData(
                                     "VeryLongTitleThatGetsTruncatedOrSplitOverMultipleLines",
                                     1,
-                                    R.string.tab_group_bottom_sheet_row_accessibility_text));
+                                    R.plurals.tab_group_bottom_sheet_row_accessibility_text));
                     builder.with(
                             TIMESTAMP_EVENT,
                             new TabGroupTimeAgo(
@@ -229,7 +229,7 @@
                             new TabGroupRowViewTitleData(
                                     "A generic title",
                                     1,
-                                    R.string.tab_group_bottom_sheet_row_accessibility_text));
+                                    R.plurals.tab_group_bottom_sheet_row_accessibility_text));
                     builder.with(
                             TIMESTAMP_EVENT,
                             new TabGroupTimeAgo(
@@ -256,7 +256,7 @@
                             new TabGroupRowViewTitleData(
                                     "A generic title",
                                     1,
-                                    R.string.tab_group_bottom_sheet_row_accessibility_text));
+                                    R.plurals.tab_group_bottom_sheet_row_accessibility_text));
                     builder.with(OPEN_RUNNABLE, null);
                     mPropertyModel = builder.build();
                     PropertyModelChangeProcessor.create(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewUnitTest.java
index 55d7f037..01d999f1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupRowViewUnitTest.java
@@ -153,31 +153,31 @@
         remakeWithProperty(
                 TITLE_DATA,
                 new TabGroupRowViewTitleData(
-                        "Title", 3, R.string.tab_group_bottom_sheet_row_accessibility_text));
+                        "Title", 3, R.plurals.tab_group_bottom_sheet_row_accessibility_text));
         assertEquals("Title", mTitleTextView.getText());
 
         remakeWithProperty(
                 TITLE_DATA,
                 new TabGroupRowViewTitleData(
-                        " ", 3, R.string.tab_group_bottom_sheet_row_accessibility_text));
+                        " ", 3, R.plurals.tab_group_bottom_sheet_row_accessibility_text));
         assertEquals(" ", mTitleTextView.getText());
 
         remakeWithProperty(
                 TITLE_DATA,
                 new TabGroupRowViewTitleData(
-                        "", 3, R.string.tab_group_bottom_sheet_row_accessibility_text));
+                        "", 3, R.plurals.tab_group_bottom_sheet_row_accessibility_text));
         assertEquals("3 tabs", mTitleTextView.getText());
 
         remakeWithProperty(
                 TITLE_DATA,
                 new TabGroupRowViewTitleData(
-                        null, 3, R.string.tab_group_bottom_sheet_row_accessibility_text));
+                        null, 3, R.plurals.tab_group_bottom_sheet_row_accessibility_text));
         assertEquals("3 tabs", mTitleTextView.getText());
 
         remakeWithProperty(
                 TITLE_DATA,
                 new TabGroupRowViewTitleData(
-                        "", 1, R.string.tab_group_bottom_sheet_row_accessibility_text));
+                        "", 1, R.plurals.tab_group_bottom_sheet_row_accessibility_text));
         assertEquals("1 tab", mTitleTextView.getText());
     }
 
@@ -186,7 +186,7 @@
         remakeWithProperty(
                 TITLE_DATA,
                 new TabGroupRowViewTitleData(
-                        "", 1, R.string.tab_group_bottom_sheet_row_accessibility_text));
+                        "", 1, R.plurals.tab_group_bottom_sheet_row_accessibility_text));
         assertEquals(GONE, mTextSpace.getVisibility());
         assertEquals(GONE, mSubtitleTextView.getVisibility());
     }
@@ -312,8 +312,8 @@
         remakeWithProperty(
                 TITLE_DATA,
                 new TabGroupRowViewTitleData(
-                        "Title", 3, R.string.tab_group_row_accessibility_text));
-        assertEquals("Open Title", mTitleTextView.getContentDescription());
+                        "Title", 3, R.plurals.tab_group_row_accessibility_text));
+        assertEquals("Open Title with 3 tabs", mTitleTextView.getContentDescription());
         assertEquals("Title tab group options", mListMenuButton.getContentDescription());
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
index 5e4ff1db..7bd8136 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupUiCoordinator.java
@@ -255,7 +255,7 @@
                 DataSharingService dataSharingService =
                         DataSharingServiceFactory.getForProfile(profile);
                 sharedImageTilesConfigBuilder =
-                        new SharedImageTilesConfig.Builder(mActivity)
+                        SharedImageTilesConfig.Builder.createForButton(mActivity)
                                 .setIconSizeDp(R.dimen.tab_strip_shared_image_tiles_size);
                 sharedImageTilesCoordinator =
                         new SharedImageTilesCoordinator(
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
index bfc453e7..38a088e 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
@@ -848,7 +848,10 @@
         Open
       </message>
       <message name="IDS_TAB_GROUP_ROW_ACCESSIBILITY_TEXT" desc="Content description for accessibility to describe action of tapping on a tab group row.">
-        Open <ph name="TITLE_OF_GROUP">%s<ex>shopping</ex></ph>
+        {TAB_COUNT, plural,
+          =1 {Open <ph name="TITLE_OF_GROUP">%1$s<ex>shopping</ex></ph> with <ph name="TAB_COUNT2">%2$d<ex>12</ex></ph> tab}
+          other {Open <ph name="TITLE_OF_GROUP">%1$s<ex>shopping</ex></ph> with <ph name="TAB_COUNT2">%2$d<ex>12</ex></ph> tabs}
+        }
       </message>
       <message name="IDS_TAB_GROUP_MENU_ACCESSIBILITY_TEXT" desc="Content description for accessibility to describe the action of tapping on the more menu button.">
         <ph name="TITLE_OF_GROUP">%s<ex>shopping</ex></ph> tab group options
@@ -1008,7 +1011,10 @@
         Tab group list closed
       </message>
       <message name="IDS_TAB_GROUP_BOTTOM_SHEET_ROW_ACCESSIBILITY_TEXT" desc="Content description for accessibility to describe action of tapping on a tab group row on the bottom sheet.">
-        Add to <ph name="TITLE_OF_GROUP">%s<ex>shopping</ex></ph>
+        {TAB_COUNT, plural,
+          =1 {Add to group named <ph name="TITLE_OF_GROUP">%1$s<ex>shopping</ex></ph> with <ph name="TAB_COUNT1">%2$d<ex>12</ex></ph> tab}
+          other {Add to group named <ph name="TITLE_OF_GROUP">%1$s<ex>shopping</ex></ph> with <ph name="TAB_COUNT1">%2$d<ex>12</ex></ph> tabs}
+        }
       </message>
     </messages>
   </release>
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_BOTTOM_SHEET_ROW_ACCESSIBILITY_TEXT.png.sha1 b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_BOTTOM_SHEET_ROW_ACCESSIBILITY_TEXT.png.sha1
index a188ab3..116be1c0 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_BOTTOM_SHEET_ROW_ACCESSIBILITY_TEXT.png.sha1
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_BOTTOM_SHEET_ROW_ACCESSIBILITY_TEXT.png.sha1
@@ -1 +1 @@
-2e618c2ecf60e842f372e0b423cba0b030c3fcd6
\ No newline at end of file
+40c09fff94704777ecabf7d3e858a2dbadf90d51
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_ROW_ACCESSIBILITY_TEXT.png.sha1 b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_ROW_ACCESSIBILITY_TEXT.png.sha1
index 8e99524..2f811d4 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_ROW_ACCESSIBILITY_TEXT.png.sha1
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_TAB_GROUP_ROW_ACCESSIBILITY_TEXT.png.sha1
@@ -1 +1 @@
-5e43e2549ed1b8cb092798f8e340a70467ef7c88
\ No newline at end of file
+a6145855770fa209bcc06d27ff01b1ba3037b0d0
\ No newline at end of file
diff --git a/chrome/android/java/res/menu/custom_tabs_menu.xml b/chrome/android/java/res/menu/custom_tabs_menu.xml
index 0984c8a..31416ff 100644
--- a/chrome/android/java/res/menu/custom_tabs_menu.xml
+++ b/chrome/android/java/res/menu/custom_tabs_menu.xml
@@ -12,18 +12,23 @@
             <menu>
               <item android:id="@+id/forward_menu_id"
                 android:title="@string/accessibility_menu_forward"
+                android:titleCondensed="@string/menu_forward"
                 android:icon="@drawable/btn_forward"/>
               <item android:id="@+id/bookmark_this_page_id"
                 android:title="@string/accessibility_menu_bookmark"
+                android:titleCondensed="@string/menu_bookmark"
                 android:icon="@drawable/star_outline_24dp"/>
               <item android:id="@+id/offline_page_id"
                 android:title="@string/download_page"
+                android:titleCondensed="@string/menu_download"
                 android:icon="@drawable/ic_file_download_white_24dp"/>
               <item android:id="@+id/info_menu_id"
                 android:title="@string/accessibility_menu_info"
+                android:titleCondensed="@string/menu_page_info"
                 android:icon="@drawable/btn_info" />
               <item android:id="@+id/reload_menu_id"
                 android:title="@string/accessibility_btn_refresh"
+                android:titleCondensed="@string/refresh"
                 android:icon="@drawable/btn_reload_stop"/>
             </menu>
         </item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
index 9a41dbad..ad85c06 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -317,52 +317,67 @@
                 continue;
             }
             visibleBeforeReadAloudCount++;
-            PropertyModel propertyModel = AppMenuUtil.menuItemToPropertyModel(item);
-            propertyModel.set(AppMenuItemProperties.ICON_COLOR_RES, getMenuItemIconColorRes(item));
-            propertyModel.set(
-                    AppMenuItemProperties.ICON_SHOW_BADGE, shouldShowBadgeOnMenuItemIcon(item));
-            propertyModel.set(AppMenuItemProperties.SUPPORT_ENTER_ANIMATION, true);
-            propertyModel.set(AppMenuItemProperties.MENU_ICON_AT_START, isMenuIconAtStart());
-            propertyModel.set(AppMenuItemProperties.TITLE_CONDENSED, getContentDescription(item));
-            propertyModel.set(AppMenuItemProperties.MANAGED, isMenuItemManaged(item));
-            if (item.hasSubMenu()) {
-                // Only support top level menu items have SUBMENU, and a SUBMENU item cannot have a
-                // SUBMENU.
-                // TODO(crbug.com/40171109) : Create a new SubMenuItemProperties property key set
-                // for
-                // SUBMENU items.
+
+            PropertyModel propertyModel;
+            @AppMenuItemType int menuType;
+            if (item.getItemId() == R.id.icon_row_menu_id) {
+                menuType = AppMenuItemType.BUTTON_ROW;
+
                 ModelList subList = new ModelList();
-                for (int j = 0; j < item.getSubMenu().size(); ++j) {
+                assert item.getSubMenu().size() <= 5 : "At most 5 buttons currently supported.";
+                for (int j = 0; j < item.getSubMenu().size(); j++) {
                     MenuItem subitem = item.getSubMenu().getItem(j);
                     if (!subitem.isVisible()) continue;
 
-                    PropertyModel subModel = AppMenuUtil.menuItemToPropertyModel(subitem);
+                    PropertyModel subModel = AppMenuUtil.buildPropertyModelForIcon(subitem);
                     subList.add(new MVCListAdapter.ListItem(0, subModel));
                     if (subitem.getItemId() == R.id.reload_menu_id) {
                         mReloadPropertyModel = subModel;
                         Tab currentTab = mActivityTabProvider.get();
-                        loadingStateChanged(currentTab == null ? false : currentTab.isLoading());
+                        loadingStateChanged(currentTab != null && currentTab.isLoading());
                     }
                 }
-                propertyModel.set(AppMenuItemProperties.SUBMENU, subList);
-            }
-            int menutype = AppMenuItemType.STANDARD;
-            if (item.getItemId() == R.id.request_desktop_site_row_menu_id
-                    || item.getItemId() == R.id.share_row_menu_id
-                    || item.getItemId() == R.id.auto_dark_web_contents_row_menu_id) {
-                menutype = AppMenuItemType.TITLE_BUTTON;
-            } else if (item.getItemId() == R.id.icon_row_menu_id) {
-                int viewCount = item.getSubMenu().size();
-                menutype = AppMenuItemType.BUTTON_ROW;
-                assert viewCount <= 5 : "At most 5 buttons currently supported.";
+                propertyModel =
+                        new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS)
+                                .with(AppMenuItemProperties.MENU_ITEM_ID, item.getItemId())
+                                .with(AppMenuItemProperties.ADDITIONAL_ICONS, subList)
+                                .with(AppMenuItemProperties.MENU_ICON_AT_START, isMenuIconAtStart())
+                                .build();
             } else {
-                // Could be standard items or custom items.
                 int customType = customItemViewTypeProvider.fromMenuItemId(item.getItemId());
-                if (customType != CustomViewBinder.NOT_HANDLED) {
-                    menutype = customType;
+                boolean isTitleButtonType = item.hasSubMenu() && item.getSubMenu().size() == 2;
+                MenuItem textItem = item;
+                if (isTitleButtonType) {
+                    menuType = AppMenuItemType.TITLE_BUTTON;
+                    textItem = item.getSubMenu().getItem(0);
+                } else if (customType != CustomViewBinder.NOT_HANDLED) {
+                    menuType = customType;
+                } else {
+                    menuType = AppMenuItemType.STANDARD;
+                }
+
+                propertyModel = AppMenuUtil.menuItemToPropertyModel(textItem);
+                propertyModel.set(
+                        AppMenuItemProperties.ICON_COLOR_RES, getMenuItemIconColorRes(textItem));
+                propertyModel.set(
+                        AppMenuItemProperties.ICON_SHOW_BADGE,
+                        shouldShowBadgeOnMenuItemIcon(textItem));
+                propertyModel.set(AppMenuItemProperties.SUPPORT_ENTER_ANIMATION, true);
+                propertyModel.set(AppMenuItemProperties.MENU_ICON_AT_START, isMenuIconAtStart());
+                propertyModel.set(
+                        AppMenuItemProperties.TITLE_CONDENSED, getContentDescription(textItem));
+                propertyModel.set(AppMenuItemProperties.MANAGED, isMenuItemManaged(textItem));
+
+                if (isTitleButtonType) {
+                    ModelList subList = new ModelList();
+                    MenuItem subitem = item.getSubMenu().getItem(1);
+                    assert subitem.isVisible();
+                    PropertyModel subModel = AppMenuUtil.buildPropertyModelForIcon(subitem);
+                    subList.add(new MVCListAdapter.ListItem(0, subModel));
+                    propertyModel.set(AppMenuItemProperties.ADDITIONAL_ICONS, subList);
                 }
             }
-            modelList.add(new MVCListAdapter.ListItem(menutype, propertyModel));
+            modelList.add(new MVCListAdapter.ListItem(menuType, propertyModel));
         }
         int lastIndex = modelList.size() - 1;
         int itemId = modelList.get(lastIndex).model.get(AppMenuItemProperties.MENU_ITEM_ID);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerImpl.java
index bb7d26b..150ee66 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerOpenerImpl.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.app.bookmarks.BookmarkEditActivity;
 import org.chromium.chrome.browser.app.bookmarks.BookmarkFolderPickerActivity;
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -44,6 +45,7 @@
             Activity activity, Profile profile, @Nullable BookmarkId folderId) {
         ThreadUtils.assertOnUiThread();
         String url = getFirstUrlToLoad(folderId);
+        boolean isIncognito = profile.isOffTheRecord();
 
         if (ChromeSharedPreferences.getInstance()
                 .contains(ChromePreferenceKeys.BOOKMARKS_LAST_USED_URL)) {
@@ -51,7 +53,7 @@
         }
 
         if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(activity)) {
-            showBookmarkManagerOnTablet(activity, activity.getComponentName(), url);
+            showBookmarkManagerOnTablet(activity, activity.getComponentName(), url, isIncognito);
         } else {
             showBookmarkManagerOnPhone(activity, url, profile);
         }
@@ -113,13 +115,20 @@
     }
 
     private void showBookmarkManagerOnTablet(
-            Context context, @Nullable ComponentName componentName, String url) {
+            Context context, @Nullable ComponentName componentName, String url,
+            boolean isIncognito) {
         Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
         intent.putExtra(
                 Browser.EXTRA_APPLICATION_ID, context.getApplicationContext().getPackageName());
         IntentHandler.setTabLaunchType(intent, TabLaunchType.FROM_CHROME_UI);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
+        if (ChromeFeatureList.sAndroidNativePagesInNewTab.isEnabled()
+                && ChromeFeatureList.sAndroidNativePagesInNewTabBookmarksEnabled.getValue()) {
+            intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true);
+            intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, isIncognito);
+        }
+
         if (componentName != null) {
             ActivityUtils.setNonAliasedComponentForMainBrowsingActivity(intent, componentName);
         } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
index 9e82d30..13991b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutGroupTitle.java
@@ -348,7 +348,8 @@
         // Initialize the shared image tiles coordinator if it doesn't exist.
         if (mSharedImageTilesCoordinator == null) {
             mSharedImageTilesConfigBuilder =
-                    SharedImageTilesConfig.Builder.createThumbnail(mContext, mColorId);
+                    SharedImageTilesConfig.Builder.createForTabGroupColorContext(
+                            mContext, mColorId);
             mSharedImageTilesCoordinator =
                     new SharedImageTilesCoordinator(
                             mContext,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
index c7cee5e..cd046d8b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -298,9 +298,6 @@
                 BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding(
                         R.string.open_new_tab_in_group_context_menu_item,
                         R.id.open_new_tab_in_group,
-                        R.drawable.ic_open_new_tab_in_group_24dp,
-                        R.color.default_icon_color_light_tint_list,
-                        R.style.TextAppearance_TextLarge_Primary_Baseline_Light,
                         isIncognito,
                         /* enabled= */ true));
 
@@ -309,9 +306,6 @@
                     BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding(
                             R.string.ungroup_tab_group_menu_item,
                             R.id.ungroup_tab,
-                            R.drawable.ic_ungroup_tabs_24dp,
-                            R.color.default_icon_color_light_tint_list,
-                            R.style.TextAppearance_TextLarge_Primary_Baseline_Light,
                             isIncognito,
                             /* enabled= */ true));
         }
@@ -324,7 +318,7 @@
                     BrowserUiListMenuUtils.buildMenuListItem(
                             R.string.share_tab_group_context_menu_item,
                             R.id.share_group,
-                            R.drawable.ic_group_24dp,
+                            /* startIconId= */ 0,
                             /* enabled= */ true));
         }
 
@@ -332,9 +326,6 @@
                 BrowserUiListMenuUtils.buildMenuListItemWithIncognitoBranding(
                         R.string.tab_grid_dialog_toolbar_close_group,
                         R.id.close_tab_group,
-                        R.drawable.ic_tab_close_24dp,
-                        R.color.default_icon_color_light_tint_list,
-                        R.style.TextAppearance_TextLarge_Primary_Baseline_Light,
                         isIncognito,
                         /* enabled= */ true));
 
@@ -345,7 +336,7 @@
                     BrowserUiListMenuUtils.buildMenuListItem(
                             R.string.tab_grid_dialog_toolbar_delete_group,
                             R.id.delete_tab_group,
-                            R.drawable.material_ic_delete_24dp,
+                            /* startIconId= */ 0,
                             /* enabled= */ true));
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
index b67527d..d2e96710 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
@@ -18,7 +18,7 @@
 import org.chromium.chrome.browser.layouts.scene_layer.SceneLayer;
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
-import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.ui.resources.ResourceManager;
 import org.chromium.ui.util.ColorUtils;
@@ -123,7 +123,8 @@
             LayoutTab t = tabs[i];
             final float decoration = t.getDecorationAlpha();
             boolean useIncognitoColors = t.isIncognito();
-            int defaultThemeColor = ChromeColors.getDefaultThemeColor(context, useIncognitoColors);
+            int defaultThemeColor =
+                    SurfaceColorUpdateUtils.getDefaultThemeColor(context, useIncognitoColors);
 
             // TODO(dtrainor, clholgat): remove "* dpToPx" once the native part fully supports dp.
             TabListSceneLayerJni.get()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java
index cab54c9..622883e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/AuthTabColorProvider.java
@@ -20,7 +20,7 @@
 
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
-import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.ui.util.ColorUtils;
 
 /** {@link ColorProvider} implementation used for Auth Tab. */
@@ -70,7 +70,7 @@
         if (hasCustomToolbarColor) {
             return ColorUtils.getOpaqueColor(params.getToolbarColor());
         }
-        return ChromeColors.getDefaultThemeColor(context, /* isIncognito= */ false);
+        return SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ false);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabColorProviderImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabColorProviderImpl.java
index 22afc6e2..c98e4d6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabColorProviderImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabColorProviderImpl.java
@@ -19,7 +19,7 @@
 import org.chromium.base.IntentUtils;
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
-import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.ui.util.ColorUtils;
 
 /** ColorProvider implementation used for normal profiles. */
@@ -78,7 +78,8 @@
             CustomTabColorSchemeParams schemeParams,
             Context context,
             boolean hasCustomToolbarColor) {
-        int defaultColor = ChromeColors.getDefaultThemeColor(context, /* isIncognito= */ false);
+        int defaultColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ false);
         int color = hasCustomToolbarColor ? schemeParams.toolbarColor : defaultColor;
         return ColorUtils.getOpaqueColor(color);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
index ca49c4a..33cb5e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabObserver.java
@@ -57,9 +57,6 @@
     // the API was not used.
     @Nullable private Boolean mUsedHiddenTabSpeculation;
 
-    // The time page load started in the most recent Custom Tab launch.
-    private long mPageLoadStartedRealtimeMillis;
-
     // The time of the first navigation commit in the most recent Custom Tab launch.
     private long mFirstCommitRealtimeMillis;
 
@@ -124,7 +121,6 @@
         mIntentReceivedRealtimeMillis = BrowserIntentUtils.getStartupRealtimeMillis(sourceIntent);
         mIntentReceivedUptimeMillis = BrowserIntentUtils.getStartupUptimeMillis(sourceIntent);
         if (tab.isLoading()) {
-            mPageLoadStartedRealtimeMillis = -1;
             mCurrentState = State.WAITING_LOAD_FINISH;
         } else {
             mCurrentState = State.WAITING_LOAD_START;
@@ -181,14 +177,12 @@
     @Override
     public void onPageLoadStarted(Tab tab, GURL url) {
         if (mCurrentState == State.WAITING_LOAD_START) {
-            mPageLoadStartedRealtimeMillis = SystemClock.elapsedRealtime();
             mCurrentState = State.WAITING_LOAD_FINISH;
         } else if (mCurrentState == State.WAITING_LOAD_FINISH) {
             if (mCustomTabsConnection != null) {
                 mCustomTabsConnection.sendNavigationInfo(
                         mSession, tab.getUrl().getSpec(), tab.getTitle(), (Uri) null);
             }
-            mPageLoadStartedRealtimeMillis = SystemClock.elapsedRealtime();
         }
         if (mCustomTabsConnection != null) {
             mCustomTabsConnection.setSendNavigationInfoForSession(mSession, false);
@@ -203,60 +197,6 @@
 
     @Override
     public void onPageLoadFinished(Tab tab, GURL url) {
-        long pageLoadFinishedTimestamp = SystemClock.elapsedRealtime();
-
-        if (mCurrentState == State.WAITING_LOAD_FINISH && mIntentReceivedRealtimeMillis > 0) {
-            String histogramPrefix = mOpenedByChrome ? "ChromeGeneratedCustomTab" : "CustomTabs";
-            long timeToPageLoadFinishedMs =
-                    pageLoadFinishedTimestamp - mIntentReceivedRealtimeMillis;
-            if (mPageLoadStartedRealtimeMillis > 0) {
-                long timeToPageLoadStartedMs =
-                        mPageLoadStartedRealtimeMillis - mIntentReceivedRealtimeMillis;
-                // Intent to Load Start is recorded here to make sure we do not record
-                // failed/aborted page loads.
-                RecordHistogram.recordCustomTimesHistogram(
-                        histogramPrefix + ".IntentToFirstNavigationStartTime.ZoomedOut",
-                        timeToPageLoadStartedMs,
-                        50,
-                        DateUtils.MINUTE_IN_MILLIS * 10,
-                        50);
-                RecordHistogram.recordCustomTimesHistogram(
-                        histogramPrefix + ".IntentToFirstNavigationStartTime.ZoomedIn",
-                        timeToPageLoadStartedMs,
-                        200,
-                        DateUtils.SECOND_IN_MILLIS,
-                        100);
-            }
-            // Same bounds and bucket count as PLT histograms.
-            RecordHistogram.recordCustomTimesHistogram(
-                    histogramPrefix + ".IntentToPageLoadedTime",
-                    timeToPageLoadFinishedMs,
-                    10,
-                    DateUtils.MINUTE_IN_MILLIS * 10,
-                    100);
-
-            // Not all page loads go through a navigation commit (prerender for instance).
-            if (mPageLoadStartedRealtimeMillis != 0) {
-                long timeToFirstCommitMs =
-                        mFirstCommitRealtimeMillis - mIntentReceivedRealtimeMillis;
-                // Current median is 550ms, and long tail is very long. ZoomedIn gives good view of
-                // the median and ZoomedOut gives a good overview.
-                RecordHistogram.recordCustomTimesHistogram(
-                        "CustomTabs.IntentToFirstCommitNavigationTime3.ZoomedIn",
-                        timeToFirstCommitMs,
-                        200,
-                        DateUtils.SECOND_IN_MILLIS,
-                        100);
-                // For ZoomedOut very rarely is it under 50ms and this range matches
-                // CustomTabs.IntentToFirstCommitNavigationTime2.ZoomedOut.
-                RecordHistogram.recordCustomTimesHistogram(
-                        "CustomTabs.IntentToFirstCommitNavigationTime3.ZoomedOut",
-                        timeToFirstCommitMs,
-                        50,
-                        DateUtils.MINUTE_IN_MILLIS * 10,
-                        50);
-            }
-        }
         resetPageLoadTracking();
         mNavigationInfoCaptureTrigger.onLoadFinished(tab);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java
index 5e7cfa26..e098b05 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabsOpenTimeRecorder.java
@@ -145,7 +145,8 @@
         @FinishReason int finishReason = mNavigationController.getFinishReason();
         if (finishReason == FinishReason.USER_NAVIGATION
                 || finishReason == FinishReason.REPARENTING
-                || finishReason == FinishReason.OPEN_IN_BROWSER) {
+                || finishReason == FinishReason.OPEN_IN_BROWSER
+                || finishReason == FinishReason.HANDLED_BY_OS) {
             mCloseCause = CloseCause.USER_ACTION_CHROME;
         }
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java
index 68ff9267..63d1145 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/IncognitoCustomTabColorProvider.java
@@ -10,7 +10,7 @@
 import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
-import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 
 /** ColorProvider implementation used for incognito profiles. */
 public final class IncognitoCustomTabColorProvider implements ColorProvider {
@@ -23,7 +23,8 @@
         mToolbarColor =
                 mBottomBarColor =
                         mNavigationBarColor =
-                                ChromeColors.getDefaultThemeColor(context, /* isIncognito= */ true);
+                                SurfaceColorUpdateUtils.getDefaultThemeColor(
+                                        context, /* isIncognito= */ true);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
index 0a24a12..b39b2f2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -57,7 +57,6 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.content.res.AppCompatResources;
 import androidx.browser.customtabs.CustomTabsIntent.CloseButtonPosition;
-import androidx.core.content.ContextCompat;
 import androidx.core.view.MarginLayoutParamsCompat;
 import androidx.core.widget.ImageViewCompat;
 
@@ -99,6 +98,7 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TrustedCdn;
 import org.chromium.chrome.browser.tabmodel.TabCreator;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.toolbar.LocationBarModel;
 import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
@@ -284,7 +284,9 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        final int backgroundColor = ChromeColors.getDefaultThemeColor(getContext(), false);
+        final int backgroundColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(
+                        getContext(), /* isIncognito= */ false);
         setBackground(new ColorDrawable(backgroundColor));
         mBrandedColorScheme = BrandedColorScheme.APP_DEFAULT;
 
@@ -2445,7 +2447,8 @@
                             getContext(), R.drawable.custom_tabs_url_bar_omnibox_bg);
             mOmniboxBackground.mutate();
             mOmniboxBackground.setTint(
-                    ContextCompat.getColor(getContext(), R.color.toolbar_text_box_bg_color));
+                    SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                            getContext(), /* isIncognito= */ false));
             mLocationBarFrameLayout.setBackground(mOmniboxBackground);
             var lp = mLocationBarFrameLayout.getLayoutParams();
             lp.height =
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
index 1969cd48..6d2b850 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
@@ -25,12 +25,12 @@
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabSelectionType;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.chrome.browser.ui.google_bottom_bar.GoogleBottomBarCoordinator;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.ui.util.ColorUtils;
 import org.chromium.url.GURL;
 
@@ -223,7 +223,7 @@
     }
 
     private int getDefaultColor() {
-        return ChromeColors.getDefaultThemeColor(
+        return SurfaceColorUpdateUtils.getDefaultThemeColor(
                 mActivity, mIntentDataProvider.getCustomTabMode() == INCOGNITO);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarPhone.java
index a5cd2930..d983c64 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarPhone.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/findinpage/FindToolbarPhone.java
@@ -13,6 +13,7 @@
 import androidx.core.widget.ImageViewCompat;
 
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 
@@ -42,7 +43,7 @@
 
     @Override
     protected void updateVisualsForTabModel(boolean isIncognito) {
-        setBackgroundColor(ChromeColors.getDefaultThemeColor(getContext(), isIncognito));
+        setBackgroundColor(SurfaceColorUpdateUtils.getDefaultThemeColor(getContext(), isIncognito));
         final ColorStateList color = ChromeColors.getPrimaryIconTint(getContext(), isIncognito);
         ImageViewCompat.setImageTintList(mFindNextButton, color);
         ImageViewCompat.setImageTintList(mFindPrevButton, color);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java
index 942c705..a1e8464 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/AppThemeColorProvider.java
@@ -15,12 +15,12 @@
 import org.chromium.chrome.browser.lifecycle.TopResumedActivityChangedObserver;
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
 import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider.IncognitoStateObserver;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 
 /** A ThemeColorProvider for the app theme (incognito or standard theming). */
 public class AppThemeColorProvider extends ThemeColorProvider
@@ -76,8 +76,10 @@
         super(context);
 
         mActivityContext = context;
-        mStandardPrimaryColor = ChromeColors.getDefaultThemeColor(context, false);
-        mIncognitoPrimaryColor = ChromeColors.getDefaultThemeColor(context, true);
+        mStandardPrimaryColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ false);
+        mIncognitoPrimaryColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ true);
 
         mLayoutStateObserver =
                 new LayoutStateProvider.LayoutStateObserver() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
index 87d47bb..4c16411a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/DEPS
@@ -10,6 +10,7 @@
   "-chrome/android/java/src/org/chromium/chrome/browser",
   "+base/android/java/src/org/chromium/base",
   "+chrome/android/java/src/org/chromium/chrome/browser/toolbar",
+  "+components/browser_ui/styles/android",
   "+components/browser_ui/widget/android/java",
   "+components/commerce/core/android/java",
   "+ui/android/java/src/org/chromium/ui",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index 5628dcd..3f1ab51 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -127,6 +127,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabGroupUi;
 import org.chromium.chrome.browser.tasks.tab_management.TabGroupUiOneshotSupplier;
 import org.chromium.chrome.browser.theme.BottomUiThemeColorProvider;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeColorProvider;
 import org.chromium.chrome.browser.theme.ThemeColorProvider.ThemeColorObserver;
 import org.chromium.chrome.browser.theme.ThemeColorProvider.TintObserver;
@@ -172,7 +173,6 @@
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler.BackPressResult;
 import org.chromium.components.browser_ui.widget.scrim.ScrimManager;
@@ -2690,7 +2690,7 @@
         }
         if (previousTab != tab || wasIncognitoBranded != isIncognitoBranded) {
             int defaultPrimaryColor =
-                    ChromeColors.getDefaultThemeColor(mActivity, isIncognitoBranded);
+                    SurfaceColorUpdateUtils.getDefaultThemeColor(mActivity, isIncognitoBranded);
             int primaryColor =
                     tab != null
                             ? mTopUiThemeColorProvider.calculateColor(tab, tab.getThemeColor())
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
index 8cabc1ac..3bd46b2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
@@ -36,13 +36,13 @@
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tasks.tab_management.TabUiThemeUtil;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
 import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator;
 import org.chromium.chrome.browser.ui.desktop_windowing.AppHeaderUtils;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
 import org.chromium.components.browser_ui.edge_to_edge.EdgeToEdgeSystemBarColorHelper;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.widget.scrim.ScrimProperties;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.base.WindowAndroid;
@@ -167,8 +167,10 @@
         mAllowToolbarColorOnTablets = false;
         mOverviewColorSupplier = overviewColorSupplier;
 
-        mStandardDefaultThemeColor = ChromeColors.getDefaultThemeColor(context, false);
-        mIncognitoDefaultThemeColor = ChromeColors.getDefaultThemeColor(context, true);
+        mStandardDefaultThemeColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ false);
+        mIncognitoDefaultThemeColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ true);
         mBackgroundColorForNtp =
                 ContextCompat.getColor(context, R.color.home_surface_background_color);
         mStatusIndicatorColor = UNDEFINED_STATUS_BAR_COLOR;
@@ -180,7 +182,8 @@
         mIncognitoActiveOmniboxColor = context.getColor(R.color.omnibox_dropdown_bg_incognito);
         // TODO(b/41494931): Share code with ToolbarPhone#getToolbarDefaultColor().
         mStandardScrolledOmniboxColor =
-                ContextCompat.getColor(context, R.color.toolbar_text_box_bg_color);
+                SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                        context, /* isIncognito= */ false);
         mIncognitoScrolledOmniboxColor = context.getColor(R.color.omnibox_scrolled_bg_incognito);
 
         mStatusBarColorTabObserver =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
index 1bb016e..420c8ab4 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
@@ -245,8 +245,7 @@
 
         int requestDesktopSiteIndex =
                 AppMenuTestSupport.findIndexOfMenuItemById(
-                        mActivityTestRule.getAppMenuCoordinator(),
-                        R.id.request_desktop_site_row_menu_id);
+                        mActivityTestRule.getAppMenuCoordinator(), R.id.request_desktop_site_id);
         Assert.assertNotEquals("No request desktop site row found.", -1, requestDesktopSiteIndex);
 
         Callable<Boolean> isVisible =
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
index b0e7991..63ff656 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityAppMenuTest.java
@@ -270,11 +270,10 @@
         Assert.assertNotNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
                         mCustomTabActivityTestRule.getAppMenuCoordinator(),
-                        R.id.request_desktop_site_row_menu_id));
+                        R.id.request_desktop_site_id));
         Assert.assertNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
-                        mCustomTabActivityTestRule.getAppMenuCoordinator(),
-                        R.id.share_row_menu_id));
+                        mCustomTabActivityTestRule.getAppMenuCoordinator(), R.id.share_menu_id));
 
         assertHistoryMenuVisibility();
 
@@ -391,13 +390,13 @@
         Assert.assertNotNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
                         mCustomTabActivityTestRule.getAppMenuCoordinator(),
-                        R.id.request_desktop_site_row_menu_id));
+                        R.id.request_desktop_site_id));
 
         ModelList iconRowModelList =
                 AppMenuTestSupport.getMenuItemPropertyModel(
                                 mCustomTabActivityTestRule.getAppMenuCoordinator(),
                                 R.id.icon_row_menu_id)
-                        .get(AppMenuItemProperties.SUBMENU);
+                        .get(AppMenuItemProperties.ADDITIONAL_ICONS);
         final int expectedIconMenuSize = 4;
         assertEquals(expectedIconMenuSize, iconRowModelList.size());
         Assert.assertNotNull(
@@ -458,7 +457,7 @@
         Assert.assertNotNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
                         mCustomTabActivityTestRule.getAppMenuCoordinator(),
-                        R.id.request_desktop_site_row_menu_id));
+                        R.id.request_desktop_site_id));
         Assert.assertNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
                         mCustomTabActivityTestRule.getAppMenuCoordinator(),
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
index 13f9f83..41f812c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityIncognitoTest.java
@@ -197,7 +197,7 @@
                 AppMenuTestSupport.getMenuItemPropertyModel(
                                 mCustomTabActivityTestRule.getAppMenuCoordinator(),
                                 R.id.icon_row_menu_id)
-                        .get(AppMenuItemProperties.SUBMENU);
+                        .get(AppMenuItemProperties.ADDITIONAL_ICONS);
 
         int expectedTopActionIconsCount = 4;
         assertEquals(expectedTopActionIconsCount, iconRowModelList.size());
@@ -338,7 +338,7 @@
     @MediumTest
     public void shareMenuItemByDefaultIsNotVisibile() throws Exception {
         launchAndTestMenuItemIsNotVisible(
-                R.id.share_row_menu_id, "Share menu item not visible by default");
+                R.id.share_menu_id, "Share menu item not visible by default");
     }
 
     @Test
@@ -351,8 +351,7 @@
 
         assertNotNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
-                        mCustomTabActivityTestRule.getAppMenuCoordinator(),
-                        R.id.share_row_menu_id));
+                        mCustomTabActivityTestRule.getAppMenuCoordinator(), R.id.share_menu_id));
     }
 
     @Test
@@ -387,7 +386,7 @@
         assertNotNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
                         mCustomTabActivityTestRule.getAppMenuCoordinator(),
-                        R.id.request_desktop_site_row_menu_id));
+                        R.id.request_desktop_site_id));
 
         // Check top icons are still the same.
         testTopActionIconsIsVisible();
@@ -422,7 +421,7 @@
         assertNull(
                 AppMenuTestSupport.getMenuItemPropertyModel(
                         mCustomTabActivityTestRule.getAppMenuCoordinator(),
-                        R.id.request_desktop_site_row_menu_id));
+                        R.id.request_desktop_site_id));
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 52657b20..00871b0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -1140,53 +1140,6 @@
         checkPageLoadMetrics(false);
     }
 
-    private static void assertSuffixedHistogramTotalCount(long expected, String histogramPrefix) {
-        for (String suffix : new String[] {".ZoomedIn", ".ZoomedOut"}) {
-            assertEquals(
-                    expected,
-                    RecordHistogram.getHistogramTotalCountForTesting(histogramPrefix + suffix));
-        }
-    }
-
-    /**
-     * Tests that one navigation in a custom tab records the histograms reflecting time from intent
-     * to first navigation start/commit.
-     */
-    @Test
-    @SmallTest
-    public void testNavigationHistogramsRecorded() throws Exception {
-        String startHistogramPrefix = "CustomTabs.IntentToFirstNavigationStartTime";
-        String commitHistogramPrefix = "CustomTabs.IntentToFirstCommitNavigationTime3";
-        assertSuffixedHistogramTotalCount(0, startHistogramPrefix);
-        assertSuffixedHistogramTotalCount(0, commitHistogramPrefix);
-
-        final Semaphore semaphore = new Semaphore(0);
-        CustomTabsSession session =
-                CustomTabsTestUtils.bindWithCallback(
-                                new CustomTabsCallback() {
-                                    @Override
-                                    public void onNavigationEvent(
-                                            int navigationEvent, Bundle extras) {
-                                        if (navigationEvent
-                                                == CustomTabsCallback.NAVIGATION_FINISHED) {
-                                            semaphore.release();
-                                        }
-                                    }
-                                })
-                        .session;
-        Intent intent = new CustomTabsIntent.Builder(session).build().intent;
-        intent.setData(Uri.parse(mTestPage));
-        intent.setComponent(
-                new ComponentName(
-                        ApplicationProvider.getApplicationContext(), ChromeLauncherActivity.class));
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
-        Assert.assertTrue(semaphore.tryAcquire(TIMEOUT_PAGE_LOAD_SECONDS, TimeUnit.SECONDS));
-
-        assertSuffixedHistogramTotalCount(1, startHistogramPrefix);
-        assertSuffixedHistogramTotalCount(1, commitHistogramPrefix);
-    }
-
     /** Tests that TITLE_ONLY state works as expected with a title getting set onload. */
     @Test
     @SmallTest
@@ -2885,7 +2838,7 @@
 
         openAppMenuAndAssertMenuShown();
         Assert.assertNull(
-                "Share option should be hidden.", activity.findViewById(R.id.share_row_menu_id));
+                "Share option should be hidden.", activity.findViewById(R.id.share_menu_id));
     }
 
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
index f01cc6f1..5c0c955 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabsTestUtils.java
@@ -188,8 +188,8 @@
         for (int i = 0; i < list.size(); i++) {
             PropertyModel model = list.get(i).model;
             items.append("\n").append(model.get(AppMenuItemProperties.TITLE));
-            if (model.get(AppMenuItemProperties.SUBMENU) != null) {
-                for (var submenu : model.get(AppMenuItemProperties.SUBMENU)) {
+            if (model.get(AppMenuItemProperties.ADDITIONAL_ICONS) != null) {
+                for (var submenu : model.get(AppMenuItemProperties.ADDITIONAL_ICONS)) {
                     items.append("\n - ").append(submenu.model.get(AppMenuItemProperties.TITLE));
                 }
             }
diff --git a/chrome/app/bookmarks_strings.grdp b/chrome/app/bookmarks_strings.grdp
index a23ec6a..b22cf3d 100644
--- a/chrome/app/bookmarks_strings.grdp
+++ b/chrome/app/bookmarks_strings.grdp
@@ -463,6 +463,9 @@
   <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_TAB_GROUP" desc="Menu description for opening a bookmark in a new tab group">
     Open in new tab group
   </message>
+  <message name="IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_SPLIT_VIEW" desc="Menu description for opening a bookmark in a split view">
+    Open in split view
+  </message>
   <message name="IDS_BOOKMARK_MANAGER_MENU_RENAME" desc="Title of the bookmark list dropdown menu item that renames folders.">
     Rename
   </message>
diff --git a/chrome/app/bookmarks_strings_grdp/IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_SPLIT_VIEW.png.sha1 b/chrome/app/bookmarks_strings_grdp/IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_SPLIT_VIEW.png.sha1
new file mode 100644
index 0000000..a78c517
--- /dev/null
+++ b/chrome/app/bookmarks_strings_grdp/IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_SPLIT_VIEW.png.sha1
@@ -0,0 +1 @@
+a8f66e71923717b2861bf1084e3ec6d03a654afc
\ No newline at end of file
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index dce2ac5b..7dbc937b 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -579,9 +579,6 @@
 // Glic button context menu and tabstrip context menu
 #define IDC_GLIC_TOGGLE_PIN 53320
 
-// Glic focus navigation
-#define IDC_GLIC_TOGGLE_FOCUS 53325
-
 // NOTE: The last valid command value is 57343 (0xDFFF)
 // See http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx
 
diff --git a/chrome/app/chrome_dll.rc b/chrome/app/chrome_dll.rc
index 12f741a..857c1c6f 100644
--- a/chrome/app/chrome_dll.rc
+++ b/chrome/app/chrome_dll.rc
@@ -56,7 +56,6 @@
     VK_F3,          IDC_FIND_NEXT,              VIRTKEY
     "G",            IDC_FIND_PREVIOUS,          VIRTKEY, CONTROL, SHIFT
     VK_F3,          IDC_FIND_PREVIOUS,          VIRTKEY, SHIFT
-    "G",            IDC_GLIC_TOGGLE_FOCUS,      VIRTKEY, SHIFT, ALT
     "B",            IDC_FOCUS_BOOKMARKS,        VIRTKEY, SHIFT, ALT
     "A",            IDC_FOCUS_INACTIVE_POPUP_FOR_ACCESSIBILITY,         VIRTKEY, SHIFT, ALT
     "D",            IDC_FOCUS_LOCATION,         VIRTKEY, ALT
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 58c92ce8..e13b327 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -7587,7 +7587,7 @@
         You can also ask things like “summarize”
       </message>
       <message name="IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_ERROR_TEXT" desc="The error text displayed in the overlay contextual searchbox ghost loader. It is meant to inform the user that they can still ask questions about the page even though suggestions were failed to be generated.">
-        No suggested questions available
+        No suggestions available. Try asking your own question about this page.
       </message>
       <message name="IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_LOADING_HINT_PRIMARY_ALT" desc="The hint text displayed in the overlay contextual searchbox ghost loader. It is meant to inform the user that suggestions are still being generated.">
         Generating suggestions…
@@ -16140,10 +16140,10 @@
         </message>
       </if>
       <message name="IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT" desc="Spoken by screen readers when a tab group header that has not been named by the user gets focused but is not visually rendered. Example: 'Unnamed group - Google Search and 3 other tabs - Expanded'">
-        Unnamed group - <ph name="GROUP_CONTENTS">$2<ex>Google Search and 3 other tabs</ex></ph> - <ph name="COLLAPSED_STATE">$1</ph>
+       <ph name="SHARE_STATE">$1<ex>Shared</ex></ph> unnamed group - <ph name="GROUP_CONTENTS">$2<ex>Google Search and 3 other tabs</ex></ph> - <ph name="COLLAPSED_STATE">$3</ph>
       </message>
       <message name="IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT" desc="Spoken by screen readers when a tab group header that has been named by the user gets focused but is not visually rendered. Example: 'Group MyGroupName - Google Search and 3 other tabs - Collapsed'">
-        Group <ph name="GROUP_NAME">$2</ph> - <ph name="GROUP_CONTENTS">$3<ex>Google Search and 3 other tabs</ex></ph> - <ph name="COLLAPSED_STATE">$1</ph>
+       <ph name="SHARE_STATE">$1<ex>Shared</ex></ph> group <ph name="GROUP_NAME">$2</ph> - <ph name="GROUP_CONTENTS">$3<ex>Google Search and 3 other tabs</ex></ph> - <ph name="COLLAPSED_STATE">$4</ph>
       </message>
       <message name="IDS_GROUP_AX_LABEL_COLLAPSED" desc="Used as part of the accessibility label text of a group header for a collapsed tab group.">
         Collapsed
diff --git a/chrome/app/generated_resources_grd/IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_ERROR_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_ERROR_TEXT.png.sha1
index 0ee78fd..8e7b460c 100644
--- a/chrome/app/generated_resources_grd/IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_ERROR_TEXT.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_GOOGLE_SEARCH_BOX_CONTEXTUAL_ERROR_TEXT.png.sha1
@@ -1 +1 @@
-8cc2d92b820ea0dc73f33b5d20d212b8b7c36036
\ No newline at end of file
+335f040168cdf75bbe700eea881cd3de6f5e6c5b
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT.png.sha1 b/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT.png.sha1
index aba1aea6..7300911 100644
--- a/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT.png.sha1
@@ -1 +1 @@
-a1dc9a0abcb6b4d836746ac3949df2464ba5bd53
\ No newline at end of file
+24856247c13daff4107d68e97d3d3c943de37e50
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT.png.sha1 b/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT.png.sha1
index 7bd0169..e6ef9bd 100644
--- a/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT.png.sha1
+++ b/chrome/app/generated_resources_grd/IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT.png.sha1
@@ -1 +1 @@
-21b31755a919cf69870ccf00eed315828dc09650
\ No newline at end of file
+4d064996340fbf0197194b80f6845965816670b3
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 9422b081..03805c70 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -980,10 +980,6 @@
     "passage_embeddings/embedder_tab_observer.h",
     "passage_embeddings/passage_embedder_model_observer_factory.cc",
     "passage_embeddings/passage_embedder_model_observer_factory.h",
-    "passage_embeddings/passage_embeddings_coordinator.cc",
-    "passage_embeddings/passage_embeddings_coordinator.h",
-    "passage_embeddings/passage_embeddings_coordinator_factory.cc",
-    "passage_embeddings/passage_embeddings_coordinator_factory.h",
     "password_manager/account_password_store_factory.cc",
     "password_manager/account_password_store_factory.h",
     "password_manager/bulk_leak_check_service_factory.cc",
@@ -3963,6 +3959,12 @@
       "page_info/web_view_side_panel_throttle.h",
       "page_load_metrics/observers/non_tab_webui_page_load_metrics_observer.cc",
       "page_load_metrics/observers/non_tab_webui_page_load_metrics_observer.h",
+      "passage_embeddings/omnibox_focus_change_listener.cc",
+      "passage_embeddings/omnibox_focus_change_listener.h",
+      "passage_embeddings/passage_embeddings_coordinator.cc",
+      "passage_embeddings/passage_embeddings_coordinator.h",
+      "passage_embeddings/passage_embeddings_coordinator_factory.cc",
+      "passage_embeddings/passage_embeddings_coordinator_factory.h",
       "password_manager/generated_password_leak_detection_pref.cc",
       "password_manager/generated_password_leak_detection_pref.h",
       "password_manager/web_app_profile_switcher.cc",
diff --git a/chrome/browser/ai/ai_data_keyed_service.cc b/chrome/browser/ai/ai_data_keyed_service.cc
index 27799e2..31df99ee 100644
--- a/chrome/browser/ai/ai_data_keyed_service.cc
+++ b/chrome/browser/ai/ai_data_keyed_service.cc
@@ -690,10 +690,8 @@
 #endif  // BUILDFLAG(ENABLE_GLIC)
 
 void RunLater(base::OnceClosure task) {
-  base::ThreadPool::PostTask(FROM_HERE,
-                             {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-                              base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-                             std::move(task));
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
+                                                              std::move(task));
 }
 
 // Feature to add allow listed extensions remotely for data collection.
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingChecker.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingChecker.java
index e7d41dc6..0efd0dff 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingChecker.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingChecker.java
@@ -4,13 +4,13 @@
 
 package org.chromium.chrome.browser.customtabs.features.branding;
 
+import static org.chromium.build.NullUtil.assumeNonNull;
+
 import android.os.SystemClock;
 import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
 
@@ -18,8 +18,11 @@
 import org.chromium.base.PackageUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.AsyncTask;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 
 /** Class that maintain the data for the client app id -> last time branding is shown. */
+@NullMarked
 class BrandingChecker extends AsyncTask<BrandingInfo> {
     public static final int BRANDING_TIME_NOT_FOUND = -1;
 
@@ -83,8 +86,7 @@
          * @return {@link MismatchNotificationData} object.
          */
         @WorkerThread
-        @Nullable
-        MismatchNotificationData getMimData();
+        @Nullable MismatchNotificationData getMimData();
 
         /**
          * Record the account mismatch notification data. Putting empty or null data is no-op since
@@ -116,7 +118,7 @@
     BrandingChecker(
             String appId,
             BrandingLaunchTimeStorage storage,
-            @NonNull Callback<BrandingInfo> brandingCheckCallback,
+            Callback<BrandingInfo> brandingCheckCallback,
             long brandingCadence,
             @BrandingDecision int defaultBrandingDecision) {
         mAppId = appId;
@@ -188,11 +190,13 @@
 
         // Note: Branding decision can be altered to MIM when mismatch notification UI overrides it
         // later, but this still counts as 'shown' to the respect global rate-limiting policy.
-        if (info.getDecision() != BrandingDecision.NONE && !TextUtils.isEmpty(mAppId)) {
+        if (assumeNonNull(info.getDecision()) != BrandingDecision.NONE
+                && !TextUtils.isEmpty(mAppId)) {
             mStorage.put(mAppId, taskFinishedTime);
         }
 
         // Remove the storage from reference.
-        mStorage = null;
+        // Setting non-nullable to null because object is no longer usable afterwards.
+        mStorage = assumeNonNull(null);
     }
 }
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java
index a267abfa..02dff0b 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingController.java
@@ -9,8 +9,6 @@
 import android.view.LayoutInflater;
 import android.widget.TextView;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
 
@@ -21,6 +19,9 @@
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
+import org.chromium.build.annotations.MonotonicNonNull;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.components.crash.PureJavaExceptionReporter;
 import org.chromium.ui.widget.Toast;
@@ -28,6 +29,7 @@
 import java.util.concurrent.TimeUnit;
 
 /** Controls the strategy to start branding, and the duration to show branding. */
+@NullMarked
 public class BrandingController {
     private static final String TAG = "CctBrand";
 
@@ -55,8 +57,9 @@
     private final String mAppId;
     private final String mBrowserName;
     private final int mToastTemplateId;
-    @Nullable private final PureJavaExceptionReporter mExceptionReporter;
-    private ToolbarBrandingDelegate mToolbarBrandingDelegate;
+    private final @Nullable PureJavaExceptionReporter mExceptionReporter;
+    // Non-null once initialized by onToolbarInitialized.
+    private @MonotonicNonNull ToolbarBrandingDelegate mToolbarBrandingDelegate;
     private @Nullable Toast mToast;
     private long mToolbarInitializedTime;
     private boolean mIsDestroyed;
@@ -78,7 +81,7 @@
             String appId,
             String browserName,
             @StringRes int toastTemplateId,
-            @NonNull Supplier<MismatchNotificationChecker> mismatchNotificationChecker,
+            Supplier<MismatchNotificationChecker> mismatchNotificationChecker,
             @Nullable PureJavaExceptionReporter exceptionReporter) {
         mContext = context;
         mAppId = appId;
@@ -105,7 +108,7 @@
      *
      * @param delegate {@link ToolbarBrandingDelegate} instance from CCT Toolbar.
      */
-    public void onToolbarInitialized(@NonNull ToolbarBrandingDelegate delegate) {
+    public void onToolbarInitialized(ToolbarBrandingDelegate delegate) {
         if (mIsDestroyed) {
             reportErrorMessage("BrandingController should not be access after destroyed.");
             return;
@@ -136,7 +139,7 @@
         BrandingInfo info = mBrandingInfo.get();
         if (mToolbarBrandingDelegate == null || info == null) return;
 
-        @BrandingDecision int brandingDecision = info.getDecision();
+        @BrandingDecision Integer brandingDecision = info.getDecision();
 
         // Mismatch notification checker is invoked when branding decision data is available
         // to respect the timing with which the decision is made. The decision making takes
@@ -154,6 +157,8 @@
         long timeToolbarEmpty = SystemClock.elapsedRealtime() - mToolbarInitializedTime;
         long remainingBrandingTime = TOTAL_BRANDING_DELAY_MS - timeToolbarEmpty;
 
+        assert brandingDecision != null : "Unreachable state!";
+
         switch (brandingDecision) {
             case BrandingDecision.MIM:
             case BrandingDecision.NONE:
@@ -169,11 +174,12 @@
             default:
                 assert false : "Unreachable state!";
         }
-        mBrandingInfo.get().setDecision(brandingDecision);
+        info.setDecision(brandingDecision);
         finish();
     }
 
     private void showToolbarBranding(long durationMs) {
+        if (mToolbarBrandingDelegate == null) return;
         mToolbarBrandingDelegate.showBrandingLocationBar();
 
         Runnable hideToolbarBranding =
@@ -229,7 +235,8 @@
     }
 
     private void finish() {
-        if (getBrandingDecision() == BrandingDecision.MIM) {
+        var brandingDecision = getBrandingDecision();
+        if (brandingDecision != null && brandingDecision == BrandingDecision.MIM) {
             var storage = SharedPreferencesBrandingTimeStorage.getInstance();
             storage.putLastShowTimeGlobal(SystemClock.elapsedRealtime());
         }
@@ -253,7 +260,7 @@
 
     @VisibleForTesting
     @BrandingDecision
-    Integer getBrandingDecision() {
+    @Nullable Integer getBrandingDecision() {
         BrandingInfo info = mBrandingInfo.get();
         return info != null ? info.getDecision() : null;
     }
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingDecision.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingDecision.java
index d0bba13..b5a92b6 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingDecision.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingDecision.java
@@ -6,6 +6,8 @@
 
 import androidx.annotation.IntDef;
 
+import org.chromium.build.annotations.NullMarked;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -18,6 +20,7 @@
     BrandingDecision.MIM,
     BrandingDecision.NUM_ENTRIES
 })
+@NullMarked
 @interface BrandingDecision {
     int NONE = 0;
     int TOOLBAR = 1;
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingInfo.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingInfo.java
index 55587c2..bcb7501 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingInfo.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/BrandingInfo.java
@@ -4,7 +4,11 @@
 
 package org.chromium.chrome.browser.customtabs.features.branding;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
 /** Collection of Chrome branding-related data read from/written to storage. */
+@NullMarked
 class BrandingInfo {
     public static final BrandingInfo EMPTY =
             new BrandingInfo(null, BrandingChecker.BRANDING_TIME_NOT_FOUND, null);
@@ -12,14 +16,14 @@
     /** The last time any branding/mismatch notification UI was shown. */
     public final long lastShowTime;
 
-    public final MismatchNotificationData mimData;
+    public final @Nullable MismatchNotificationData mimData;
 
-    private @BrandingDecision Integer mDecision; // May be updated, thus not final
+    private @BrandingDecision @Nullable Integer mDecision; // May be updated, thus not final
 
     public BrandingInfo(
-            @BrandingDecision Integer decision,
+            @BrandingDecision @Nullable Integer decision,
             long lastShowTime,
-            MismatchNotificationData mimData) {
+            @Nullable MismatchNotificationData mimData) {
         mDecision = decision;
         this.lastShowTime = lastShowTime;
         this.mimData = mimData;
@@ -31,6 +35,8 @@
     }
 
     /** Returns branding decision. */
+    @BrandingDecision
+    @Nullable
     public Integer getDecision() {
         return mDecision;
     }
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationChecker.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationChecker.java
index 90edf55..b5da694 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationChecker.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationChecker.java
@@ -4,11 +4,12 @@
 
 package org.chromium.chrome.browser.customtabs.features.branding;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
 import org.chromium.base.CallbackController;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.customtabs.features.branding.proto.AccountMismatchData.CloseType;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
@@ -23,14 +24,15 @@
  * Class that drives the account mismatch notification flow. Works in conjunction with {@link
  * BrandingController} for global branding rate-limiting policy.
  */
+@NullMarked
 public class MismatchNotificationChecker {
-    private @NonNull final Profile mProfile;
-    private @NonNull final Delegate mDelegate;
-    private @NonNull final IdentityManager mIdentityManager;
+    private final Profile mProfile;
+    private final Delegate mDelegate;
+    private final IdentityManager mIdentityManager;
     private final CallbackController mCallbackController = new CallbackController();
 
     /** Used to suppress IPH UIs while the mismatch notification UI is on the screen. */
-    private Tracker.DisplayLockHandle mFeatureEngagementLock;
+    private Tracker.@Nullable DisplayLockHandle mFeatureEngagementLock;
 
     /**
      * Whether the other prompt UIs should be suppressed. This var is set to {@code true} while the
@@ -52,7 +54,7 @@
         boolean maybeShow(
                 String accountId,
                 long lastShownTime,
-                MismatchNotificationData mimData,
+                @Nullable MismatchNotificationData mimData,
                 Callback<Integer> onClose);
     }
 
@@ -64,9 +66,7 @@
      * @param delegate Delegate providing the actual decision/UI logic.
      */
     public MismatchNotificationChecker(
-            @NonNull Profile profile,
-            @NonNull IdentityManager identityManager,
-            @NonNull Delegate delegate) {
+            Profile profile, IdentityManager identityManager, Delegate delegate) {
         mProfile = profile;
         mIdentityManager = identityManager;
         mDelegate = delegate;
@@ -76,7 +76,7 @@
     public boolean maybeShow(
             String appId,
             long lastShowTime,
-            MismatchNotificationData data,
+            @Nullable MismatchNotificationData data,
             Callback<MismatchNotificationData> closeCallback) {
         String accountId = getAccountId();
         MismatchNotificationData mimData = data; // effective final
@@ -121,8 +121,9 @@
     String getAccountId() {
         CoreAccountInfo account = mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN);
         GaiaId gaiaId = CoreAccountInfo.getGaiaIdFrom(account);
-        var hash =
-                HashUtil.getMd5Hash(new HashUtil.Params(gaiaId != null ? gaiaId.toString() : null));
+        if (gaiaId == null) return "";
+        var hash = HashUtil.getMd5Hash(new HashUtil.Params(gaiaId.toString()));
+        if (hash == null) return "";
         return hash.substring(0, 16);
     }
 
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationData.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationData.java
index 4b5bd20..cbcfad1 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationData.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/MismatchNotificationData.java
@@ -4,11 +4,11 @@
 
 package org.chromium.chrome.browser.customtabs.features.branding;
 
-import androidx.annotation.NonNull;
-
 import com.google.common.io.BaseEncoding;
 import com.google.protobuf.InvalidProtocolBufferException;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.customtabs.features.branding.proto.AccountMismatchData.Account;
 import org.chromium.chrome.browser.customtabs.features.branding.proto.AccountMismatchData.AllAccounts;
 import org.chromium.chrome.browser.customtabs.features.branding.proto.AccountMismatchData.App;
@@ -18,6 +18,7 @@
 import java.util.Map;
 
 /** Collection of app data storing the information on sign-in prompt UI for a specific profile. */
+@NullMarked
 public class MismatchNotificationData {
     /** Per-app prompt UI data */
     public static class AppUiData {
@@ -60,7 +61,7 @@
      * @param account Account ID
      * @param appId App ID
      */
-    public @NonNull AppUiData getAppData(String accountId, String appId) {
+    public AppUiData getAppData(String accountId, String appId) {
         Map<String, AppUiData> accountData = mDataMap.get(accountId);
         AppUiData res = accountData != null ? accountData.get(appId) : null;
         return res != null ? res : new AppUiData();
@@ -72,7 +73,7 @@
      * @param account Account ID
      * @param appId App ID
      */
-    public void setAppData(String accountId, String appId, @NonNull AppUiData data) {
+    public void setAppData(String accountId, String appId, AppUiData data) {
         Map<String, AppUiData> accountData = mDataMap.get(accountId);
         if (accountData != null) {
             accountData.put(appId, data);
@@ -109,7 +110,7 @@
     }
 
     /** Restore the class from a serialized string in Base64 encoding. */
-    static MismatchNotificationData fromBase64(String s) {
+    static @Nullable MismatchNotificationData fromBase64(String s) {
         MismatchNotificationData mimData = null;
         AllAccounts protoData = null;
         try {
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java
index ec7e1821..55fdef5a 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/SharedPreferencesBrandingTimeStorage.java
@@ -17,6 +17,8 @@
 import org.chromium.base.PackageUtils;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.util.HashUtil;
 
 import java.util.Map;
@@ -25,6 +27,7 @@
  * Shared preference that stores the last branding time for CCT client apps. When recording, the
  * instance uses {@link SharedPreferences.Editor#commit()}.
  */
+@NullMarked
 class SharedPreferencesBrandingTimeStorage implements BrandingChecker.BrandingLaunchTimeStorage {
     private static final String KEY_SHARED_PREF = "pref_cct_brand_show_time";
     private static final String NON_PACKAGE_PREFIX = "REFERRER_";
@@ -32,10 +35,10 @@
     private static final String KEY_MIM_DATA = "MIM_DATA";
     @VisibleForTesting static final int MAX_NON_PACKAGE_ENTRIES = 50;
 
-    private static SharedPreferencesBrandingTimeStorage sInstance;
+    private static @Nullable SharedPreferencesBrandingTimeStorage sInstance;
 
     /** Shared pref that must be read / write on background thread. */
-    private SharedPreferences mSharedPref;
+    private @Nullable SharedPreferences mSharedPref;
 
     private SharedPreferencesBrandingTimeStorage() {}
 
@@ -71,7 +74,7 @@
                 });
     }
 
-    private String getKey(String appId) {
+    private @Nullable String getKey(String appId) {
         assert !TextUtils.isEmpty(appId);
         String key = hash(appId);
         // Keys will have a prefix if they are not a valid package name. They are likely
@@ -82,7 +85,7 @@
     }
 
     @WorkerThread
-    private String getOldEntryToTrim() {
+    private @Nullable String getOldEntryToTrim() {
         String oldEntry = null;
         long oldTime = -1;
         int count = 0;
@@ -120,7 +123,7 @@
 
     @WorkerThread
     @Override
-    public MismatchNotificationData getMimData() {
+    public @Nullable MismatchNotificationData getMimData() {
         String str = getSharedPref().getString(KEY_MIM_DATA, null);
         return str != null ? MismatchNotificationData.fromBase64(str) : null;
     }
@@ -153,7 +156,7 @@
         return size;
     }
 
-    private String hash(String packageName) {
+    private @Nullable String hash(String packageName) {
         return HashUtil.getMd5Hash(new HashUtil.Params(packageName));
     }
 
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java
index a7282484..223195b 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingDelegate.java
@@ -4,7 +4,10 @@
 
 package org.chromium.chrome.browser.customtabs.features.branding;
 
+import org.chromium.build.annotations.NullMarked;
+
 /** Delegate class for Toolbar strategy to control location bar UI elements. */
+@NullMarked
 public interface ToolbarBrandingDelegate {
     /** Show the branding information on location bar, with start & end transition. */
     void showBrandingLocationBar();
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayCoordinator.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayCoordinator.java
index 9dfa811..3ba15a00 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayCoordinator.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayCoordinator.java
@@ -15,6 +15,8 @@
 import androidx.core.animation.AnimatorListenerAdapter;
 import androidx.core.animation.ValueAnimator;
 
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.R;
 import org.chromium.ui.interpolators.AndroidxInterpolators;
 import org.chromium.ui.modelutil.PropertyModel;
@@ -24,12 +26,13 @@
  * Coordinator that creates/shows and hides/destroys the toolbar branding overlay over the title and
  * url.
  */
+@NullMarked
 public class ToolbarBrandingOverlayCoordinator {
     @VisibleForTesting static final int HIDING_DURATION_MS = 300;
 
-    private View mView;
+    private @Nullable View mView;
     private PropertyModel mModel;
-    private ValueAnimator mHidingAnimator;
+    private @Nullable ValueAnimator mHidingAnimator;
 
     /**
      * Constructs and shows the toolbar branding overlay.
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayProperties.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayProperties.java
index 270950c1..c7fa3a1 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayProperties.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayProperties.java
@@ -6,11 +6,13 @@
 
 import androidx.annotation.ColorInt;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** Properties of the toolbar branding overlay. */
+@NullMarked
 public class ToolbarBrandingOverlayProperties {
     /** Color data used to determine the background, text and icon tint. */
     public static class ColorData {
diff --git a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayViewBinder.java b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayViewBinder.java
index 6556e1b..838ee2ac 100644
--- a/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayViewBinder.java
+++ b/chrome/browser/android/customtabs/branding/java/src/org/chromium/chrome/browser/customtabs/features/branding/ToolbarBrandingOverlayViewBinder.java
@@ -12,6 +12,7 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import org.chromium.build.annotations.NullMarked;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.theme.ThemeUtils;
@@ -19,6 +20,7 @@
 import org.chromium.ui.modelutil.PropertyModel;
 
 /** View binder for the toolbar branding overlay. */
+@NullMarked
 public class ToolbarBrandingOverlayViewBinder {
     private static final int MAX_DRAWABLE_LEVEL = 10_000;
 
diff --git a/chrome/browser/ash/app_list/app_context_menu_unittest.cc b/chrome/browser/ash/app_list/app_context_menu_unittest.cc
index 6768b6c..800efb3 100644
--- a/chrome/browser/ash/app_list/app_context_menu_unittest.cc
+++ b/chrome/browser/ash/app_list/app_context_menu_unittest.cc
@@ -34,7 +34,6 @@
 #include "chrome/browser/ash/arc/icon_decode_request.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/extensions/menu_manager_factory.h"
 #include "chrome/common/chrome_paths.h"
@@ -299,7 +298,7 @@
 
     scoped_refptr<extensions::Extension> store = MakeApp(app_id, platform_app);
     registrar()->AddExtension(store.get());
-    service_->EnableExtension(app_id);
+    registrar()->EnableExtension(app_id);
 
     controller_ = std::make_unique<FakeAppListControllerDelegate>();
     controller_->SetAppPinnable(app_id, pinnable);
diff --git a/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc b/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc
index 3693af1..24baaaf3 100644
--- a/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc
+++ b/chrome/browser/ash/app_list/app_service/app_service_app_model_builder_unittest.cc
@@ -41,7 +41,6 @@
 #include "chrome/browser/ash/plugin_vm/plugin_vm_features.h"
 #include "chrome/browser/ash/plugin_vm/plugin_vm_test_helper.h"
 #include "chrome/browser/extensions/chrome_app_icon.h"
-#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/install_tracker.h"
 #include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -432,11 +431,11 @@
 }
 
 TEST_F(ExtensionAppTest, DisableAndEnable) {
-  service_->DisableExtension(kHostedAppId,
-                             extensions::disable_reason::DISABLE_USER_ACTION);
+  registrar()->DisableExtension(
+      kHostedAppId, {extensions::disable_reason::DISABLE_USER_ACTION});
   EXPECT_EQ(preinstalled_apps_, GetModelContent(model_updater_.get()));
 
-  service_->EnableExtension(kHostedAppId);
+  registrar()->EnableExtension(kHostedAppId);
   EXPECT_EQ(preinstalled_apps_, GetModelContent(model_updater_.get()));
 }
 
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 3fc4d0c..28e71a7 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
@@ -1198,7 +1198,7 @@
     const user_manager::UserManager* user_manager =
         user_manager::UserManager::Get();
     if (arc::ArcSessionManager::Get()->skipped_terms_of_service_negotiation() &&
-        !user_manager->IsLoggedInAsKioskApp() &&
+        !user_manager->IsLoggedInAsKioskChromeApp() &&
         !ash::UserSessionManager::GetInstance()->ui_shown_time().is_null()) {
       UMA_HISTOGRAM_CUSTOM_TIMES(
           "Arc.FirstAppLaunchRequest.TimeDelta",
diff --git a/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc b/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc
index 930a21c..f774b9f9 100644
--- a/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc
+++ b/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ash/app_list/search/app_search_provider_test_base.h"
 #include "chrome/browser/ash/app_list/search/types.h"
 #include "chrome/browser/ash/crostini/crostini_test_helper.h"
-#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chromeos/ash/components/dbus/chunneld/chunneld_client.h"
 #include "chromeos/ash/components/dbus/cicerone/cicerone_client.h"
@@ -40,6 +39,7 @@
 #include "components/services/app_service/public/cpp/icon_types.h"
 #include "components/services/app_service/public/cpp/stub_icon_loader.h"
 #include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/install_prefs_helper.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -151,12 +151,12 @@
   AddExtension(test_app_id_1, "Тестна апликација 1",
                ManifestLocation::kExternalPrefDownload,
                extensions::Extension::WAS_INSTALLED_BY_DEFAULT);
-  service_->EnableExtension(test_app_id_1);
+  registrar()->EnableExtension(test_app_id_1);
   const std::string test_app_id_2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
   AddExtension(test_app_id_2, "Тестна апликација 2",
                ManifestLocation::kExternalPrefDownload,
                extensions::Extension::WAS_INSTALLED_BY_DEFAULT);
-  service_->EnableExtension(test_app_id_2);
+  registrar()->EnableExtension(test_app_id_2);
 
   AddArcApp("Лажна апликација 1", "fake.app.first", "activity");
   AddArcApp("Лажна апликација 2", "fake.app.second", "activity");
@@ -197,11 +197,11 @@
 
   EXPECT_EQ("Hosted App", RunQuery("host"));
 
-  service_->DisableExtension(kHostedAppId,
-                             extensions::disable_reason::DISABLE_USER_ACTION);
+  registrar()->DisableExtension(
+      kHostedAppId, {extensions::disable_reason::DISABLE_USER_ACTION});
   EXPECT_EQ("Hosted App", RunQuery("host"));
 
-  service_->EnableExtension(kHostedAppId);
+  registrar()->EnableExtension(kHostedAppId);
   EXPECT_EQ("Hosted App", RunQuery("host"));
 }
 
@@ -521,7 +521,7 @@
                  ManifestLocation::kExternalPrefDownload,
                  extensions::Extension::WAS_INSTALLED_BY_OEM);
 
-    service_->EnableExtension(internal_app_id);
+    registrar()->EnableExtension(internal_app_id);
 
     EXPECT_TRUE(WasInstalledByOem(prefs, internal_app_id));
   }
diff --git a/chrome/browser/ash/app_list/search/app_zero_state_provider_unittest.cc b/chrome/browser/ash/app_list/search/app_zero_state_provider_unittest.cc
index 4ca178f0..fc036ea0 100644
--- a/chrome/browser/ash/app_list/search/app_zero_state_provider_unittest.cc
+++ b/chrome/browser/ash/app_list/search/app_zero_state_provider_unittest.cc
@@ -10,10 +10,10 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ash/app_list/search/app_search_provider_test_base.h"
-#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/crx_file/id_util.h"
 #include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/uninstall_reason.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -116,7 +116,7 @@
                  kDefaultRecommendedWebAppNames[i],
                  ManifestLocation::kExternalPrefDownload,
                  extensions::Extension::WAS_INSTALLED_BY_DEFAULT);
-    service_->EnableExtension(kDefaultRecommendedWebAppIds[i]);
+    registrar()->EnableExtension(kDefaultRecommendedWebAppIds[i]);
   }
 
   // Install the default ARC app (Play Store). This is marked here as sticky so
@@ -189,14 +189,14 @@
   AddExtension(shown_app_id, kNormalAppName, ManifestLocation::kComponent,
                extensions::Extension::WAS_INSTALLED_BY_DEFAULT,
                /*display_in_launcher=*/true);
-  service_->EnableExtension(shown_app_id);
+  registrar()->EnableExtension(shown_app_id);
 
   const std::string hidden_app_id =
       crx_file::id_util::GenerateId(kHiddenAppName);
   AddExtension(hidden_app_id, kHiddenAppName, ManifestLocation::kComponent,
                extensions::Extension::WAS_INSTALLED_BY_DEFAULT,
                /*display_in_launcher=*/false);
-  service_->EnableExtension(hidden_app_id);
+  registrar()->EnableExtension(hidden_app_id);
 
   base::RunLoop().RunUntilIdle();
 
diff --git a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
index a54c8d6..b12af6bf 100644
--- a/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
+++ b/chrome/browser/ash/app_mode/startup_app_launcher_unittest.cc
@@ -1294,8 +1294,8 @@
   // initially, so the test can verify the app gets enabled regardless of the
   // initial state.
   PreinstallApp(*SecondaryAppBuilder(kExtraSecondaryAppId).Build());
-  service()->DisableExtension(kExtraSecondaryAppId,
-                              extensions::disable_reason::DISABLE_USER_ACTION);
+  registrar()->DisableExtension(
+      kExtraSecondaryAppId, {extensions::disable_reason::DISABLE_USER_ACTION});
 
   InitializeLauncherWithNetworkReady();
   ASSERT_TRUE(DownloadAndInstallPrimaryApp(*primary_app));
@@ -1326,8 +1326,8 @@
   PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build());
 
   PreinstallApp(*SecondaryAppBuilder(kExtraSecondaryAppId).Build());
-  service()->DisableExtension(kExtraSecondaryAppId,
-                              extensions::disable_reason::DISABLE_USER_ACTION);
+  registrar()->DisableExtension(
+      kExtraSecondaryAppId, {extensions::disable_reason::DISABLE_USER_ACTION});
 
   InitializeLauncherWithNetworkReady();
   ASSERT_TRUE(DownloadAndInstallPrimaryApp(*primary_app));
@@ -1360,7 +1360,7 @@
   PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build());
   // Disable the secodnary app for a reason different than user action - that
   // disable reason should not be overriden during the kiosk launch.
-  service()->DisableExtension(
+  registrar()->DisableExtension(
       kSecondaryAppId, {extensions::disable_reason::DISABLE_USER_ACTION,
                         extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY});
 
@@ -1425,8 +1425,8 @@
            .set_offline_enabled(false)
            .Build());
   PreinstallApp(*SecondaryAppBuilder(kSecondaryAppId).Build());
-  service()->DisableExtension(kSecondaryAppId,
-                              extensions::disable_reason::DISABLE_USER_ACTION);
+  registrar()->DisableExtension(
+      kSecondaryAppId, {extensions::disable_reason::DISABLE_USER_ACTION});
 
   scoped_refptr<const Extension> primary_app_update =
       PrimaryAppBuilder()
@@ -1462,8 +1462,8 @@
   // Add the secondary app that should be enabled on launch - make it disabled
   // initially, and let test verify the app remains disabled during the launch.
   PreinstallApp(*SecondaryAppBuilder(kExtraSecondaryAppId).Build());
-  service()->DisableExtension(kExtraSecondaryAppId,
-                              extensions::disable_reason::DISABLE_USER_ACTION);
+  registrar()->DisableExtension(
+      kExtraSecondaryAppId, {extensions::disable_reason::DISABLE_USER_ACTION});
 
   startup_app_launcher_ = CreateStartupAppLauncherForSessionRestore();
 
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc
index d47d0dd7..7b34725 100644
--- a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc
+++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle.cc
@@ -68,6 +68,11 @@
          (url.host() == extension_urls::GetNewWebstoreLaunchURL().host());
 }
 
+bool IsBocaAppHostURL(const GURL& url) {
+  return (url.SchemeIs(content::kChromeUIUntrustedScheme) &&
+          url.host() == boca::kChromeBocaAppHost);
+}
+
 }  // namespace
 
 OnTaskLockedSessionNavigationThrottle::OnTaskLockedSessionNavigationThrottle(
@@ -180,14 +185,11 @@
   // an exception), blob urls, non-boca app chrome urls, and other local
   // schemes.
   const GURL& url = navigation_handle()->GetURL();
-  bool is_boca_app_host_url =
-      (url.SchemeIs(content::kChromeUIUntrustedScheme) &&
-       url.host() == boca::kChromeBocaAppHost);
   return (navigation_handle()->IsDownload() ||
           (navigation_handle()->GetRequestMethod() !=
                net::HttpRequestHeaders::kGetMethod &&
            !navigation_handle()->IsFormSubmission()) ||
-          (!url.SchemeIsHTTPOrHTTPS() && !is_boca_app_host_url) ||
+          (!url.SchemeIsHTTPOrHTTPS() && !IsBocaAppHostURL(url)) ||
           IsChromeWebStoreURL(url));
 }
 
@@ -241,6 +243,14 @@
   }
   const GURL& url = navigation_handle()->GetURL();
 
+  // There is no nav restriction associated with the home tab so the blocklist
+  // may enforce nav restrictions based on the previous active tab. We allow all
+  // requests to the home URL to go through for now.
+  // TODO(crbug.com/413468168) - Associate a nav restriction with the home tab.
+  if (IsBocaAppHostURL(url)) {
+    return PROCEED;
+  }
+
   // Checks if the query is the end of an OAuth login. If so, then we want
   // to let these pass.
   if (IsOauthLoginComplete(url)) {
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc
index 6b59c08..c787bf98 100644
--- a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc
+++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc
@@ -7,6 +7,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/system/toast_data.h"
+#include "ash/webui/boca_ui/url_constants.h"
 #include "ash/webui/system_apps/public/system_web_app_type.h"
 #include "base/containers/span.h"
 #include "base/memory/raw_ptr.h"
@@ -346,6 +347,40 @@
 }
 
 IN_PROC_BROWSER_TEST_F(OnTaskLockedSessionNavigationThrottleInteractiveUITest,
+                       AllowNavigationsToBocaHomepage) {
+  // Launch OnTask SWA.
+  base::test::TestFuture<bool> launch_future;
+  system_web_app_manager()->LaunchSystemWebAppAsync(
+      launch_future.GetCallback());
+  ASSERT_TRUE(launch_future.Get());
+  Browser* const boca_app_browser = FindBocaSystemWebAppBrowser();
+  ASSERT_THAT(boca_app_browser, NotNull());
+  ASSERT_TRUE(boca_app_browser->IsLockedForOnTask());
+
+  // Set up window tracker to track the app window.
+  const SessionID window_id = boca_app_browser->session_id();
+  ASSERT_TRUE(window_id.is_valid());
+  system_web_app_manager()->SetWindowTrackerForSystemWebAppWindow(
+      window_id, /*observers=*/{});
+
+  // Spawn tab for testing purposes.
+  CreateBackgroundTabAndWait(window_id,
+                             embedded_test_server()->GetURL(kTabUrl1Host, "/"),
+                             ::boca::LockedNavigationOptions::BLOCK_NAVIGATION);
+  auto* const tab_strip_model = boca_app_browser->tab_strip_model();
+  ASSERT_EQ(tab_strip_model->count(), 2);
+  tab_strip_model->ActivateTabAt(1);
+  WaitForUrlBlocklistUpdate();
+
+  // Navigate to Boca homepage and verify it goes through.
+  const GURL boca_app_url(boca::kChromeBocaAppUntrustedURL);
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(boca_app_browser, boca_app_url));
+  EXPECT_EQ(tab_strip_model->GetActiveWebContents()->GetLastCommittedURL(),
+            boca_app_url);
+  VerifyUrlBlockedToastShown(/*toast_was_shown=*/false);
+}
+
+IN_PROC_BROWSER_TEST_F(OnTaskLockedSessionNavigationThrottleInteractiveUITest,
                        AllowUrlsForOpenNavRestriction) {
   // Launch OnTask SWA.
   base::test::TestFuture<bool> launch_future;
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker.cc
index f07fe61..42740b6 100644
--- a/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker.cc
+++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker.cc
@@ -374,6 +374,11 @@
 // BrowserListObserver Implementation
 void LockedSessionWindowTracker::OnBrowserClosing(Browser* browser) {
   if (browser == browser_) {
+    // Notify not in workbook when boca closed.
+    for (auto& observer : observers_) {
+      observer.OnActiveTabChanged(
+          l10n_util::GetStringUTF16(IDS_NOT_IN_CLASS_TOOLS));
+    }
     CleanupWindowTracker();
   }
   if (browser->type() == Browser::Type::TYPE_APP_POPUP) {
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc
index fd957dc5..18f52fb 100644
--- a/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc
+++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_window_tracker_browsertest.cc
@@ -894,18 +894,29 @@
   // Set up window tracker to track the app window.
   const SessionID window_id = boca_app_browser->session_id();
   ASSERT_TRUE(window_id.is_valid());
+  MockBocaWindowObserver window_observer;
   system_web_app_manager()->SetWindowTrackerForSystemWebAppWindow(
-      window_id, /*observers=*/{});
+      window_id, /*observers=*/{&window_observer});
   system_web_app_manager()->SetPinStateForSystemWebAppWindow(/*pinned=*/true,
                                                              window_id);
   ASSERT_TRUE(platform_util::IsBrowserLockedFullscreen(boca_app_browser));
 
+  // The first one triggered by boca no longer set active. The second triggered
+  // due to browser closing.
+  EXPECT_CALL(
+      window_observer,
+      OnActiveTabChanged(l10n_util::GetStringUTF16(IDS_NOT_IN_CLASS_TOOLS)))
+      .Times(2);
+  EXPECT_CALL(window_observer, OnWindowTrackerCleanedup).Times(1);
+
   // Close the app and verify the window tracker stops tracking it.
   boca_app_browser->window()->Close();
   content::RunAllTasksUntilIdle();
+
   auto* const window_tracker =
       LockedSessionWindowTrackerFactory::GetInstance()->GetForBrowserContext(
           profile());
+
   EXPECT_THAT(window_tracker->browser(), IsNull());
 }
 
diff --git a/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl.cc b/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl.cc
index af66700..cdc27e1 100644
--- a/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl.cc
+++ b/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl.cc
@@ -16,6 +16,7 @@
 #include "base/functional/bind.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/metrics/histogram_functions.h"
 #include "chrome/browser/ash/policy/remote_commands/crd/public/crd_session_result_codes.h"
 #include "chrome/browser/ash/policy/remote_commands/crd/public/shared_crd_session.h"
 #include "chrome/browser/ash/policy/remote_commands/crd/public/shared_crd_session_provider.h"
@@ -23,10 +24,11 @@
 namespace ash::boca {
 namespace {
 using SessionParameters = policy::SharedCrdSession::SessionParameters;
+constexpr char kCrdResultUma[] = "Enterprise.Boca.Spotlight.Crd.Result";
 
-// TODO: dorianbrandon - Log result to UMA.
 void LogCrdError(policy::ExtendedStartCrdSessionResultCode result_code,
                  const std::string& message) {
+  base::UmaHistogramEnumeration(kCrdResultUma, result_code);
   LOG(WARNING) << "[Boca] Failed to start Spotlight session on student due to "
                << "CRD error (code " << static_cast<int>(result_code)
                << ", message '" << message << "')";
diff --git a/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl_browsertest.cc b/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl_browsertest.cc
index 365e29d..81c365d 100644
--- a/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl_browsertest.cc
+++ b/chrome/browser/ash/boca/spotlight/spotlight_crd_manager_impl_browsertest.cc
@@ -14,6 +14,8 @@
 #include "base/functional/callback_forward.h"
 #include "base/json/json_writer.h"
 #include "base/memory/raw_ptr.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
@@ -41,6 +43,7 @@
 namespace {
 constexpr char kSpotlightConnectionCode[] = "123";
 constexpr char kUserEmail[] = "cat@gmail.com";
+constexpr char kCrdResultUma[] = "Enterprise.Boca.Spotlight.Crd.Result";
 
 class MockSharedCrdSession : public policy::SharedCrdSession {
  public:
@@ -108,10 +111,16 @@
 IN_PROC_BROWSER_TEST_F(
     SpotlightCrdManagerImplTest,
     InitiateSpotlightSessionWithCrdFailureShouldRunErrorCallback) {
+  base::HistogramTester histograms;
+
   TestFuture<void> error_callback_future;
   EXPECT_CALL(*crd_session_, StartCrdHost)
-      .WillOnce(WithArg<2>(
-          Invoke([&](auto callback) { error_callback_future.SetValue(); })));
+      .WillOnce(WithArg<2>(Invoke([&](auto callback) {
+        base::UmaHistogramEnumeration(
+            kCrdResultUma,
+            policy::ExtendedStartCrdSessionResultCode::kFailureCrdHostError);
+        error_callback_future.SetValue();
+      })));
 
   manager_->OnSessionStarted(kUserEmail);
   manager_->InitiateSpotlightSession(
@@ -121,6 +130,11 @@
   ::testing::Mock::VerifyAndClearExpectations(crd_session_);
 
   EXPECT_TRUE(error_callback_future.Wait());
+  EXPECT_EQ(
+      histograms.GetBucketCount(
+          kCrdResultUma,
+          policy::ExtendedStartCrdSessionResultCode::kFailureCrdHostError),
+      1);
 }
 
 IN_PROC_BROWSER_TEST_F(SpotlightCrdManagerImplTest,
diff --git a/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc b/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
index 99060da3c..777ac539 100644
--- a/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
+++ b/chrome/browser/ash/child_accounts/time_limits/app_service_wrapper_unittest.cc
@@ -107,13 +107,11 @@
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         switches::kDisableDefaultApps);
 
-    extensions::TestExtensionSystem* extension_system(
-        static_cast<extensions::TestExtensionSystem*>(
-            extensions::ExtensionSystem::Get(&profile_)));
-    extension_service_ = extension_system->CreateExtensionService(
+    auto* extension_system(static_cast<extensions::TestExtensionSystem*>(
+        extensions::ExtensionSystem::Get(&profile_)));
+    auto* extension_service = extension_system->CreateExtensionService(
         base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
-    extension_service_->Init();
-    extension_registrar_ = extensions::ExtensionRegistrar::Get(&profile_);
+    extension_service->Init();
 
     web_app::test::AwaitStartWebAppProviderAndSubsystems(&profile_);
 
@@ -126,7 +124,8 @@
     // Install Chrome.
     scoped_refptr<extensions::Extension> chrome = CreateExtension(
         app_constants::kChromeAppId, kExtensionNameChrome, kExtensionAppUrl);
-    extension_registrar_->AddComponentExtension(chrome.get());
+    extensions::ExtensionRegistrar::Get(&profile_)->AddComponentExtension(
+        chrome.get());
     task_environment_.RunUntilIdle();
   }
 
@@ -198,7 +197,7 @@
 
     if (app_id.app_type() == apps::AppType::kChromeApp ||
         app_id.app_type() == apps::AppType::kWeb) {
-      extension_registrar_->RemoveExtension(
+      extensions::ExtensionRegistrar::Get(&profile_)->RemoveExtension(
           app_id.app_id(), extensions::UnloadedExtensionReason::UNINSTALL);
       task_environment_.RunUntilIdle();
       return;
@@ -227,12 +226,13 @@
 
     if (app_id.app_type() == apps::AppType::kChromeApp ||
         app_id.app_type() == apps::AppType::kWeb) {
+      auto* registrar = extensions::ExtensionRegistrar::Get(&profile_);
       if (disabled) {
-        extension_service_->DisableExtension(
+        registrar->DisableExtension(
             app_id.app_id(),
-            extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY);
+            {extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY});
       } else {
-        extension_service_->EnableExtension(app_id.app_id());
+        registrar->EnableExtension(app_id.app_id());
       }
       task_environment_.RunUntilIdle();
       return;
@@ -247,9 +247,6 @@
   apps::AppServiceTest app_service_test_;
   ArcAppTest arc_test_;
 
-  raw_ptr<extensions::ExtensionService> extension_service_ = nullptr;
-  raw_ptr<extensions::ExtensionRegistrar> extension_registrar_ = nullptr;
-
   AppServiceWrapper tested_wrapper_{&profile_};
   MockListener test_listener_;
 };
diff --git a/chrome/browser/ash/login/demo_mode/demo_login_controller.cc b/chrome/browser/ash/login/demo_mode/demo_login_controller.cc
index e69c8340..53b1db4 100644
--- a/chrome/browser/ash/login/demo_mode/demo_login_controller.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_login_controller.cc
@@ -77,6 +77,21 @@
 const char kErrorMessagePath[] = "error.message";
 const char kErrorStatusPath[] = "error.status";
 
+// Server may return a 200 for setup demo account request with Quota exhuasted
+// error. Sample response:
+//  {
+//    "status": {
+//      "code": 8
+//    }
+//    "retry_details": {}
+//  }
+constexpr char kStatusCodePath[] = "status.code";
+constexpr char kRetryDetailsPath[] = "retry_details";
+
+// TODO(crbugs.com/355727308): Consider using
+// components/enterprise/common/proto/google3_protos.proto.
+constexpr int kServerResourceExhuastedCode = 8;
+
 constexpr char kDemoModeSignInEnabledPath[] = "forceEnabled";
 
 constexpr net::NetworkTrafficAnnotationTag kSetupAccountTrafficAnnotation =
@@ -253,7 +268,6 @@
   const std::optional<int> code = error->FindIntByDottedPath(kErrorCodePath);
   const auto* msg = error->FindStringByDottedPath(kErrorMessagePath);
   const auto* status = error->FindStringByDottedPath(kErrorStatusPath);
-
   LOG(ERROR) << base::StringPrintf(
       "%s error code: %d; message: %s; status: %s.", response_name,
       code ? *code : -1, msg ? *msg : "", status ? *status : "");
@@ -373,9 +387,6 @@
   // Try demo account login first by disable auto-login to managed guest
   // session.
   state_ = State::kSetupDemoAccountInProgress;
-  // TODO(crbug.com/387572263): figure out whether should ignore the power idle
-  // policy when fallback to MGS when sign in is enable.
-  demo_mode::SetDoNothingWhenPowerIdle();
 
   MaybeCleanupPreviousDemoAccount();
 }
@@ -451,16 +462,32 @@
 void DemoLoginController::HandleSetupDemoAcountResponse(
     const std::string& sign_in_scoped_device_id,
     const std::unique_ptr<std::string> response_body) {
-  std::optional<base::Value::Dict> gaia_creds(
+  std::optional<base::DictValue> response_json(
       base::JSONReader::ReadDict(*response_body));
-  if (!gaia_creds) {
+  if (!response_json) {
     OnSetupDemoAccountError(ResultCode::kResponseParsingError);
     return;
   }
 
-  const auto* email = gaia_creds->FindString(kDemoAccountEmail);
-  const auto* gaia_id = gaia_creds->FindString(kDemoAccountGaiaId);
-  const auto* auth_code = gaia_creds->FindString(kDemoAccountAuthCode);
+  const std::optional<int> code =
+      response_json->FindIntByDottedPath(kStatusCodePath);
+  if (code && *code == kServerResourceExhuastedCode) {
+    // TODO(crbugs.com/355727308): Right now, we retry with a random delay if
+    // `retry_details` exists. In later version we will decide the retry delay
+    // from `retry_details`.
+    base::DictValue* retry_details = response_json->FindDict(kRetryDetailsPath);
+    if (retry_details) {
+      demo_mode::TurnOnScheduleLogoutForMGS();
+      OnSetupDemoAccountError(ResultCode::kQuotaExhaustedRetriable);
+    } else {
+      OnSetupDemoAccountError(ResultCode::kQuotaExhaustedNotRetriable);
+    }
+    return;
+  }
+
+  const auto* email = response_json->FindString(kDemoAccountEmail);
+  const auto* gaia_id = response_json->FindString(kDemoAccountGaiaId);
+  const auto* auth_code = response_json->FindString(kDemoAccountAuthCode);
   if (!email || !gaia_id || !auth_code) {
     OnSetupDemoAccountError(ResultCode::kInvalidCreds);
     return;
@@ -474,6 +501,9 @@
       gaia::CanonicalizeEmail(*email));
   DCHECK_EQ(State::kSetupDemoAccountInProgress, state_);
   state_ = State::kLoginDemoAccount;
+
+  // Enable 24 hour session by overriding power policy.
+  demo_mode::SetDoNothingWhenPowerIdle();
   DemoSessionMetricsRecorder::SetCurrentSessionType(
       DemoSessionMetricsRecorder::SessionType::kSignedInDemoSession);
 
diff --git a/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc b/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc
index 96be6ac..f33c614 100644
--- a/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_login_controller_unittest.cc
@@ -73,6 +73,14 @@
           }
     })";
 
+constexpr char kSetupDemoAccountFailedRetriableResponse[] =
+    R"({
+      "retry_details":{},
+      "status":{
+        "code":8
+      }
+    })";
+
 constexpr char kSetupDemoAccountUrl[] =
     "https://demomode-pa.googleapis.com/v1/accounts";
 
@@ -647,6 +655,33 @@
   loop.Run();
 }
 
+TEST_F(DemoLoginControllerTest, SetupDemoAccountErrorRetriable) {
+  SetUpPolicyClient();
+  test_url_loader_factory_.AddResponse(
+      GetSetupUrl().spec(), kSetupDemoAccountFailedRetriableResponse);
+
+  EXPECT_CALL(login_display_host(), CompleteLogin).Times(0);
+  base::RunLoop loop;
+  GetDemoLoginController()->SetSetupFailedCallbackForTest(
+      base::BindLambdaForTesting([&]() {
+        // Expect the setup request to fail by checking metrics.
+        histogram_tester_.ExpectTotalCount(
+            kSetupDemoAccountRequestResultHistogram, 1);
+        histogram_tester_.ExpectBucketCount(
+            kSetupDemoAccountRequestResultHistogram,
+            DemoSessionMetricsRecorder::DemoAccountRequestResultCode::
+                kQuotaExhaustedRetriable,
+            1);
+        EXPECT_EQ(DemoSessionMetricsRecorder::GetCurrentSessionTypeForTesting(),
+                  DemoSessionMetricsRecorder::SessionType::kFallbackMGS);
+        loop.Quit();
+      }));
+  // Verify demo account login gets triggered by `ExistingUserController`.
+  ConfigureAutoLoginSetting();
+  loop.Run();
+  EXPECT_TRUE(demo_mode::GetShouldScheduleLogoutForMGS());
+}
+
 // TODO(crbug.com/372771485): Add more request fail test cases.
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.cc b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.cc
index ab6b463..ac8f5d4f 100644
--- a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.cc
@@ -10,13 +10,18 @@
 #include "ash/constants/ash_pref_names.h"
 #include "ash/metrics/demo_session_metrics_recorder.h"
 #include "ash/public/cpp/wallpaper/wallpaper_controller.h"
+#include "ash/session/session_controller_impl.h"
+#include "ash/shell.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
+#include "base/rand_util.h"
+#include "base/syslog_logging.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/ash/drive/drive_integration_service.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
 #include "chromeos/ash/experiences/idle_detector/idle_detector.h"
 #include "components/drive/file_system_core_util.h"
 #include "components/prefs/pref_service.h"
@@ -29,7 +34,12 @@
 
 // Amount of idle time for re-launch demo mode swa with demo account login.
 // TODO(crbug.com/380941267): Use a policy to control this the idle duration.
-const base::TimeDelta kReLuanchDemoAppIdleDuration = base::Seconds(90);
+constexpr base::TimeDelta kReLuanchDemoAppIdleDuration = base::Seconds(90);
+
+// The range of logout delay for current fallback MGS.
+// TODO(crbugs.com/355727308): Get logout delay from server response.
+constexpr base::TimeDelta kLogoutDelayMin = base::Minutes(60);
+constexpr base::TimeDelta kLogoutDelayMax = base::Minutes(90);
 
 // The list of prefs that are reset on the start of each shopper session.
 const char* const kPrefsPrefixToReset[] = {
@@ -90,6 +100,14 @@
       FROM_HERE, base::BindOnce(&DeleteAllFilesUnderPath, root));
 }
 
+void LogoutCurrentSession() {
+  if (Shell::HasInstance()) {
+    SYSLOG(INFO)
+        << "Logout current demo mode session for retrying sign in demo account";
+    Shell::Get()->session_controller()->RequestSignOut();
+  }
+}
+
 }  // namespace
 
 DemoModeIdleHandler::DemoModeIdleHandler(
@@ -99,11 +117,34 @@
       blocking_task_runner_(blocking_task_runner),
       file_cleaner_(blocking_task_runner) {
   user_activity_observer_.Observe(ui::UserActivityDetector::Get());
+
+  // Maybe schedule a logout for current session. The timer will be reset if
+  // there's any user activity.
+  if (demo_mode::GetShouldScheduleLogoutForMGS()) {
+    mgs_logout_timer_.emplace();
+    SYSLOG(INFO) << "Start Logout timer to retry login with demo account.";
+    mgs_logout_timer_->Start(
+        FROM_HERE, base::RandTimeDelta(kLogoutDelayMin, kLogoutDelayMax),
+        base::BindOnce(&LogoutCurrentSession));
+  }
 }
 
 DemoModeIdleHandler::~DemoModeIdleHandler() = default;
 
 void DemoModeIdleHandler::OnUserActivity(const ui::Event* event) {
+  // If there's user activity, no need `mgs_logout_timer_` any more. Device will
+  // auto logout after 90s idle if logout is required.
+  if (mgs_logout_timer_ && mgs_logout_timer_->IsRunning()) {
+    mgs_logout_timer_.reset();
+  }
+
+  // Check whether the power policy has been overridden and a 24 hour session is
+  // enable. If the 24h session is disabled, return early. The power policy will
+  // detect device idle and handle session behaviour.
+  if (!demo_mode::ForceSessionLengthCountFromSessionStarts()) {
+    return;
+  }
+
   // We only start the `idle_detector_` timer on the first user activity. If
   // the user is already active, we don't need to do this again.
   if (is_user_active_) {
diff --git a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.h b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.h
index 87a8153..f88e5d8 100644
--- a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.h
+++ b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_ASH_LOGIN_DEMO_MODE_DEMO_MODE_IDLE_HANDLER_H_
 #define CHROME_BROWSER_ASH_LOGIN_DEMO_MODE_DEMO_MODE_IDLE_HANDLER_H_
 
+#include <optional>
 #include <string>
 
 #include "base/functional/callback.h"
@@ -12,6 +13,7 @@
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "base/scoped_observation.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/ash/login/demo_mode/demo_mode_window_closer.h"
 #include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h"
 #include "ui/base/user_activity/user_activity_detector.h"
@@ -48,6 +50,9 @@
   // Removes an observer from the observer list.
   void RemoveObserver(Observer* observer);
 
+  const std::optional<base::OneShotTimer>& GetMGSLogoutTimeoutForTest() const {
+    return mgs_logout_timer_;
+  }
   void SetIdleTimeoutForTest(std::optional<base::TimeDelta> timeout);
 
  private:
@@ -70,6 +75,10 @@
   // with device.
   std::unique_ptr<IdleDetector> idle_detector_;
 
+  // Timer to schedule logout for the fallback MGS due to peak time requests of
+  // demo account.
+  std::optional<base::OneShotTimer> mgs_logout_timer_;
+
   // Not owned:
   raw_ptr<DemoModeWindowCloser> window_closer_;
 
diff --git a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc
index 4469ffa..ab272f2 100644
--- a/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc
+++ b/chrome/browser/ash/login/demo_mode/demo_mode_idle_handler_unittest.cc
@@ -28,6 +28,7 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -40,6 +41,7 @@
 namespace {
 
 const base::TimeDelta kReLuanchDemoAppIdleDuration = base::Seconds(90);
+const base::TimeDelta kLogoutDelayMax = base::Minutes(90);
 
 const char kUser[] = "user@gmail.com";
 const AccountId kAccountId =
@@ -48,23 +50,17 @@
 
 }  // namespace
 
-class DemoModeIdleHandlerTest : public ChromeAshTestBase {
+class DemoModeIdleHandlerTestBase : public ChromeAshTestBase {
  protected:
-  DemoModeIdleHandlerTest()
+  DemoModeIdleHandlerTestBase()
       : ChromeAshTestBase(std::make_unique<content::BrowserTaskEnvironment>(
             base::test::TaskEnvironment::TimeSource::MOCK_TIME)),
         profile_manager_(TestingBrowserProcess::GetGlobal()) {
     window_closer_ = std::make_unique<DemoModeWindowCloser>(
-        base::BindRepeating(&DemoModeIdleHandlerTest::MockLaunchDemoModeApp,
+        base::BindRepeating(&DemoModeIdleHandlerTestBase::MockLaunchDemoModeApp,
                             base::Unretained(this)));
-
-    // OK to unretained `this` since the life cycle of `demo_mode_idle_handler_`
-    // is the same as the tests.
-    demo_mode_idle_handler_ = std::make_unique<DemoModeIdleHandler>(
-        window_closer_.get(),
-        base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}));
   }
-  ~DemoModeIdleHandlerTest() override = default;
+  ~DemoModeIdleHandlerTestBase() override = default;
 
   void SetUp() override {
     ChromeAshTestBase::SetUp();
@@ -92,6 +88,12 @@
     // to be not null. Once `metrics_recorder_` is created, it'll observe user
     // activity to set `first_user_activity_`.
     metrics_recorder_ = std::make_unique<DemoSessionMetricsRecorder>();
+
+    // OK to unretained `this` since the life cycle of `demo_mode_idle_handler_`
+    // is the same as the tests.
+    demo_mode_idle_handler_ = std::make_unique<DemoModeIdleHandler>(
+        window_closer_.get(),
+        base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}));
   }
 
   void TearDown() override {
@@ -122,6 +124,10 @@
     return wallpaper_controller_;
   }
 
+  DemoModeIdleHandler* demo_mode_idle_handler() {
+    return demo_mode_idle_handler_.get();
+  }
+
  private:
   user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
       fake_user_manager_{std::make_unique<FakeChromeUserManager>()};
@@ -140,7 +146,19 @@
   std::unique_ptr<DemoSessionMetricsRecorder> metrics_recorder_;
 };
 
+// DemoIdleHandler test for shopper session.
+class DemoModeIdleHandlerTest : public DemoModeIdleHandlerTestBase {
+  void SetUp() override {
+    DemoModeIdleHandlerTestBase::SetUp();
+    demo_mode::SetDoNothingWhenPowerIdle();
+  }
+};
+
 TEST_F(DemoModeIdleHandlerTest, CloseAllBrowsers) {
+  // Ensure MGS logout timer not started.
+  EXPECT_FALSE(
+      demo_mode_idle_handler()->GetMGSLogoutTimeoutForTest().has_value());
+
   // Initialize 2 browsers.
   std::unique_ptr<Browser> browser_1 = CreateBrowserWithTestWindowForParams(
       Browser::CreateParams(profile(), /*user_gesture=*/true));
@@ -275,4 +293,29 @@
   EXPECT_EQ(get_launch_demo_app_count(), 2);
 }
 
+// DemoIdleHandler test for fallback MGS.
+class DemoModeIdleHandlerTestMGS : public DemoModeIdleHandlerTestBase {
+  void SetUp() override {
+    demo_mode::TurnOnScheduleLogoutForMGS();
+    DemoModeIdleHandlerTestBase::SetUp();
+  }
+};
+
+TEST_F(DemoModeIdleHandlerTestMGS, ScheduleLogout) {
+  // Ensure logout in `kLogoutDelayMax`.
+  FastForwardBy(kLogoutDelayMax);
+  EXPECT_EQ(GetSessionControllerClient()->request_sign_out_count(), 1);
+}
+
+TEST_F(DemoModeIdleHandlerTestMGS, LogoutTimerResetOnUserActivity) {
+  // TODO(crbugs.com/355727308): `SimulateUserActivity` is not call synchronized
+  // here. Figure why.
+  demo_mode_idle_handler()->OnUserActivity(nullptr);
+  FastForwardBy(kReLuanchDemoAppIdleDuration);
+
+  // Expected the timer is reset.
+  EXPECT_FALSE(
+      demo_mode_idle_handler()->GetMGSLogoutTimeoutForTest().has_value());
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ash/login/session/session_length_limiter_unittest.cc b/chrome/browser/ash/login/session/session_length_limiter_unittest.cc
index a37929a..b020cb5 100644
--- a/chrome/browser/ash/login/session/session_length_limiter_unittest.cc
+++ b/chrome/browser/ash/login/session/session_length_limiter_unittest.cc
@@ -20,7 +20,10 @@
 #include "chrome/browser/ash/settings/scoped_cros_settings_test_helper.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
+#include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
 #include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
+#include "chromeos/dbus/power/fake_power_manager_client.h"
+#include "chromeos/dbus/power/power_policy_controller.h"
 #include "components/prefs/testing_pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -825,6 +828,10 @@
     SessionLengthLimiterTest::SetUp();
     features_.InitAndEnableFeature(features::kDemoModeSignIn);
 
+    chromeos::PowerManagerClient::InitializeFake();
+    chromeos::PowerPolicyController::Initialize(
+        chromeos::FakePowerManagerClient::Get());
+
     settings_helper_.InstallAttributes()->SetDemoMode();
     settings_helper_.ReplaceDeviceSettingsProviderWithStub();
   }
@@ -837,6 +844,7 @@
 // Verifies that local state is correctly updated when waiting for initial user
 // activity is toggled and no user activity has occurred yet.
 TEST_F(DemoModeSessionLengthLimiterTest, DemoModeForceNotWaitUserActivity) {
+  demo_mode::SetDoNothingWhenPowerIdle();
   CreateSessionLengthLimiter(false);
 
   // Verify that the pref indicating user activity was not set and the session
diff --git a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
index 6799777..a033551a 100644
--- a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
@@ -378,7 +378,7 @@
                      : false;
 }
 
-bool FakeChromeUserManager::IsLoggedInAsKioskApp() const {
+bool FakeChromeUserManager::IsLoggedInAsKioskChromeApp() const {
   const user_manager::User* active_user = GetActiveUser();
   return active_user
              ? active_user->GetType() == user_manager::UserType::kKioskApp
diff --git a/chrome/browser/ash/login/users/fake_chrome_user_manager.h b/chrome/browser/ash/login/users/fake_chrome_user_manager.h
index 7b343ee..9254eee 100644
--- a/chrome/browser/ash/login/users/fake_chrome_user_manager.h
+++ b/chrome/browser/ash/login/users/fake_chrome_user_manager.h
@@ -104,7 +104,7 @@
   bool IsLoggedInAsChildUser() const override;
   bool IsLoggedInAsManagedGuestSession() const override;
   bool IsLoggedInAsGuest() const override;
-  bool IsLoggedInAsKioskApp() const override;
+  bool IsLoggedInAsKioskChromeApp() const override;
   bool IsLoggedInAsWebKioskApp() const override;
   bool IsLoggedInAsKioskIWA() const override;
   bool IsLoggedInAsAnyKioskApp() const override;
diff --git a/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc b/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc
index 69c17901..d89c0f9 100644
--- a/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd/crd_remote_command_utils.cc
@@ -39,7 +39,7 @@
 
 const ash::KioskAppManagerBase* GetKioskAppManager(
     const user_manager::UserManager& user_manager) {
-  if (user_manager.IsLoggedInAsKioskApp()) {
+  if (user_manager.IsLoggedInAsKioskChromeApp()) {
     return ash::KioskChromeAppManager::Get();
   }
   if (user_manager.IsLoggedInAsWebKioskApp()) {
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc
index 302f4ec..a5fee81b 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client_browsertest.cc
@@ -32,12 +32,14 @@
 class MockLensOverlayController : public LensOverlayController {
  public:
   MockLensOverlayController(tabs::TabInterface* tab,
+                            LensSearchController* lens_search_controller,
                             variations::VariationsClient* variations_client,
                             signin::IdentityManager* identity_manager,
                             PrefService* pref_service,
                             syncer::SyncService* sync_service,
                             ThemeService* theme_service)
       : LensOverlayController(tab,
+                              lens_search_controller,
                               variations_client,
                               identity_manager,
                               pref_service,
@@ -61,14 +63,15 @@
 
   std::unique_ptr<LensOverlayController> CreateLensOverlayController(
       tabs::TabInterface* tab,
+      LensSearchController* lens_search_controller,
       variations::VariationsClient* variations_client,
       signin::IdentityManager* identity_manager,
       PrefService* pref_service,
       syncer::SyncService* sync_service,
       ThemeService* theme_service) override {
     return std::make_unique<MockLensOverlayController>(
-        tab, variations_client, identity_manager, pref_service, sync_service,
-        theme_service);
+        tab, lens_search_controller, variations_client, identity_manager,
+        pref_service, sync_service, theme_service);
   }
 };
 
diff --git a/chrome/browser/chooser_controller/BUILD.gn b/chrome/browser/chooser_controller/BUILD.gn
index 92d896f5..b99bf55 100644
--- a/chrome/browser/chooser_controller/BUILD.gn
+++ b/chrome/browser/chooser_controller/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//extensions/buildflags/buildflags.gni")
+
 assert(is_win || is_mac || is_linux || is_chromeos || is_android)
 
 source_set("chooser_controller") {
@@ -45,6 +47,9 @@
     "//services/data_decoder/public/cpp:test_support",
     "//url",
   ]
+  if (enable_extensions) {
+    deps += [ "//chrome/browser/extensions" ]
+  }
   if (!is_android) {
     deps += [
       "//chrome/browser/extensions:test_support",
diff --git a/chrome/browser/chooser_controller/title_util_unittest.cc b/chrome/browser/chooser_controller/title_util_unittest.cc
index da0bbf9..0b94cfa 100644
--- a/chrome/browser/chooser_controller/title_util_unittest.cc
+++ b/chrome/browser/chooser_controller/title_util_unittest.cc
@@ -14,7 +14,7 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "base/values.h"
-#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_service.h"  // nogncheck
 #include "chrome/browser/extensions/test_extension_system.h"
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/extension_registry.h"
diff --git a/chrome/browser/commerce/android/java/res/layout/commerce_bottom_sheet_content_item_container.xml b/chrome/browser/commerce/android/java/res/layout/commerce_bottom_sheet_content_item_container.xml
index b0ac49947..08445664 100644
--- a/chrome/browser/commerce/android/java/res/layout/commerce_bottom_sheet_content_item_container.xml
+++ b/chrome/browser/commerce/android/java/res/layout/commerce_bottom_sheet_content_item_container.xml
@@ -15,6 +15,7 @@
         android:id="@+id/title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:screenReaderFocusable="true"
         android:accessibilityHeading="true"
         android:layout_marginBottom="@dimen/content_item_title_bottom_margin"
         android:textAppearance="@style/TextAppearance.TextAccentMediumThick" />
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java
index a1172f4..b7409eb8 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/ui/shared_image_tiles/SharedImageTilesConfig.java
@@ -105,13 +105,25 @@
         }
 
         /**
-         * Creates a builder with preset values for a tab group thumbnail.
+         * Creates a builder with preset values for a button.
+         *
+         * @param context The Android context.
+         * @return A builder instance configured for a button.
+         */
+        public static Builder createForButton(Context context) {
+            return new Builder(context)
+                    .setTextColor(SemanticColorUtils.getDefaultTextColorAccent1(context))
+                    .setTextStyle(R.style.TextAppearance_TextAccentMediumThick_Primary);
+        }
+
+        /**
+         * Creates a builder with preset values for being drawn on a tab group color.
          *
          * @param context The Android context.
          * @param tabGroupColorId The color id of the tab group.
-         * @return A builder instance configured for a tab group thumbnail.
+         * @return A builder instance configured for a tab group favicon.
          */
-        public static Builder createThumbnail(
+        public static Builder createForTabGroupColorContext(
                 Context context, @TabGroupColorId int tabGroupColorId) {
             return new Builder(context)
                     .setIconSizeDp(R.dimen.small_shared_image_tiles_icon_height)
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 65860b7..e8b7042 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -219,6 +219,8 @@
     "event_router_forwarder.h",
     "extension_action_dispatcher.cc",
     "extension_action_dispatcher.h",
+    "extension_action_storage_manager.cc",
+    "extension_action_storage_manager.h",
     "extension_allowlist.cc",
     "extension_allowlist.h",
     "extension_allowlist_factory.cc",
@@ -247,6 +249,8 @@
     "extension_management_internal.h",
     "extension_migrator.cc",
     "extension_migrator.h",
+    "extension_service.cc",
+    "extension_service.h",
     "extension_special_storage_policy.cc",
     "extension_special_storage_policy.h",
     "extension_tab_util.cc",
@@ -286,6 +290,10 @@
     "favicon/favicon_util.h",
     "forced_extensions/assessment_assistant_tracker.cc",
     "forced_extensions/assessment_assistant_tracker.h",
+    "forced_extensions/force_installed_metrics.cc",
+    "forced_extensions/force_installed_metrics.h",
+    "forced_extensions/force_installed_tracker.cc",
+    "forced_extensions/force_installed_tracker.h",
     "forced_extensions/install_stage_tracker.cc",
     "forced_extensions/install_stage_tracker.h",
     "forced_extensions/install_stage_tracker_factory.cc",
@@ -676,8 +684,6 @@
       "device_permissions_dialog_controller.h",
       "extension_action_runner.cc",
       "extension_action_runner.h",
-      "extension_action_storage_manager.cc",
-      "extension_action_storage_manager.h",
       "extension_browser_window_helper.cc",
       "extension_browser_window_helper.h",
       "extension_commands_global_registry.cc",
@@ -697,8 +703,6 @@
       "extension_menu_icon_loader.h",
       "extension_safety_check_utils.cc",
       "extension_safety_check_utils.h",
-      "extension_service.cc",
-      "extension_service.h",
       "extension_sync_data.cc",
       "extension_sync_data.h",
       "extension_sync_service.cc",
@@ -718,10 +722,6 @@
       "external_install_error_desktop.h",
       "file_handlers/file_handling_launch_utils.cc",
       "file_handlers/file_handling_launch_utils.h",
-      "forced_extensions/force_installed_metrics.cc",
-      "forced_extensions/force_installed_metrics.h",
-      "forced_extensions/force_installed_tracker.cc",
-      "forced_extensions/force_installed_tracker.h",
       "launch_util.cc",
       "launch_util.h",
       "manifest_v2_experiment_manager.cc",
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
index 1f17859..44dcf39 100644
--- a/chrome/browser/extensions/api/autofill_private/autofill_util.cc
+++ b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
@@ -244,17 +244,23 @@
   return iban_entry;
 }
 
-std::string PayOverTimeIssuerToIconResourceIdString(
+std::pair<std::string, std::string> PayOverTimeIssuerToIconResourceIdString(
     autofill::BnplIssuer::IssuerId issuer) {
   switch (issuer) {
     case autofill::BnplIssuer::IssuerId::kBnplAffirm:
-      return "chrome://theme/IDR_AUTOFILL_AFFIRM_LINKED";
+      return std::pair<std::string, std::string>(
+          "chrome://theme/IDR_AUTOFILL_AFFIRM_LINKED",
+          "chrome://theme/IDR_AUTOFILL_AFFIRM_LINKED_DARK");
     case autofill::BnplIssuer::IssuerId::kBnplZip:
-      return "chrome://theme/IDR_AUTOFILL_ZIP_LINKED";
+      return std::pair<std::string, std::string>(
+          "chrome://theme/IDR_AUTOFILL_ZIP_LINKED",
+          "chrome://theme/IDR_AUTOFILL_ZIP_LINKED_DARK");
     // TODO(crbug.com/408268581): Handle Afterpay issuer enum value when adding
     // Afterpay to the BNPL flow.
     case autofill::BnplIssuer::IssuerId::kBnplAfterpay:
-      return "chrome://theme/IDR_AUTOFILL_METADATA_BNPL_GENERIC";
+      return std::pair<std::string, std::string>(
+          "chrome://theme/IDR_AUTOFILL_METADATA_BNPL_GENERIC",
+          "chrome://theme/IDR_AUTOFILL_METADATA_BNPL_GENERIC");
   }
   NOTREACHED();
 }
@@ -270,8 +276,11 @@
   issuer_entry.instrument_id =
       base::NumberToString(issuer.payment_instrument()->instrument_id());
   issuer_entry.display_name = base::UTF16ToUTF8(issuer.GetDisplayName());
-  issuer_entry.image_src =
+
+  std::pair<std::string, std::string> issuer_icons =
       PayOverTimeIssuerToIconResourceIdString(issuer.issuer_id());
+  issuer_entry.image_src = std::move(issuer_icons.first);
+  issuer_entry.image_src_dark = std::move(issuer_icons.second);
 
   return issuer_entry;
 }
diff --git a/chrome/browser/extensions/chrome_extension_system.cc b/chrome/browser/extensions/chrome_extension_system.cc
index 8ce07fb..102709f 100644
--- a/chrome/browser/extensions/chrome_extension_system.cc
+++ b/chrome/browser/extensions/chrome_extension_system.cc
@@ -25,6 +25,7 @@
 #include "chrome/browser/extensions/extension_error_controller.h"
 #include "chrome/browser/extensions/extension_garbage_collector.h"
 #include "chrome/browser/extensions/extension_management.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/install_verifier.h"
 #include "chrome/browser/extensions/load_error_reporter.h"
 #include "chrome/browser/extensions/shared_module_service.h"
@@ -61,7 +62,6 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/chrome_app_sorting.h"
 #include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
-#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_sync_service.h"
 #include "extensions/browser/content_verifier/content_verifier.h"
 #else
@@ -215,7 +215,6 @@
 
   user_script_manager_ = std::make_unique<UserScriptManager>(profile_);
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   bool autoupdate_enabled =
       !profile_->IsGuestSession() && !profile_->IsSystemProfile();
 #if BUILDFLAG(IS_CHROMEOS)
@@ -232,22 +231,10 @@
       ExtensionErrorController::Get(profile_), autoupdate_enabled,
       extensions_enabled, &ready_);
 
-  // TODO(crbug.com/413460628): This depends on the initialization of
-  // ExtensionUpdater by ExtensionService.
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   uninstall_ping_sender_ = std::make_unique<UninstallPingSender>(
       ExtensionRegistry::Get(profile_),
       base::BindRepeating(&ShouldSendUninstallPing, profile_));
-#else
-  // Perform initialization usually handled by ExtensionService.
-  registrar_delegate_ =
-      std::make_unique<ChromeExtensionRegistrarDelegate>(profile_);
-  auto* registrar = ExtensionRegistrar::Get(profile_);
-  registrar->Init(
-      registrar_delegate_.get(), extensions_enabled,
-      base::CommandLine::ForCurrentProcess(),
-      profile_->GetPath().AppendASCII(kInstallDirectoryName),
-      profile_->GetPath().AppendASCII(kUnpackedInstallDirectoryName));
-  registrar_delegate_->Init(registrar);
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
   // These services must be registered before the ExtensionService tries to
@@ -312,12 +299,7 @@
 
   InitInstallGates();
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   extension_service_->Init();
-#else
-  // This is usually handled by ExtensionSystem::Init().
-  ready_.Signal();
-#endif
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   // Make sure ExtensionSyncService is created.
@@ -338,12 +320,10 @@
   if (content_verifier_.get()) {
     content_verifier_->Shutdown();
   }
+#endif
   if (extension_service_) {
     extension_service_->Shutdown();
   }
-#else
-  registrar_delegate_.reset();
-#endif
 }
 
 ServiceWorkerManager* ChromeExtensionSystem::Shared::service_worker_manager() {
@@ -368,11 +348,7 @@
 }
 
 ExtensionService* ChromeExtensionSystem::Shared::extension_service() {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   return extension_service_.get();
-#else
-  return nullptr;
-#endif
 }
 
 ManagementPolicy* ChromeExtensionSystem::Shared::management_policy() {
diff --git a/chrome/browser/extensions/chrome_extension_system.h b/chrome/browser/extensions/chrome_extension_system.h
index e5551b2..e490b2b 100644
--- a/chrome/browser/extensions/chrome_extension_system.h
+++ b/chrome/browser/extensions/chrome_extension_system.h
@@ -34,7 +34,6 @@
 
 namespace extensions {
 
-class ChromeExtensionRegistrarDelegate;
 class ChromeExtensionSystemSharedFactory;
 class UninstallPingSender;
 class InstallGate;
@@ -133,11 +132,7 @@
     // manifests. This region is shared between all extensions.
     std::unique_ptr<UserScriptManager> user_script_manager_;
     // ExtensionService depends on StateStore and Blocklist.
-#if BUILDFLAG(ENABLE_EXTENSIONS)
     std::unique_ptr<ExtensionService> extension_service_;
-#else
-    std::unique_ptr<ChromeExtensionRegistrarDelegate> registrar_delegate_;
-#endif
     std::unique_ptr<ManagementPolicy> management_policy_;
     std::unique_ptr<QuotaService> quota_service_;
     std::unique_ptr<AppSorting> app_sorting_;
diff --git a/chrome/browser/extensions/chrome_extension_test.cc b/chrome/browser/extensions/chrome_extension_test.cc
index d43a12e..7367251 100644
--- a/chrome/browser/extensions/chrome_extension_test.cc
+++ b/chrome/browser/extensions/chrome_extension_test.cc
@@ -17,6 +17,7 @@
 ChromeExtensionTest::~ChromeExtensionTest() = default;
 
 void ChromeExtensionTest::SetUp() {
+  // Ensure the profile uses a TestExtensionSystem.
   profile_ = TestingProfile::Builder().Build();
 
   TestExtensionSystem* test_extension_system =
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index 44a7845..1b738ce0 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -51,7 +51,6 @@
 #include "chrome/browser/extensions/menu_manager.h"
 #include "chrome/browser/extensions/pref_mapping.h"
 #include "chrome/browser/extensions/tab_helper.h"
-#include "chrome/browser/extensions/updater/chrome_update_client_config.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
 #include "chrome/browser/media/webrtc/media_device_salt_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
@@ -134,9 +133,6 @@
 
 namespace {
 
-const char kCrxUrlPath[] = "/service/update2/crx";
-const char kJsonUrlPath[] = "/service/update2/json";
-
 // If true, the extensions client will behave as though there is always a
 // new chrome update.
 bool g_did_chrome_update_for_testing = false;
@@ -655,17 +651,7 @@
 scoped_refptr<update_client::UpdateClient>
 ChromeExtensionsBrowserClient::CreateUpdateClient(
     content::BrowserContext* context) {
-  std::optional<GURL> override_url;
-  GURL update_url = extension_urls::GetWebstoreUpdateUrl();
-  if (update_url != extension_urls::GetDefaultWebstoreUpdateUrl()) {
-    if (update_url.path() == kCrxUrlPath) {
-      override_url = update_url.GetWithEmptyPath().Resolve(kJsonUrlPath);
-    } else {
-      override_url = update_url;
-    }
-  }
-  return update_client::UpdateClientFactory(
-      ChromeUpdateClientConfig::Create(context, override_url));
+  return util::CreateUpdateClient(context);
 }
 
 std::unique_ptr<ScopedExtensionUpdaterKeepAlive>
diff --git a/chrome/browser/extensions/component_loader_unittest.cc b/chrome/browser/extensions/component_loader_unittest.cc
index 49627df..f955546 100644
--- a/chrome/browser/extensions/component_loader_unittest.cc
+++ b/chrome/browser/extensions/component_loader_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "chrome/browser/extensions/chrome_extension_registrar_delegate.h"
+#include "chrome/browser/extensions/test_extension_system.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -85,7 +86,8 @@
         /*user_name=*/std::u16string(),
         /*avatar_id=*/0, /*testing_factories=*/{});
 
-    extension_system_ = ExtensionSystem::Get(profile());
+    extension_system_ =
+        static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
 
     extension_path_ =
         GetBasePath().AppendASCII("good")
@@ -135,13 +137,6 @@
     }
   }
 
-  void SetExtensionSystemReady() {
-    // The const_cast is ugly, but it's much simpler and more readable than
-    // trying to inject a MockExtensionSystem across desktop and Android,
-    // which use different ExtensionSystems and different factories.
-    const_cast<base::OneShotEvent*>(&extension_system_->ready())->Signal();
-  }
-
   TestingProfile* profile() { return profile_; }
 
  protected:
@@ -152,7 +147,7 @@
   std::unique_ptr<ChromeExtensionRegistrarDelegate>
       extension_registrar_delegate_;
   raw_ptr<ExtensionRegistrar> extension_registrar_ = nullptr;
-  raw_ptr<ExtensionSystem> extension_system_ = nullptr;
+  raw_ptr<TestExtensionSystem> extension_system_ = nullptr;
   raw_ptr<ComponentLoader> component_loader_ = nullptr;
 
   // The root directory of the text extension.
@@ -225,7 +220,7 @@
 
 // Test that it *is* loaded when the extension service *is* ready.
 TEST_F(ComponentLoaderTest, AddWhenReady) {
-  SetExtensionSystemReady();
+  extension_system_->SetReady();
   std::string extension_id =
       component_loader_->Add(manifest_contents_, extension_path_);
   EXPECT_NE("", extension_id);
@@ -248,7 +243,7 @@
   EXPECT_EQ(0u, registry->enabled_extensions().size());
 
   // Load an extension, and check that it's unloaded when Remove() is called.
-  SetExtensionSystemReady();
+  extension_system_->SetReady();
   std::string extension_id =
       component_loader_->Add(manifest_contents_, extension_path_);
   EXPECT_EQ(1u, registry->enabled_extensions().size());
@@ -354,7 +349,7 @@
   EXPECT_EQ(default_count + 1,
             component_loader_->registered_extensions_count());
 
-  SetExtensionSystemReady();
+  extension_system_->SetReady();
   component_loader_->LoadAll();
 
   EXPECT_EQ(default_count + 1, registry->enabled_extensions().size());
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index c506cbd..1e395a63 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -763,7 +763,7 @@
     bool in_kiosk_mode = false;
 #if BUILDFLAG(IS_CHROMEOS)
     user_manager::UserManager* user_manager = user_manager::UserManager::Get();
-    in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
+    in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskChromeApp();
 #endif
     if (!in_kiosk_mode) {
       ReportFailureFromUIThread(CrxInstallError(
diff --git a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
index 42df3b8c..c59c8536 100644
--- a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
+++ b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
@@ -15,9 +15,11 @@
 #include "chrome/browser/extensions/desktop_android/desktop_android_extension_host_delegate.h"
 #include "chrome/browser/extensions/desktop_android/desktop_android_runtime_api_delegate.h"
 #include "chrome/browser/extensions/error_console/error_console.h"
+#include "chrome/browser/extensions/extension_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_selections.h"
+#include "components/update_client/update_client.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -340,6 +342,12 @@
   return ChromeExtensionWebContentsObserver::FromWebContents(web_contents);
 }
 
+scoped_refptr<update_client::UpdateClient>
+DesktopAndroidExtensionsBrowserClient::CreateUpdateClient(
+    content::BrowserContext* context) {
+  return util::CreateUpdateClient(context);
+}
+
 KioskDelegate* DesktopAndroidExtensionsBrowserClient::GetKioskDelegate() {
   return kiosk_delegate_.get();
 }
diff --git a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.h b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.h
index 7351e37..2dc5a5a 100644
--- a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.h
+++ b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.h
@@ -148,6 +148,8 @@
       content::WebContents* web_contents) override;
   ExtensionWebContentsObserver* GetExtensionWebContentsObserver(
       content::WebContents* web_contents) override;
+  scoped_refptr<update_client::UpdateClient> CreateUpdateClient(
+      content::BrowserContext* context) override;
   KioskDelegate* GetKioskDelegate() override;
   std::string GetApplicationLocale() override;
 
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 6f402ff2..95878fd 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -54,8 +54,6 @@
 #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h"
 #include "chrome/browser/extensions/install_verifier.h"
 #include "chrome/browser/extensions/installed_loader.h"
-#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
-#include "chrome/browser/extensions/mv2_experiment_stage.h"
 #include "chrome/browser/extensions/omaha_attributes_handler.h"
 #include "chrome/browser/extensions/permissions/permissions_updater.h"
 #include "chrome/browser/extensions/profile_util.h"
@@ -67,7 +65,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
-#include "chrome/browser/upgrade_detector/upgrade_detector.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/extensions/extension_constants.h"
@@ -122,6 +119,15 @@
 #include "chromeos/constants/chromeos_features.h"
 #endif
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/upgrade_detector/upgrade_detector.h"
+#endif
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/manifest_v2_experiment_manager.h"
+#include "chrome/browser/extensions/mv2_experiment_stage.h"
+#endif
+
 using content::BrowserContext;
 using content::BrowserThread;
 using extensions::mojom::ManifestLocation;
@@ -232,7 +238,11 @@
     profile_manager_observation_.Observe(g_browser_process->profile_manager());
   }
 
+#if !BUILDFLAG(IS_ANDROID)
+  // TODO(crbug.com/413455412): Find another way to report Chrome updates to
+  // extensions on Android, which uses the Play Store for updates.
   UpgradeDetector::GetInstance()->AddObserver(this);
+#endif
 
   cws_info_service_observation_.Observe(CWSInfoService::Get(profile_));
 
@@ -277,7 +287,9 @@
 }
 
 ExtensionService::~ExtensionService() {
+#if !BUILDFLAG(IS_ANDROID)
   UpgradeDetector::GetInstance()->RemoveObserver(this);
+#endif
 }
 
 void ExtensionService::Shutdown() {
@@ -527,8 +539,10 @@
     PermissionsUpdater(profile()).ApplyPolicyHostRestrictions(*extension);
   }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
   ManifestV2ExperimentManager* mv2_experiment_manager =
       ManifestV2ExperimentManager::Get(profile_);
+#endif
 
   // Loop through the disabled extension list, find extensions to re-enable
   // automatically. These extensions are exclusive from the |to_disable| list
@@ -581,6 +595,7 @@
       to_remove.insert(disable_reason::DISABLE_BLOCKED_BY_POLICY);
     }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     // Note: `mv2_experiment_manager` may be null for certain types of profiles
     // (such as the sign-in profile). We can ignore this check in this case,
     // since users can't install extensions in these profiles.
@@ -593,6 +608,7 @@
         !mv2_experiment_manager->ShouldBlockExtensionEnable(*extension)) {
       to_remove.insert(disable_reason::DISABLE_UNSUPPORTED_MANIFEST_VERSION);
     }
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
     // If this profile is not supervised, then remove any supervised user
     // related disable reasons.
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 71bc585..e5439e5 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -48,7 +48,7 @@
 #include "extensions/common/extension_id.h"
 #include "extensions/common/manifest.h"
 
-static_assert(BUILDFLAG(ENABLE_EXTENSIONS));
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
 
 class BlocklistedExtensionSyncServiceTest;
 class Profile;
diff --git a/chrome/browser/extensions/extension_util.cc b/chrome/browser/extensions/extension_util.cc
index aa0ed89..b0c8a903 100644
--- a/chrome/browser/extensions/extension_util.cc
+++ b/chrome/browser/extensions/extension_util.cc
@@ -14,6 +14,7 @@
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/extensions/permissions/permissions_updater.h"
 #include "chrome/browser/extensions/shared_module_service.h"
+#include "chrome/browser/extensions/updater/chrome_update_client_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
 #include "chrome/common/extensions/api/url_handlers/url_handlers_parser.h"
@@ -21,6 +22,7 @@
 #include "chrome/common/pref_names.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "components/update_client/update_client.h"
 #include "components/variations/variations_associated_data.h"
 #include "content/public/browser/site_instance.h"
 #include "extensions/browser/disable_reason.h"
@@ -57,6 +59,9 @@
 
 namespace {
 
+const char kCrxUrlPath[] = "/service/update2/crx";
+const char kJsonUrlPath[] = "/service/update2/json";
+
 // Returns |extension_id|. See note below.
 std::string ReloadExtension(const std::string& extension_id,
                             content::BrowserContext* context) {
@@ -351,4 +356,19 @@
                                 false);
 }
 
+scoped_refptr<update_client::UpdateClient> CreateUpdateClient(
+    content::BrowserContext* context) {
+  std::optional<GURL> override_url;
+  GURL update_url = extension_urls::GetWebstoreUpdateUrl();
+  if (update_url != extension_urls::GetDefaultWebstoreUpdateUrl()) {
+    if (update_url.path() == kCrxUrlPath) {
+      override_url = update_url.GetWithEmptyPath().Resolve(kJsonUrlPath);
+    } else {
+      override_url = update_url;
+    }
+  }
+  return update_client::UpdateClientFactory(
+      ChromeUpdateClientConfig::Create(context, override_url));
+}
+
 } // namespace extensions::util
diff --git a/chrome/browser/extensions/extension_util.h b/chrome/browser/extensions/extension_util.h
index 43d5fb7..de4f097 100644
--- a/chrome/browser/extensions/extension_util.h
+++ b/chrome/browser/extensions/extension_util.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <string>
 
+#include "base/memory/scoped_refptr.h"
 #include "base/values.h"
 #include "extensions/buildflags/buildflags.h"
 #include "extensions/common/constants.h"
@@ -22,6 +23,10 @@
 class PermissionSet;
 }
 
+namespace update_client {
+class UpdateClient;
+}
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -92,6 +97,10 @@
 // Registers miscellaneous chrome-level extension-related prefs.
 void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
+// Returns a new UpdateClient.
+scoped_refptr<update_client::UpdateClient> CreateUpdateClient(
+    content::BrowserContext* context);
+
 }  // namespace util
 }  // namespace extensions
 
diff --git a/chrome/browser/extensions/external_provider_impl.cc b/chrome/browser/extensions/external_provider_impl.cc
index 5d87554..e0b2293 100644
--- a/chrome/browser/extensions/external_provider_impl.cc
+++ b/chrome/browser/extensions/external_provider_impl.cc
@@ -54,7 +54,8 @@
 #include "extensions/common/manifest.h"
 #include "ui/base/l10n/l10n_util.h"
 
-#if !BUILDFLAG(IS_ANDROID)
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/preinstalled_apps.h"
 #include "chrome/browser/web_applications/preinstalled_app_install_features.h"
 #endif
 
@@ -73,7 +74,6 @@
 #include "chromeos/components/kiosk/kiosk_utils.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
 #else
-#include "chrome/browser/extensions/preinstalled_apps.h"
 #include "components/policy/core/common/device_local_account_type.h"
 #endif
 
@@ -869,7 +869,7 @@
 #endif
   }
 
-#if !BUILDFLAG(IS_CHROMEOS)
+#if !BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(ENABLE_EXTENSIONS)
   // The pre-installed apps are installed as INTERNAL but use the external
   // extension installer codeflow.
   provider_list->push_back(std::make_unique<preinstalled_apps::Provider>(
diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc
index e06d93fe..9420df5 100644
--- a/chrome/browser/extensions/test_extension_system.cc
+++ b/chrome/browser/extensions/test_extension_system.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/extensions/cws_info_service_factory.h"
 #include "chrome/browser/extensions/extension_error_controller.h"
 #include "chrome/browser/extensions/extension_management.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/external_provider_manager.h"
 #include "chrome/browser/extensions/shared_module_service.h"
 #include "chrome/browser/profiles/profile.h"
@@ -47,7 +48,6 @@
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/chrome_app_sorting.h"
-#include "chrome/browser/extensions/extension_service.h"
 #else
 #include "extensions/browser/null_app_sorting.h"
 #endif
@@ -91,13 +91,11 @@
   return CWSInfoServiceInterface::CWSInfo();
 }
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 std::unique_ptr<KeyedService> BuildFakeCWSService(
     content::BrowserContext* context) {
   return std::make_unique<FakeCWSInfoService>(
       Profile::FromBrowserContext(context));
 }
-#endif
 
 }  // namespace
 
@@ -140,13 +138,9 @@
 TestExtensionSystem::~TestExtensionSystem() = default;
 
 void TestExtensionSystem::Shutdown() {
-#if BUILDFLAG(IS_ANDROID)
-  registrar_delegate_.reset();
-#else
   if (extension_service_) {
     extension_service_->Shutdown();
   }
-#endif
 
   in_process_data_decoder_.reset();
 }
@@ -165,21 +159,11 @@
       params.unpacked_install_directory.value_or(
           profile_->GetPath().AppendASCII(kUnpackedInstallDirectoryName));
 
-#if BUILDFLAG(IS_ANDROID)
-  registrar_delegate_ =
-      std::make_unique<ChromeExtensionRegistrarDelegate>(profile_.get());
-  ExtensionRegistrar* registrar = ExtensionRegistrar::Get(profile_.get());
-  registrar->Init(registrar_delegate_.get(), params.enable_extensions,
-                  command_line, install_directory, unpacked_install_directory);
-  registrar_delegate_->Init(registrar);
-#else
   CreateExtensionService(command_line, install_directory,
                          unpacked_install_directory, params.autoupdate_enabled,
                          params.enable_extensions);
-#endif
 }
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 ExtensionService* TestExtensionSystem::CreateExtensionService(
     const base::CommandLine* command_line,
     const base::FilePath& install_directory,
@@ -223,7 +207,6 @@
   ExternalProviderManager::Get(profile_)->ClearProvidersForTesting();
   return extension_service_.get();
 }
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 void TestExtensionSystem::CreateUserScriptManager() {
   user_script_manager_ = std::make_unique<UserScriptManager>(profile_);
diff --git a/chrome/browser/extensions/test_extension_system.h b/chrome/browser/extensions/test_extension_system.h
index e595ebf..fb2c2d9 100644
--- a/chrome/browser/extensions/test_extension_system.h
+++ b/chrome/browser/extensions/test_extension_system.h
@@ -37,7 +37,6 @@
 }  // namespace value_store
 
 namespace extensions {
-class ChromeExtensionRegistrarDelegate;
 
 // Test ExtensionSystem, for use with TestingProfile.
 class TestExtensionSystem : public ExtensionSystem {
@@ -78,12 +77,9 @@
   void Init();
   void Init(const InitParams& init_params);
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-  // NOTE: Please don't use these in new code. Prefer using `Init()`, which will
-  // work on builds that don't support ExtensionService.
-
   // Creates an ExtensionService initialized with the testing profile and
   // returns it, and creates ExtensionPrefs if it hasn't been created yet.
+  // DEPRECATED: Prefer Init().
   ExtensionService* CreateExtensionService(
       const base::CommandLine* command_line,
       const base::FilePath& install_directory,
@@ -91,13 +87,13 @@
       bool enable_extensions = true);
   // Similar to the above, but also allows specifying unpacked install directory
   // if needed.
+  // DEPRECATED: Prefer Init().
   ExtensionService* CreateExtensionService(
       const base::CommandLine* command_line,
       const base::FilePath& install_directory,
       const base::FilePath& unpacked_install_directory,
       bool autoupdate_enabled,
       bool enable_extensions = true);
-#endif
 
   void CreateSocketManager();
 
@@ -155,11 +151,7 @@
   std::unique_ptr<StateStore> state_store_;
   std::unique_ptr<ManagementPolicy> management_policy_;
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<ExtensionService> extension_service_;
-#else
-  std::unique_ptr<ChromeExtensionRegistrarDelegate> registrar_delegate_;
-#endif
 
   std::unique_ptr<AppSorting> app_sorting_;
 
diff --git a/chrome/browser/extensions/updater/extension_updater.cc b/chrome/browser/extensions/updater/extension_updater.cc
index 5fe6b4b..ea7d049 100644
--- a/chrome/browser/extensions/updater/extension_updater.cc
+++ b/chrome/browser/extensions/updater/extension_updater.cc
@@ -373,7 +373,7 @@
   bool kiosk_crx_manifest_update_url_ignored = false;
 #if BUILDFLAG(IS_CHROMEOS)
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
-  if (user_manager && user_manager->IsLoggedInAsKioskApp()) {
+  if (user_manager && user_manager->IsLoggedInAsKioskChromeApp()) {
     ash::CrosSettings::Get()->GetBoolean(
         ash::kKioskCRXManifestUpdateURLIgnored,
         &kiosk_crx_manifest_update_url_ignored);
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 e92a99a..901f53b 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
@@ -1439,6 +1439,10 @@
     // Mutable*ParamWithSafeDefault instances.
     /* Alphabetical: */
     public static final MutableBooleanParamWithSafeDefault
+            sAndroidNativePagesInNewTabBookmarksEnabled =
+            sAndroidNativePagesInNewTab.newBooleanParam(
+                    "android_native_pages_in_new_tab_bookmarks_enabled", true);
+    public static final MutableBooleanParamWithSafeDefault
             sAndroidNativePagesInNewTabDownloadsEnabled =
             sAndroidNativePagesInNewTab.newBooleanParam(
                     "android_native_pages_in_new_tab_downloads_enabled", true);
diff --git a/chrome/browser/glic/BUILD.gn b/chrome/browser/glic/BUILD.gn
index 463aaf32..db48cd3 100644
--- a/chrome/browser/glic/BUILD.gn
+++ b/chrome/browser/glic/BUILD.gn
@@ -37,10 +37,13 @@
     "host/glic_ui.h",
     "host/glic_web_client_access.h",
     "host/guest_util.h",
+    "widget/application_hotkey_delegate.h",
     "widget/glic_modal_manager.h",
     "widget/glic_modal_view.h",
     "widget/glic_view.h",
     "widget/glic_window_controller.h",
+    "widget/glic_window_hotkey_delegate.h",
+    "widget/local_hotkey_manager.h",
   ]
   public_deps = [
     ":enum",
@@ -127,6 +130,7 @@
     "host/webui_contents_container.h",
     "media/glic_media_integration.cc",
     "media/glic_media_integration.h",
+    "widget/application_hotkey_delegate.cc",
     "widget/browser_conditions.cc",
     "widget/browser_conditions.h",
     "widget/glic_modal_manager.cc",
@@ -137,8 +141,10 @@
     "widget/glic_window_animator.cc",
     "widget/glic_window_animator.h",
     "widget/glic_window_controller.cc",
+    "widget/glic_window_hotkey_delegate.cc",
     "widget/glic_window_resize_animation.cc",
     "widget/glic_window_resize_animation.h",
+    "widget/local_hotkey_manager.cc",
   ]
   public_deps = [ "//chrome/browser:browser_public_dependencies" ]
   deps = [
diff --git a/chrome/browser/glic/glic_keyed_service.cc b/chrome/browser/glic/glic_keyed_service.cc
index e00a496..7a8d5bf 100644
--- a/chrome/browser/glic/glic_keyed_service.cc
+++ b/chrome/browser/glic/glic_keyed_service.cc
@@ -158,10 +158,6 @@
   SetContextAccessIndicator(false);
 }
 
-void GlicKeyedService::FocusUI() {
-  window_controller_->FocusIfOpen();
-}
-
 void GlicKeyedService::PrepareForOpen() {
   window_controller_->fre_controller()->MaybePreconnect();
 
diff --git a/chrome/browser/glic/glic_keyed_service.h b/chrome/browser/glic/glic_keyed_service.h
index 68732b8..430cf641 100644
--- a/chrome/browser/glic/glic_keyed_service.h
+++ b/chrome/browser/glic/glic_keyed_service.h
@@ -76,8 +76,6 @@
   // manager.
   void CloseUI();
 
-  void FocusUI();
-
   // The user has performed an action suggesting that they made open the UI
   // soon.
   void PrepareForOpen();
diff --git a/chrome/browser/glic/glic_pref_names.cc b/chrome/browser/glic/glic_pref_names.cc
index 8150e54..52aef1a 100644
--- a/chrome/browser/glic/glic_pref_names.cc
+++ b/chrome/browser/glic/glic_pref_names.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/glic/glic_pref_names.h"
 
 #include "chrome/browser/background/glic/glic_launcher_configuration.h"
+#include "chrome/browser/glic/widget/local_hotkey_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_registry.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -38,6 +39,11 @@
       prefs::kGlicLauncherHotkey,
       ui::Command::AcceleratorToString(
           GlicLauncherConfiguration::GetDefaultHotkey()));
+  registry->RegisterStringPref(
+      prefs::kGlicFocusToggleHotkey,
+      ui::Command::AcceleratorToString(
+          LocalHotkeyManager::GetDefaultAccelerator(
+              LocalHotkeyManager::Hotkey::kFocusToggle)));
 }
 
 }  // namespace glic::prefs
diff --git a/chrome/browser/glic/glic_pref_names.h b/chrome/browser/glic/glic_pref_names.h
index 4fd79756..c25d527 100644
--- a/chrome/browser/glic/glic_pref_names.h
+++ b/chrome/browser/glic/glic_pref_names.h
@@ -22,6 +22,10 @@
 // hotkey for Glic.
 inline constexpr char kGlicLauncherHotkey[] = "glic.launcher_hotkey";
 
+// String pref that keeps track of the non-localized version of the registered
+// hotkey for toggling focus between Glic and the browser window.
+inline constexpr char kGlicFocusToggleHotkey[] = "glic.focus_toggle_hotkey";
+
 // ************* PROFILE PREFS ***************
 // Prefs below are tied to a user profile
 
diff --git a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
index 67feca00..19dfce9 100644
--- a/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
+++ b/chrome/browser/glic/host/context/glic_focused_tab_manager.cc
@@ -150,6 +150,12 @@
   MaybeUpdateFocusedTab();
 }
 
+void GlicFocusedTabManager::OnWidgetVisibilityOnScreenChanged(
+    views::Widget* widget,
+    bool visible) {
+  MaybeUpdateFocusedTab();
+}
+
 void GlicFocusedTabManager::OnWidgetDestroyed(views::Widget* widget) {
   widget_observation_.Reset();
 }
@@ -364,6 +370,10 @@
     return false;
   }
 
+  if (!browser_interface->IsVisibleOnScreen()) {
+    return false;
+  }
+
   return true;
 }
 
diff --git a/chrome/browser/glic/host/context/glic_focused_tab_manager.h b/chrome/browser/glic/host/context/glic_focused_tab_manager.h
index d10a442..0e8a28f 100644
--- a/chrome/browser/glic/host/context/glic_focused_tab_manager.h
+++ b/chrome/browser/glic/host/context/glic_focused_tab_manager.h
@@ -200,6 +200,10 @@
   // Callback for browser window visibility changes (e.g. cmd+h on Mac).
   void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
 
+  // Callback for visibility on screen changes (e.g. Spaces on Mac).
+  void OnWidgetVisibilityOnScreenChanged(views::Widget* widget,
+                                         bool visible) override;
+
   // Callback for browser window widget being destroyed.
   void OnWidgetDestroyed(views::Widget* widget) override;
 
diff --git a/chrome/browser/glic/widget/application_hotkey_delegate.cc b/chrome/browser/glic/widget/application_hotkey_delegate.cc
new file mode 100644
index 0000000..cabbbc23
--- /dev/null
+++ b/chrome/browser/glic/widget/application_hotkey_delegate.cc
@@ -0,0 +1,125 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/glic/widget/application_hotkey_delegate.h"
+
+#include "base/containers/fixed_flat_map.h"
+#include "base/containers/flat_map.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/widget/glic_window_controller.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "ui/base/accelerators/accelerator.h"
+#include "ui/base/accelerators/accelerator_manager.h"
+
+namespace glic {
+
+namespace {
+
+constexpr std::array<glic::LocalHotkeyManager::Hotkey, 1> kSupportedHotkeys = {
+    glic::LocalHotkeyManager::Hotkey::kFocusToggle};
+
+// Implementation of ScopedHotkeyRegistration specifically for application-wide
+// hotkeys. It registers and unregisters accelerators with the FocusManager of
+// all current and future BrowserViews.
+class ApplicationScopedHotkeyRegistration
+    : public LocalHotkeyManager::ScopedHotkeyRegistration,
+      public BrowserListObserver {
+ public:
+  ApplicationScopedHotkeyRegistration(
+      ui::Accelerator accelerator,
+      base::WeakPtr<ui::AcceleratorTarget> target)
+      : accelerator_(accelerator), target_(target) {
+    CHECK(!accelerator_.IsEmpty());
+    for (Browser* browser : *BrowserList::GetInstance()) {
+      RegisterAccelerator(browser);
+    }
+    browser_list_observation_.Observe(BrowserList::GetInstance());
+  }
+
+  ~ApplicationScopedHotkeyRegistration() override {
+    CHECK(target_);
+    for (Browser* browser : *BrowserList::GetInstance()) {
+      if (auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser)) {
+        browser_view->GetFocusManager()->UnregisterAccelerator(accelerator_,
+                                                               target_.get());
+      }
+    }
+  }
+
+ private:
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override {
+    RegisterAccelerator(browser);
+  }
+
+  void RegisterAccelerator(Browser* browser) {
+    CHECK(target_);
+    if (auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser)) {
+      browser_view->GetFocusManager()->RegisterAccelerator(
+          accelerator_,
+          ui::AcceleratorManager::HandlerPriority::kNormalPriority,
+          target_.get());
+    }
+  }
+
+  ui::Accelerator accelerator_;
+  base::WeakPtr<ui::AcceleratorTarget> target_;
+  base::ScopedObservation<BrowserList, BrowserListObserver>
+      browser_list_observation_{this};
+};
+}  // namespace
+
+ApplicationHotkeyDelegate::ApplicationHotkeyDelegate(
+    base::WeakPtr<GlicWindowController> window_controller)
+    : window_controller_(window_controller) {}
+
+ApplicationHotkeyDelegate::~ApplicationHotkeyDelegate() = default;
+
+const base::span<const LocalHotkeyManager::Hotkey>
+ApplicationHotkeyDelegate::GetSupportedHotkeys() const {
+  return kSupportedHotkeys;
+}
+
+std::unique_ptr<LocalHotkeyManager::ScopedHotkeyRegistration>
+ApplicationHotkeyDelegate::CreateScopedHotkeyRegistration(
+    ui::Accelerator accelerator,
+    base::WeakPtr<ui::AcceleratorTarget> target) {
+  return std::make_unique<ApplicationScopedHotkeyRegistration>(accelerator,
+                                                               target);
+}
+
+bool ApplicationHotkeyDelegate::AcceleratorPressed(
+    LocalHotkeyManager::Hotkey hotkey) {
+  if (!window_controller_) {
+    return false;
+  }
+
+  switch (hotkey) {
+    case LocalHotkeyManager::Hotkey::kFocusToggle:
+      window_controller_->FocusIfOpen();
+      return true;
+    default:
+      NOTREACHED()
+          << "no handling implemented for "
+          << LocalHotkeyManager::GetAccelerator(hotkey).GetShortcutText();
+  }
+}
+
+std::unique_ptr<LocalHotkeyManager> MakeApplicationHotkeyManager(
+    base::WeakPtr<GlicWindowController> window_controller) {
+  auto application_hotkey_delegate = std::make_unique<LocalHotkeyManager>(
+      window_controller,
+      std::make_unique<ApplicationHotkeyDelegate>(window_controller));
+  application_hotkey_delegate->InitializeAccelerators();
+  return application_hotkey_delegate;
+}
+}  // namespace glic
diff --git a/chrome/browser/glic/widget/application_hotkey_delegate.h b/chrome/browser/glic/widget/application_hotkey_delegate.h
new file mode 100644
index 0000000..a6cdb00
--- /dev/null
+++ b/chrome/browser/glic/widget/application_hotkey_delegate.h
@@ -0,0 +1,51 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GLIC_WIDGET_APPLICATION_HOTKEY_DELEGATE_H_
+#define CHROME_BROWSER_GLIC_WIDGET_APPLICATION_HOTKEY_DELEGATE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/glic/widget/local_hotkey_manager.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace glic {
+
+class GlicWindowController;
+
+// Manages hotkeys that are active application-wide when Glic is relevant.
+// This class acts as a delegate for a LocalHotkeyManager instance, configuring
+// it with the set of hotkeys relevant application-wide (like focus toggle)
+// and handling the registration and dispatch of those hotkeys within the
+// application's scope (typically via BrowserView's FocusManager).
+class ApplicationHotkeyDelegate : public LocalHotkeyManager::Delegate {
+ public:
+  explicit ApplicationHotkeyDelegate(
+      base::WeakPtr<GlicWindowController> window_controller);
+  ~ApplicationHotkeyDelegate() override;
+
+  // LocalHotkeyManager::Delegate:
+  const base::span<const LocalHotkeyManager::Hotkey> GetSupportedHotkeys()
+      const override;
+
+  bool AcceleratorPressed(LocalHotkeyManager::Hotkey Hotkey) override;
+  std::unique_ptr<LocalHotkeyManager::ScopedHotkeyRegistration>
+  CreateScopedHotkeyRegistration(
+      ui::Accelerator accelerator,
+      base::WeakPtr<ui::AcceleratorTarget> target) override;
+
+  base::WeakPtr<ApplicationHotkeyDelegate> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  base::WeakPtr<GlicWindowController> window_controller_;
+  base::WeakPtrFactory<ApplicationHotkeyDelegate> weak_ptr_factory_{this};
+};
+
+std::unique_ptr<LocalHotkeyManager> MakeApplicationHotkeyManager(
+    base::WeakPtr<GlicWindowController> window_controller);
+
+}  // namespace glic
+
+#endif  // CHROME_BROWSER_GLIC_WIDGET_APPLICATION_HOTKEY_DELEGATE_H_
diff --git a/chrome/browser/glic/widget/glic_view.h b/chrome/browser/glic/widget/glic_view.h
index 55f31fe0..19581804 100644
--- a/chrome/browser/glic/widget/glic_view.h
+++ b/chrome/browser/glic/widget/glic_view.h
@@ -59,6 +59,10 @@
 
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
+  base::WeakPtr<GlicView> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
  private:
   std::optional<SkColor> GetClientBackgroundColor();
 
@@ -67,6 +71,7 @@
   // Defines the areas of the view from which it can be dragged. These areas can
   // be updated by the glic web client.
   std::vector<gfx::Rect> draggable_areas_;
+  base::WeakPtrFactory<GlicView> weak_ptr_factory_{this};
 };
 
 }  // namespace glic
diff --git a/chrome/browser/glic/widget/glic_window_controller.cc b/chrome/browser/glic/widget/glic_window_controller.cc
index 7f00f33..1753df04 100644
--- a/chrome/browser/glic/widget/glic_window_controller.cc
+++ b/chrome/browser/glic/widget/glic_window_controller.cc
@@ -79,20 +79,6 @@
 
 constexpr static base::TimeDelta kAnimationDuration = base::Milliseconds(300);
 
-#if BUILDFLAG(IS_MAC)
-constexpr int kFocusToggleAcceleratorModifiers =
-    ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN;
-#else
-constexpr int kFocusToggleAcceleratorModifiers =
-    ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN;
-#endif
-constexpr ui::KeyboardCode kFocusToggleAcceleratorKey = ui::VKEY_G;
-
-ui::Accelerator GetFocusToggleAccelerator() {
-  return ui::Accelerator(kFocusToggleAcceleratorKey,
-                         kFocusToggleAcceleratorModifiers);
-}
-
 mojom::PanelState CreatePanelState(bool widget_visible,
                                    Browser* attached_browser) {
   mojom::PanelState panel_state;
@@ -326,6 +312,7 @@
       glic_service_(glic_service),
       enabling_(enabling) {
   previous_position_ = GetPreviousPositionFromPrefs(profile_->GetPrefs());
+  application_hotkey_manager_ = MakeApplicationHotkeyManager(GetWeakPtr());
 }
 
 GlicWindowController::~GlicWindowController() = default;
@@ -410,7 +397,9 @@
     GetGlicView()->GetViewAccessibility().AnnounceAlert(
         l10n_util::GetStringFUTF16(
             IDS_GLIC_WINDOW_FIRST_FOCUS_LOST_ANNOUNCEMENT,
-            GetFocusToggleAccelerator().GetShortcutText()));
+            LocalHotkeyManager::GetAccelerator(
+                LocalHotkeyManager::Hotkey::kFocusToggle)
+                .GetShortcutText()));
     do_focus_loss_announcement_ = false;
   }
   window_activation_callback_list_.Notify(active);
@@ -661,52 +650,6 @@
   return glic_service_->host();
 }
 
-bool GlicWindowController::AcceleratorPressed(
-    const ui::Accelerator& accelerator) {
-  if (accelerator.key_code() == ui::VKEY_ESCAPE) {
-    Close();
-    return true;
-  }
-  if (accelerator.key_code() == kFocusToggleAcceleratorKey &&
-      accelerator.modifiers() == kFocusToggleAcceleratorModifiers) {
-    // Transfer focus back to the browser.
-    if (IsAttached()) {
-      attached_browser_->window()->Activate();
-      return true;
-    }
-    if (auto* last_active = BrowserList::GetInstance()->GetLastActive()) {
-      last_active->window()->Activate();
-      return true;
-    }
-  }
-#if BUILDFLAG(IS_WIN)
-  if (accelerator.key_code() == ui::VKEY_SPACE &&
-      accelerator.modifiers() == ui::EF_ALT_DOWN) {
-    ShowTitleBarContextMenuAt(gfx::Point());
-  }
-#endif  //  BUILDFLAG(IS_WIN)
-  return false;
-}
-
-bool GlicWindowController::CanHandleAccelerators() const {
-  NOTIMPLEMENTED();
-  return false;
-}
-
-void GlicWindowController::AddAccelerators() {
-  GlicView* glic_view = GetGlicView();
-  if (!glic_view) {
-    return;
-  }
-
-  glic_view->AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));
-  glic_view->AddAccelerator(ui::Accelerator(kFocusToggleAcceleratorKey,
-                                            kFocusToggleAcceleratorModifiers));
-#if BUILDFLAG(IS_WIN)
-  glic_view->AddAccelerator(ui::Accelerator(ui::VKEY_SPACE, ui::EF_ALT_DOWN));
-#endif
-}
-
 void GlicWindowController::Show(Browser* browser,
                                 mojom::InvocationSource source) {
   // At this point State must be kClosed, and all glic window state must be
@@ -766,12 +709,13 @@
 
 void GlicWindowController::SetupGlicWidget(Browser* browser) {
   auto initial_bounds = GetInitialBounds(browser);
+  glic_window_hotkey_manager_ = MakeGlicWindowHotkeyManager(GetWeakPtr());
   glic_widget_ = GlicWidget::Create(profile_, initial_bounds,
-                                    /*accelerator_delegate=*/GetWeakPtr(),
+                                    glic_window_hotkey_manager_->GetWeakPtr(),
                                     user_resizable_);
   glic_widget_observation_.Observe(glic_widget_.get());
   SetupGlicWidgetAccessibilityText();
-  AddAccelerators();
+  glic_window_hotkey_manager_->InitializeAccelerators();
 
   if (AlwaysDetached()) {
     SetGlicWindowToFloatingMode(true);
@@ -805,9 +749,11 @@
 void GlicWindowController::SetupGlicWidgetAccessibilityText() {
   auto* widget_delegate = glic_widget_->widget_delegate();
   if (opening_source_ == mojom::InvocationSource::kFre) {
-    widget_delegate->SetAccessibleTitle(l10n_util::GetStringFUTF16(
-        IDS_GLIC_WINDOW_TITLE_FIRST_LOAD,
-        GetFocusToggleAccelerator().GetShortcutText()));
+    widget_delegate->SetAccessibleTitle(
+        l10n_util::GetStringFUTF16(IDS_GLIC_WINDOW_TITLE_FIRST_LOAD,
+                                   LocalHotkeyManager::GetAccelerator(
+                                       LocalHotkeyManager::Hotkey::kFocusToggle)
+                                       .GetShortcutText()));
     do_focus_loss_announcement_ = true;
   } else {
     widget_delegate->SetAccessibleTitle(
@@ -1212,6 +1158,7 @@
   attached_browser_ = nullptr;
   window_event_observer_.reset();
   browser_close_subscription_.reset();
+  glic_window_hotkey_manager_.reset();
   glic_widget_observation_.Reset();
   glic_widget_.reset();
   scoped_glic_button_indicator_.reset();
diff --git a/chrome/browser/glic/widget/glic_window_controller.h b/chrome/browser/glic/widget/glic_window_controller.h
index 296bee9..36a34d4 100644
--- a/chrome/browser/glic/widget/glic_window_controller.h
+++ b/chrome/browser/glic/widget/glic_window_controller.h
@@ -18,11 +18,13 @@
 #include "chrome/browser/glic/glic_enabling.h"
 #include "chrome/browser/glic/host/glic.mojom.h"
 #include "chrome/browser/glic/host/glic_web_client_access.h"
+#include "chrome/browser/glic/widget/application_hotkey_delegate.h"
 #include "chrome/browser/glic/widget/glic_modal_manager.h"
+#include "chrome/browser/glic/widget/glic_window_hotkey_delegate.h"
+#include "chrome/browser/glic/widget/local_hotkey_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "content/public/browser/web_contents.h"
-#include "ui/base/accelerators/accelerator.h"
 #include "ui/base/interaction/element_tracker.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
@@ -60,8 +62,7 @@
 // See the |State| enum below for the lifecycle of the window. When the glic
 // window is open |attached_browser_| indicates if the window is attached or
 // standalone. See |IsAttached|
-class GlicWindowController : public views::WidgetObserver,
-                             public ui::AcceleratorTarget {
+class GlicWindowController : public views::WidgetObserver {
  public:
   // Observes the state of the glic window.
   class StateObserver : public base::CheckedObserver {
@@ -286,12 +287,6 @@
 
   Host& host() const;
 
-  // ui::AcceleratorTarget
-  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
-  bool CanHandleAccelerators() const override;
-
-  void AddAccelerators();
-
   // Sets the floating attributes of the glic window.
   //
   // When set to true, the glic window is set to have a `kFloatingWindow`
@@ -503,6 +498,9 @@
 
   std::unique_ptr<GlicModalManager> glic_modal_manager_;
 
+  std::unique_ptr<LocalHotkeyManager> application_hotkey_manager_;
+  std::unique_ptr<LocalHotkeyManager> glic_window_hotkey_manager_;
+
   raw_ptr<GlicKeyedService> glic_service_;  // Owns this.
   raw_ptr<GlicEnabling> enabling_;
 
diff --git a/chrome/browser/glic/widget/glic_window_hotkey_delegate.cc b/chrome/browser/glic/widget/glic_window_hotkey_delegate.cc
new file mode 100644
index 0000000..4a7cf43
--- /dev/null
+++ b/chrome/browser/glic/widget/glic_window_hotkey_delegate.cc
@@ -0,0 +1,121 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/glic/widget/glic_window_hotkey_delegate.h"
+
+#include "base/containers/fixed_flat_map.h"
+#include "base/containers/flat_map.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/widget/glic_view.h"
+#include "chrome/browser/glic/widget/glic_window_controller.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "ui/base/accelerators/accelerator_manager.h"
+#include "ui/base/accelerators/command.h"
+
+namespace glic {
+
+namespace {
+
+static constexpr std::array<glic::LocalHotkeyManager::Hotkey, 3>
+    kSupportedHotkeys = {
+        glic::LocalHotkeyManager::Hotkey::kClose,
+        glic::LocalHotkeyManager::Hotkey::kFocusToggle,
+#if BUILDFLAG(IS_WIN)
+        glic::LocalHotkeyManager::Hotkey::kTitleBarContextMenu,
+#endif
+};
+
+// Implementation of ScopedHotkeyRegistration specifically for the Glic window.
+// It registers and unregisters accelerators directly with the GlicView.
+class GlicWindowScopedHotkeyRegistration
+    : public LocalHotkeyManager::ScopedHotkeyRegistration {
+ public:
+  GlicWindowScopedHotkeyRegistration(ui::Accelerator accelerator,
+                                     base::WeakPtr<GlicView> glic_view)
+      : accelerator_(accelerator), glic_view_(glic_view) {
+    CHECK(!accelerator.IsEmpty());
+    CHECK(glic_view_);
+    glic_view_->AddAccelerator(accelerator_);
+  }
+
+  ~GlicWindowScopedHotkeyRegistration() override {
+    if (!glic_view_) {
+      return;
+    }
+    glic_view_->RemoveAccelerator(accelerator_);
+  }
+
+ private:
+  ui::Accelerator accelerator_;
+  base::WeakPtr<GlicView> glic_view_;
+};
+
+}  // namespace
+
+GlicWindowHotkeyDelegate::GlicWindowHotkeyDelegate(
+    base::WeakPtr<GlicWindowController> window_controller)
+    : window_controller_(window_controller) {}
+
+GlicWindowHotkeyDelegate::~GlicWindowHotkeyDelegate() = default;
+
+const base::span<const LocalHotkeyManager::Hotkey>
+GlicWindowHotkeyDelegate::GetSupportedHotkeys() const {
+  return kSupportedHotkeys;
+}
+
+bool GlicWindowHotkeyDelegate::AcceleratorPressed(
+    LocalHotkeyManager::Hotkey hotkey) {
+  if (!window_controller_) {
+    return false;
+  }
+
+  switch (hotkey) {
+    case LocalHotkeyManager::Hotkey::kClose:
+      window_controller_->Close();
+      return true;
+    case glic::LocalHotkeyManager::Hotkey::kFocusToggle:
+      if (window_controller_->IsAttached()) {
+        window_controller_->attached_browser()->window()->Activate();
+        return true;
+      }
+      if (auto* last_active = BrowserList::GetInstance()->GetLastActive()) {
+        last_active->window()->Activate();
+        return true;
+      }
+      return false;
+#if BUILDFLAG(IS_WIN)
+    case LocalHotkeyManager::Hotkey::kTitleBarContextMenu:
+      window_controller_->ShowTitleBarContextMenuAt(gfx::Point());
+      return true;
+#endif  //  BUILDFLAG(IS_WIN)
+
+    default:
+      NOTREACHED()
+          << "no handling implemented for "
+          << LocalHotkeyManager::GetAccelerator(hotkey).GetShortcutText();
+  }
+}
+
+std::unique_ptr<LocalHotkeyManager::ScopedHotkeyRegistration>
+GlicWindowHotkeyDelegate::CreateScopedHotkeyRegistration(
+    ui::Accelerator accelerator,
+    base::WeakPtr<ui::AcceleratorTarget> target) {
+  CHECK(window_controller_);
+  CHECK(window_controller_->GetGlicView());
+  return std::make_unique<GlicWindowScopedHotkeyRegistration>(
+      accelerator, window_controller_->GetGlicView()->GetWeakPtr());
+}
+
+std::unique_ptr<LocalHotkeyManager> MakeGlicWindowHotkeyManager(
+    base::WeakPtr<GlicWindowController> window_controller) {
+  return std::make_unique<LocalHotkeyManager>(
+      window_controller,
+      std::make_unique<GlicWindowHotkeyDelegate>(window_controller));
+}
+
+}  // namespace glic
diff --git a/chrome/browser/glic/widget/glic_window_hotkey_delegate.h b/chrome/browser/glic/widget/glic_window_hotkey_delegate.h
new file mode 100644
index 0000000..bcf899b
--- /dev/null
+++ b/chrome/browser/glic/widget/glic_window_hotkey_delegate.h
@@ -0,0 +1,51 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GLIC_WIDGET_GLIC_WINDOW_HOTKEY_DELEGATE_H_
+#define CHROME_BROWSER_GLIC_WIDGET_GLIC_WINDOW_HOTKEY_DELEGATE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/glic/widget/local_hotkey_manager.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace glic {
+
+class GlicWindowController;
+
+// Manages hotkeys that are active only when the Glic window itself has focus.
+// This class acts as a delegate for a LocalHotkeyManager instance, configuring
+// it with the set of hotkeys relevant to the Glic window (like Escape to close)
+// and handling the registration and dispatch of those hotkeys within the
+// Glic window's scope (typically via GlicView).
+class GlicWindowHotkeyDelegate : public LocalHotkeyManager::Delegate {
+ public:
+  explicit GlicWindowHotkeyDelegate(
+      base::WeakPtr<GlicWindowController> window_controller);
+  ~GlicWindowHotkeyDelegate() override;
+
+  // LocalHotkeyManager::Delegate:
+  const base::span<const LocalHotkeyManager::Hotkey> GetSupportedHotkeys()
+      const override;
+
+  bool AcceleratorPressed(LocalHotkeyManager::Hotkey hotkey) override;
+  std::unique_ptr<LocalHotkeyManager::ScopedHotkeyRegistration>
+  CreateScopedHotkeyRegistration(
+      ui::Accelerator accelerator,
+      base::WeakPtr<ui::AcceleratorTarget> target) override;
+
+  base::WeakPtr<GlicWindowHotkeyDelegate> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  base::WeakPtr<GlicWindowController> window_controller_;
+  base::WeakPtrFactory<GlicWindowHotkeyDelegate> weak_ptr_factory_{this};
+};
+
+std::unique_ptr<LocalHotkeyManager> MakeGlicWindowHotkeyManager(
+    base::WeakPtr<GlicWindowController> window_controller);
+
+}  // namespace glic
+
+#endif  // CHROME_BROWSER_GLIC_WIDGET_GLIC_WINDOW_HOTKEY_DELEGATE_H_
diff --git a/chrome/browser/glic/widget/local_hotkey_manager.cc b/chrome/browser/glic/widget/local_hotkey_manager.cc
new file mode 100644
index 0000000..31f3f803
--- /dev/null
+++ b/chrome/browser/glic/widget/local_hotkey_manager.cc
@@ -0,0 +1,150 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/glic/widget/local_hotkey_manager.h"
+
+#include "base/containers/fixed_flat_map.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/glic/glic_pref_names.h"
+#include "chrome/browser/glic/widget/glic_window_controller.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "ui/base/accelerators/accelerator_manager.h"
+#include "ui/base/accelerators/command.h"
+
+namespace glic {
+
+namespace {
+
+#if BUILDFLAG(IS_MAC)
+constexpr int kFocusToggleAcceleratorModifiers =
+    ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN;
+#else
+constexpr int kFocusToggleAcceleratorModifiers =
+    ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN;
+#endif
+
+constexpr auto kHotkeyToPrefMap =
+    base::MakeFixedFlatMap<LocalHotkeyManager::Hotkey, const char*>(
+        {{LocalHotkeyManager::Hotkey::kFocusToggle,
+          prefs::kGlicFocusToggleHotkey}});
+
+}  // namespace
+
+LocalHotkeyManager::LocalHotkeyManager(
+    base::WeakPtr<GlicWindowController> window_controller,
+    std::unique_ptr<Delegate> delegate)
+    : window_controller_(window_controller), delegate_(std::move(delegate)) {
+  CHECK(delegate_);
+  pref_registrar_.Init(g_browser_process->local_state());
+  for (Hotkey hotkey : delegate_->GetSupportedHotkeys()) {
+    auto pref_name_iter = kHotkeyToPrefMap.find(hotkey);
+    if (pref_name_iter == kHotkeyToPrefMap.end()) {
+      continue;
+    }
+    pref_registrar_.Add(pref_name_iter->second,
+                        base::BindRepeating(&LocalHotkeyManager::RegisterHotkey,
+                                            base::Unretained(this), hotkey));
+  }
+}
+
+LocalHotkeyManager::~LocalHotkeyManager() {
+  // Ensure that registrations are deleted before the WeakPtrs are invalidated
+  // as they might be depending on it.
+  hotkey_registrations_.clear();
+}
+
+ui::Accelerator LocalHotkeyManager::GetDefaultAccelerator(Hotkey hotkey) {
+  switch (hotkey) {
+    case Hotkey::kFocusToggle:
+      return ui::Accelerator{ui::VKEY_G, kFocusToggleAcceleratorModifiers};
+    case Hotkey::kClose:
+      return ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
+#if BUILDFLAG(IS_WIN)
+    case Hotkey::kTitleBarContextMenu:
+      return ui::Accelerator(ui::VKEY_SPACE, ui::EF_ALT_DOWN);
+#endif
+    default:
+      NOTREACHED();
+  }
+}
+
+ui::Accelerator LocalHotkeyManager::GetAccelerator(Hotkey hotkey) {
+  auto pref_name_iter = kHotkeyToPrefMap.find(hotkey);
+  if (pref_name_iter == kHotkeyToPrefMap.end()) {
+    return GetDefaultAccelerator(hotkey);
+  }
+
+  const ui::Accelerator accelerator = ui::Command::StringToAccelerator(
+      g_browser_process->local_state()->GetString(pref_name_iter->second));
+
+  // Return empty accelerator if an invalid modifier was set.
+  if (!accelerator.IsEmpty() &&
+      ui::Accelerator::MaskOutKeyEventFlags(accelerator.modifiers()) == 0) {
+    return ui::Accelerator();
+  }
+
+  return accelerator;
+}
+
+LocalHotkeyManager::Hotkey LocalHotkeyManager::GetHotkeyEnum(
+    ui::Accelerator accelerator) {
+  CHECK(delegate_);
+  for (Hotkey hotkey : delegate_->GetSupportedHotkeys()) {
+    if (GetAccelerator(hotkey) == accelerator) {
+      return hotkey;
+    }
+  }
+
+  NOTREACHED() << accelerator.GetShortcutText() << " isn't a supported hotkey";
+}
+
+void LocalHotkeyManager::InitializeAccelerators() {
+  CHECK(delegate_);
+  for (Hotkey hotkey : delegate_->GetSupportedHotkeys()) {
+    RegisterHotkey(hotkey);
+  }
+}
+
+bool LocalHotkeyManager::AcceleratorPressed(
+    const ui::Accelerator& accelerator) {
+  if (!window_controller_ || !delegate_) {
+    return false;
+  }
+  auto hotkey = GetHotkeyEnum(accelerator);
+  return delegate_->AcceleratorPressed(hotkey);
+}
+
+bool LocalHotkeyManager::CanHandleAccelerators() const {
+  if (!window_controller_) {
+    return false;
+  }
+
+  return window_controller_->IsShowing();
+}
+
+void LocalHotkeyManager::RegisterHotkey(Hotkey hotkey) {
+  auto accelerator = GetAccelerator(hotkey);
+
+  hotkey_registrations_.erase(hotkey);
+  if (accelerator.IsEmpty()) {
+    return;
+  }
+
+  if (!delegate_) {
+    return;
+  }
+
+  if (auto registration = delegate_->CreateScopedHotkeyRegistration(
+          accelerator, GetWeakPtr())) {
+    hotkey_registrations_.emplace(hotkey, std::move(registration));
+  }
+}
+
+}  // namespace glic
diff --git a/chrome/browser/glic/widget/local_hotkey_manager.h b/chrome/browser/glic/widget/local_hotkey_manager.h
new file mode 100644
index 0000000..5e93216
--- /dev/null
+++ b/chrome/browser/glic/widget/local_hotkey_manager.h
@@ -0,0 +1,109 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GLIC_WIDGET_LOCAL_HOTKEY_MANAGER_H_
+#define CHROME_BROWSER_GLIC_WIDGET_LOCAL_HOTKEY_MANAGER_H_
+
+#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "ui/base/accelerators/accelerator.h"
+
+namespace glic {
+class GlicWindowController;
+
+// Manages hotkeys that are active within a specific local scope, such as the
+// Glic window itself or the broader Chrome application when Glic is relevant.
+// This class handles retrieving accelerator definitions (potentially from user
+// preferences), registering them within the appropriate scope via a Delegate,
+// and dispatching pressed accelerators back to the Delegate for handling.
+class LocalHotkeyManager : public ui::AcceleratorTarget {
+ public:
+  // Enum representing the different hotkeys managed by this class.
+  enum class Hotkey {
+    // Close the Glic window. Only works when the Glic window has focus.
+    kClose,
+    // Toggle focus between the Glic window and the last active
+    // browser window.
+    kFocusToggle,
+#if BUILDFLAG(IS_WIN)
+    // Show the title bar context menu
+    kTitleBarContextMenu,
+#endif
+  };
+
+  // Interface for managing the lifetime of a registered hotkey.
+  // Implementations handle the specific registration/unregistration logic
+  // for their scope (e.g., adding/removing from a views::View or
+  // views::FocusManager).
+  class ScopedHotkeyRegistration {
+   public:
+    virtual ~ScopedHotkeyRegistration() = default;
+  };
+
+  // Delegate interface responsible for the actual registration and handling
+  // of hotkeys within a specific scope (e.g., Glic window, application-wide).
+  class Delegate {
+   public:
+    virtual ~Delegate() = default;
+
+    virtual const base::span<const Hotkey> GetSupportedHotkeys() const = 0;
+
+    // Creates a ScopedHotkeyRegistration for the given accelerator.
+    // The implementation should register the hotkey within its specific scope
+    // and return an object that unregisters it upon destruction.
+    virtual std::unique_ptr<ScopedHotkeyRegistration>
+    CreateScopedHotkeyRegistration(
+        ui::Accelerator accelerator,
+        base::WeakPtr<ui::AcceleratorTarget> target) = 0;
+
+    // Called when a registered hotkey associated with this manager is pressed.
+    // Returns true if the accelerator was handled, false otherwise.
+    virtual bool AcceleratorPressed(Hotkey) = 0;
+  };
+
+  explicit LocalHotkeyManager(
+      base::WeakPtr<GlicWindowController> window_controller,
+      std::unique_ptr<Delegate> delegate);
+  ~LocalHotkeyManager() override;
+
+  static ui::Accelerator GetDefaultAccelerator(Hotkey hotkey_enum);
+
+  // Returns the current accelerator for a given hotkey, potentially reading
+  // from user preferences. Falls back to the default if no preference is set
+  // or the preference is invalid.
+  static ui::Accelerator GetAccelerator(Hotkey hotkey_enum);
+
+  // Returns the Hotkey enum value corresponding to the given accelerator.
+  // CHECKs if the accelerator is not supported by this manager.
+  Hotkey GetHotkeyEnum(ui::Accelerator accelerator);
+
+  void InitializeAccelerators();
+
+  // ui::AcceleratorTarget:
+  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
+  bool CanHandleAccelerators() const override;
+
+  base::WeakPtr<LocalHotkeyManager> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
+ private:
+  void RegisterHotkey(Hotkey hotkey_enum);
+
+  base::WeakPtr<GlicWindowController> window_controller_;
+  std::unique_ptr<Delegate> delegate_;
+
+  PrefChangeRegistrar pref_registrar_;
+  base::flat_map<Hotkey, std::unique_ptr<ScopedHotkeyRegistration>>
+      hotkey_registrations_;
+  base::WeakPtrFactory<LocalHotkeyManager> weak_ptr_factory_{this};
+};
+
+}  // namespace glic
+
+#endif  // CHROME_BROWSER_GLIC_WIDGET_LOCAL_HOTKEY_MANAGER_H_
diff --git a/chrome/browser/global_keyboard_shortcuts_mac.mm b/chrome/browser/global_keyboard_shortcuts_mac.mm
index 580eebba..cbc0d472 100644
--- a/chrome/browser/global_keyboard_shortcuts_mac.mm
+++ b/chrome/browser/global_keyboard_shortcuts_mac.mm
@@ -150,10 +150,6 @@
       {true,  false, false, true,  kVK_DownArrow,         IDC_FOCUS_NEXT_PANE},
       {true,  false, false, true,  kVK_UpArrow,           IDC_FOCUS_PREVIOUS_PANE},
       {true,  true,  false, true,  kVK_ANSI_A,            IDC_FOCUS_INACTIVE_POPUP_FOR_ACCESSIBILITY},
-
-#if BUILDFLAG(ENABLE_GLIC)
-      {true,  false,  true, false,  kVK_ANSI_G,            IDC_GLIC_TOGGLE_FOCUS},
-#endif
     });
     // clang-format on
 
diff --git a/chrome/browser/invalidation/profile_invalidation_provider_factory.cc b/chrome/browser/invalidation/profile_invalidation_provider_factory.cc
index 6694b22..8e488bf 100644
--- a/chrome/browser/invalidation/profile_invalidation_provider_factory.cc
+++ b/chrome/browser/invalidation/profile_invalidation_provider_factory.cc
@@ -123,7 +123,7 @@
   policy::BrowserPolicyConnectorAsh* connector =
       g_browser_process->platform_part()->browser_policy_connector_ash();
   if (user_manager::UserManager::IsInitialized() &&
-      user_manager::UserManager::Get()->IsLoggedInAsKioskApp() &&
+      user_manager::UserManager::Get()->IsLoggedInAsKioskChromeApp() &&
       connector->IsDeviceEnterpriseManaged()) {
     identity_provider = std::make_unique<DeviceIdentityProvider>(
         DeviceOAuth2TokenServiceFactory::Get());
diff --git a/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc b/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
index 18531cd..af777e52 100644
--- a/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
+++ b/chrome/browser/new_tab_page/modules/new_tab_page_modules.cc
@@ -56,7 +56,7 @@
         IDS_NTP_MICROSOFT_AUTHENTICATION_SIDE_PANEL_DESCRIPTION);
   }
 
-  if (base::FeatureList::IsEnabled(
+  if (IsEnUSLocaleOnlyFeatureEnabled(
           ntp_features::kNtpMostRelevantTabResumptionModule)) {
     details.emplace_back(ntp_modules::kMostRelevantTabResumptionModuleId,
                          IDS_NTP_MODULES_MOST_RELEVANT_TAB_RESUMPTION_TITLE);
diff --git a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
index 44e4e82..67e9de98 100644
--- a/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
+++ b/chrome/browser/on_device_translation/on_device_translation_browsertest.cc
@@ -1995,10 +1995,13 @@
         GURL("https://translation-api.test/index.html")));
   }
 
-  // Adds an iframe to the test page.
-  void AddIframe(size_t index, Browser* target_browser) {
-    EXPECT_EQ(EvalJsCatchingError(JsReplace("return addIframe($1);",
-                                            CreateCrossOriginIframeUrl(index)),
+  // Adds an iframe to the test page and optionally sets its permission policy.
+  void AddIframe(size_t index,
+                 Browser* target_browser,
+                 bool permission_policy_enabled) {
+    EXPECT_EQ(EvalJsCatchingError(JsReplace("return addIframe($1, $2);",
+                                            CreateCrossOriginIframeUrl(index),
+                                            permission_policy_enabled),
                                   target_browser),
               "loaded");
   }
@@ -2022,18 +2025,22 @@
   std::string TryCreateTranslatorAndTranslateInIframe(size_t index,
                                                       Browser* target_browser) {
     const std::string_view translateTestScript = R"(
-      (async () => {
-        try {
-          window._translator = await Translator.create({
-            sourceLanguage: 'en',
-            targetLanguage: 'ja',
-          });
-          return await window._translator.translate('hello');
-        } catch (e) {
-          return e.toString();
-        }
-      })()
-    )";
+        (async () => {
+          try {
+            window._translator = await Translator.create({
+              sourceLanguage: 'en',
+              targetLanguage: 'ja',
+            });
+            if (window._translator) {
+              return await window._translator.translate('hello');
+            } else {
+              return 'NotSupportedError';
+            }
+          } catch (e) {
+            return e.name.toString();
+          }
+        })()
+      )";
     return EvalJsCatchingError(
         JsReplace("return evalInIframe($1, $2);",
                   CreateCrossOriginIframeUrl(index), translateTestScript),
@@ -2046,9 +2053,7 @@
                               bool expect_success,
                               Browser* target_browser) {
     EXPECT_EQ(TryCreateTranslatorAndTranslateInIframe(index, target_browser),
-              expect_success ? "en to ja: hello"
-                             : "NotSupportedError: Unable to create translator "
-                               "for the given source and target language.");
+              expect_success ? "en to ja: hello" : "NotSupportedError");
   }
 
   // Checks the result of availability() in the iframe.
@@ -2061,7 +2066,7 @@
             targetLanguage: 'ja',
           });
         } catch (e) {
-          return e.toString();
+          return e.name.toString();
         }
       })()
     )";
@@ -2081,11 +2086,14 @@
           R"(
           <head><script>
           const frames = {};
-          async function addIframe(url) {
-            const frame = document.createElement("iframe");
+          async function addIframe(url, permissionPolicyEnabled) {
+            const frame = document.createElement('iframe');
             frames[url] = frame;
+            if (permissionPolicyEnabled) {
+              frame.setAttribute('allow', 'translator');
+            }
             const loadedPromise = new Promise(resolve => {
-              frame.addEventListener("load", () => {
+              frame.addEventListener('load', () => {
                 resolve('loaded');
               });
             });
@@ -2102,7 +2110,7 @@
               });
             });
             channel.port1.start();
-            frame.contentWindow.postMessage(script, "*", [channel.port2]);
+            frame.contentWindow.postMessage(script, '*', [channel.port2]);
             return await onMessagePromise;
           }
           function removeIframe(url) {
@@ -2137,13 +2145,15 @@
 IN_PROC_BROWSER_TEST_F(OnDeviceTranslationCrossOriginBrowserTest,
                        TranslateInCrossOriginIframe) {
   MockComponentManager mock_component_manager(GetTempDir());
-  mock_component_manager.ExpectCallRegisterTranslateKitComponentAndInstall();
-  mock_component_manager.ExpectCallRegisterLanguagePackComponentAndInstall(
-      {LanguagePackKey::kEn_Ja});
+  EXPECT_CALL(mock_component_manager, RegisterTranslateKitComponentImpl())
+      .Times(0);
 
   NavigateToTestPage(browser());
-  AddIframe(0, browser());
-  CheckTranslateInIframe(0, /*expect_success=*/true, browser());
+  AddIframe(0, browser(), /*enable_permission_policy=*/false);
+
+  // Translation is not available in cross-origin iframes without permission
+  // policy.
+  CheckTranslateInIframe(0, /*expect_success=*/false, browser());
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe when the
@@ -2160,13 +2170,14 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    AddIframe(i, browser());
+    AddIframe(i, browser(), /*enable_permission_policy=*/true);
     CheckTranslateInIframe(i, /*expect_success=*/true, browser());
     EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
   }
 
-  // When the service count exceeds the limit, the translator cannot be created.
-  AddIframe(i, browser());
+  // When the service count exceeds the limit, the translator cannot be created,
+  // even when the permission policy is still enabled.
+  AddIframe(i, browser(), /*enable_permission_policy=*/true);
   auto console_observer = CreateConsoleObserver(
       "The translation service count exceeded the limitation.");
   CheckTranslateInIframe(i, /*expect_success=*/false, browser());
@@ -2192,8 +2203,11 @@
   Browser* incognito_browser = CreateIncognitoBrowser();
 
   NavigateToTestPage(incognito_browser);
-  AddIframe(0, incognito_browser);
+  AddIframe(0, incognito_browser, /*enable_permission_policy=*/true);
   CheckTranslateInIframe(0, /*expect_success=*/true, incognito_browser);
+
+  AddIframe(1, incognito_browser, /*enable_permission_policy=*/false);
+  CheckTranslateInIframe(1, /*expect_success=*/false, incognito_browser);
 }
 
 // Tests the behavior of the Translation API in a cross origin iframe using the
@@ -2208,7 +2222,7 @@
   Browser* guest_browser = CreateGuestBrowser();
 
   NavigateToTestPage(guest_browser);
-  AddIframe(0, guest_browser);
+  AddIframe(0, guest_browser, /*enable_permission_policy=*/true);
   CheckTranslateInIframe(0, /*expect_success=*/true, guest_browser);
 }
 
@@ -2244,7 +2258,7 @@
   // translation is successful.
   for (size_t i = 0; i < kTranslationAPIMaxServiceCount.Get(); i++) {
     for (auto* target_browser : browsers) {
-      AddIframe(i, target_browser);
+      AddIframe(i, target_browser, /*enable_permission_policy=*/true);
       CheckTranslateInIframe(i, /*expect_success=*/true, target_browser);
     }
   }
@@ -2254,7 +2268,7 @@
   // When the service count per profile exceeds the limit, the translator
   // cannot be created.
   for (auto* target_browser : browsers) {
-    AddIframe(limit_count, target_browser);
+    AddIframe(limit_count, target_browser, /*enable_permission_policy=*/true);
     auto console_observer = CreateConsoleObserver(
         "The translation service count exceeded the limitation.",
         target_browser);
@@ -2304,13 +2318,13 @@
   // Until the service count exceeds the limit, the translator can be created,
   // and the translation is successful.
   for (; i < kTranslationAPIMaxServiceCount.Get(); i++) {
-    AddIframe(i, browser());
+    AddIframe(i, browser(), /*enable_permission_policy=*/true);
     CheckTranslateInIframe(i, /*expect_success=*/true, browser());
     EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "available");
   }
 
   // When the service count exceeds the limit, the translator cannot be created.
-  AddIframe(i, browser());
+  AddIframe(i, browser(), /*enable_permission_policy=*/true);
   CheckTranslateInIframe(i, /*expect_success=*/false, browser());
   EXPECT_EQ(TryCanTranslateInIframe(i, browser()), "unavailable");
 
diff --git a/chrome/browser/passage_embeddings/omnibox_focus_change_listener.cc b/chrome/browser/passage_embeddings/omnibox_focus_change_listener.cc
new file mode 100644
index 0000000..1f39fae
--- /dev/null
+++ b/chrome/browser/passage_embeddings/omnibox_focus_change_listener.cc
@@ -0,0 +1,60 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/passage_embeddings/omnibox_focus_change_listener.h"
+
+#include <utility>
+
+#include "base/check.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
+#include "ui/views/view.h"
+
+SingleOmniboxFocusChangeListener::SingleOmniboxFocusChangeListener(
+    views::View* omnibox_view,
+    OmniboxFocusChangedCallback focus_changed_callback)
+    : omnibox_view_(omnibox_view),
+      focus_changed_callback_(focus_changed_callback) {
+  observation_.Observe(omnibox_view_->GetWidget()->GetFocusManager());
+}
+
+SingleOmniboxFocusChangeListener::~SingleOmniboxFocusChangeListener() = default;
+
+void SingleOmniboxFocusChangeListener::OnDidChangeFocus(
+    views::View* focused_before,
+    views::View* focused_now) {
+  if (focused_before == omnibox_view_ || focused_now == omnibox_view_) {
+    DCHECK_NE(focused_before, focused_now);
+    focus_changed_callback_.Run(focused_now == omnibox_view_);
+  }
+}
+
+OmniboxFocusChangedListener::OmniboxFocusChangedListener(
+    OmniboxFocusChangedCallback focus_changed_callback)
+    : focus_changed_callback_(std::move(focus_changed_callback)) {
+  BrowserList::GetInstance()->ForEachCurrentBrowser(
+      [this](Browser* browser) { OnBrowserAdded(browser); });
+  browser_list_observation_.Observe(BrowserList::GetInstance());
+}
+
+OmniboxFocusChangedListener::~OmniboxFocusChangedListener() = default;
+
+void OmniboxFocusChangedListener::OnBrowserAdded(Browser* browser) {
+  views::View* const omnibox_view =
+      browser->GetBrowserView().toolbar()->location_bar()->omnibox_view();
+
+  single_omnibox_focus_change_listeners_.emplace(
+      omnibox_view, std::make_unique<SingleOmniboxFocusChangeListener>(
+                        omnibox_view, focus_changed_callback_));
+  omnibox_view_observation_.AddObservation(omnibox_view);
+}
+
+void OmniboxFocusChangedListener::OnViewIsDeleting(views::View* observed_view) {
+  omnibox_view_observation_.RemoveObservation(observed_view);
+  single_omnibox_focus_change_listeners_.erase(observed_view);
+}
diff --git a/chrome/browser/passage_embeddings/omnibox_focus_change_listener.h b/chrome/browser/passage_embeddings/omnibox_focus_change_listener.h
new file mode 100644
index 0000000..c2e86889
--- /dev/null
+++ b/chrome/browser/passage_embeddings/omnibox_focus_change_listener.h
@@ -0,0 +1,97 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PASSAGE_EMBEDDINGS_OMNIBOX_FOCUS_CHANGE_LISTENER_H_
+#define CHROME_BROWSER_PASSAGE_EMBEDDINGS_OMNIBOX_FOCUS_CHANGE_LISTENER_H_
+
+#include <map>
+#include <memory>
+
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
+#include "base/scoped_multi_source_observation.h"
+#include "base/scoped_observation.h"
+#include "chrome/browser/ui/browser_list_observer.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/view_observer.h"
+
+namespace views {
+class View;
+}  // namespace views
+
+class BrowserList;
+
+using OmniboxFocusChangedCallback =
+    base::RepeatingCallback<void(bool is_focused)>;
+
+class SingleOmniboxFocusChangeListener : public views::FocusChangeListener {
+ public:
+  SingleOmniboxFocusChangeListener(
+      views::View* omnibox_view,
+      OmniboxFocusChangedCallback focus_changed_callback);
+  ~SingleOmniboxFocusChangeListener() override;
+
+  SingleOmniboxFocusChangeListener(const SingleOmniboxFocusChangeListener&) =
+      delete;
+  SingleOmniboxFocusChangeListener& operator=(
+      const SingleOmniboxFocusChangeListener&) = delete;
+
+  // views::FocusChangeListener:
+  void OnDidChangeFocus(views::View* focused_before,
+                        views::View* focused_now) override;
+
+ private:
+  raw_ptr<views::View> const omnibox_view_;
+  OmniboxFocusChangedCallback focus_changed_callback_;
+
+  base::ScopedObservation<views::FocusManager, SingleOmniboxFocusChangeListener>
+      observation_{this};
+};
+
+namespace base {
+
+template <>
+struct ScopedObservationTraits<views::FocusManager,
+                               SingleOmniboxFocusChangeListener> {
+  static void AddObserver(views::FocusManager* source,
+                          SingleOmniboxFocusChangeListener* observer) {
+    source->AddFocusChangeListener(observer);
+  }
+  static void RemoveObserver(views::FocusManager* source,
+                             SingleOmniboxFocusChangeListener* observer) {
+    source->RemoveFocusChangeListener(observer);
+  }
+};
+
+}  // namespace base
+
+class OmniboxFocusChangedListener : public BrowserListObserver,
+                                    public views::ViewObserver {
+ public:
+  OmniboxFocusChangedListener(
+      OmniboxFocusChangedCallback focus_changed_callback);
+  ~OmniboxFocusChangedListener() override;
+
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override;
+
+  // views::ViewObserver:
+  void OnViewIsDeleting(views::View* observed_view) override;
+
+ private:
+  OmniboxFocusChangedCallback focus_changed_callback_;
+
+  // Holds a focus listener for each browser's omnibox for the lifetime of the
+  // browser.
+  std::map<views::View*, std::unique_ptr<SingleOmniboxFocusChangeListener>>
+      single_omnibox_focus_change_listeners_;
+
+  base::ScopedObservation<BrowserList, OmniboxFocusChangedListener>
+      browser_list_observation_{this};
+
+  base::ScopedMultiSourceObservation<views::View, OmniboxFocusChangedListener>
+      omnibox_view_observation_{this};
+};
+
+#endif  // CHROME_BROWSER_PASSAGE_EMBEDDINGS_OMNIBOX_FOCUS_CHANGE_LISTENER_H_
diff --git a/chrome/browser/passage_embeddings/passage_embeddings_coordinator.cc b/chrome/browser/passage_embeddings/passage_embeddings_coordinator.cc
index cfb99fea..e30d7cf 100644
--- a/chrome/browser/passage_embeddings/passage_embeddings_coordinator.cc
+++ b/chrome/browser/passage_embeddings/passage_embeddings_coordinator.cc
@@ -151,7 +151,10 @@
 
 PassageEmbeddingsCoordinator::PassageEmbeddingsCoordinator(
     page_content_annotations::PageContentExtractionService*
-        page_content_extraction_service) {
+        page_content_extraction_service)
+    : omnibox_focus_changed_listener_(base::BindRepeating(
+          &PassageEmbeddingsCoordinator::OnOmniboxFocusChanged,
+          base::Unretained(this))) {
   page_content_extraction_observation_.Observe(page_content_extraction_service);
 }
 
@@ -177,7 +180,7 @@
       ChromePassageEmbeddingsServiceController::Get()
           ->GetEmbedder()
           ->ComputePassagesEmbeddings(
-              PassagePriority::kPassive, std::move(passages),
+              current_priority_, std::move(passages),
               base::BindOnce(
                   &PassageEmbeddingsCoordinator::OnPassageEmbeddingsComputed,
                   weak_ptr_factory_.GetWeakPtr(), web_contents_id));
@@ -204,4 +207,17 @@
   }
 }
 
+void PassageEmbeddingsCoordinator::OnOmniboxFocusChanged(bool is_focused) {
+  current_priority_ = is_focused ? kUrgent : kPassive;
+
+  std::set<Embedder::TaskId> task_ids;
+  for (const auto [web_contents, task_id] : web_contents_task_ids_) {
+    task_ids.insert(task_id);
+  }
+
+  ChromePassageEmbeddingsServiceController::Get()
+      ->GetEmbedder()
+      ->ReprioritizeTasks(current_priority_, task_ids);
+}
+
 }  // namespace passage_embeddings
diff --git a/chrome/browser/passage_embeddings/passage_embeddings_coordinator.h b/chrome/browser/passage_embeddings/passage_embeddings_coordinator.h
index 62ace1f..fc995f0 100644
--- a/chrome/browser/passage_embeddings/passage_embeddings_coordinator.h
+++ b/chrome/browser/passage_embeddings/passage_embeddings_coordinator.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
+#include "chrome/browser/passage_embeddings/omnibox_focus_change_listener.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/passage_embeddings/passage_embeddings_types.h"
 
@@ -46,6 +47,8 @@
                                    Embedder::TaskId task_id,
                                    ComputeEmbeddingsStatus status);
 
+  void OnOmniboxFocusChanged(bool is_focused);
+
   // The key is an id for a WebContents.
   std::map<uintptr_t, Embedder::TaskId> web_contents_task_ids_;
 
@@ -54,6 +57,10 @@
       PassageEmbeddingsCoordinator>
       page_content_extraction_observation_{this};
 
+  OmniboxFocusChangedListener omnibox_focus_changed_listener_;
+
+  PassagePriority current_priority_ = PassagePriority::kPassive;
+
   base::WeakPtrFactory<PassageEmbeddingsCoordinator> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/passage_embeddings/passage_embeddings_coordinator_factory.cc b/chrome/browser/passage_embeddings/passage_embeddings_coordinator_factory.cc
index 78ce220..7b225317 100644
--- a/chrome/browser/passage_embeddings/passage_embeddings_coordinator_factory.cc
+++ b/chrome/browser/passage_embeddings/passage_embeddings_coordinator_factory.cc
@@ -6,6 +6,7 @@
 
 #include "base/feature_list.h"
 #include "base/no_destructor.h"
+#include "chrome/browser/history_embeddings/history_embeddings_utils.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service_factory.h"
 #include "chrome/browser/passage_embeddings/passage_embedder_model_observer_factory.h"
 #include "chrome/browser/passage_embeddings/passage_embeddings_coordinator.h"
@@ -47,10 +48,24 @@
 std::unique_ptr<KeyedService>
 PassageEmbeddingsCoordinatorFactory::BuildServiceInstanceForBrowserContext(
     content::BrowserContext* profile) const {
+  // Don't run the experiment for clients with history embeddings enabled.
+  if (history_embeddings::IsHistoryEmbeddingsEnabledForProfile(
+          Profile::FromBrowserContext(profile))) {
+    return nullptr;
+  }
+
+  // Don't bother running if we don't have a model observer since we won't have
+  // a model to run.
+  if (!PassageEmbedderModelObserverFactory::GetForProfile(
+          Profile::FromBrowserContext(profile))) {
+    return nullptr;
+  }
+
   if (!base::FeatureList::IsEnabled(passage_embeddings::kPassageEmbedder)) {
     return nullptr;
   }
 
+  // Required to ensure the model observer starts.
   PassageEmbedderModelObserverFactory::GetForProfile(
       Profile::FromBrowserContext(profile));
 
diff --git a/chrome/browser/policy/test/sharing_policy_browsertest.cc b/chrome/browser/policy/test/sharing_policy_browsertest.cc
new file mode 100644
index 0000000..405eaa7
--- /dev/null
+++ b/chrome/browser/policy/test/sharing_policy_browsertest.cc
@@ -0,0 +1,117 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/collaboration/collaboration_service_factory.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/ui/browser.h"
+#include "components/collaboration/public/collaboration_service.h"
+#include "components/collaboration/public/pref_names.h"
+#include "components/collaboration/public/service_status.h"
+#include "components/data_sharing/public/features.h"
+#include "components/policy/core/common/policy_map.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/test/browser_test.h"
+
+namespace policy {
+
+namespace {
+
+class MockCollaborationServiceObserver
+    : public collaboration::CollaborationService::Observer {
+ public:
+  MockCollaborationServiceObserver() = default;
+  ~MockCollaborationServiceObserver() override = default;
+
+  MOCK_METHOD(void,
+              OnServiceStatusChanged,
+              (const ServiceStatusUpdate& update),
+              (override));
+};
+
+}  // namespace
+
+class TabGroupSharingEnabledTest : public PolicyTest {
+ public:
+  TabGroupSharingEnabledTest() {
+    feature_list_.InitWithFeatureStates({
+        {data_sharing::features::kDataSharingFeature, true},
+        {data_sharing::features::kCollaborationEntrepriseV2, true},
+    });
+  }
+
+  void SetUpOnMainThread() override {
+    PolicyTest::SetUpOnMainThread();
+
+    // Sign in.
+    identity_test_env_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
+            browser()->profile());
+    auto account_info =
+        identity_test_env_adaptor_->identity_test_env()
+            ->MakePrimaryAccountAvailable("user@google.com",
+                                          signin::ConsentLevel::kSignin);
+    identity_test_env_adaptor_->identity_test_env()
+        ->UpdateAccountInfoForAccount(account_info);
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    PolicyTest::SetUpInProcessBrowserTestFixture();
+
+    create_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterCreateServicesCallbackForTesting(base::BindRepeating(
+                &TabGroupSharingEnabledTest::OnWillCreateBrowserContextServices,
+                base::Unretained(this)));
+  }
+
+ private:
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    IdentityTestEnvironmentProfileAdaptor::
+        SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+
+  // Identity test support.
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_adaptor_;
+  base::CallbackListSubscription create_services_subscription_;
+};
+
+IN_PROC_BROWSER_TEST_F(TabGroupSharingEnabledTest, TabGroupSharingEnabled) {
+  const PrefService* const prefs =
+      chrome_test_utils::GetProfile(this)->GetPrefs();
+  EXPECT_EQ(0 /*disabled*/,
+            prefs->GetInteger(
+                collaboration::prefs::kSharedTabGroupsManagedAccountSetting));
+
+  testing::StrictMock<MockCollaborationServiceObserver> mock_observer;
+  auto* profile = browser()->profile();
+  auto* service =
+      collaboration::CollaborationServiceFactory::GetForProfile(profile);
+  service->AddObserver(&mock_observer);
+  collaboration::CollaborationService::Observer::ServiceStatusUpdate update;
+  EXPECT_CALL(mock_observer, OnServiceStatusChanged(testing::_))
+      .WillOnce(testing::SaveArg<0>(&update));
+
+  EXPECT_TRUE(service->GetServiceStatus().IsAllowedToJoin());
+
+  // Now set the policy and check that shared tab group managed account is
+  // turned off by policy.
+  PolicyMap policies;
+  policies.Set(key::kTabGroupSharingSettings, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, base::Value(1), nullptr);
+  UpdateProviderPolicy(policies);
+  EXPECT_EQ(1 /*enabled*/,
+            prefs->GetInteger(
+                collaboration::prefs::kSharedTabGroupsManagedAccountSetting));
+  EXPECT_FALSE(service->GetServiceStatus().IsAllowedToJoin());
+  EXPECT_EQ(update.new_status.collaboration_status,
+            collaboration::CollaborationStatus::kDisabledForPolicy);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index d6e6788..c409f160 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1975,6 +1975,7 @@
   extensions::util::RegisterProfilePrefs(registry);
   extensions_ui_prefs::RegisterProfilePrefs(registry);
   ExtensionWebUI::RegisterProfilePrefs(registry);
+  update_client::RegisterProfilePrefs(registry);
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -1985,7 +1986,6 @@
   // were nested in either a class or separate namespace with a simple
   // Register[Profile]Prefs() name.
   extensions::RegisterSettingsOverriddenUiPrefs(registry);
-  update_client::RegisterProfilePrefs(registry);
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
 
 #if BUILDFLAG(ENABLE_PDF)
diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn
index be63bce..3926c74 100644
--- a/chrome/browser/profiles/BUILD.gn
+++ b/chrome/browser/profiles/BUILD.gn
@@ -271,6 +271,7 @@
     "//components/manta",
     "//components/omnibox/browser",
     "//components/optimization_guide/core",
+    "//components/passage_embeddings",
     "//components/password_manager/content/browser",
     "//components/payments/content",
     "//components/permissions:permissions_common",
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 2da10ef..a146106 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -321,6 +321,7 @@
 #include "chrome/browser/new_tab_page/modules/file_suggestion/drive_service_factory.h"
 #include "chrome/browser/new_tab_page/one_google_bar/one_google_bar_service_factory.h"
 #include "chrome/browser/new_tab_page/promos/promo_service_factory.h"
+#include "chrome/browser/passage_embeddings/passage_embeddings_coordinator_factory.h"
 #include "chrome/browser/payments/payment_request_display_manager_factory.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_survey_desktop_controller_factory.h"
@@ -1089,6 +1090,9 @@
   PageColorsFactory::GetInstance();
 #endif
   passage_embeddings::PassageEmbedderModelObserverFactory::GetInstance();
+#if !BUILDFLAG(IS_ANDROID)
+  passage_embeddings::PassageEmbeddingsCoordinatorFactory::GetInstance();
+#endif
   password_manager::PasswordManagerLogRouterFactory::GetInstance();
   password_manager::PasswordRequirementsServiceFactory::GetInstance();
 #if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc
index a490fef..363a7c4a 100644
--- a/chrome/browser/profiles/profile.cc
+++ b/chrome/browser/profiles/profile.cc
@@ -58,7 +58,7 @@
 #include "chrome/browser/profiles/android/jni_headers/OtrProfileId_jni.h"
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 #include "extensions/browser/extension_pref_store.h"              // nogncheck
 #include "extensions/browser/extension_pref_value_map_factory.h"  // nogncheck
 #include "extensions/browser/pref_names.h"                        // nogncheck
@@ -332,7 +332,7 @@
 #endif  // BUILDFLAG(IS_ANDROID)
   registry->RegisterStringPref(prefs::kSessionExitType, std::string());
   registry->RegisterBooleanPref(prefs::kDisableExtensions, false);
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   registry->RegisterBooleanPref(extensions::pref_names::kAlertsInitialized,
                                 false);
 #endif
@@ -478,7 +478,7 @@
 // static
 PrefStore* Profile::CreateExtensionPrefStore(Profile* profile,
                                              bool incognito_pref_store) {
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
   if (ExtensionPrefValueMap* pref_value_map =
           ExtensionPrefValueMapFactory::GetForBrowserContext(profile)) {
     return new ExtensionPrefStore(pref_value_map, incognito_pref_store);
diff --git a/chrome/browser/profiles/profiles_state.cc b/chrome/browser/profiles/profiles_state.cc
index 7c1d296..7362b19 100644
--- a/chrome/browser/profiles/profiles_state.cc
+++ b/chrome/browser/profiles/profiles_state.cc
@@ -309,7 +309,7 @@
 
 bool IsChromeAppKioskSession() {
 #if BUILDFLAG(IS_CHROMEOS)
-  return user_manager::UserManager::Get()->IsLoggedInAsKioskApp();
+  return user_manager::UserManager::Get()->IsLoggedInAsKioskChromeApp();
 #else
   return false;
 #endif
diff --git a/chrome/browser/resources/settings/autofill_page/pay_over_time_issuer_list_entry.html b/chrome/browser/resources/settings/autofill_page/pay_over_time_issuer_list_entry.html
index aa4537e..3bac5fc4 100644
--- a/chrome/browser/resources/settings/autofill_page/pay_over_time_issuer_list_entry.html
+++ b/chrome/browser/resources/settings/autofill_page/pay_over_time_issuer_list_entry.html
@@ -19,6 +19,7 @@
   }
 
   #payOverTimeIssuerImage {
+    line-height: 0;
     margin-inline-end: 16px;
     vertical-align: middle;
   }
@@ -26,8 +27,12 @@
 
 <div class="list-item" role="row">
   <div class="type-column" role="cell">
-    <img id="payOverTimeIssuerImage"
-        srcset="[[getIssuerImage_(payOverTimeIssuer.imageSrc)]]" alt="">
+    <picture id="payOverTimeIssuerImage">
+      <source
+          srcset="[[getIssuerImage_(payOverTimeIssuer.imageSrcDark)]]"
+          media="(prefers-color-scheme: dark)">
+      <img alt="" srcset="[[getIssuerImage_(payOverTimeIssuer.imageSrc)]]">
+    </picture>
     <div class="summary-column screen-reader-only-host-node" role="cell">
       <div id="summaryLabel" class="ellipses">
         [[payOverTimeIssuer.displayName]]
diff --git a/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts b/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts
index 8313fdbaa8..8fc4696 100644
--- a/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/bookmarks_api_proxy.ts
@@ -46,6 +46,7 @@
       void;
   contextMenuOpenBookmarkInNewTabGroup(ids: string[], source: ActionSource):
       void;
+  contextMenuOpenBookmarkInSplitView(ids: string[], source: ActionSource): void;
   contextMenuEdit(ids: string[], source: ActionSource): void;
   contextMenuMove(ids: string[], source: ActionSource): void;
   contextMenuAddToBookmarksBar(id: string, source: ActionSource): void;
@@ -98,6 +99,11 @@
         ids.map(id => BigInt(id)), source);
   }
 
+  contextMenuOpenBookmarkInSplitView(ids: string[], source: ActionSource) {
+    this.handler.executeOpenInSplitViewCommand(
+        ids.map(id => BigInt(id)), source);
+  }
+
   contextMenuEdit(ids: string[], source: ActionSource) {
     this.handler.executeEditCommand(ids.map(id => BigInt(id)), source);
   }
diff --git a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
index bf8ae6f..3a405ae8 100644
--- a/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
+++ b/chrome/browser/resources/side_panel/bookmarks/power_bookmarks_context_menu.ts
@@ -42,6 +42,7 @@
   RENAME = 8,
   DELETE = 9,
   DIVIDER = 10,
+  OPEN_SPLIT_VIEW = 11,
 }
 
 export interface MenuItem {
@@ -157,6 +158,14 @@
       });
     }
 
+    if (loadTimeData.getBoolean('splitViewEnabled') && bookmarkCount === 1 &&
+        this.bookmarks_[0].url) {
+      menuItems.push({
+        id: MenuItemId.OPEN_SPLIT_VIEW,
+        label: loadTimeData.getString('menuOpenSplitView'),
+      });
+    }
+
     if (this.bookmarks_.length !== 1 || !this.bookmarks_[0].url) {
       menuItems.push({
         id: MenuItemId.OPEN_NEW_TAB_GROUP,
@@ -299,6 +308,11 @@
             this.bookmarks_.map(bookmark => bookmark.id),
             ActionSource.kBookmark);
         break;
+      case MenuItemId.OPEN_SPLIT_VIEW:
+        this.bookmarksApi_.contextMenuOpenBookmarkInSplitView(
+            this.bookmarks_.map(bookmark => bookmark.id),
+            ActionSource.kBookmark);
+        break;
       case MenuItemId.ADD_TO_BOOKMARKS_BAR:
         assert(this.bookmarks_.length === 1);
         if (editingDisabledByPolicy(this.bookmarks_)) {
diff --git a/chrome/browser/resources/side_panel/read_anything/language_menu.ts b/chrome/browser/resources/side_panel/read_anything/language_menu.ts
index f87019a..fa5547f 100644
--- a/chrome/browser/resources/side_panel/read_anything/language_menu.ts
+++ b/chrome/browser/resources/side_panel/read_anything/language_menu.ts
@@ -160,6 +160,11 @@
     return this.localeToDisplayName[langLower] || langLower;
   }
 
+  private getNormalizedDisplayName(lang: string) {
+    const displayName = this.getDisplayName(lang);
+    return displayName.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
+  }
+
   private getSupportedNaturalVoiceDownloadLocales(): Set<string> {
     return AVAILABLE_GOOGLE_TTS_LOCALES;
   }
@@ -184,15 +189,7 @@
 
     return availableLangs
         .filter(
-            // Check whether the search term matches the readable lang (e.g.
-            // 'ras' will match 'Portugues (Brasil)'), and also if it matches
-            // the language code (e.g. 'pt-br' matches 'Portugues (Brasil)')
-            lang => isSubstring(
-                        /* value= */ this.getDisplayName(lang),
-                        /* substring= */ this.languageSearchValue_) ||
-                isSubstring(
-                        /* value= */ lang,
-                        /* substring= */ this.languageSearchValue_))
+            lang => this.isLanguageSearchMatch(lang, this.languageSearchValue_))
         .map(lang => ({
                readableLanguage: this.getDisplayName(lang),
                checked: this.enabledLangs.includes(lang),
@@ -203,6 +200,29 @@
              }));
   }
 
+  // Check whether the search term matches the readable lang (e.g.
+  // 'ras' will match 'Portugues (Brasil)'), if it matches
+  // the language code (e.g. 'pt-br' matches 'Portugues (Brasil)'), or if it
+  // matches without accents (e.g. 'portugues' matches 'portugués').
+  private isLanguageSearchMatch(lang: string, languageSearchValue: string):
+      boolean {
+    const isDisplayNameMatch = isSubstring(
+        /* value= */ this.getDisplayName(lang),
+        /* substring= */ languageSearchValue);
+    const isLanguageCodeMatch = isSubstring(
+        /* value= */ lang,
+        /* substring= */ languageSearchValue);
+
+    // Compare the search term to the language name without
+    // accents.
+    const isNormalizedDisplayNameMatch = isSubstring(
+        /* value= */ this.getNormalizedDisplayName(lang),
+        /* substring= */ languageSearchValue);
+
+    return isDisplayNameMatch || isLanguageCodeMatch ||
+        isNormalizedDisplayNameMatch;
+  }
+
   private getNotificationFor(lang: string): Notification {
     const voicePackLanguage = getVoicePackConvertedLangIfExists(lang);
     const notification = this.currentNotifications_[voicePackLanguage];
diff --git a/chrome/browser/resources/tab_strip/tab_group.ts b/chrome/browser/resources/tab_strip/tab_group.ts
index eef33a3..cc2b51ef 100644
--- a/chrome/browser/resources/tab_strip/tab_group.ts
+++ b/chrome/browser/resources/tab_strip/tab_group.ts
@@ -106,13 +106,17 @@
 
     // Content strings are empty for the label and are instead replaced by
     // the aria-describedby attribute on the chip.
+    // TODO(crbug.com/413733034) Support share tab group for thumbnail tab
+    // strip.
     if (visualData.title) {
       this.chip_.setAttribute(
           'aria-label',
-          loadTimeData.getStringF('namedGroupLabel', visualData.title, ''));
+          loadTimeData.getStringF(
+              'namedGroupLabel', '', visualData.title, '', ''));
     } else {
       this.chip_.setAttribute(
-          'aria-label', loadTimeData.getStringF('unnamedGroupLabel', ''));
+          'aria-label',
+          loadTimeData.getStringF('unnamedGroupLabel', '', '', ''));
     }
   }
 }
diff --git a/chrome/browser/ui/android/appmenu/internal/java/res/layout/title_button_menu_item.xml b/chrome/browser/ui/android/appmenu/internal/java/res/layout/title_button_menu_item.xml
index ff75035b..cae1a3c 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/res/layout/title_button_menu_item.xml
+++ b/chrome/browser/ui/android/appmenu/internal/java/res/layout/title_button_menu_item.xml
@@ -18,24 +18,19 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:gravity="center_vertical">
-
-     <org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables
-        android:id="@+id/title"
-        style="@style/AppMenuItemTextViewWithCompoundDrawables"
-        android:layout_width="wrap_content"
+    <ViewStub android:id="@+id/menu_item_container_stub"
+        android:inflatedId="@+id/menu_item_container"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
-        android:layout_toStartOf="@id/checkbox_icon_container"
-        android:paddingStart="16dp"
-        android:ellipsize="end"
-        android:minHeight="?android:attr/listPreferredItemHeightSmall" />
+        android:layout_toStartOf="@id/action_icon_container" />
 
     <FrameLayout
-        android:id="@+id/checkbox_icon_container"
+        android:id="@+id/action_icon_container"
         android:layout_width="56dp"
         android:layout_height="wrap_content"
-        android:layout_alignTop="@id/title"
-        android:layout_alignBottom="@id/title"
+        android:layout_alignTop="@id/menu_item_container"
+        android:layout_alignBottom="@id/menu_item_container"
         android:layout_alignParentEnd="true">
 
         <!-- Checkbox.  Paddings account for built-in padding from the Android resource. -->
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
index 53b1a2f..6df2e541 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
@@ -566,8 +566,8 @@
             PropertyModel model = mModelList.get(i).model;
             if (model.get(AppMenuItemProperties.MENU_ITEM_ID) == itemId) {
                 return model;
-            } else if (model.get(AppMenuItemProperties.SUBMENU) != null) {
-                ModelList subList = model.get(AppMenuItemProperties.SUBMENU);
+            } else if (model.get(AppMenuItemProperties.ADDITIONAL_ICONS) != null) {
+                ModelList subList = model.get(AppMenuItemProperties.ADDITIONAL_ICONS);
                 for (int j = 0; j < subList.size(); j++) {
                     PropertyModel subModel = subList.get(j).model;
                     if (subModel.get(AppMenuItemProperties.MENU_ITEM_ID) == itemId) {
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
index 0d5d4b7..1c27ee6 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
@@ -15,7 +15,10 @@
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
@@ -443,15 +446,12 @@
         return mDelegate;
     }
 
-    private void registerViewBinders(
-            @Nullable List<CustomViewBinder> customViewBinders,
-            Map<CustomViewBinder, Integer> customViewTypeOffsetMap,
-            ModelListAdapter adapter,
-            boolean iconBeforeItem) {
-        int standardItemResId = R.layout.menu_item;
-        if (iconBeforeItem) {
-            standardItemResId = R.layout.menu_item_start_with_icon;
-        }
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static void registerDefaultViewBinders(
+            ModelListAdapter adapter, boolean iconBeforeItem) {
+        @LayoutRes
+        int standardItemResId =
+                iconBeforeItem ? R.layout.menu_item_start_with_icon : R.layout.menu_item;
 
         adapter.registerType(
                 AppMenuItemType.STANDARD,
@@ -459,12 +459,29 @@
                 AppMenuItemViewBinder::bindStandardItem);
         adapter.registerType(
                 AppMenuItemType.TITLE_BUTTON,
-                new LayoutViewBuilder(R.layout.title_button_menu_item),
+                new LayoutViewBuilder<ViewGroup>(R.layout.title_button_menu_item) {
+                    @Override
+                    protected ViewGroup postInflationInit(ViewGroup view) {
+                        ViewStub stub = view.findViewById(R.id.menu_item_container_stub);
+                        stub.setLayoutResource(standardItemResId);
+                        View inflatedView = stub.inflate();
+                        inflatedView.setDuplicateParentStateEnabled(true);
+                        return view;
+                    }
+                },
                 AppMenuItemViewBinder::bindTitleButtonItem);
         adapter.registerType(
                 AppMenuItemType.BUTTON_ROW,
                 new LayoutViewBuilder(R.layout.icon_row_menu_item),
                 AppMenuItemViewBinder::bindIconRowItem);
+    }
+
+    private void registerViewBinders(
+            @Nullable List<CustomViewBinder> customViewBinders,
+            Map<CustomViewBinder, Integer> customViewTypeOffsetMap,
+            ModelListAdapter adapter,
+            boolean iconBeforeItem) {
+        registerDefaultViewBinders(adapter, iconBeforeItem);
 
         if (customViewBinders == null) return;
         for (int i = 0; i < customViewBinders.size(); i++) {
@@ -519,8 +536,8 @@
                 model.set(
                         AppMenuItemProperties.HIGHLIGHTED,
                         model.get(AppMenuItemProperties.MENU_ITEM_ID) == highlightedId);
-                if (model.get(AppMenuItemProperties.SUBMENU) != null) {
-                    ModelList subList = model.get(AppMenuItemProperties.SUBMENU);
+                if (model.get(AppMenuItemProperties.ADDITIONAL_ICONS) != null) {
+                    ModelList subList = model.get(AppMenuItemProperties.ADDITIONAL_ICONS);
                     for (int j = 0; j < subList.size(); j++) {
                         PropertyModel subModel = subList.get(j).model;
                         subModel.set(AppMenuItemProperties.CLICK_HANDLER, appMenuClickHandler);
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
index f667d2bd..39ac50c9 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinder.java
@@ -4,9 +4,9 @@
 
 package org.chromium.chrome.browser.ui.appmenu;
 
-import static org.chromium.build.NullUtil.assumeNonNull;
-
+import android.annotation.SuppressLint;
 import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.view.View;
@@ -23,7 +23,6 @@
 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter;
 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter.HighlightParams;
 import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter.HighlightShape;
-import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -100,66 +99,37 @@
     }
 
     public static void bindTitleButtonItem(PropertyModel model, View view, PropertyKey key) {
-        AppMenuUtil.bindStandardItemEnterAnimation(model, view, key);
+        bindStandardItem(model, view, key);
 
-        if (key == AppMenuItemProperties.SUBMENU) {
-            ModelList subList = model.get(AppMenuItemProperties.SUBMENU);
-            PropertyModel titleModel = subList.get(0).model;
-
-            view.setId(titleModel.get(AppMenuItemProperties.MENU_ITEM_ID));
-
-            TextViewWithCompoundDrawables title =
-                    (TextViewWithCompoundDrawables) view.findViewById(R.id.title);
-            title.setText(titleModel.get(AppMenuItemProperties.TITLE));
-            title.setEnabled(titleModel.get(AppMenuItemProperties.ENABLED));
-            title.setFocusable(titleModel.get(AppMenuItemProperties.ENABLED));
-            title.setCompoundDrawablesRelative(
-                    titleModel.get(AppMenuItemProperties.ICON), null, null, null);
-            setContentDescription(title, titleModel);
+        if (key == AppMenuItemProperties.ADDITIONAL_ICONS) {
+            ModelList subList = model.get(AppMenuItemProperties.ADDITIONAL_ICONS);
+            assert subList.size() == 1;
 
             AppMenuClickHandler appMenuClickHandler =
                     model.get(AppMenuItemProperties.CLICK_HANDLER);
-            title.setOnClickListener(v -> appMenuClickHandler.onItemClick(titleModel));
-            if (titleModel.get(AppMenuItemProperties.HIGHLIGHTED)) {
-                ViewHighlighter.turnOnHighlight(
-                        view, new HighlightParams(HighlightShape.RECTANGLE));
-            } else {
-                ViewHighlighter.turnOffHighlight(view);
-            }
 
-            AppMenuItemIcon checkbox = (AppMenuItemIcon) view.findViewById(R.id.checkbox);
-            ChromeImageButton button = (ChromeImageButton) view.findViewById(R.id.button);
-            PropertyModel buttonModel = null;
-            boolean checkable = false;
-            boolean checked = false;
-            boolean buttonEnabled = true;
-            Drawable subIcon = null;
+            View titleContainer = view.findViewById(R.id.menu_item_container);
+            View actionIconContainer = view.findViewById(R.id.action_icon_container);
+            AppMenuItemIcon checkbox = view.findViewById(R.id.checkbox);
+            ChromeImageButton button = view.findViewById(R.id.button);
+            PropertyModel buttonModel = subList.get(0).model;
+            boolean hasAction = true;
 
-            if (subList.size() == 2) {
-                buttonModel = subList.get(1).model;
-                checkable = buttonModel.get(AppMenuItemProperties.CHECKABLE);
-                checked = buttonModel.get(AppMenuItemProperties.CHECKED);
-                buttonEnabled = buttonModel.get(AppMenuItemProperties.ENABLED);
-                subIcon = buttonModel.get(AppMenuItemProperties.ICON);
-            }
-
-            if (checkable) {
-                assumeNonNull(buttonModel);
+            if (buttonModel.get(AppMenuItemProperties.CHECKABLE)) {
                 // Display a checkbox for the MenuItem.
                 button.setVisibility(View.GONE);
                 checkbox.setVisibility(View.VISIBLE);
-                checkbox.setChecked(checked);
+                checkbox.setChecked(buttonModel.get(AppMenuItemProperties.CHECKED));
                 ImageViewCompat.setImageTintList(
                         checkbox,
                         AppCompatResources.getColorStateList(
                                 checkbox.getContext(), R.color.selection_control_button_tint_list));
                 setupMenuButton(checkbox, buttonModel, appMenuClickHandler);
-            } else if (subIcon != null) {
-                assumeNonNull(buttonModel);
+            } else if (buttonModel.get(AppMenuItemProperties.ICON) != null) {
                 // Display an icon alongside the MenuItem.
                 checkbox.setVisibility(View.GONE);
                 button.setVisibility(View.VISIBLE);
-                if (!buttonEnabled) {
+                if (!buttonModel.get(AppMenuItemProperties.ENABLED)) {
                     // Only grey out the icon when disabled. When the menu is enabled, use the
                     // icon's original color.
                     Drawable icon = buttonModel.get(AppMenuItemProperties.ICON);
@@ -172,23 +142,38 @@
                 }
                 setupImageButton(button, buttonModel, appMenuClickHandler);
             } else {
-                // Display just the label of the MenuItem.
-                checkbox.setVisibility(View.GONE);
-                button.setVisibility(View.GONE);
+                hasAction = false;
             }
-        } else if (key == AppMenuItemProperties.HIGHLIGHTED) {
-            if (model.get(AppMenuItemProperties.HIGHLIGHTED)) {
-                ViewHighlighter.turnOnHighlight(
-                        view, new HighlightParams(HighlightShape.RECTANGLE));
+
+            if (hasAction) {
+                actionIconContainer.setVisibility(View.VISIBLE);
+                titleContainer.setPaddingRelative(
+                        titleContainer.getPaddingStart(),
+                        titleContainer.getPaddingTop(),
+                        0,
+                        titleContainer.getPaddingBottom());
             } else {
-                ViewHighlighter.turnOffHighlight(view);
+                // Display just the label of the MenuItem.
+                actionIconContainer.setVisibility(View.GONE);
+
+                int[] attrs = {android.R.attr.paddingEnd};
+                @SuppressLint("ResourceType")
+                TypedArray ta =
+                        view.getContext().obtainStyledAttributes(R.style.AppMenuItem, attrs);
+                int paddingEnd = ta.getDimensionPixelSize(0, 0);
+                titleContainer.setPaddingRelative(
+                        titleContainer.getPaddingStart(),
+                        titleContainer.getPaddingTop(),
+                        paddingEnd,
+                        titleContainer.getPaddingBottom());
+                ta.recycle();
             }
         }
     }
 
     public static void bindIconRowItem(PropertyModel model, View view, PropertyKey key) {
-        if (key == AppMenuItemProperties.SUBMENU) {
-            ModelList iconList = model.get(AppMenuItemProperties.SUBMENU);
+        if (key == AppMenuItemProperties.ADDITIONAL_ICONS) {
+            ModelList iconList = model.get(AppMenuItemProperties.ADDITIONAL_ICONS);
 
             AppMenuClickHandler appMenuClickHandler =
                     model.get(AppMenuItemProperties.CLICK_HANDLER);
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderRenderTest.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderRenderTest.java
index a79df74..4682178 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderRenderTest.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderRenderTest.java
@@ -34,7 +34,6 @@
 import org.chromium.chrome.browser.ui.appmenu.test.R;
 import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
 import org.chromium.chrome.test.util.ChromeRenderTestRule;
-import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.ModelListAdapter;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.test.util.BlankUiTestActivity;
@@ -108,18 +107,7 @@
                     sListView = sContentView.findViewById(R.id.app_menu_list);
                     sListView.setAdapter(mModelListAdapter);
 
-                    mModelListAdapter.registerType(
-                            AppMenuItemType.STANDARD,
-                            new LayoutViewBuilder(R.layout.menu_item_start_with_icon),
-                            AppMenuItemViewBinder::bindStandardItem);
-                    mModelListAdapter.registerType(
-                            AppMenuItemType.TITLE_BUTTON,
-                            new LayoutViewBuilder(R.layout.title_button_menu_item),
-                            AppMenuItemViewBinder::bindTitleButtonItem);
-                    mModelListAdapter.registerType(
-                            AppMenuItemType.BUTTON_ROW,
-                            new LayoutViewBuilder(R.layout.icon_row_menu_item),
-                            AppMenuItemViewBinder::bindIconRowItem);
+                    AppMenuHandlerImpl.registerDefaultViewBinders(mModelListAdapter, true);
                 });
     }
 
@@ -153,7 +141,6 @@
     }
 
     private PropertyModel createTitleMenuItem(
-            int mainMenuId,
             int titleMenuId,
             String title,
             boolean enabled,
@@ -164,38 +151,32 @@
             boolean checked,
             boolean buttonEnabled,
             @Nullable Drawable buttonIcon) {
-        PropertyModel model =
-                new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS)
-                        .with(AppMenuItemProperties.MENU_ITEM_ID, mainMenuId)
-                        .build();
 
-        ModelListAdapter.ModelList subList = new ModelListAdapter.ModelList();
         PropertyModel titleModel =
                 new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS)
                         .with(AppMenuItemProperties.MENU_ITEM_ID, titleMenuId)
                         .with(AppMenuItemProperties.TITLE, title)
                         .with(AppMenuItemProperties.ENABLED, enabled)
+                        .with(AppMenuItemProperties.ICON, menuIcon)
                         .build();
-        if (menuIcon != null) {
-            titleModel.set(AppMenuItemProperties.ICON, menuIcon);
-        }
+
         PropertyModel buttonModel =
-                new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS)
+                new PropertyModel.Builder(AppMenuItemProperties.ALL_ICON_KEYS)
                         .with(AppMenuItemProperties.MENU_ITEM_ID, buttonMenuId)
                         .with(AppMenuItemProperties.TITLE, buttonTitle)
                         .with(AppMenuItemProperties.CHECKABLE, checkable)
                         .with(AppMenuItemProperties.CHECKED, checked)
                         .with(AppMenuItemProperties.ENABLED, buttonEnabled)
+                        .with(AppMenuItemProperties.ICON, buttonIcon)
                         .build();
-        if (buttonIcon != null) {
-            buttonModel.set(AppMenuItemProperties.ICON, buttonIcon);
-        }
-        subList.add(new ModelListAdapter.ListItem(0, titleModel));
-        subList.add(new ModelListAdapter.ListItem(0, buttonModel));
-        model.set(AppMenuItemProperties.SUBMENU, subList);
-        mMenuList.add(new ModelListAdapter.ListItem(AppMenuItemType.TITLE_BUTTON, model));
 
-        return model;
+        ModelListAdapter.ModelList subList = new ModelListAdapter.ModelList();
+        subList.add(new ModelListAdapter.ListItem(0, buttonModel));
+        titleModel.set(AppMenuItemProperties.ADDITIONAL_ICONS, subList);
+
+        mMenuList.add(new ModelListAdapter.ListItem(AppMenuItemType.TITLE_BUTTON, titleModel));
+
+        return titleModel;
     }
 
     private PropertyModel createIconRowMenuItem(
@@ -233,7 +214,7 @@
             }
         }
 
-        model.set(AppMenuItemProperties.SUBMENU, subList);
+        model.set(AppMenuItemProperties.ADDITIONAL_ICONS, subList);
         mMenuList.add(new ModelListAdapter.ListItem(menutype, model));
 
         return model;
@@ -303,7 +284,6 @@
                                     org.chromium.chrome.browser.ui.appmenu.test.R.drawable
                                             .test_ic_arrow_forward_black_24dp);
                     createTitleMenuItem(
-                            MENU_ID1,
                             MENU_ID2,
                             TITLE_2,
                             mMenuItemEnabled,
@@ -326,7 +306,6 @@
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     createTitleMenuItem(
-                            MENU_ID1,
                             MENU_ID2,
                             TITLE_2,
                             mMenuItemEnabled,
@@ -349,7 +328,6 @@
         ThreadUtils.runOnUiThreadBlocking(
                 () -> {
                     createTitleMenuItem(
-                            MENU_ID1,
                             MENU_ID2,
                             TITLE_2,
                             mMenuItemEnabled,
@@ -377,7 +355,6 @@
                                     org.chromium.chrome.browser.ui.appmenu.test.R.drawable
                                             .test_ic_vintage_filter);
                     createTitleMenuItem(
-                            MENU_ID1,
                             MENU_ID2,
                             TITLE_2,
                             mMenuItemEnabled,
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java
index 23b7ccd..f3eacd2 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemViewBinderTest.java
@@ -10,6 +10,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageButton;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
@@ -34,7 +35,6 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler.AppMenuItemType;
 import org.chromium.chrome.browser.ui.appmenu.test.R;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
 import org.chromium.ui.modelutil.LayoutViewBuilder;
 import org.chromium.ui.modelutil.ModelListAdapter;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -220,18 +220,7 @@
                     mMenuList = new ModelListAdapter.ModelList();
                     mModelListAdapter = new ModelListAdapter(mMenuList);
 
-                    mModelListAdapter.registerType(
-                            AppMenuItemType.STANDARD,
-                            new LayoutViewBuilder(R.layout.menu_item_start_with_icon),
-                            AppMenuItemViewBinder::bindStandardItem);
-                    mModelListAdapter.registerType(
-                            AppMenuItemType.TITLE_BUTTON,
-                            new LayoutViewBuilder(R.layout.title_button_menu_item),
-                            AppMenuItemViewBinder::bindTitleButtonItem);
-                    mModelListAdapter.registerType(
-                            AppMenuItemType.BUTTON_ROW,
-                            new LayoutViewBuilder(R.layout.icon_row_menu_item),
-                            AppMenuItemViewBinder::bindIconRowItem);
+                    AppMenuHandlerImpl.registerDefaultViewBinders(mModelListAdapter, true);
                 });
     }
 
@@ -247,7 +236,6 @@
     }
 
     private PropertyModel createTitleMenuItem(
-            int mainMenuId,
             int titleMenuId,
             String title,
             @Nullable Drawable menuIcon,
@@ -255,34 +243,28 @@
             String buttonTitle,
             boolean checkable,
             boolean checked) {
-        PropertyModel model =
-                new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS)
-                        .with(AppMenuItemProperties.MENU_ITEM_ID, mainMenuId)
-                        .build();
-
-        ModelListAdapter.ModelList subList = new ModelListAdapter.ModelList();
         PropertyModel titleModel =
                 new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS)
                         .with(AppMenuItemProperties.MENU_ITEM_ID, titleMenuId)
                         .with(AppMenuItemProperties.TITLE, title)
+                        .with(AppMenuItemProperties.ICON, menuIcon)
                         .build();
-        if (menuIcon != null) {
-            titleModel.set(AppMenuItemProperties.ICON, menuIcon);
-        }
+
         PropertyModel buttonModel =
-                new PropertyModel.Builder(AppMenuItemProperties.ALL_KEYS)
+                new PropertyModel.Builder(AppMenuItemProperties.ALL_ICON_KEYS)
                         .with(AppMenuItemProperties.MENU_ITEM_ID, buttonMenuId)
                         .with(AppMenuItemProperties.TITLE, buttonTitle)
                         .with(AppMenuItemProperties.CHECKABLE, checkable)
                         .with(AppMenuItemProperties.CHECKED, checked)
                         .build();
-        subList.add(new ModelListAdapter.ListItem(0, titleModel));
+
+        ModelListAdapter.ModelList subList = new ModelListAdapter.ModelList();
         subList.add(new ModelListAdapter.ListItem(0, buttonModel));
 
-        model.set(AppMenuItemProperties.SUBMENU, subList);
-        mMenuList.add(new ModelListAdapter.ListItem(AppMenuItemType.TITLE_BUTTON, model));
+        titleModel.set(AppMenuItemProperties.ADDITIONAL_ICONS, subList);
+        mMenuList.add(new ModelListAdapter.ListItem(AppMenuItemType.TITLE_BUTTON, titleModel));
 
-        return model;
+        return titleModel;
     }
 
     private PropertyModel createIconRowMenuItem(
@@ -319,7 +301,7 @@
             }
         }
 
-        model.set(AppMenuItemProperties.SUBMENU, subList);
+        model.set(AppMenuItemProperties.ADDITIONAL_ICONS, subList);
         mMenuList.add(new ModelListAdapter.ListItem(menutype, model));
 
         return model;
@@ -422,8 +404,8 @@
     @UiThreadTest
     @MediumTest
     public void testConvertView_Reused_TitleMenuItem() {
-        createTitleMenuItem(MENU_ID1, MENU_ID2, TITLE_2, null, MENU_ID3, TITLE_3, true, true);
-        createTitleMenuItem(MENU_ID4, MENU_ID5, TITLE_5, null, MENU_ID6, TITLE_6, true, false);
+        createTitleMenuItem(MENU_ID2, TITLE_2, null, MENU_ID3, TITLE_3, true, true);
+        createTitleMenuItem(MENU_ID5, TITLE_5, null, MENU_ID6, TITLE_6, true, false);
 
         Assert.assertEquals(
                 "Wrong item view type",
@@ -432,13 +414,13 @@
 
         ViewGroup parentView = mActivity.findViewById(android.R.id.content);
         View view1 = mModelListAdapter.getView(0, null, parentView);
-        TextViewWithCompoundDrawables titleView =
-                (TextViewWithCompoundDrawables) view1.findViewById(R.id.title);
+        TextView titleView = view1.findViewById(R.id.menu_item_text);
 
         Assert.assertEquals("Incorrect title text for item 1", TITLE_2, titleView.getText());
 
-        Assert.assertNull(
-                "Should not have icon for item 1", view1.findViewById(R.id.menu_item_icon));
+        ImageView iconView = view1.findViewById(R.id.menu_item_icon);
+        Assert.assertNotNull(iconView);
+        Assert.assertNotEquals(View.VISIBLE, iconView.getVisibility());
 
         View view2 = mModelListAdapter.getView(1, view1, parentView);
         Assert.assertEquals("Convert view should have been re-used", view1, view2);
@@ -454,8 +436,8 @@
                         mActivity,
                         org.chromium.chrome.browser.ui.appmenu.test.R.drawable
                                 .test_ic_vintage_filter);
-        createTitleMenuItem(MENU_ID1, MENU_ID2, TITLE_2, icon, MENU_ID3, TITLE_3, true, true);
-        createTitleMenuItem(MENU_ID4, MENU_ID5, TITLE_5, icon, MENU_ID6, TITLE_6, true, false);
+        createTitleMenuItem(MENU_ID2, TITLE_2, icon, MENU_ID3, TITLE_3, true, true);
+        createTitleMenuItem(MENU_ID5, TITLE_5, icon, MENU_ID6, TITLE_6, true, false);
 
         Assert.assertEquals(
                 "Wrong item view type",
@@ -464,9 +446,11 @@
 
         ViewGroup parentView = mActivity.findViewById(android.R.id.content);
         View view1 = mModelListAdapter.getView(0, null, parentView);
-        TextViewWithCompoundDrawables titleView = view1.findViewById(R.id.title);
-        Drawable[] drawables = titleView.getCompoundDrawablesRelative();
-        Assert.assertNotNull("Should have icon for item 1", drawables[0]);
+        Assert.assertNotNull(
+                "Should have icon for item 1", view1.findViewById(R.id.menu_item_icon));
+
+        View view2 = mModelListAdapter.getView(1, view1, parentView);
+        Assert.assertEquals("Convert view should have been re-used", view1, view2);
     }
 
     @Test
@@ -635,7 +619,7 @@
     @MediumTest
     public void testConvertView_NotReused() {
         createStandardMenuItem(MENU_ID1, TITLE_1);
-        createTitleMenuItem(MENU_ID2, MENU_ID3, TITLE_3, null, MENU_ID4, TITLE_4, true, true);
+        createTitleMenuItem(MENU_ID3, TITLE_3, null, MENU_ID4, TITLE_4, true, true);
 
         Assert.assertEquals(
                 "Wrong item view type for item 1",
@@ -726,7 +710,7 @@
     @UiThreadTest
     @MediumTest
     public void testTitleMenuItem_Checkbox() {
-        createTitleMenuItem(MENU_ID1, MENU_ID2, TITLE_2, null, MENU_ID3, TITLE_3, true, true);
+        createTitleMenuItem(MENU_ID2, TITLE_2, null, MENU_ID3, TITLE_3, true, true);
 
         ViewGroup parentView = mActivity.findViewById(android.R.id.content);
         View view = mModelListAdapter.getView(0, null, parentView);
@@ -739,7 +723,7 @@
     @UiThreadTest
     @MediumTest
     public void testTitleMenuItem_ToggleCheckbox() {
-        createTitleMenuItem(MENU_ID1, MENU_ID2, TITLE_2, null, MENU_ID3, TITLE_3, true, false);
+        createTitleMenuItem(MENU_ID2, TITLE_2, null, MENU_ID3, TITLE_3, true, false);
 
         ViewGroup parentView = mActivity.findViewById(android.R.id.content);
         View view = mModelListAdapter.getView(0, null, parentView);
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java
index 2472a67..0b1f023 100644
--- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java
+++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java
@@ -68,7 +68,7 @@
                         PropertyModel subModel = AppMenuUtil.menuItemToPropertyModel(subitem);
                         subList.add(new MVCListAdapter.ListItem(0, subModel));
                     }
-                    propertyModel.set(AppMenuItemProperties.SUBMENU, subList);
+                    propertyModel.set(AppMenuItemProperties.ADDITIONAL_ICONS, subList);
                 }
                 int menutype = AppMenuItemType.STANDARD;
                 if (item.getItemId() == R.id.icon_row_menu_id) {
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java
index 919209c..6bd1d50 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuItemProperties.java
@@ -86,14 +86,25 @@
             new WritableBooleanPropertyKey("MENU_ICON_AT_START");
 
     /**
-     * The sub menu for the menu item, this is used for the menu item which has sub menu items. ex.
-     * icon row. The {link ModelList} here do not need a view type since this diverges from other,
-     * non sub-menu-items that use AppMenuItemProperties.
-     *
-     * <p>A SUBMENU should not have a SUBMENU (don't support nesting).
+     * Additional icons associated with a particular menu item. Only certain menu item types support
+     * additional icons (e.g. icon rows). The number of supported icons also depends on the menu
+     * item type.
      */
-    public static final WritableObjectPropertyKey<ModelList> SUBMENU =
-            new WritableObjectPropertyKey<>("SUBMENU");
+    public static final WritableObjectPropertyKey<ModelList> ADDITIONAL_ICONS =
+            new WritableObjectPropertyKey<>("ADDITIONAL_ICONS");
+
+    public static final PropertyKey[] ALL_ICON_KEYS =
+            new PropertyKey[] {
+                MENU_ITEM_ID,
+                TITLE,
+                TITLE_CONDENSED,
+                CHECKABLE,
+                CHECKED,
+                ICON,
+                ENABLED,
+                HIGHLIGHTED,
+                CLICK_HANDLER
+            };
 
     public static final PropertyKey[] ALL_KEYS =
             new PropertyKey[] {
@@ -112,6 +123,6 @@
                 SUPPORT_ENTER_ANIMATION,
                 CLICK_HANDLER,
                 MENU_ICON_AT_START,
-                SUBMENU
+                ADDITIONAL_ICONS
             };
 }
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuUtil.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuUtil.java
index 55e229e..bcdeb19c 100644
--- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuUtil.java
+++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuUtil.java
@@ -131,6 +131,23 @@
     }
 
     /**
+     * Create a {@link PropertyModel} for an icon represented as a {@link MenuItem}.
+     *
+     * @param menuItem The MenuItem which need to be transferred to the {@link PropertyModel}.
+     * @return The {@link PropertyModel}.
+     */
+    public static PropertyModel buildPropertyModelForIcon(MenuItem menuItem) {
+        return new PropertyModel.Builder(AppMenuItemProperties.ALL_ICON_KEYS)
+                .with(AppMenuItemProperties.MENU_ITEM_ID, menuItem.getItemId())
+                .with(AppMenuItemProperties.TITLE_CONDENSED, menuItem.getTitleCondensed())
+                .with(AppMenuItemProperties.ICON, menuItem.getIcon())
+                .with(AppMenuItemProperties.ENABLED, menuItem.isEnabled())
+                .with(AppMenuItemProperties.CHECKABLE, menuItem.isCheckable())
+                .with(AppMenuItemProperties.CHECKED, menuItem.isChecked())
+                .build();
+    }
+
+    /**
      * builds a enter animation of a standard menu item.
      *
      * @param model The model containing the data for the view.
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
index 0cf51bf..711c10f2 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -70,13 +70,13 @@
 import org.chromium.chrome.browser.tab.Tab.LoadUrlResult;
 import org.chromium.chrome.browser.tab.TabLaunchType;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.ui.default_browser_promo.DefaultBrowserPromoUtils;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.embedder_support.util.UrlUtilities;
@@ -1136,7 +1136,7 @@
         // If the url bar is focused, the toolbar background color is the default color regardless
         // of whether it is branded or not.
         if (isUrlBarFocused()) {
-            return ChromeColors.getDefaultThemeColor(
+            return SurfaceColorUpdateUtils.getDefaultThemeColor(
                     mContext, mLocationBarDataProvider.isIncognitoBranded());
         } else {
             return mLocationBarDataProvider.getPrimaryColor();
diff --git a/chrome/browser/ui/android/theme/BUILD.gn b/chrome/browser/ui/android/theme/BUILD.gn
index 889a1da..8da26b77 100644
--- a/chrome/browser/ui/android/theme/BUILD.gn
+++ b/chrome/browser/ui/android/theme/BUILD.gn
@@ -51,6 +51,7 @@
 robolectric_library("junit") {
   sources = [
     "java/src/org/chromium/chrome/browser/theme/BottomUiThemeColorProviderTest.java",
+    "java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java",
     "java/src/org/chromium/chrome/browser/theme/ThemeModuleUtilsUnitTest.java",
     "java/src/org/chromium/chrome/browser/theme/ThemeUtilsUnitTest.java",
   ]
@@ -63,6 +64,7 @@
     "//base:service_loader_java",
     "//chrome/android:chrome_app_java_resources",
     "//chrome/browser/browser_controls/android:java",
+    "//chrome/browser/flags:java",
     "//chrome/browser/tabmodel:java",
     "//chrome/test/android:chrome_java_unit_test_support",
     "//components/browser_ui/styles/android:java",
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
index c9ac902b..0f457e8 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtils.java
@@ -10,7 +10,9 @@
 import androidx.core.content.ContextCompat;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 
 /** Utility class that provides color values based on feature flags enabled. */
 @NullMarked
@@ -22,7 +24,12 @@
      * @return The background color.
      */
     public static @ColorInt int getOmniboxBackgroundColor(Context context, boolean isIncognito) {
-        return ContextCompat.getColor(context, R.color.toolbar_text_box_bg_color);
+        if (isIncognito) {
+            return ContextCompat.getColor(context, R.color.toolbar_text_box_background_incognito);
+        }
+        return ChromeFeatureList.sAndroidSurfaceColorUpdate.isEnabled()
+                ? SemanticColorUtils.getColorSurface(context)
+                : ContextCompat.getColor(context, R.color.toolbar_text_box_bg_color);
     }
 
     /**
@@ -32,6 +39,9 @@
      * @return The background color.
      */
     public static @ColorInt int getDefaultThemeColor(Context context, boolean isIncognito) {
+        if (ChromeFeatureList.sAndroidSurfaceColorUpdate.isEnabled() && !isIncognito) {
+            return SemanticColorUtils.getColorSurfaceContainerHigh(context);
+        }
         return ChromeColors.getDefaultThemeColor(context, isIncognito);
     }
 }
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
new file mode 100644
index 0000000..10277aa
--- /dev/null
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/SurfaceColorUpdateUtilsUnitTest.java
@@ -0,0 +1,78 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.theme;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+
+import androidx.core.content.ContextCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
+
+@RunWith(BaseRobolectricTestRunner.class)
+public class SurfaceColorUpdateUtilsUnitTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext =
+                new ContextThemeWrapper(
+                        ContextUtils.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
+    }
+
+    @Test
+    @Features.EnableFeatures({ChromeFeatureList.ANDROID_SURFACE_COLOR_UPDATE})
+    public void testThemeAndOmniboxColors_flagEnabled() {
+        int themeColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(mContext, /* isIncognito= */ false);
+        assertEquals(SemanticColorUtils.getColorSurfaceContainerHigh(mContext), themeColor);
+
+        int omniboxColor =
+                SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                        mContext, /* isIncognito= */ false);
+        assertEquals(SemanticColorUtils.getColorSurface(mContext), omniboxColor);
+    }
+
+    @Test
+    @Features.DisableFeatures({ChromeFeatureList.ANDROID_SURFACE_COLOR_UPDATE})
+    public void testThemeAndOmniboxColors_flagDisabled() {
+        int themeColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(mContext, /* isIncognito= */ false);
+        assertEquals(
+                ChromeColors.getDefaultThemeColor(mContext, /* isIncognito= */ false), themeColor);
+
+        int omniboxColor =
+                SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                        mContext, /* isIncognito= */ false);
+        assertEquals(
+                ContextCompat.getColor(mContext, R.color.toolbar_text_box_bg_color), omniboxColor);
+    }
+
+    @Test
+    public void testThemeAndOmniboxColors_Incognito() {
+        int themeColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(mContext, /* isIncognito= */ true);
+        assertEquals(
+                ChromeColors.getDefaultThemeColor(mContext, /* isIncognito= */ true), themeColor);
+
+        int omniboxColor =
+                SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                        mContext, /* isIncognito= */ true);
+        assertEquals(
+                ContextCompat.getColor(mContext, R.color.toolbar_text_box_background_incognito),
+                omniboxColor);
+    }
+}
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/ThemeUtils.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/ThemeUtils.java
index 8d42911..5bc6366 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/ThemeUtils.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/ThemeUtils.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.content_public.browser.RenderWidgetHostView;
 import org.chromium.content_public.browser.WebContents;
@@ -54,7 +53,8 @@
         @ColorInt
         int backgroundColor = rwhv != null ? rwhv.getBackgroundColor() : Color.TRANSPARENT;
         if (backgroundColor != Color.TRANSPARENT) return backgroundColor;
-        return ChromeColors.getPrimaryBackgroundColor(tab.getContext(), false);
+        return SurfaceColorUpdateUtils.getDefaultThemeColor(
+                tab.getContext(), /* isIncognito= */ false);
     }
 
     /**
@@ -94,13 +94,15 @@
         // TODO(https://crbug.com/406890625): Update incognito mode once we have confirmation from
         // UX.
         if (isIncognito) {
-            return context.getColor(R.color.toolbar_text_box_background_incognito);
+            return SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                    context, /* isIncognito= */ true);
         }
 
         // Text box color on default toolbar background in standard mode is a pre-defined
         // color instead of a calculated color.
         if (ThemeUtils.isUsingDefaultToolbarColor(context, false, color)) {
-            return ContextCompat.getColor(context, R.color.toolbar_text_box_bg_color);
+            return SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                    context, /* isIncognito= */ false);
         }
 
         if (ColorUtils.shouldUseOpaqueTextboxBackground(color)) {
@@ -229,7 +231,7 @@
      */
     public static boolean isUsingDefaultToolbarColor(
             Context context, boolean isIncognito, @ColorInt int color) {
-        return color == ChromeColors.getDefaultThemeColor(context, isIncognito);
+        return color == SurfaceColorUpdateUtils.getDefaultThemeColor(context, isIncognito);
     }
 
     /**
diff --git a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/TopUiThemeColorProvider.java b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/TopUiThemeColorProvider.java
index ef099fc..ca4f1ca3 100644
--- a/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/TopUiThemeColorProvider.java
+++ b/chrome/browser/ui/android/theme/java/src/org/chromium/chrome/browser/theme/TopUiThemeColorProvider.java
@@ -18,7 +18,6 @@
 import org.chromium.chrome.browser.tab.TabState;
 import org.chromium.chrome.browser.ui.native_page.NativePage;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.ui.util.ColorUtils;
 
 /**
@@ -125,7 +124,7 @@
         // This method is used not only for the current tab but also for
         // any given tab. Therefore it should not alter any class state.
         if (!isUsingTabThemeColor(tab, themeColor)) {
-            themeColor = ChromeColors.getDefaultThemeColor(mContext, tab.isIncognito());
+            themeColor = SurfaceColorUpdateUtils.getDefaultThemeColor(mContext, tab.isIncognito());
             if (isThemingAllowed(tab)) {
                 int customThemeColor = mActivityThemeColorSupplier.get();
                 if (customThemeColor != TabState.UNSPECIFIED_THEME_COLOR) {
@@ -150,12 +149,14 @@
     /**
      * The default background color used for {@link Tab} if the associate web content doesn't
      * specify a background color.
+     *
      * @param tab {@link Tab} object to get the background color for.
      * @return The background color of {@link Tab}.
      */
     public int getBackgroundColor(Tab tab) {
         // This method makes it easy to mock, test-friendly.
-        return ThemeUtils.getBackgroundColor(tab);
+        return SurfaceColorUpdateUtils.getDefaultThemeColor(
+                tab.getContext(), tab.isIncognitoBranded());
     }
 
     /**
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
index f7d21e3..ab936b4 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/LocationBarModel.java
@@ -37,9 +37,9 @@
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TrustedCdn;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.embedder_support.util.UrlUtilities;
@@ -191,7 +191,8 @@
         mNtpDelegate = newTabPageDelegate;
         mUrlFormatter = urlFormatter;
         mOfflineStatus = offlineStatus;
-        mPrimaryColor = ChromeColors.getDefaultThemeColor(context, false);
+        mPrimaryColor =
+                SurfaceColorUpdateUtils.getDefaultThemeColor(context, /* isIncognito= */ false);
         mUrlForDisplay = "";
         mFormattedFullUrl = "";
     }
@@ -558,7 +559,8 @@
         mIsUsingBrandColor =
                 !isIncognitoBranded()
                         && mPrimaryColor
-                                != ChromeColors.getDefaultThemeColor(mContext, isIncognitoBranded())
+                                != SurfaceColorUpdateUtils.getDefaultThemeColor(
+                                        mContext, isIncognitoBranded())
                         && hasTab()
                         && !mTab.isNativePage();
     }
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java
index 54b1d94..cbf532f 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonCoordinator.java
@@ -153,6 +153,15 @@
     }
 
     /**
+     * Gets visibility.
+     *
+     * @return a boolean indicating whether view is visible or not.
+     */
+    public boolean isVisible() {
+        return mMediator.isVisible();
+    }
+
+    /**
      * Cleans up coordinator resources and unsubscribes from external events. An instance can't be
      * used after this method is called.
      */
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
index cea239ea..3c79036 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/back_button/BackButtonMediator.java
@@ -180,6 +180,15 @@
     }
 
     /**
+     * Checks whether view is visible or not.
+     *
+     * @return true - view is visible, false - view is not visible.
+     */
+    public boolean isVisible() {
+        return mModel.get(BackButtonProperties.IS_VISIBLE);
+    }
+
+    /**
      * Sets a key event listener on the view.
      *
      * @param listener {@link View.OnKeyListener}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonCoordinator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonCoordinator.java
index ee39fd2..ae12e83 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonCoordinator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonCoordinator.java
@@ -133,6 +133,15 @@
         return rect;
     }
 
+    /**
+     * Gets visibility.
+     *
+     * @return a Boolean indicating whether view is visible or not.
+     */
+    public boolean isVisibile() {
+        return mMediator.isVisible();
+    }
+
     /** Destroys current object instance. It can't be used after this call. */
     public void destroy() {
         mMediator.destroy();
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonMediator.java
index abbdbebb..5ad465b 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonMediator.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/reload_button/ReloadButtonMediator.java
@@ -196,6 +196,15 @@
     }
 
     /**
+     * Checks whether view is visible or not.
+     *
+     * @return true - view is visible, false - view is not visible.
+     */
+    public boolean isVisible() {
+        return mModel.get(ReloadButtonProperties.IS_VISIBLE);
+    }
+
+    /**
      * Sets a listeners that allows parent to intercept key events.
      *
      * @param listener a callback that is invoked when hardware key is pressed.
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
index 5f0d287..d71b590 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarPhone.java
@@ -55,6 +55,7 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.NullUnmarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.LocationBar;
 import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
 import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
@@ -64,6 +65,7 @@
 import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsDropdownScrollListener;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
@@ -81,7 +83,6 @@
 import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.components.embedder_support.util.UrlUtilities;
@@ -314,10 +315,14 @@
         float LocationBarBackgroundColorAlphaForNtp =
                 ResourcesCompat.getFloat(
                         getResources(), R.dimen.home_surface_search_box_background_alpha);
+        // Explicitly checking the flag value since the default color value is colorPrimary tinted.
         mLocationBarBackgroundColorForNtp =
-                ColorUtils.setAlphaComponentWithFloat(
-                        SemanticColorUtils.getDefaultIconColorAccent1(context),
-                        LocationBarBackgroundColorAlphaForNtp);
+                ChromeFeatureList.sAndroidSurfaceColorUpdate.isEnabled()
+                        ? SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                                getContext(), /* isIncognito= */ false)
+                        : ColorUtils.setAlphaComponentWithFloat(
+                                SemanticColorUtils.getDefaultIconColorAccent1(context),
+                                LocationBarBackgroundColorAlphaForNtp);
         mTabCountSupplierObserver = this::onTabCountChanged;
     }
 
@@ -413,7 +418,9 @@
                                 R.drawable.modern_toolbar_text_box_background_with_primary_color);
         assumeNonNull(drawable);
         drawable.mutate();
-        drawable.setColor(ContextCompat.getColor(context, R.color.toolbar_text_box_bg_color));
+        drawable.setColor(
+                SurfaceColorUpdateUtils.getOmniboxBackgroundColor(
+                        context, /* isIncognito= */ false));
         return drawable;
     }
 
@@ -487,7 +494,7 @@
             return OmniboxResourceProvider.getSuggestionsDropdownBackgroundColor(
                     getContext(), mThemeColorProvider.getBrandedColorScheme());
         }
-        return ChromeColors.getDefaultThemeColor(getContext(), isIncognitoBranded());
+        return SurfaceColorUpdateUtils.getDefaultThemeColor(getContext(), isIncognitoBranded());
     }
 
     /**
@@ -793,9 +800,11 @@
                 if (mIsInLoadingPhaseFromNtpToWebpage) {
                     return mToolbarBackgroundColorForNtp;
                 }
-                return ChromeColors.getDefaultThemeColor(getContext(), false);
+                return SurfaceColorUpdateUtils.getDefaultThemeColor(
+                        getContext(), /* isIncognito= */ false);
             case VisualState.INCOGNITO:
-                return ChromeColors.getDefaultThemeColor(getContext(), true);
+                return SurfaceColorUpdateUtils.getDefaultThemeColor(
+                        getContext(), /* isIncognito= */ true);
             case VisualState.BRAND_COLOR:
                 if (urlHasFocus()) {
                     return getToolbarDefaultColor(/* shouldUseFocusColor= */ false);
@@ -803,7 +812,8 @@
                 return getToolbarDataProvider().getPrimaryColor();
             default:
                 assert false;
-                return SemanticColorUtils.getToolbarBackgroundPrimary(getContext());
+                return SurfaceColorUpdateUtils.getDefaultThemeColor(
+                        getContext(), /* isIncognito= */ false);
         }
     }
 
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index 0e6da415..e2751bf 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -44,6 +44,7 @@
 import org.chromium.chrome.browser.omnibox.UrlBarData;
 import org.chromium.chrome.browser.omnibox.status.StatusCoordinator;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.theme.ThemeUtils;
 import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
@@ -58,7 +59,6 @@
 import org.chromium.chrome.browser.toolbar.top.NavigationPopup.HistoryDelegate;
 import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
-import org.chromium.components.browser_ui.styles.ChromeColors;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.components.feature_engagement.Tracker;
@@ -268,7 +268,8 @@
             // TODO (amaralp): Have progress bar observe theme color and incognito changes directly.
             getProgressBar()
                     .setThemeColor(
-                            ChromeColors.getDefaultThemeColor(getContext(), incognitoBranded),
+                            SurfaceColorUpdateUtils.getDefaultThemeColor(
+                                    getContext(), incognitoBranded),
                             incognitoBranded);
             updateRippleBackground();
             mIsIncognitoBranded = incognitoBranded;
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
index 8862a36..818196d 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
@@ -12,6 +12,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.blink.mojom.DisplayMode;
@@ -26,6 +27,8 @@
 import org.chromium.chrome.browser.web_app_header.R;
 import org.chromium.components.browser_ui.desktop_windowing.AppHeaderState;
 import org.chromium.components.browser_ui.desktop_windowing.DesktopWindowStateManager;
+import org.chromium.ui.display.DisplayAndroid;
+import org.chromium.ui.display.DisplayUtil;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
 
@@ -50,6 +53,12 @@
     private final ThemeColorProvider mThemeColorProvider;
     private final @DisplayMode.EnumType int mDisplayMode;
     private final NavigationPopup.HistoryDelegate mHistoryDelegate;
+    private int mMinUIControlsMinWidthPx;
+    private int mAppHeaderUnoccludedWidthPx;
+    private final Callback<Integer> mOnUnoccludedWidthCallback;
+
+    // 48dp * 2 (back and reload button) + 4dp (start padding).
+    private static final int MIN_HEADER_WIDTH_DP = 100;
 
     /**
      * Creates an instance of {@link WebAppHeaderLayoutCoordinator}.
@@ -78,6 +87,10 @@
         mTabSupplier = tabSupplier;
         mThemeColorProvider = themeColorProvider;
 
+        mOnUnoccludedWidthCallback = this::onUnoccludedWidthChanged;
+        mMinUIControlsMinWidthPx = 0;
+        mAppHeaderUnoccludedWidthPx = 0;
+
         final var appHeaderState = desktopWindowStateManager.getAppHeaderState();
         if (appHeaderState != null) {
             onAppHeaderStateChanged(appHeaderState);
@@ -96,6 +109,10 @@
         final var model = new PropertyModel.Builder(WebAppHeaderLayoutProperties.ALL_KEYS).build();
         final int headerMinHeight =
                 mView.getResources().getDimensionPixelSize(R.dimen.web_app_header_min_height);
+
+        mMinUIControlsMinWidthPx =
+                DisplayUtil.dpToPx(
+                        DisplayAndroid.getNonMultiDisplay(mView.getContext()), MIN_HEADER_WIDTH_DP);
         mMediator =
                 new WebAppHeaderLayoutMediator(
                         model,
@@ -105,6 +122,7 @@
                         headerMinHeight);
         PropertyModelChangeProcessor.create(model, mView, WebAppHeaderLayoutViewBinder::bind);
 
+        mMediator.getUnoccludedWidthSupplier().addObserver(mOnUnoccludedWidthCallback);
         if (mDisplayMode == DisplayMode.MINIMAL_UI) {
             initMinUiControls();
         }
@@ -122,7 +140,6 @@
                         mTabSupplier,
                         new ObservableSupplierImpl<>(),
                         mThemeColorProvider);
-        mReloadButtonCoordinator.setVisibility(true);
 
         final ImageButton backButton = mView.findViewById(R.id.back_button);
         mBackButtonCoordinator =
@@ -132,16 +149,30 @@
                         mThemeColorProvider,
                         mTabSupplier,
                         mHistoryDelegate);
-        mBackButtonCoordinator.setVisibility(true);
+    }
+
+    private void onUnoccludedWidthChanged(int newUnoccludedWidthPx) {
+        mAppHeaderUnoccludedWidthPx = newUnoccludedWidthPx;
+        updateButtonVisibility();
+    }
+
+    private void updateButtonVisibility() {
+        boolean showButtons = mAppHeaderUnoccludedWidthPx >= mMinUIControlsMinWidthPx;
+        if (mReloadButtonCoordinator != null) {
+            mReloadButtonCoordinator.setVisibility(showButtons);
+        }
+        if (mBackButtonCoordinator != null) {
+            mBackButtonCoordinator.setVisibility(showButtons);
+        }
     }
 
     private List<Rect> collectNonDraggableAreas() {
         final var areas = new ArrayList<Rect>();
-        if (mReloadButtonCoordinator != null) {
+        if (mReloadButtonCoordinator != null && mReloadButtonCoordinator.isVisibile()) {
             areas.add(mReloadButtonCoordinator.getHitRect());
         }
 
-        if (mBackButtonCoordinator != null) {
+        if (mBackButtonCoordinator != null && mBackButtonCoordinator.isVisible()) {
             areas.add(mBackButtonCoordinator.getHitRect());
         }
 
@@ -175,6 +206,7 @@
         }
 
         if (mMediator != null) {
+            mMediator.getUnoccludedWidthSupplier().removeObserver(mOnUnoccludedWidthCallback);
             mMediator.destroy();
         }
 
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
index 4b23cf1d..f58daf3 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
@@ -9,10 +9,12 @@
 import static org.junit.Assert.assertNull;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.app.Activity;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.Looper;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
@@ -180,6 +182,9 @@
         mTabSupplier.set(mTab);
         createCoordinator();
 
+        // Wait for animation to finish and update the view.
+        shadowOf(Looper.getMainLooper()).idle();
+
         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.refresh_button).getVisibility());
         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.back_button).getVisibility());
     }
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
index 4d06b165..c4a2c43d 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
@@ -38,6 +38,7 @@
     private final Callback<Integer> mOnWidthChangedCallback;
     private final int mWebAppMinHeaderHeight;
     private @Nullable AppHeaderState mCurrentHeaderState;
+    private final ObservableSupplierImpl<Integer> mAppHeaderUnoccludedWidthSupplier;
 
     /**
      * Constructs the instance of {@link WebAppHeaderLayoutMediator}.
@@ -60,6 +61,7 @@
         mNonDraggableAreasSupplier = nonDraggableAreasSupplier;
 
         mWidthSupplier = new ObservableSupplierImpl<>();
+        mAppHeaderUnoccludedWidthSupplier = new ObservableSupplierImpl<>();
         mOnWidthChangedCallback = (width) -> updateNonDraggableAreas();
         mWidthSupplier.addObserver(mOnWidthChangedCallback);
 
@@ -79,6 +81,8 @@
         mCurrentHeaderState = newState;
 
         updatePaddings();
+
+        mAppHeaderUnoccludedWidthSupplier.set(mCurrentHeaderState.getUnoccludedRectWidth());
         mModel.set(
                 WebAppHeaderLayoutProperties.MIN_HEIGHT,
                 Math.max(mCurrentHeaderState.getAppHeaderHeight(), getDefaultMinHeight()));
@@ -86,6 +90,10 @@
                 WebAppHeaderLayoutProperties.IS_VISIBLE, mCurrentHeaderState.isInDesktopWindow());
     }
 
+    public ObservableSupplier<Integer> getUnoccludedWidthSupplier() {
+        return mAppHeaderUnoccludedWidthSupplier;
+    }
+
     private void updatePaddings() {
         if (mCurrentHeaderState == null) return;
 
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index f7f7e0e..d5e652e 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -1213,13 +1213,6 @@
       }
       break;
     }
-    case IDC_GLIC_TOGGLE_FOCUS: {
-      if (auto* glic_keyed_service =
-              glic::GlicProfileManager::GetInstance()->GetLastActiveGlic()) {
-        glic_keyed_service->FocusUI();
-      }
-      break;
-    }
 #endif
     default:
       LOG(WARNING) << "Received Unimplemented Command: " << id;
@@ -1565,8 +1558,6 @@
       IDC_GLIC_TOGGLE_PIN, glic::GlicEnabling::IsProfileEligible(profile()));
   command_updater_.UpdateCommandEnabled(
       IDC_OPEN_GLIC, glic::GlicEnabling::IsEnabledForProfile(profile()));
-  command_updater_.UpdateCommandEnabled(
-      IDC_GLIC_TOGGLE_FOCUS, glic::GlicEnabling::IsProfileEligible(profile()));
 #endif
 
   // Initialize other commands whose state changes based on various conditions.
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.cc b/chrome/browser/ui/lens/lens_overlay_controller.cc
index 38beec27..0233300 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller.cc
@@ -50,6 +50,7 @@
 #include "chrome/browser/ui/lens/lens_overlay_url_builder.h"
 #include "chrome/browser/ui/lens/lens_permission_bubble_controller.h"
 #include "chrome/browser/ui/lens/lens_preselection_bubble.h"
+#include "chrome/browser/ui/lens/lens_search_controller.h"
 #include "chrome/browser/ui/lens/page_content_type_conversions.h"
 #include "chrome/browser/ui/search/omnibox_utils.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
@@ -303,12 +304,14 @@
 
 LensOverlayController::LensOverlayController(
     tabs::TabInterface* tab,
+    LensSearchController* lens_search_controller,
     variations::VariationsClient* variations_client,
     signin::IdentityManager* identity_manager,
     PrefService* pref_service,
     syncer::SyncService* sync_service,
     ThemeService* theme_service)
     : tab_(tab),
+      lens_search_controller_(lens_search_controller),
       variations_client_(variations_client),
       identity_manager_(identity_manager),
       pref_service_(pref_service),
@@ -474,10 +477,10 @@
   pref_service_->SetInteger(prefs::kLensOverlayStartCount,
                             lens_overlay_start_count + 1);
 
-  // Create the results side panel coordinator when showing the UI if it does
-  // not already exist for this tab's web contents.
+  // Grab reference to the side panel coordinator it not already done so.
   if (!results_side_panel_coordinator_) {
-    results_side_panel_coordinator_ = CreateLensOverlaySidePanelCoordinator();
+    results_side_panel_coordinator_ =
+        lens_search_controller_->lens_overlay_side_panel_coordinator();
   }
 
   Profile* profile =
@@ -1097,6 +1100,10 @@
   OnZeroSuggestShown();
 }
 
+void LensOverlayController::OpenSidePanelForTesting() {
+  MaybeOpenSidePanel();
+}
+
 const lens::proto::LensOverlaySuggestInputs&
 LensOverlayController::GetLensSuggestInputsForTesting() {
   return GetLensSuggestInputs();
@@ -1129,11 +1136,6 @@
       profile, invocation_source, use_dark_mode, gen204_controller);
 }
 
-std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
-LensOverlayController::CreateLensOverlaySidePanelCoordinator() {
-  return std::make_unique<lens::LensOverlaySidePanelCoordinator>(this);
-}
-
 std::string LensOverlayController::GetVsridForNewTab() {
   return lens_overlay_query_controller_->GetVsridForNewTab();
 }
@@ -1231,7 +1233,7 @@
   lens_overlay_query_controller_->SendRegionSearch(
       region.Clone(), selection_type,
       initialization_data_->additional_search_query_params_, region_bytes);
-  results_side_panel_coordinator_->RegisterEntryAndShow();
+  MaybeOpenSidePanel();
   RecordTimeToFirstInteraction(
       lens::LensOverlayFirstInteractionType::kRegionSelect);
   state_ = State::kOverlayAndResults;
@@ -2127,6 +2129,14 @@
   overlay_view_->SetVisible(false);
 }
 
+void LensOverlayController::MaybeOpenSidePanel() {
+  if (side_panel_in_use_) {
+    // Exit early if this class has already requested access to the side panel.
+    return;
+  }
+  side_panel_in_use_ = results_side_panel_coordinator_->RegisterEntryAndShow();
+}
+
 void LensOverlayController::CloseUIPart2(
     lens::LensOverlayDismissalSource dismissal_source) {
   if (state_ == State::kOff) {
@@ -2159,7 +2169,8 @@
 
   permission_bubble_controller_.reset();
   side_panel_searchbox_handler_.reset();
-  results_side_panel_coordinator_.reset();
+  results_side_panel_coordinator_ = nullptr;
+  side_panel_in_use_.reset();
   pre_initialization_suggest_inputs_.reset();
   pre_initialization_objects_.reset();
   pre_initialization_text_.reset();
@@ -3065,7 +3076,7 @@
   lens_overlay_query_controller_->SendTextOnlyQuery(
       query, lens_selection_type_,
       initialization_data_->additional_search_query_params_);
-  results_side_panel_coordinator_->RegisterEntryAndShow();
+  MaybeOpenSidePanel();
   RecordTimeToFirstInteraction(
       lens::LensOverlayFirstInteractionType::kTextSelect);
   state_ = State::kOverlayAndResults;
@@ -3299,7 +3310,7 @@
   // a long query loads.
   SetSearchboxInputText(search_box_text);
 
-  results_side_panel_coordinator_->RegisterEntryAndShow();
+  MaybeOpenSidePanel();
   results_side_panel_coordinator_->SetSidePanelIsLoadingResults(true);
   MaybeLaunchSurvey();
 
@@ -3351,6 +3362,7 @@
 
 void LensOverlayController::HandleInteractionURLResponse(
     lens::proto::LensOverlayUrlResponse response) {
+  MaybeOpenSidePanel();
   results_side_panel_coordinator_->LoadURLInResultsFrame(GURL(response.url()));
 }
 
diff --git a/chrome/browser/ui/lens/lens_overlay_controller.h b/chrome/browser/ui/lens/lens_overlay_controller.h
index f2c10352..a985fdb 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller.h
+++ b/chrome/browser/ui/lens/lens_overlay_controller.h
@@ -84,6 +84,7 @@
 class LensPermissionBubbleController;
 class LensOverlayEventHandler;
 struct SearchQuery;
+class SidePanelInUse;
 }  // namespace lens
 
 namespace optimization_guide {
@@ -113,6 +114,7 @@
 
 class PrefService;
 class Profile;
+class LensSearchController;
 enum class SidePanelEntryHideReason;
 
 extern void* kLensOverlayPreselectionWidgetIdentifier;
@@ -148,6 +150,7 @@
                               public find_in_page::FindResultObserver {
  public:
   LensOverlayController(tabs::TabInterface* tab,
+                        LensSearchController* lens_search_controller,
                         variations::VariationsClient* variations_client,
                         signin::IdentityManager* identity_manager,
                         PrefService* pref_service,
@@ -425,6 +428,8 @@
   // Called when the lens side panel has been hidden.
   void OnSidePanelHidden();
 
+  // Returns the tab interface that that owns the search controller that owns
+  // this overlay controller.
   tabs::TabInterface* GetTabInterface();
 
   // Show preselection toast bubble. Creates a preselection bubble if it does
@@ -535,6 +540,10 @@
   // Handles the event where zero suggest was shown for testing.
   void OnZeroSuggestShownForTesting();
 
+  // Opens the side panel for testing. If the side panel is already open, this
+  // does nothing.
+  void OpenSidePanelForTesting();
+
   // Returns the lens suggest inputs stored in this controller for testing.
   const lens::proto::LensOverlaySuggestInputs& GetLensSuggestInputsForTesting();
 
@@ -588,11 +597,6 @@
       bool use_dark_mode,
       lens::LensOverlayGen204Controller* gen204_controller);
 
-  // Override these methods to be able to track calls made to the side panel
-  // coordinator.
-  virtual std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
-  CreateLensOverlaySidePanelCoordinator();
-
   // Returns the vsrid to use for the new tab URL.
   std::string GetVsridForNewTab();
 
@@ -926,6 +930,10 @@
   // visible.
   void MaybeHideSharedOverlayView();
 
+  // Requests to open the side panel if this class has not already done so.
+  // Must be called before issuing results to the side panel.
+  void MaybeOpenSidePanel();
+
   // Closes the overlay UI and sets state to kOff. This method is the final
   // cleanup of closing the overlay UI. This resets all state internal to the
   // LensOverlayController.
@@ -1215,6 +1223,9 @@
   // Owns the LensSearchController which owns this class
   raw_ptr<tabs::TabInterface> tab_;
 
+  // Owns this class.
+  raw_ptr<LensSearchController> lens_search_controller_;
+
   // A monotonically increasing id. This is used to differentiate between
   // different screenshot attempts.
   int screenshot_attempt_id_ = 0;
@@ -1504,8 +1515,9 @@
   // be assumed to be non-null.
   raw_ptr<SidePanelCoordinator> side_panel_coordinator_ = nullptr;
 
-  // Side panel coordinator for showing results in the panel.
-  std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
+  // Side panel coordinator for the side panel coordinator that controls the
+  // results side panel. Guaranteed to exist if the overlay is not `kOff`.
+  raw_ptr<lens::LensOverlaySidePanelCoordinator>
       results_side_panel_coordinator_;
 
   // Class for handling key events from the renderer that were not handled.
@@ -1515,6 +1527,10 @@
   std::unique_ptr<lens::LensOverlayBlurLayerDelegate>
       lens_overlay_blur_layer_delegate_;
 
+  // Keeps alive an instance of the side panel while it is open. This is
+  // necessary to prevent the side panel from closing when the overlay is open.
+  std::unique_ptr<lens::SidePanelInUse> side_panel_in_use_;
+
   // Pointer to the view that houses our overlay as a child of the tab
   // contents web view.
   raw_ptr<views::View> overlay_view_;
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index 927b693..ba25bda4 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -512,12 +512,14 @@
 class LensOverlayControllerFake : public lens::TestLensOverlayController {
  public:
   LensOverlayControllerFake(tabs::TabInterface* tab,
+                            LensSearchController* lens_search_controller,
                             variations::VariationsClient* variations_client,
                             signin::IdentityManager* identity_manager,
                             PrefService* pref_service,
                             syncer::SyncService* sync_service,
                             ThemeService* theme_service)
       : lens::TestLensOverlayController(tab,
+                                        lens_search_controller,
                                         variations_client,
                                         identity_manager,
                                         pref_service,
@@ -567,11 +569,6 @@
     return fake_query_controller;
   }
 
-  std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
-  CreateLensOverlaySidePanelCoordinator() override {
-    return std::make_unique<lens::TestLensOverlaySidePanelCoordinator>(this);
-  }
-
   void BindOverlay(mojo::PendingReceiver<lens::mojom::LensPageHandler> receiver,
                    mojo::PendingRemote<lens::mojom::LensPage> page) override {
     // Reset the receiver to close any existing connection.
@@ -623,6 +620,7 @@
  protected:
   std::unique_ptr<LensOverlayController> CreateLensOverlayController(
       tabs::TabInterface* tab,
+      LensSearchController* lens_search_controller,
       variations::VariationsClient* variations_client,
       signin::IdentityManager* identity_manager,
       PrefService* pref_service,
@@ -633,8 +631,13 @@
         ThemeService::BrowserColorScheme::kLight);
 
     return std::make_unique<LensOverlayControllerFake>(
-        tab, variations_client, identity_manager, pref_service, sync_service,
-        theme_service);
+        tab, lens_search_controller, variations_client, identity_manager,
+        pref_service, sync_service, theme_service);
+  }
+
+  std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
+  CreateLensOverlaySidePanelCoordinator() override {
+    return std::make_unique<lens::TestLensOverlaySidePanelCoordinator>(this);
   }
 };
 
@@ -1170,7 +1173,7 @@
   EXPECT_FALSE(fake_controller->fake_overlay_page_.did_notify_results_opened_);
 
   // Now show the side panel.
-  controller->results_side_panel_coordinator()->RegisterEntryAndShow();
+  controller->OpenSidePanelForTesting();
 
   // Prevent flakiness by flushing the tasks.
   fake_controller->FlushForTesting();
@@ -1216,6 +1219,9 @@
 // dialogs (e.g., screen-sharing permission request dialog). If lens overlay
 // dies (e.g., due to tab refresh) before the side panel web view, the modal
 // should close normally without crashing.
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, SidePanelModalDialog) {
   WaitForPaint();
 
@@ -1229,8 +1235,8 @@
   ASSERT_TRUE(base::test::RunUntil(
       [&]() { return controller->state() == State::kOverlay; }));
 
-  // Now show the side panel.
-  controller->results_side_panel_coordinator()->RegisterEntryAndShow();
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
 
   // Prevent flakiness by flushing the tasks.
   auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller);
@@ -1326,8 +1332,8 @@
   ASSERT_TRUE(fake_controller);
   EXPECT_FALSE(fake_controller->fake_overlay_page_.did_notify_overlay_closing_);
 
-  // Now show the side panel.
-  controller->results_side_panel_coordinator()->RegisterEntryAndShow();
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
 
   // Ensure the side panel is showing.
   auto* coordinator = browser()->GetFeatures().side_panel_coordinator();
@@ -1918,11 +1924,11 @@
   EXPECT_FALSE(coordinator->IsSidePanelShowing());
   EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting());
 
-  // Loading a url in the side panel should show the side panel even if we
-  // expect the navigation to fail.
-  const GURL search_url("https://www.google.com/search");
-  controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting(
-      search_url);
+  // Issuing a request should show the side panel even if navigation is expected
+  // to fail.
+  controller->IssueTextSelectionRequestForTesting("test query",
+                                                  /*selection_start_index=*/0,
+                                                  /*selection_end_index=*/0);
   EXPECT_TRUE(content::WaitForLoadStop(
       controller->GetSidePanelWebContentsForTesting()));
 
@@ -1983,11 +1989,11 @@
   EXPECT_FALSE(coordinator->IsSidePanelShowing());
   EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting());
 
-  // Loading a url in the side panel should show the side panel even if we
-  // expect the navigation to fail.
-  const GURL search_url("https://www.google.com/search");
-  controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting(
-      search_url);
+  // Issuing a request should show the side panel even if navigation is expected
+  // to fail.
+  controller->IssueTextSelectionRequestForTesting("test query",
+                                                  /*selection_start_index=*/0,
+                                                  /*selection_end_index=*/0);
   EXPECT_TRUE(content::WaitForLoadStop(
       controller->GetSidePanelWebContentsForTesting()));
 
@@ -2123,8 +2129,11 @@
   ASSERT_FALSE(browser()->GetWebView()->GetEnabled());
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
-                       LoadURLInResultsFrameForTesting) {
+                       LoadURLInResultsFrame) {
   WaitForPaint();
 
   // State should start in off.
@@ -2143,6 +2152,9 @@
   auto* coordinator = browser()->GetFeatures().side_panel_coordinator();
   EXPECT_FALSE(coordinator->IsSidePanelShowing());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page.
   const GURL search_url("https://www.google.com/search");
   controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting(
@@ -2154,6 +2166,9 @@
             SidePanelEntry::Id::kLensOverlayResults);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanelResultStatusHistogram_ResultShown) {
   base::HistogramTester histogram_tester;
@@ -2180,6 +2195,9 @@
   EXPECT_FALSE(coordinator->IsSidePanelShowing());
   EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the side panel even if we
   // expect the navigation to fail.
   const GURL search_url("https://www.google.com/search");
@@ -2201,6 +2219,9 @@
                                      /*expected_count=*/1);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        OfflineErrorPageInSidePanel) {
   base::HistogramTester histogram_tester;
@@ -2233,11 +2254,11 @@
   EXPECT_FALSE(coordinator->IsSidePanelShowing());
   EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting());
 
-  // Loading a url in the side panel should show the side panel even if we
-  // expect the navigation to fail.
-  const GURL search_url("https://www.google.com/search");
-  controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting(
-      search_url);
+  // Issuing a request should show the side panel even if navigation is expected
+  // to fail.
+  controller->IssueTextSelectionRequestForTesting("test query",
+                                                  /*selection_start_index=*/0,
+                                                  /*selection_end_index=*/0);
   EXPECT_TRUE(content::WaitForLoadStop(
       controller->GetSidePanelWebContentsForTesting()));
 
@@ -2258,11 +2279,13 @@
   scoped_mock_network_change_notifier->mock_network_change_notifier()
       ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
 
-  // Loading a url in the side panel should show the results page.
+  // Issuing a new request after the network connection is back should show the
+  // results page.
   content::TestNavigationObserver observer(
       controller->GetSidePanelWebContentsForTesting());
-  controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting(
-      search_url);
+  controller->IssueTextSelectionRequestForTesting("test query",
+                                                  /*selection_start_index=*/0,
+                                                  /*selection_end_index=*/0);
   observer.WaitForNavigationFinished();
 
   // Verify the error page was set correctly. It should be hidden after a
@@ -2274,6 +2297,9 @@
                                      /*expected_count=*/1);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_SameTabSameOriginLinkClick) {
   WaitForPaint();
@@ -2290,6 +2316,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page. This needs to
   // be done to set up the WebContentsObserver.
   const GURL search_url("https://www.google.com/search");
@@ -2353,6 +2382,9 @@
                   .ExtractBool());
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(
     LensOverlayControllerBrowserTest,
     SidePanel_UnsupportedSearchLinkClick_ShouldOpenSearchURLInNewTab) {
@@ -2370,6 +2402,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page. This needs to
   // be done to set up the WebContentsObserver.
   const GURL search_url("https://www.google.com/search");
@@ -2421,6 +2456,9 @@
   EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_SameTabCrossOriginLinkClick) {
   WaitForPaint();
@@ -2437,6 +2475,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page. This needs to
   // be done to set up the WebContentsObserver.
   const GURL search_url("https://www.google.com/search");
@@ -2483,6 +2524,9 @@
   EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_SearchURLClickWithTextDirective) {
   WaitForPaint();
@@ -2552,6 +2596,9 @@
   EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_LinkClickWithTextDirective_TextIsPresent) {
   WaitForPaint();
@@ -2645,6 +2692,9 @@
   }
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 // TODO(crbug.com/399899383): Disabled due to flakiness on Mac.
 #if BUILDFLAG(IS_MAC)
 #define MAYBE_SidePanel_LinkClickWithTextDirective_TextIsMissing \
@@ -2737,6 +2787,9 @@
   }));
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 // TODO(crbug.com/399899383): Disabled due to flakiness on Mac.
 #if BUILDFLAG(IS_MAC)
 #define MAYBE_SidePanel_LinkClickWithTextDirective_TextIsIncomplete \
@@ -2829,6 +2882,9 @@
   }));
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_TopLevelSameOriginLinkClick) {
   WaitForPaint();
@@ -2845,6 +2901,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page. This needs to
   // be done to set up the WebContentsObserver.
   const GURL search_url("https://www.google.com/search");
@@ -2906,6 +2965,9 @@
                   .ExtractBool());
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_NewTabCrossOriginLinkClick) {
   WaitForPaint();
@@ -2922,6 +2984,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page. This needs to
   // be done to set up the WebContentsObserver.
   const GURL search_url("https://www.google.com/search");
@@ -2969,6 +3034,9 @@
   EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_NewTabCrossOriginLinkClickFromUntrustedSite) {
   WaitForPaint();
@@ -2984,6 +3052,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page. This needs to
   // be done to set up the WebContentsObserver.
   const GURL search_url("https://www.google.com/search");
@@ -3061,6 +3132,9 @@
             metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_OpenInNewTab) {
   base::HistogramTester histogram_tester;
@@ -3079,6 +3153,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page. This needs to
   // be done to set up the WebContentsObserver.
   const GURL search_url("https://www.google.com/search?gsc=2&vsrid=12345");
@@ -3132,6 +3209,9 @@
   EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        SidePanel_OpenInNewTabDisabledForContextualQueries) {
   base::UserActionTester user_action_tester;
@@ -3255,6 +3335,7 @@
             lens::MULTIMODAL_SUGGEST_TYPEAHEAD);
 }
 
+// TODO THIS SHOULD NOT BE HERE
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        PopAndLoadQueryFromHistory) {
   WaitForPaint();
@@ -3270,6 +3351,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page.
   const GURL first_search_url(
       "https://www.google.com/"
@@ -3973,6 +4057,9 @@
             lens::INJECTED_IMAGE);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
                        AddQueryToHistoryAfterResize) {
   WaitForPaint();
@@ -3988,6 +4075,9 @@
   EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
   EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
 
+  // Open the side panel.
+  controller->OpenSidePanelForTesting();
+
   // Loading a url in the side panel should show the results page.
   const GURL first_search_url(
       "https://www.google.com/search?q=oranges&gsc=2&hl=en-US");
@@ -5849,6 +5939,9 @@
       true, /*expected_count=*/1);
 }
 
+// TODO(crbug.com/413042395): This test is not testing overlay logic, but
+// instead the side panel logic. Therefore, this test should be moved to a side
+// panel browsertest file.
 IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest,
                        SidePanel_SameTabCrossOriginLinkClick_PdfWithFragment) {
   const GURL pdf_url = embedded_test_server()->GetURL(kMultiPagePdf);
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.cc b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
index 21b98407..781e9ef2 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller.cc
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller.cc
@@ -401,7 +401,6 @@
 
 // Creates the lens::LensOverlayUploadChunkRequest for the given chunk.
 lens::LensOverlayUploadChunkRequest CreateUploadChunkRequest(
-    lens::LensOverlayRequestId request_id,
     int64_t chunk_id,
     int64_t total_chunks,
     std::string chunk,
@@ -1413,6 +1412,7 @@
   compression_task_tracker_->TryCancelAll();
   page_contents_request_start_time_ = base::TimeTicks::Now();
   page_content_request_in_progress_ = true;
+  remaining_upload_chunk_responses_ = 0;
 
   // The initial request id should be set by the time we get here. If not, call
   // below will crash.
@@ -1473,10 +1473,11 @@
 
   std::vector<lens::LensOverlayUploadChunkRequest> requests;
   for (size_t i = 0; i < chunks.size(); i++) {
-    requests.push_back(CreateUploadChunkRequest(request_id, i, chunks.size(),
-                                                chunks[i], request_context));
+    requests.push_back(
+        CreateUploadChunkRequest(i, chunks.size(), chunks[i], request_context));
   }
   pending_upload_chunk_requests_ = requests;
+  upload_chunk_sequence_id = request_id.sequence_id();
 
   chunk_upload_access_token_fetcher_ =
       CreateOAuthHeadersAndContinue(base::BindOnce(
@@ -1488,6 +1489,7 @@
     std::vector<std::string> headers) {
   chunk_upload_access_token_fetcher_.reset();
   pending_upload_chunk_headers_ = headers;
+  remaining_upload_chunk_responses_ = pending_upload_chunk_requests_.size();
   for (size_t i = 0; i < pending_upload_chunk_requests_.size(); i++) {
     FetchUploadChunkRequest(i);
   }
@@ -1499,14 +1501,6 @@
   std::string request_string;
   CHECK(request.SerializeToString(&request_string));
 
-  EndpointFetcherCallback response_received_callback = base::DoNothing();
-  if (chunk_request_index == pending_upload_chunk_requests_.size() - 1) {
-    response_received_callback = base::BindOnce(
-        &LensOverlayQueryController::UploadChunkResponseHandler,
-        weak_ptr_factory_.GetWeakPtr(), request.request_context().request_id(),
-        pending_upload_chunk_requests_.size());
-  }
-
   PerformFetchRequest(
       request_string, &pending_upload_chunk_headers_,
       base::Milliseconds(
@@ -1515,23 +1509,49 @@
           &LensOverlayQueryController::OnChunkUploadEndpointFetcherCreated,
           weak_ptr_factory_.GetWeakPtr(),
           request.request_context().request_id()),
-      std::move(response_received_callback), base::NullCallback(),
+      base::BindOnce(&LensOverlayQueryController::UploadChunkResponseHandler,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     request.request_context().request_id(),
+                     pending_upload_chunk_requests_.size(),
+                     /*is_last=*/chunk_request_index ==
+                         pending_upload_chunk_requests_.size() - 1),
+      base::NullCallback(),
       GURL(lens::features::GetLensOverlayUploadChunkEndpointURL()));
 }
 
 void LensOverlayQueryController::UploadChunkResponseHandler(
     lens::LensOverlayRequestId request_id,
     size_t total_chunks,
+    bool is_last,
     std::unique_ptr<EndpointResponse> response) {
-  chunk_upload_endpoint_fetchers_.clear();
-  base::SequencedTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&CreatePageContentPayloadForChunks,
-                     underlying_page_contents_, primary_content_type_,
-                     page_url_, page_title_, total_chunks),
-      base::BindOnce(
-          &LensOverlayQueryController::PrepareAndFetchPageContentRequestPart2,
-          weak_ptr_factory_.GetWeakPtr(), request_id));
+  // If there is a newer sequence id, a new request has been initiated before
+  // this one has completed. Do nothing and return.
+  if (request_id.sequence_id() != upload_chunk_sequence_id) {
+    return;
+  }
+
+  remaining_upload_chunk_responses_--;
+
+  // If this is the last chunk in sequence, perform the page content request.
+  // Note: in the case that this is also the last chunk to receive a response,
+  // PrepareAndFetchPageContentRequestPart2() is expected to send the gen204
+  // ping.
+  if (is_last) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(&CreatePageContentPayloadForChunks,
+                       underlying_page_contents_, primary_content_type_,
+                       page_url_, page_title_, total_chunks),
+        base::BindOnce(
+            &LensOverlayQueryController::PrepareAndFetchPageContentRequestPart2,
+            weak_ptr_factory_.GetWeakPtr(), request_id));
+    return;
+  }
+
+  // If the page content request has already finished, and this is the last
+  // chunk to receive a response, this will send the gen204 ping and clear the
+  // endpoint fetchers.
+  MaybeSendPageContentUploadLatencyGen204(request_id);
 }
 
 void LensOverlayQueryController::PrepareAndFetchPageContentRequestPart2(
@@ -1584,12 +1604,24 @@
   // interaction request to be sent.
   PageContentUploadFinished();
 
-  SendLatencyGen204IfEnabled(
-      LatencyType::kPageContentUploadLatency, page_contents_request_start_time_,
-      VitQueryParamValueForMimeType(primary_content_type_),
-      /*cluster_info_latency=*/std::nullopt,
-      /*encoded_analytics_id=*/std::nullopt,
-      std::make_optional<lens::LensOverlayRequestId>(request_id));
+  // If the chunk uploads have already completed, or if upload chunking was not
+  // done, this will send the gen204 ping and clear the endpoint fetchers.
+  MaybeSendPageContentUploadLatencyGen204(request_id);
+}
+
+void LensOverlayQueryController::MaybeSendPageContentUploadLatencyGen204(
+    lens::LensOverlayRequestId request_id) {
+  if (!page_content_request_in_progress_ &&
+      remaining_upload_chunk_responses_ == 0) {
+    chunk_upload_endpoint_fetchers_.clear();
+    SendLatencyGen204IfEnabled(
+        LatencyType::kPageContentUploadLatency,
+        page_contents_request_start_time_,
+        VitQueryParamValueForMimeType(primary_content_type_),
+        /*cluster_info_latency=*/std::nullopt,
+        /*encoded_analytics_id=*/std::nullopt,
+        std::make_optional<lens::LensOverlayRequestId>(request_id));
+  }
 }
 
 void LensOverlayQueryController::PageContentUploadProgressHandler(
diff --git a/chrome/browser/ui/lens/lens_overlay_query_controller.h b/chrome/browser/ui/lens/lens_overlay_query_controller.h
index c235423..839ca261 100644
--- a/chrome/browser/ui/lens/lens_overlay_query_controller.h
+++ b/chrome/browser/ui/lens/lens_overlay_query_controller.h
@@ -430,6 +430,7 @@
   // request.
   void UploadChunkResponseHandler(lens::LensOverlayRequestId request_id,
                                   size_t total_chunks,
+                                  bool is_last,
                                   std::unique_ptr<EndpointResponse> response);
 
   // Creates the PageContentRequest that is sent to the server and performs the
@@ -452,6 +453,10 @@
   void PageContentResponseHandler(lens::LensOverlayRequestId request_id,
                                   std::unique_ptr<EndpointResponse> response);
 
+  // Sends a page content upload latency Gen204 ping if enabled.
+  void MaybeSendPageContentUploadLatencyGen204(
+      lens::LensOverlayRequestId request_id);
+
   // Handles the prgress of the page content upload request.
   void PageContentUploadProgressHandler(uint64_t position, uint64_t total);
 
@@ -785,13 +790,22 @@
   // cancelled, and all other tasks will wait on it if needed.
   std::unique_ptr<base::CancelableTaskTracker> encoding_task_tracker_;
 
-  // Bounding boxes for significant regions identified in the original
-  // screenshot image.
+  // Upload chunk requests being sent.
   std::vector<lens::LensOverlayUploadChunkRequest>
       pending_upload_chunk_requests_;
 
+  // Headers to be sent with each upload chunk request.
   std::vector<std::string> pending_upload_chunk_headers_;
 
+  // Number of upload chunk responses expected. Set to
+  // pending_upload_chunk_requests_.size() when starting to send requests, and
+  // decremented each time a request receives a response.
+  size_t remaining_upload_chunk_responses_;
+
+  // The sequence ID for the upload chunk requests that were last started. Used
+  // to verify that the responses received correspond to the latest upload.
+  int upload_chunk_sequence_id;
+
   // The current suggest inputs. The fields in this proto are updated
   // whenever new data is available (i.e. after an objects or interaction
   // response is received) and the overlay controller notified via the
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
index 54628b7..983fc3e 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/ui/lens/lens_overlay_controller.h"
 #include "chrome/browser/ui/lens/lens_overlay_side_panel_web_view.h"
 #include "chrome/browser/ui/lens/lens_overlay_url_builder.h"
+#include "chrome/browser/ui/lens/lens_search_controller.h"
 #include "chrome/browser/ui/lens/page_content_type_conversions.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_content_proxy.h"
@@ -124,15 +125,8 @@
 SearchQuery::~SearchQuery() = default;
 
 LensOverlaySidePanelCoordinator::LensOverlaySidePanelCoordinator(
-    LensOverlayController* lens_overlay_controller)
-    : lens_overlay_controller_(lens_overlay_controller) {
-  side_panel_coordinator_ = lens_overlay_controller->GetTabInterface()
-                                ->GetBrowserWindowInterface()
-                                ->GetFeatures()
-                                .side_panel_coordinator();
-  initialization_data_ = std::make_unique<SidePanelInitializationData>();
-  CHECK(side_panel_coordinator_);
-}
+    LensSearchController* lens_search_controller)
+    : lens_search_controller_(lens_search_controller) {}
 
 LensOverlaySidePanelCoordinator::~LensOverlaySidePanelCoordinator() {
   // If the coordinator is destroyed before the web view, clear the reference
@@ -141,36 +135,27 @@
     side_panel_web_view_->ClearCoordinator();
     side_panel_web_view_ = nullptr;
   }
-
-  auto* registry = lens_overlay_controller_->GetTabInterface()
-                       ->GetTabFeatures()
-                       ->side_panel_registry();
-  CHECK(registry);
-
-  // Remove the side panel entry observer if it is present.
-  auto* registered_entry = registry->GetEntryForKey(
-      SidePanelEntry::Key(SidePanelEntry::Id::kLensOverlayResults));
-  if (registered_entry) {
-    registered_entry->RemoveObserver(this);
-  }
-
-  // This is a no-op if the entry does not exist.
-  registry->Deregister(
-      SidePanelEntry::Key(SidePanelEntry::Id::kLensOverlayResults));
 }
 
-void LensOverlaySidePanelCoordinator::RegisterEntryAndShow() {
-  // Exit early if the side panel is already registered or is in the process of
-  // being registered.
-  if(state_ != State::kOff) {
-    return;
-  }
-
+std::unique_ptr<SidePanelInUse>
+LensOverlaySidePanelCoordinator::RegisterEntryAndShow() {
   state_ = State::kOpeningSidePanel;
   RegisterEntry();
-  GetSidePanelUI(lens_overlay_controller_)
+  GetSidePanelUI(GetLensOverlayController())
       ->Show(SidePanelEntry::Id::kLensOverlayResults);
-  lens_overlay_controller_->NotifyResultsPanelOpened();
+  GetLensOverlayController()->NotifyResultsPanelOpened();
+
+  // Create the initialization data for this journey.
+  initialization_data_ = std::make_unique<SidePanelInitializationData>();
+
+  // Store reference to the side panel coordinator for this journey.
+  side_panel_coordinator_ = lens_search_controller_->GetTabInterface()
+                                ->GetBrowserWindowInterface()
+                                ->GetFeatures()
+                                .side_panel_coordinator();
+  CHECK(side_panel_coordinator_);
+
+  return std::make_unique<SidePanelInUseImpl>(this);
 }
 
 void LensOverlaySidePanelCoordinator::RecordAndShowSidePanelErrorPage() {
@@ -187,7 +172,7 @@
 void LensOverlaySidePanelCoordinator::OnEntryWillHide(
     SidePanelEntry* entry,
     SidePanelEntryHideReason reason) {
-  lens_overlay_controller_->OnSidePanelWillHide(reason);
+  GetLensOverlayController()->OnSidePanelWillHide(reason);
 }
 
 void LensOverlaySidePanelCoordinator::OnEntryHidden(SidePanelEntry* entry) {
@@ -197,14 +182,14 @@
   //   (2) The user clicked the 'x' button while the overlay is showing.
   //   (3) The side panel naturally went away after a tab switch.
   // Forward to LensOverlayController to have it disambiguate.
-  lens_overlay_controller_->OnSidePanelHidden();
+  GetLensOverlayController()->OnSidePanelHidden();
 }
 
 void LensOverlaySidePanelCoordinator::WebViewClosing() {
   // This is called from the destructor of the WebView. Synchronously clear all
   // state associated with the WebView.
   if (side_panel_web_view_) {
-    lens_overlay_controller_->ResetSidePanelSearchboxHandler();
+    GetLensOverlayController()->ResetSidePanelSearchboxHandler();
     side_panel_web_view_ = nullptr;
   }
 }
@@ -220,7 +205,7 @@
 bool LensOverlaySidePanelCoordinator::MaybeHandleTextDirectives(
     const GURL& nav_url) {
   if (ShouldHandleTextDirectives(nav_url)) {
-    const GURL& page_url = lens_overlay_controller_->GetTabInterface()
+    const GURL& page_url = lens_search_controller_->GetTabInterface()
                                ->GetContents()
                                ->GetLastCommittedURL();
     // Need to check if the page URL matches the navigation URL again. This is
@@ -234,7 +219,7 @@
       if (page_url.host() != nav_url.host() ||
           page_url.path() != nav_url.path() ||
           page_url_text_query != nav_url_text_query) {
-        lens_overlay_controller_->GetTabInterface()
+        lens_search_controller_->GetTabInterface()
             ->GetBrowserWindowInterface()
             ->OpenGURL(nav_url, WindowOpenDisposition::NEW_FOREGROUND_TAB);
         return true;
@@ -246,7 +231,7 @@
         shared_highlighting::ExtractTextFragments(nav_url.ref());
 
     // Create and attach a `TextFinderManager` to the primary page.
-    content::Page& page = lens_overlay_controller_->GetTabInterface()
+    content::Page& page = lens_search_controller_->GetTabInterface()
                               ->GetContents()
                               ->GetPrimaryPage();
     companion::TextFinderManager* text_finder_manager =
@@ -262,7 +247,7 @@
 }
 
 bool LensOverlaySidePanelCoordinator::IsEntryShowing() {
-  return GetSidePanelUI(lens_overlay_controller_)
+  return GetSidePanelUI(GetLensOverlayController())
       ->IsSidePanelEntryShowing(
           SidePanelEntry::Key(SidePanelEntry::Id::kLensOverlayResults));
 }
@@ -286,17 +271,17 @@
   // selection and thumbnail state.
   const std::string lens_mode = lens::GetLensModeParameterValue(search_url);
   if (lens_mode.empty()) {
-    lens_overlay_controller_->SetAdditionalSearchQueryParams(
+    GetLensOverlayController()->SetAdditionalSearchQueryParams(
         /*additional_search_query_params=*/{});
-    lens_overlay_controller_->SetSearchboxThumbnail("");
-    lens_overlay_controller_->ClearAllSelections();
-    lens_overlay_controller_->SetSearchboxThumbnail(std::string());
+    GetLensOverlayController()->SetSearchboxThumbnail("");
+    GetLensOverlayController()->ClearAllSelections();
+    GetLensOverlayController()->SetSearchboxThumbnail(std::string());
   }
 
   // Grab the current state of the overlay and use it to update populate the
   // query stack and currently loaded query.
   lens::SearchQuery search_query(query, search_url);
-  lens_overlay_controller_->AddOverlayStateToSearchQuery(search_query);
+  GetLensOverlayController()->AddOverlayStateToSearchQuery(search_query);
 
   // Add what was the currently loaded search query to the query stack,
   // if it is present.
@@ -311,7 +296,7 @@
   initialization_data_->currently_loaded_search_query_ = search_query;
 
   // Update searchbox and selection state to match the new query.
-  lens_overlay_controller_->SetSearchboxInputText(query);
+  GetLensOverlayController()->SetSearchboxInputText(query);
 }
 void LensOverlaySidePanelCoordinator::PopAndLoadQueryFromHistory() {
   if (initialization_data_->search_query_history_stack_.empty()) {
@@ -331,30 +316,30 @@
   // Set the translate mode for the new query. Even if there are no translate
   // options, still need to pass the std::nullopt to disable translate
   // mode in the overlay.
-  lens_overlay_controller_->SetTranslateMode(query.translate_options_);
+  GetLensOverlayController()->SetTranslateMode(query.translate_options_);
 
   // Clear any active selections on the page and then re-add selections for this
   // query and update the selection, thumbnail and searchbox state.
-  lens_overlay_controller_->ClearAllSelections();
+  GetLensOverlayController()->ClearAllSelections();
 
   // Do not reset text selections for translated text since it may
   // not be on the screen until the full image request is resent.
   if (query.selected_text_.has_value() &&
       !query.translate_options_.has_value()) {
-    lens_overlay_controller_->SetTextSelection(query.selected_text_->first,
-                                               query.selected_text_->second);
+    GetLensOverlayController()->SetTextSelection(query.selected_text_->first,
+                                                 query.selected_text_->second);
   } else if (query.selected_region_) {
-    lens_overlay_controller_->SetPostRegionSelection(
+    GetLensOverlayController()->SetPostRegionSelection(
         query.selected_region_->Clone());
   }
-  lens_overlay_controller_->SetAdditionalSearchQueryParams(
+  GetLensOverlayController()->SetAdditionalSearchQueryParams(
       query.additional_search_query_params_);
-  lens_overlay_controller_->SetSearchboxInputText(query.search_query_text_);
-  lens_overlay_controller_->SetSearchboxThumbnail(
+  GetLensOverlayController()->SetSearchboxInputText(query.search_query_text_);
+  GetLensOverlayController()->SetSearchboxThumbnail(
       query.selected_region_thumbnail_uri_);
 
   const bool is_contextual_query =
-      lens_overlay_controller_->IsContextualSearchbox();
+      GetLensOverlayController()->IsContextualSearchbox();
   const bool query_has_image =
       query.selected_region_ || !query.selected_region_bitmap_.drawsNothing();
   const bool should_send_interaction = query_has_image || is_contextual_query;
@@ -382,14 +367,14 @@
 
     // If the query also has text, we should send it as a multimodal query.
     if (query.search_query_text_.empty()) {
-      lens_overlay_controller_->IssueLensRequest(
+      GetLensOverlayController()->IssueLensRequest(
           query.selected_region_->Clone(), query.lens_selection_type_,
           selected_region_bitmap);
     } else {
       // TODO(crbug.com/404941800): It might be better to send the multimodal
       // request directly to the query controller once the query controller is
       // owned by the search controller.
-      lens_overlay_controller_->IssueMultimodalRequest(
+      GetLensOverlayController()->IssueMultimodalRequest(
           query.selected_region_->Clone(), query.search_query_text_,
           query.lens_selection_type_, selected_region_bitmap);
     }
@@ -402,7 +387,7 @@
     // TODO(crbug.com/404941800): It might be better to send the contextual
     // request directly to the query controller once the query controller is
     // owned by the search controller.
-    lens_overlay_controller_->IssueContextualTextRequest(
+    GetLensOverlayController()->IssueContextualTextRequest(
         query.search_query_text_, query.lens_selection_type_);
     return;
   }
@@ -427,19 +412,19 @@
     case COMMAND_MY_ACTIVITY: {
       lens::RecordSidePanelMenuOptionSelected(
           lens::LensOverlaySidePanelMenuOption::kMyActivity);
-      lens_overlay_controller_->ActivityRequestedByEvent(event_flags);
+      GetLensOverlayController()->ActivityRequestedByEvent(event_flags);
       break;
     }
     case COMMAND_LEARN_MORE: {
       lens::RecordSidePanelMenuOptionSelected(
           lens::LensOverlaySidePanelMenuOption::kLearnMore);
-      lens_overlay_controller_->InfoRequestedByEvent(event_flags);
+      GetLensOverlayController()->InfoRequestedByEvent(event_flags);
       break;
     }
     case COMMAND_SEND_FEEDBACK: {
       lens::RecordSidePanelMenuOptionSelected(
           lens::LensOverlaySidePanelMenuOption::kSendFeedback);
-      lens_overlay_controller_->FeedbackRequestedByEvent(event_flags);
+      GetLensOverlayController()->FeedbackRequestedByEvent(event_flags);
       break;
     }
     default: {
@@ -457,7 +442,6 @@
 
   side_panel_receiver_.Bind(std::move(receiver));
   side_panel_page_.Bind(std::move(page));
-  state_ = State::kOpen;
 
   if (pending_side_panel_url_.has_value()) {
     side_panel_page_->LoadResultsInFrame(*pending_side_panel_url_);
@@ -480,6 +464,9 @@
 }
 
 void LensOverlaySidePanelCoordinator::LoadURLInResultsFrame(const GURL& url) {
+  CHECK(state_ != State::kOff) << "Side panel is not registered. You must "
+                                  "first call RegisterEntryAndShow().";
+
   if (side_panel_page_) {
     side_panel_page_->LoadResultsInFrame(url);
     return;
@@ -565,6 +552,54 @@
 LensOverlaySidePanelCoordinator::SidePanelInitializationData::
     ~SidePanelInitializationData() = default;
 
+LensOverlaySidePanelCoordinator::SidePanelInUseImpl::SidePanelInUseImpl(
+    LensOverlaySidePanelCoordinator* coordinator)
+    : coordinator_(coordinator->weak_ptr_factory_.GetWeakPtr()) {
+  coordinator_->side_panel_in_use_count_++;
+}
+
+LensOverlaySidePanelCoordinator::SidePanelInUseImpl::~SidePanelInUseImpl() {
+  if (coordinator_) {
+    coordinator_->side_panel_in_use_count_--;
+    if (coordinator_->side_panel_in_use_count_ == 0) {
+      coordinator_->DeregisterEntryAndCleanup();
+    }
+  }
+}
+
+void LensOverlaySidePanelCoordinator::DeregisterEntryAndCleanup() {
+  auto* registry = lens_search_controller_->GetTabInterface()
+                       ->GetTabFeatures()
+                       ->side_panel_registry();
+  CHECK(registry);
+
+  // Remove the side panel entry observer if it is present.
+  auto* registered_entry = registry->GetEntryForKey(
+      SidePanelEntry::Key(SidePanelEntry::Id::kLensOverlayResults));
+  if (registered_entry) {
+    registered_entry->RemoveObserver(this);
+  }
+
+  // This is a no-op if the entry does not exist.
+  registry->Deregister(
+      SidePanelEntry::Key(SidePanelEntry::Id::kLensOverlayResults));
+
+  // Remove the reference to the side panel coordinator to prevent dangling
+  // pointers.
+  side_panel_coordinator_ = nullptr;
+
+  // Cleanup internal state.
+  side_panel_receiver_.reset();
+  side_panel_page_.reset();
+  initialization_data_.reset();
+  pending_side_panel_url_.reset();
+  side_panel_should_show_error_page_ = false;
+  side_panel_new_tab_url_ = GURL();
+  side_panel_result_status_ = lens::SidePanelResultStatus::kUnknown;
+
+  state_ = State::kOff;
+}
+
 // This method is called when the WebContents wants to open a link in a new
 // tab (e.g. an anchor tag with target="_blank"). This delegate does not
 // override AddNewContents(), so the WebContents is not actually created.
@@ -640,7 +675,7 @@
 
 #if BUILDFLAG(ENABLE_PDF)
     content::WebContents* web_contents =
-        lens_overlay_controller_->GetTabInterface()->GetContents();
+        lens_search_controller_->GetTabInterface()->GetContents();
 
     // If a PDFDocumentHelper is found attached to the current web contents,
     // that means that the PDF viewer is currently loaded in it.
@@ -662,7 +697,7 @@
       return;
     }
 
-    lens_overlay_controller_->GetTabInterface()
+    lens_search_controller_->GetTabInterface()
         ->GetBrowserWindowInterface()
         ->OpenGURL(nav_url, WindowOpenDisposition::NEW_FOREGROUND_TAB);
     return;
@@ -670,7 +705,7 @@
 
   // If the search URL should be opened in a new tab, open it here.
   if (ShouldOpenSearchURLInNewTab(nav_url)) {
-    lens_overlay_controller_->GetTabInterface()
+    lens_search_controller_->GetTabInterface()
         ->GetBrowserWindowInterface()
         ->OpenGURL(nav_url, WindowOpenDisposition::NEW_FOREGROUND_TAB);
     return;
@@ -710,7 +745,7 @@
 
 web_modal::WebContentsModalDialogHost*
 LensOverlaySidePanelCoordinator::GetWebContentsModalDialogHost() {
-  return lens_overlay_controller_->GetTabInterface()
+  return lens_search_controller_->GetTabInterface()
       ->GetBrowserWindowInterface()
       ->GetWebContentsModalDialogHostForWindow();
 }
@@ -720,11 +755,11 @@
   // Only handle text directives if the feature is enabled and the overlay is
   // not covering the current tab.
   if (!lens::features::HandleSidePanelTextDirectivesEnabled() ||
-      lens_overlay_controller_->IsOverlayShowing()) {
+      GetLensOverlayController()->IsOverlayShowing()) {
     return false;
   }
 
-  const GURL& page_url = lens_overlay_controller_->GetTabInterface()
+  const GURL& page_url = lens_search_controller_->GetTabInterface()
                              ->GetContents()
                              ->GetLastCommittedURL();
   // Only handle text directives when the page URL and the URL being navigated
@@ -750,11 +785,11 @@
   // Only handle text directives if the feature is enabled and the overlay is
   // not covering the current tab.
   if (!lens::features::HandleSidePanelTextDirectivesEnabled() ||
-      lens_overlay_controller_->IsOverlayShowing()) {
+      GetLensOverlayController()->IsOverlayShowing()) {
     return false;
   }
 
-  const GURL& page_url = lens_overlay_controller_->GetTabInterface()
+  const GURL& page_url = lens_search_controller_->GetTabInterface()
                              ->GetContents()
                              ->GetLastCommittedURL();
   // Handle the PDF hash change if the URL being navigated to is the same as the
@@ -768,7 +803,7 @@
 void LensOverlaySidePanelCoordinator::OnTextFinderLookupComplete(
     const GURL& nav_url,
     const std::vector<std::pair<std::string, bool>>& lookup_results) {
-  const GURL& page_url = lens_overlay_controller_->GetTabInterface()
+  const GURL& page_url = lens_search_controller_->GetTabInterface()
                              ->GetContents()
                              ->GetLastCommittedURL();
   if (lookup_results.empty()) {
@@ -778,7 +813,7 @@
       return;
     }
 
-    lens_overlay_controller_->GetTabInterface()
+    lens_search_controller_->GetTabInterface()
         ->GetBrowserWindowInterface()
         ->OpenGURL(nav_url, WindowOpenDisposition::NEW_FOREGROUND_TAB);
     return;
@@ -794,7 +829,7 @@
         return;
       }
 
-      lens_overlay_controller_->GetTabInterface()
+      lens_search_controller_->GetTabInterface()
           ->GetBrowserWindowInterface()
           ->OpenGURL(nav_url, WindowOpenDisposition::NEW_FOREGROUND_TAB);
       return;
@@ -804,7 +839,7 @@
 
   // Delete any existing `TextHighlighterManager` on the page. Without this, any
   // text highlights after the first to be rendered on the page will not render.
-  auto& page = lens_overlay_controller_->GetTabInterface()
+  auto& page = lens_search_controller_->GetTabInterface()
                    ->GetContents()
                    ->GetPrimaryPage();
   if (companion::TextHighlighterManager::GetForPage(page)) {
@@ -813,7 +848,7 @@
 
   // If every text fragment was found, then create a text highlighter manager to
   // render the text highlights. Focus the main tab first.
-  lens_overlay_controller_->GetTabInterface()->GetContents()->Focus();
+  lens_search_controller_->GetTabInterface()->GetContents()->Focus();
   companion::TextHighlighterManager* text_highlighter_manager =
       companion::TextHighlighterManager::GetOrCreateForPage(page);
   text_highlighter_manager->CreateTextHighlightersAndRemoveExisting(
@@ -822,13 +857,13 @@
 
 void LensOverlaySidePanelCoordinator::OpenURLInBrowser(
     const content::OpenURLParams& params) {
-  lens_overlay_controller_->GetTabInterface()
+  lens_search_controller_->GetTabInterface()
       ->GetBrowserWindowInterface()
       ->OpenURL(params, /*navigation_handle_callback=*/{});
 }
 
 void LensOverlaySidePanelCoordinator::RegisterEntry() {
-  auto* registry = lens_overlay_controller_->GetTabInterface()
+  auto* registry = lens_search_controller_->GetTabInterface()
                        ->GetTabFeatures()
                        ->side_panel_registry();
   CHECK(registry);
@@ -861,7 +896,7 @@
   // TODO(crbug.com/328295358): Change task manager string ID in view creation
   // when available.
   auto view = std::make_unique<LensOverlaySidePanelWebView>(
-      lens_overlay_controller_->GetTabInterface()
+      lens_search_controller_->GetTabInterface()
           ->GetContents()
           ->GetBrowserContext(),
       this, scope);
@@ -877,7 +912,7 @@
 
 GURL LensOverlaySidePanelCoordinator::GetSidePanelNewTabUrl() {
   return lens::GetSidePanelNewTabUrl(
-      side_panel_new_tab_url_, lens_overlay_controller_->GetVsridForNewTab());
+      side_panel_new_tab_url_, GetLensOverlayController()->GetVsridForNewTab());
 }
 
 void LensOverlaySidePanelCoordinator::ShowToast(std::string message) {
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
index 0e3f568..6c0192e4 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h
@@ -10,6 +10,7 @@
 #include "chrome/browser/lens/core/mojom/lens_side_panel.mojom.h"
 #include "chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h"
 #include "chrome/browser/ui/lens/lens_overlay_translate_options.h"
+#include "chrome/browser/ui/lens/lens_search_controller.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_entry_observer.h"
 #include "components/lens/lens_overlay_side_panel_result.h"
 #include "content/public/browser/navigation_handle.h"
@@ -71,6 +72,17 @@
   std::optional<lens::TranslateOptions> translate_options_;
 };
 
+// A Lens feature that wants to keep results in the side panel should call
+// `LensOveraySidePanelCoordinator::RegisterEntryAndShow()` and keep alive the
+// instance of `SidePanelInUse` for the duration of using the side panel. When
+// all instances of `SidePanelInUse` are destroyed, the side panel will close
+// and the `LensOveraySidePanelCoordinator` will clean up.
+class SidePanelInUse {
+ public:
+  SidePanelInUse() = default;
+  virtual ~SidePanelInUse() = default;
+};
+
 // Handles the creation and registration of the lens overlay side panel entry.
 // There are two ways for this instance to be torn down.
 //   (1) Its owner, LensOverlayController can destroy it.
@@ -89,7 +101,7 @@
       public ui::SimpleMenuModel::Delegate {
  public:
   explicit LensOverlaySidePanelCoordinator(
-      LensOverlayController* lens_overlay_controller);
+      LensSearchController* lens_search_controller);
   LensOverlaySidePanelCoordinator(const LensOverlaySidePanelCoordinator&) =
       delete;
   LensOverlaySidePanelCoordinator& operator=(
@@ -97,8 +109,9 @@
   ~LensOverlaySidePanelCoordinator() override;
 
   // Registers the side panel entry in the side panel if it doesn't already
-  // exist and then shows it.
-  void RegisterEntryAndShow();
+  // exist and then shows it. Calls must keep the returned `SidePanelInUse`
+  // alive for the duration of using the side panel.
+  std::unique_ptr<SidePanelInUse> RegisterEntryAndShow();
 
   // SidePanelEntryObserver:
   void OnEntryWillHide(SidePanelEntry* entry,
@@ -110,9 +123,14 @@
 
   content::WebContents* GetSidePanelWebContents();
 
-  // Return the LensOverlayController that owns this side panel coordinator.
+  // Return the LensSearchController that owns this side panel coordinator.
+  LensSearchController* GetLensSearchController() {
+    return lens_search_controller_.get();
+  }
+
+  // Return the LensOverlayController that is part of this tab.
   LensOverlayController* GetLensOverlayController() {
-    return lens_overlay_controller_.get();
+    return lens_search_controller_->lens_overlay_controller();
   }
 
   // Handles rendering text highlights on the main browser window based on
@@ -236,6 +254,21 @@
     std::optional<SearchQuery> currently_loaded_search_query_;
   };
 
+  // Tracks whether the side panel is in use.
+  class SidePanelInUseImpl : public SidePanelInUse {
+   public:
+    explicit SidePanelInUseImpl(LensOverlaySidePanelCoordinator* coordinator);
+    ~SidePanelInUseImpl() override;
+
+   private:
+    // Owns this.
+    base::WeakPtr<LensOverlaySidePanelCoordinator> coordinator_;
+  };
+
+  // Cleans up the side panel entry and closes the side panel if there are no
+  // other SidePanelInUse instances.
+  void DeregisterEntryAndCleanup();
+
   // content::WebContentsObserver:
   void DidOpenRequestedURL(content::WebContents* new_contents,
                            content::RenderFrameHost* source_render_frame_host,
@@ -307,7 +340,7 @@
   std::unique_ptr<ui::MenuModel> GetMoreInfoMenuModel();
 
   // Owns this.
-  const raw_ptr<LensOverlayController> lens_overlay_controller_;
+  const raw_ptr<LensSearchController> lens_search_controller_;
 
   // Connections to and from the side panel WebUI. Only valid when the side
   // panel is currently open and after the WebUI has started executing JS and
@@ -344,6 +377,10 @@
   // this can be assumed to be non-null.
   raw_ptr<SidePanelCoordinator> side_panel_coordinator_ = nullptr;
 
+  // Counts the number of SidePanelInUse instances that are alive. When this
+  // reaches zero, the side panel will close.
+  uint16_t side_panel_in_use_count_ = 0;
+
   raw_ptr<LensOverlaySidePanelWebView> side_panel_web_view_;
   base::WeakPtrFactory<LensOverlaySidePanelCoordinator> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc
index 7954f201..10fbd90 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_web_view.cc
@@ -73,7 +73,7 @@
     const content::OpenURLParams& params,
     base::OnceCallback<void(content::NavigationHandle&)>
         navigation_handle_callback) {
-  coordinator_->GetLensOverlayController()
+  coordinator_->GetLensSearchController()
       ->GetTabInterface()
       ->GetBrowserWindowInterface()
       ->OpenURL(params, std::move(navigation_handle_callback));
@@ -86,7 +86,8 @@
   if (!coordinator_) {
     return false;
   }
-  return coordinator_->GetLensOverlayController()
+  return coordinator_->GetLensSearchController()
+      ->lens_overlay_controller()
       ->lens_overlay_event_handler()
       ->HandleKeyboardEvent(source, event, GetFocusManager());
 }
diff --git a/chrome/browser/ui/lens/lens_search_controller.cc b/chrome/browser/ui/lens/lens_search_controller.cc
index 3eca5678..3cdd6185 100644
--- a/chrome/browser/ui/lens/lens_search_controller.cc
+++ b/chrome/browser/ui/lens/lens_search_controller.cc
@@ -7,6 +7,7 @@
 #include "base/check.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/lens/lens_overlay_controller.h"
+#include "chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h"
 
 LensSearchController::LensSearchController(tabs::TabInterface* tab)
     : tab_(tab) {}
@@ -21,9 +22,16 @@
   CHECK(!initialized_);
   initialized_ = true;
 
-  lens_overlay_controller_ =
-      CreateLensOverlayController(tab_, variations_client, identity_manager,
-                                  pref_service, sync_service, theme_service);
+  lens_overlay_controller_ = CreateLensOverlayController(
+      tab_, this, variations_client, identity_manager, pref_service,
+      sync_service, theme_service);
+
+  lens_overlay_side_panel_coordinator_ =
+      CreateLensOverlaySidePanelCoordinator();
+}
+
+tabs::TabInterface* LensSearchController::GetTabInterface() {
+  return tab_;
 }
 
 LensOverlayController* LensSearchController::lens_overlay_controller() {
@@ -33,15 +41,29 @@
   return lens_overlay_controller_.get();
 }
 
+lens::LensOverlaySidePanelCoordinator*
+LensSearchController::lens_overlay_side_panel_coordinator() {
+  CHECK(initialized_)
+      << "The LensSearchController has not been initialized. Initialize() must "
+         "be called before using the LensSearchController.";
+  return lens_overlay_side_panel_coordinator_.get();
+}
+
 std::unique_ptr<LensOverlayController>
 LensSearchController::CreateLensOverlayController(
     tabs::TabInterface* tab,
+    LensSearchController* lens_search_controller,
     variations::VariationsClient* variations_client,
     signin::IdentityManager* identity_manager,
     PrefService* pref_service,
     syncer::SyncService* sync_service,
     ThemeService* theme_service) {
-  return std::make_unique<LensOverlayController>(tab, variations_client,
-                                                 identity_manager, pref_service,
-                                                 sync_service, theme_service);
+  return std::make_unique<LensOverlayController>(
+      tab, lens_search_controller, variations_client, identity_manager,
+      pref_service, sync_service, theme_service);
+}
+
+std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
+LensSearchController::CreateLensOverlaySidePanelCoordinator() {
+  return std::make_unique<lens::LensOverlaySidePanelCoordinator>(this);
 }
diff --git a/chrome/browser/ui/lens/lens_search_controller.h b/chrome/browser/ui/lens/lens_search_controller.h
index 6cbd610..b294cf2f 100644
--- a/chrome/browser/ui/lens/lens_search_controller.h
+++ b/chrome/browser/ui/lens/lens_search_controller.h
@@ -10,6 +10,10 @@
 
 class LensOverlayController;
 
+namespace lens {
+class LensOverlaySidePanelCoordinator;
+}  // namespace lens
+
 namespace variations {
 class VariationsClient;
 }  // namespace variations
@@ -40,26 +44,43 @@
                   syncer::SyncService* sync_service,
                   ThemeService* theme_service);
 
+  // Returns the tab interface that owns this controller.
+  tabs::TabInterface* GetTabInterface();
+
   // Returns the LensOverlayController.
   LensOverlayController* lens_overlay_controller();
 
+  // Returns the LensOverlaySidePanelCoordinator.
+  lens::LensOverlaySidePanelCoordinator* lens_overlay_side_panel_coordinator();
+
  protected:
   // Override these methods to stub out individual feature controllers for
   // testing.
   virtual std::unique_ptr<LensOverlayController> CreateLensOverlayController(
       tabs::TabInterface* tab,
+      LensSearchController* lens_search_controller,
       variations::VariationsClient* variations_client,
       signin::IdentityManager* identity_manager,
       PrefService* pref_service,
       syncer::SyncService* sync_service,
       ThemeService* theme_service);
 
+  // Override these methods to be able to track calls made to the side panel
+  // coordinator.
+  virtual std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
+  CreateLensOverlaySidePanelCoordinator();
+
  private:
   // Whether the LensSearchController has been initialized.
   bool initialized_ = false;
 
+  // The overlay controller for the Lens Search feature on this tab.
   std::unique_ptr<LensOverlayController> lens_overlay_controller_;
 
+  // The side panel coordinator for the Lens Search feature on this tab.
+  std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
+      lens_overlay_side_panel_coordinator_;
+
   // Owns this class.
   raw_ptr<tabs::TabInterface> tab_;
 };
diff --git a/chrome/browser/ui/lens/test_lens_overlay_controller.h b/chrome/browser/ui/lens/test_lens_overlay_controller.h
index 263bb6c..5dd1eea 100644
--- a/chrome/browser/ui/lens/test_lens_overlay_controller.h
+++ b/chrome/browser/ui/lens/test_lens_overlay_controller.h
@@ -13,12 +13,14 @@
 class TestLensOverlayController : public LensOverlayController {
  public:
   TestLensOverlayController(tabs::TabInterface* tab,
+                            LensSearchController* lens_search_controller,
                             variations::VariationsClient* variations_client,
                             signin::IdentityManager* identity_manager,
                             PrefService* pref_service,
                             syncer::SyncService* sync_service,
                             ThemeService* theme_service)
       : LensOverlayController(tab,
+                              lens_search_controller,
                               variations_client,
                               identity_manager,
                               pref_service,
diff --git a/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.cc b/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.cc
index 636c12c..fce83a9 100644
--- a/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.cc
+++ b/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.cc
@@ -4,13 +4,13 @@
 
 #include "test_lens_overlay_side_panel_coordinator.h"
 
-#include "chrome/browser/ui/lens/lens_overlay_controller.h"
+#include "chrome/browser/ui/lens/lens_search_controller.h"
 
 namespace lens {
 
 TestLensOverlaySidePanelCoordinator::TestLensOverlaySidePanelCoordinator(
-    LensOverlayController* lens_overlay_controller)
-    : LensOverlaySidePanelCoordinator(lens_overlay_controller) {}
+    LensSearchController* lens_search_controller)
+    : LensOverlaySidePanelCoordinator(lens_search_controller) {}
 
 TestLensOverlaySidePanelCoordinator::~TestLensOverlaySidePanelCoordinator() =
     default;
diff --git a/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.h b/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.h
index e585d02..be614759b 100644
--- a/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.h
+++ b/chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.h
@@ -16,7 +16,7 @@
     : public LensOverlaySidePanelCoordinator {
  public:
   explicit TestLensOverlaySidePanelCoordinator(
-      LensOverlayController* lens_overlay_controller);
+      LensSearchController* lens_search_controller);
   ~TestLensOverlaySidePanelCoordinator() override;
 
   void SetSidePanelIsLoadingResults(bool is_loading) override;
diff --git a/chrome/browser/ui/lens/test_lens_search_controller.cc b/chrome/browser/ui/lens/test_lens_search_controller.cc
index ade4849..b066a86 100644
--- a/chrome/browser/ui/lens/test_lens_search_controller.cc
+++ b/chrome/browser/ui/lens/test_lens_search_controller.cc
@@ -17,6 +17,7 @@
 std::unique_ptr<LensOverlayController>
 TestLensSearchController::CreateLensOverlayController(
     tabs::TabInterface* tab,
+    LensSearchController* lens_search_controller,
     variations::VariationsClient* variations_client,
     signin::IdentityManager* identity_manager,
     PrefService* pref_service,
@@ -27,8 +28,8 @@
       ThemeService::BrowserColorScheme::kLight);
 
   return std::make_unique<TestLensOverlayController>(
-      tab, variations_client, identity_manager, pref_service, sync_service,
-      theme_service);
+      tab, lens_search_controller, variations_client, identity_manager,
+      pref_service, sync_service, theme_service);
 }
 
 }  // namespace lens
diff --git a/chrome/browser/ui/lens/test_lens_search_controller.h b/chrome/browser/ui/lens/test_lens_search_controller.h
index ee3aa367..3be2168 100644
--- a/chrome/browser/ui/lens/test_lens_search_controller.h
+++ b/chrome/browser/ui/lens/test_lens_search_controller.h
@@ -20,6 +20,7 @@
 
   std::unique_ptr<LensOverlayController> CreateLensOverlayController(
       tabs::TabInterface* tab,
+      LensSearchController* lens_search_controller,
       variations::VariationsClient* variations_client,
       signin::IdentityManager* identity_manager,
       PrefService* pref_service,
diff --git a/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.cc b/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.cc
index eaf2067..7768f52 100644
--- a/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.cc
+++ b/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/pdf/infobar/pdf_infobar_controller.h"
 
+#include <optional>
+
 #include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
@@ -12,6 +14,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/shell_integration.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/pdf/infobar/pdf_infobar_delegate.h"
 #include "chrome/browser/ui/pdf/infobar/pdf_infobar_prefs.h"
@@ -65,6 +68,8 @@
 
 }  // namespace
 
+std::optional<bool> PdfInfoBarController::default_browser_prompt_shown_;
+
 PdfInfoBarController::PdfInfoBarController(BrowserWindowInterface* browser)
     : browser_(browser) {
   CHECK(base::FeatureList::IsEnabled(features::kPdfInfoBar));
@@ -88,14 +93,21 @@
 
 PdfInfoBarController::~PdfInfoBarController() = default;
 
-void PdfInfoBarController::ShowAtStartup() {
-  // This is the entry point to the PDF infobar if it shows at startup.
-  if (!IsAppropriateForInfoBar(browser_)) {
+// static
+void PdfInfoBarController::MaybeShowInfoBarAtStartup(
+    base::WeakPtr<BrowserWindowInterface> startup_browser,
+    bool default_browser_prompt_shown) {
+  default_browser_prompt_shown_ = default_browser_prompt_shown;
+  if (!startup_browser) {
     return;
   }
+  if (!IsAppropriateForInfoBar(startup_browser.get())) {
+    return;
+  }
+  // This is the entry point to the PDF infobar if it shows at startup.
   if (features::kPdfInfoBarTrigger.Get() ==
       features::PdfInfoBarTrigger::kStartup) {
-    MaybeShowInfoBar();
+    startup_browser->GetFeatures().pdf_infobar_controller()->MaybeShowInfoBar();
   }
 }
 
@@ -166,7 +178,6 @@
   if (IsDefaultBrowserPolicyControlled()) {
     return;
   }
-
   // Don't show the infobar if it's already showing or was recently shown.
   if (infobar_) {
     return;
@@ -174,6 +185,13 @@
   if (InfoBarShownRecentlyOrMaxTimes()) {
     return;
   }
+  // Don't show the infobar if the default-browser prompt has been shown or
+  // might be about to show, to avoid asking too many similar questions in a
+  // session.
+  if (!default_browser_prompt_shown_.has_value() ||
+      default_browser_prompt_shown_.value()) {
+    return;
+  }
 
   // Show the PDF infobar.
   content::WebContents* web_contents =
@@ -184,3 +202,9 @@
   infobar_ = PdfInfoBarDelegate::Create(infobar_manager_);
   SetInfoBarShownRecently();
 }
+
+// static
+void PdfInfoBarController::SetDefaultBrowserPromptShownForTesting(
+    bool default_browser_prompt_shown) {
+  default_browser_prompt_shown_ = default_browser_prompt_shown;
+}
diff --git a/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.h b/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.h
index d64222a..7ebb3a43 100644
--- a/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.h
+++ b/chrome/browser/ui/pdf/infobar/pdf_infobar_controller.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_PDF_INFOBAR_PDF_INFOBAR_CONTROLLER_H_
 #define CHROME_BROWSER_UI_PDF_INFOBAR_PDF_INFOBAR_CONTROLLER_H_
 
+#include <optional>
+
 #include "base/callback_list.h"
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/shell_integration.h"
@@ -30,9 +32,12 @@
   explicit PdfInfoBarController(BrowserWindowInterface* browser);
   ~PdfInfoBarController() override;
 
-  // If the PDF-infobar experiment is enabled and should be shown at startup,
-  // shows the PDF infobar.
-  void ShowAtStartup();
+  // Enables the PDF infobar to show only if `default_browser_prompt_shown` is
+  // false. If the PDF-infobar experiment is enabled and should be shown at
+  // startup, shows the infobar for `startup_browser`.
+  static void MaybeShowInfoBarAtStartup(
+      base::WeakPtr<BrowserWindowInterface> startup_browser,
+      bool default_browser_prompt_shown);
 
   // Callback passed to `BrowserWindowInterface::RegisterActiveTabDidChange()`.
   void OnActiveTabChanged(BrowserWindowInterface* browser);
@@ -52,10 +57,15 @@
   //   the default PDF viewer
   // * the PDF viewer is enabled in settings
   // * setting Chrome as default isn't forbidden by policy
+  // * the infobar wasn't shown recently or the max number of times
+  // * the default-browser prompt wasn't shown in this session
   // Exposed for testing.
   void MaybeShowInfoBarCallback(
       shell_integration::DefaultWebClientState default_state);
 
+  static void SetDefaultBrowserPromptShownForTesting(
+      bool default_browser_prompt_shown);
+
  private:
   // Asynchronously checks if Chrome is the default PDF viewer, and calls
   // `MaybeShowInfoBarCallback()` with the result.
@@ -76,6 +86,11 @@
 
   // Must be the last member variable.
   base::WeakPtrFactory<PdfInfoBarController> weak_factory_{this};
+
+  // True if the default-browser prompt has been shown in this session, false
+  // if it was not shown. Has no value if the default-browser prompt is still
+  // deciding whether to appear.
+  static std::optional<bool> default_browser_prompt_shown_;
 };
 
 #endif  // CHROME_BROWSER_UI_PDF_INFOBAR_PDF_INFOBAR_CONTROLLER_H_
diff --git a/chrome/browser/ui/pdf/infobar/pdf_infobar_controller_unittest.cc b/chrome/browser/ui/pdf/infobar/pdf_infobar_controller_unittest.cc
index be1e1e8..6e655bf 100644
--- a/chrome/browser/ui/pdf/infobar/pdf_infobar_controller_unittest.cc
+++ b/chrome/browser/ui/pdf/infobar/pdf_infobar_controller_unittest.cc
@@ -144,6 +144,7 @@
   void SetUp() override {
     feature_list().InitAndEnableFeatureWithParameters(features::kPdfInfoBar,
                                                       {{"trigger", "startup"}});
+    PdfInfoBarController::SetDefaultBrowserPromptShownForTesting(false);
     PdfInfoBarControllerTest::SetUp();
   }
 };
@@ -189,3 +190,11 @@
       DidShowInfoBar(BrowserWindowInterface::Type::TYPE_NORMAL,
                      shell_integration::DefaultWebClientState::NOT_DEFAULT));
 }
+
+TEST_F(PdfInfoBarControllerStartupTest,
+       DontShowInfoBarIfDefaultBrowserPromptShown) {
+  PdfInfoBarController::SetDefaultBrowserPromptShownForTesting(true);
+  EXPECT_FALSE(
+      DidShowInfoBar(BrowserWindowInterface::Type::TYPE_NORMAL,
+                     shell_integration::DefaultWebClientState::NOT_DEFAULT));
+}
diff --git a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.cc b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.cc
index d43cd67..ccf663c 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.cc
@@ -78,7 +78,9 @@
 
 void OnCheckIsDefaultBrowserFinished(
     Profile* profile,
+    base::OnceCallback<void(bool)> done_callback,
     shell_integration::DefaultWebClientState state) {
+  bool did_show_prompt = false;
   if (state == shell_integration::IS_DEFAULT) {
     // Notify the user in the future if Chrome ceases to be the user's chosen
     // default browser.
@@ -91,8 +93,10 @@
     // Only show the prompt if some other program is the user's default browser.
     // In particular, don't show it if another install mode is default (e.g.,
     // don't prompt for Chrome Beta if stable Chrome is the default).
-    DefaultBrowserPromptManager::GetInstance()->MaybeShowPrompt();
+    did_show_prompt =
+        DefaultBrowserPromptManager::GetInstance()->MaybeShowPrompt();
   }
+  std::move(done_callback).Run(did_show_prompt);
 }
 
 }  // namespace
@@ -145,7 +149,8 @@
   }
 }
 
-void ShowDefaultBrowserPrompt(Profile* profile) {
+void ShowDefaultBrowserPrompt(Profile* profile,
+                              base::OnceCallback<void(bool)> done_callback) {
   // Do not check if Chrome is the default browser if there is a policy in
   // control of this setting.
   if (g_browser_process->local_state()->IsManagedPreference(
@@ -157,8 +162,8 @@
 
   scoped_refptr<shell_integration::DefaultBrowserWorker>(
       new shell_integration::DefaultBrowserWorker())
-      ->StartCheckIsDefault(
-          base::BindOnce(&OnCheckIsDefaultBrowserFinished, profile));
+      ->StartCheckIsDefault(base::BindOnce(&OnCheckIsDefaultBrowserFinished,
+                                           profile, std::move(done_callback)));
 }
 
 void ShowPromptForTesting() {
diff --git a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.h b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.h
index 945f600..fb9e6fd4 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.h
+++ b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_STARTUP_DEFAULT_BROWSER_PROMPT_DEFAULT_BROWSER_PROMPT_H_
 #define CHROME_BROWSER_UI_STARTUP_DEFAULT_BROWSER_PROMPT_DEFAULT_BROWSER_PROMPT_H_
 
+#include "base/functional/callback_forward.h"
+
 class PrefRegistrySimple;
 class PrefService;
 class Profile;
@@ -15,8 +17,10 @@
 // time local pref.
 void MigrateDefaultBrowserLastDeclinedPref(PrefService* profile_prefs);
 
-// Shows a prompt UI to set the default browser if necessary.
-void ShowDefaultBrowserPrompt(Profile* profile);
+// Shows a prompt UI to set the default browser if necessary. Passes a bool
+// indicating whether or not the prompt was shown to `done_callback` when done.
+void ShowDefaultBrowserPrompt(Profile* profile,
+                              base::OnceCallback<void(bool)> done_callback);
 
 // Only used within tests to confirm the behavior of the default browser prompt.
 void ShowPromptForTesting();
diff --git a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc
index 91edc60..14f286e 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.cc
@@ -77,14 +77,14 @@
   browser_tab_strip_tracker_->Init();
 }
 
-void DefaultBrowserPromptManager::MaybeShowPrompt() {
+bool DefaultBrowserPromptManager::MaybeShowPrompt() {
 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
   NOTREACHED() << "Unsupported platforms for showing default browser prompts.";
 #else
   SetAppMenuItemVisibility(true);
 
   if (!ShouldShowPrompts()) {
-    return;
+    return false;
   }
 
 #if BUILDFLAG(IS_WIN)
@@ -98,11 +98,12 @@
         ShellUtil::GetBrowserModelId(InstallUtil::IsPerUserInstall()),
         base::BindOnce(&DefaultBrowserPromptManager::OnCanPinToTaskbarResult,
                        base::Unretained(this)));
-    return;
+    return true;
   }
 #endif  // BUILDFLAG(IS_WIN)
 
   InitTabStripTracker();
+  return true;
 #endif  // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
 }
 
diff --git a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.h b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.h
index d85a4d8a..9b1933a 100644
--- a/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.h
+++ b/chrome/browser/ui/startup/default_browser_prompt/default_browser_prompt_manager.h
@@ -43,7 +43,8 @@
   // This will trigger the showing of the info bar.
   void InitTabStripTracker();
 
-  void MaybeShowPrompt();
+  // Returns true if the prompt was shown, false if not.
+  bool MaybeShowPrompt();
 
   void CloseAllPrompts(CloseReason close_reason);
 
diff --git a/chrome/browser/ui/startup/infobar_utils.cc b/chrome/browser/ui/startup/infobar_utils.cc
index e9c6699cc..d953dc89 100644
--- a/chrome/browser/ui/startup/infobar_utils.cc
+++ b/chrome/browser/ui/startup/infobar_utils.cc
@@ -178,19 +178,20 @@
 #if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
     if (!is_web_app &&
         !startup_command_line.HasSwitch(switches::kNoDefaultBrowserCheck)) {
+      base::OnceCallback<void(bool)> default_browser_prompt_shown_callback =
+          base::DoNothing();
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+      if (base::FeatureList::IsEnabled(features::kPdfInfoBar)) {
+        default_browser_prompt_shown_callback =
+            base::BindOnce(&PdfInfoBarController::MaybeShowInfoBarAtStartup,
+                           browser->GetWeakPtr());
+      }
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+
       // The default browser prompt should only be shown after the first run.
       if (is_first_run == chrome::startup::IsFirstRun::kNo) {
-        ShowDefaultBrowserPrompt(profile);
-
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-        // TODO(crbug.com/396202897): don't show the PDF infobar if the default-
-        // browser infobar is showing.
-        if (base::FeatureList::IsEnabled(features::kPdfInfoBar)) {
-          browser->browser_window_features()
-              ->pdf_infobar_controller()
-              ->ShowAtStartup();
-        }
-#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+        ShowDefaultBrowserPrompt(
+            profile, std::move(default_browser_prompt_shown_callback));
       }
     }
 #endif
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc
index 3be020b..1e8fc26 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.cc
@@ -334,12 +334,6 @@
   NOTIMPLEMENTED();
 }
 
-void TabGroupSyncServiceProxy::UpdateTabLastSeenTime(const base::Uuid& group_id,
-                                                     const base::Uuid& tab_id,
-                                                     TriggerSource source) {
-  NOTIMPLEMENTED();
-}
-
 TabGroupSyncMetricsLogger*
 TabGroupSyncServiceProxy::GetTabGroupSyncMetricsLogger() {
   return service_->GetTabGroupSyncMetricsLogger();
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h
index 23144ecf..9ca52818 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h
+++ b/chrome/browser/ui/tabs/saved_tab_groups/tab_group_sync_service_proxy.h
@@ -119,10 +119,6 @@
   void RecordTabGroupEvent(const EventDetails& event_details) override;
   void UpdateArchivalStatus(const base::Uuid& sync_id,
                             bool archival_status) override;
-  void UpdateTabLastSeenTime(const base::Uuid& group_id,
-                             const base::Uuid& tab_id,
-                             TriggerSource source) override;
-
   TabGroupSyncMetricsLogger* GetTabGroupSyncMetricsLogger() override;
   base::WeakPtr<syncer::DataTypeControllerDelegate>
   GetSavedTabGroupControllerDelegate() override;
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index 442f8035..d3f7236 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -190,12 +190,6 @@
           std::make_unique<tab_groups::CollaborationMessagingTabData>(profile);
     }
 
-    if (base::FeatureList::IsEnabled(passage_embeddings::kPassageEmbedder)) {
-      embedder_tab_observer_ =
-          std::make_unique<passage_embeddings::EmbedderTabObserver>(
-              tab.GetContents());
-    }
-
 #if BUILDFLAG(ENABLE_GLIC)
     if (glic::GlicEnabling::IsProfileEligible(
             tab.GetBrowserWindowInterface()->GetProfile())) {
diff --git a/chrome/browser/ui/views/accelerator_table.cc b/chrome/browser/ui/views/accelerator_table.cc
index 24b7a24..6db32fe 100644
--- a/chrome/browser/ui/views/accelerator_table.cc
+++ b/chrome/browser/ui/views/accelerator_table.cc
@@ -60,9 +60,6 @@
     {ui::VKEY_G, ui::EF_PLATFORM_ACCELERATOR, IDC_FIND_NEXT},
     {ui::VKEY_G, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR,
      IDC_FIND_PREVIOUS},
-#if !BUILDFLAG(IS_CHROMEOS)
-    {ui::VKEY_G, ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN, IDC_GLIC_TOGGLE_FOCUS},
-#endif
     {ui::VKEY_L, ui::EF_PLATFORM_ACCELERATOR, IDC_FOCUS_LOCATION},
     {ui::VKEY_O, ui::EF_PLATFORM_ACCELERATOR, IDC_OPEN_FILE},
     {ui::VKEY_P, ui::EF_PLATFORM_ACCELERATOR, IDC_PRINT},
diff --git a/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.cc b/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.cc
index 8f67a2b..00cf41e 100644
--- a/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.cc
+++ b/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.cc
@@ -45,6 +45,11 @@
   layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(size.height() / 2));
 }
 
+std::u16string BnplLinkedIssuerPill::GetAccessibilityDescription() {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_CARD_BNPL_LINKED_ISSUER_PILL_LABEL);
+}
+
 BEGIN_METADATA(BnplLinkedIssuerPill)
 END_METADATA
 
diff --git a/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.h b/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.h
index 7d3e287..2e0deed 100644
--- a/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.h
+++ b/chrome/browser/ui/views/autofill/payments/bnpl_issuer_linked_pill.h
@@ -22,6 +22,10 @@
 
   // views::View overrides.
   void AddedToWidget() override;
+
+  // Returns the view description for accessibility message, which will be read
+  // when this view is focused by a screenreader.
+  std::u16string GetAccessibilityDescription();
 };
 
 BEGIN_VIEW_BUILDER(, BnplLinkedIssuerPill, views::Label)
diff --git a/chrome/browser/ui/views/autofill/payments/bnpl_issuer_view.cc b/chrome/browser/ui/views/autofill/payments/bnpl_issuer_view.cc
index 96a6ec2..16d8df3 100644
--- a/chrome/browser/ui/views/autofill/payments/bnpl_issuer_view.cc
+++ b/chrome/browser/ui/views/autofill/payments/bnpl_issuer_view.cc
@@ -30,6 +30,7 @@
 #include "ui/events/event.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/views/accessibility/view_accessibility.h"
 #include "ui/views/animation/ink_drop.h"
 #include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_host.h"
@@ -155,6 +156,8 @@
                                    views::DISTANCE_RELATED_BUTTON_HORIZONTAL),
                                0, 0))
               .Build());
+      issuer_button->GetViewAccessibility().SetDescription(
+          linked_pill->GetAccessibilityDescription());
     }
     issuer_button->AddChildView(
         views::Builder<views::ImageView>()
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
index eccb250..031e708a 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
@@ -276,7 +276,7 @@
   void SetExtensionIconVisibility(ToolbarActionsModel::ActionId id,
                                   bool visible);
 
-  // Returns whether the contianer should be showing, e.g. not if there are no
+  // Returns whether the container should be showing, e.g. not if there are no
   // extensions installed, nor if the container is inactive in kAutoHide mode.
   bool ShouldContainerBeVisible() const;
 
@@ -326,13 +326,14 @@
   std::unique_ptr<ToolbarActionHoverCardController>
       action_hover_card_controller_;
 
-  // TODO(pbos): Create actions and icons only for pinned pinned / popped out
-  // actions (lazily). Currently code expects GetActionForId() to return
-  // actions for extensions that aren't visible.
+  // TODO(pbos): Create actions and icons only for pinned / popped out actions
+  // (lazily). Currently code expects GetActionForId() to return actions for
+  // extensions that aren't visible.
   // Actions for all extensions.
   std::vector<std::unique_ptr<ToolbarActionViewController>> actions_;
   // View for every action, does not imply pinned or currently shown.
   ToolbarIcons icons_;
+
   // Popped-out extension, if any.
   std::optional<extensions::ExtensionId> popped_out_action_;
   // The action that triggered the current popup, if any.
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 2348b4c..8670c81 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -857,10 +857,6 @@
     return controller->find_bar()->MoveWindowIfNecessary();
   }
 
-  bool IsWindowControlsOverlayEnabled() const override {
-    return browser_view_->IsWindowControlsOverlayEnabled();
-  }
-
   void UpdateWindowControlsOverlay(
       const gfx::Rect& available_titlebar_area) override {
     content::WebContents* web_contents = browser_view_->GetActiveWebContents();
@@ -2708,7 +2704,11 @@
   // When Window Controls Overlay is enabled or disabled, the browser window
   // needs to be re-layed out to make sure the title bar and web contents appear
   // in the correct locations.
-  InvalidateLayout();
+  auto* browser_view_layout = GetBrowserViewLayout();
+  if (browser_view_layout) {
+    browser_view_layout->SetWindowControlsOverlayEnabled(
+        window_controls_overlay_enabled_);
+  }
 
   const std::u16string& state_change_text =
       IsWindowControlsOverlayEnabled()
@@ -5229,6 +5229,8 @@
           immersive_mode_controller_.get(), contents_separator_));
   browser_view_layout->SetUseBrowserContentMinimumSize(
       ShouldUseBrowserContentMinimumSize());
+  browser_view_layout->SetWindowControlsOverlayEnabled(
+      IsWindowControlsOverlayEnabled());
 
   EnsureFocusOrder();
 
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.cc b/chrome/browser/ui/views/frame/browser_view_layout.cc
index 39fc0f2..9c3bd14 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout.cc
@@ -232,6 +232,16 @@
   InvalidateLayout();
 }
 
+void BrowserViewLayout::SetWindowControlsOverlayEnabled(
+    bool window_controls_overlay_enabled) {
+  if (window_controls_overlay_enabled_ == window_controls_overlay_enabled) {
+    return;
+  }
+
+  window_controls_overlay_enabled_ = window_controls_overlay_enabled;
+  InvalidateLayout();
+}
+
 WebContentsModalDialogHost* BrowserViewLayout::GetWebContentsModalDialogHost() {
   return dialog_host_.get();
 }
@@ -395,7 +405,7 @@
   // Make sure `top_container_` is after `contents_container_` in paint order
   // when this is a window using WindowControlsOverlay, to make sure the window
   // controls are in fact drawn on top of the web contents.
-  if (delegate_->IsWindowControlsOverlayEnabled()) {
+  if (window_controls_overlay_enabled_) {
     auto top_container_iter = std::ranges::find(result, top_container_);
     auto contents_container_iter =
         std::ranges::find(result, contents_container_);
@@ -444,7 +454,7 @@
     return top;
   }
 
-  if (delegate_->IsWindowControlsOverlayEnabled()) {
+  if (window_controls_overlay_enabled_) {
     web_app_frame_toolbar_->LayoutForWindowControlsOverlay(toolbar_bounds);
     toolbar_bounds.Subtract(web_app_frame_toolbar_->bounds());
     delegate_->UpdateWindowControlsOverlay(toolbar_bounds);
diff --git a/chrome/browser/ui/views/frame/browser_view_layout.h b/chrome/browser/ui/views/frame/browser_view_layout.h
index a4602bd..35314900 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout.h
+++ b/chrome/browser/ui/views/frame/browser_view_layout.h
@@ -88,6 +88,7 @@
   views::Widget* contents_border_widget() { return contents_border_widget_; }
 
   void SetUseBrowserContentMinimumSize(bool use_browser_content_minimum_size);
+  void SetWindowControlsOverlayEnabled(bool window_controls_overlay_enabled);
 
   // Sets the bounds for the contents border.
   // * If nullopt, no specific bounds are set, and the border will be drawn
@@ -222,6 +223,9 @@
 
   // Whether or not to use the browser based content minimum size.
   bool use_browser_content_minimum_size_ = false;
+
+  // Whether or not window controls overlay is enabled.
+  bool window_controls_overlay_enabled_ = false;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_VIEW_LAYOUT_H_
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_delegate.h b/chrome/browser/ui/views/frame/browser_view_layout_delegate.h
index 9f393a42..53cdcff 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_delegate.h
+++ b/chrome/browser/ui/views/frame/browser_view_layout_delegate.h
@@ -38,7 +38,6 @@
   virtual gfx::NativeView GetHostViewForAnchoring() const = 0;
   virtual bool HasFindBarController() const = 0;
   virtual void MoveWindowForFindBarIfNecessary() const = 0;
-  virtual bool IsWindowControlsOverlayEnabled() const = 0;
   virtual void UpdateWindowControlsOverlay(
       const gfx::Rect& available_titlebar_area) = 0;
   virtual bool ShouldLayoutTabStrip() const = 0;
diff --git a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
index 5a8bec0a..033630d 100644
--- a/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_layout_unittest.cc
@@ -97,7 +97,6 @@
   }
   bool HasFindBarController() const override { return false; }
   void MoveWindowForFindBarIfNecessary() const override {}
-  bool IsWindowControlsOverlayEnabled() const override { return false; }
   void UpdateWindowControlsOverlay(const gfx::Rect& rect) override {}
   bool ShouldLayoutTabStrip() const override { return true; }
   int GetExtraInfobarOffset() const override { return 0; }
diff --git a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
index 062ea76..a1825d6 100644
--- a/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
+++ b/chrome/browser/ui/views/side_panel/extensions/extension_side_panel_coordinator.cc
@@ -284,7 +284,7 @@
   // Triggers actual image loading with all supported scale factors.
   // TODO(crbug.com/40910886): This is a temporary fix since the combobox and
   // its drop down menu currently do not automatically get an image's
-  // representation when they are shown. Remove this when the aforementioend
+  // representation when they are shown. Remove this when the aforementioned
   // crbug has been fixed.
   extension_icon_->image_skia().EnsureRepsForSupportedScales();
 }
diff --git a/chrome/browser/ui/views/tabs/tab_group_header.cc b/chrome/browser/ui/views/tabs/tab_group_header.cc
index 6a20061..9c23455 100644
--- a/chrome/browser/ui/views/tabs/tab_group_header.cc
+++ b/chrome/browser/ui/views/tabs/tab_group_header.cc
@@ -460,13 +460,20 @@
       is_collapsed ? l10n_util::GetStringUTF16(IDS_GROUP_AX_LABEL_COLLAPSED)
                    : l10n_util::GetStringUTF16(IDS_GROUP_AX_LABEL_EXPANDED);
 #endif
+
+  const std::u16string& shared_state =
+      should_show_header_icon_
+          ? l10n_util::GetStringUTF16(IDS_SAVED_GROUP_AX_LABEL_SHARED)
+          : u"";
+
   if (title.empty()) {
-    GetViewAccessibility().SetName(l10n_util::GetStringFUTF16(
-        IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT, contents, collapsed_state));
-  } else {
     GetViewAccessibility().SetName(
-        l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT, title,
-                                   contents, collapsed_state));
+        l10n_util::GetStringFUTF16(IDS_GROUP_AX_LABEL_UNNAMED_GROUP_FORMAT,
+                                   shared_state, contents, collapsed_state));
+  } else {
+    GetViewAccessibility().SetName(l10n_util::GetStringFUTF16(
+        IDS_GROUP_AX_LABEL_NAMED_GROUP_FORMAT, shared_state, title, contents,
+        collapsed_state));
   }
 }
 
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 10690b88..3bd9304 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -199,9 +199,6 @@
   source->AddInteger(
       "multipleLoadedModulesMaxModuleInstanceCount",
       ntp_features::GetMultipleLoadedModulesMaxModuleInstanceCount());
-  source->AddBoolean("mostRelevantTabResumptionEnabled",
-                     IsEnUSLocaleOnlyFeatureEnabled(
-                         ntp_features::kNtpMostRelevantTabResumptionModule));
   source->AddBoolean(
       "mostRelevantTabResumptionAllowFaviconServerFallback",
       base::FeatureList::IsEnabled(
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 9e16a79..3eaa1b39 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -470,6 +470,7 @@
 }
 
 void AboutHandler::HandleRefreshUpdateStatus(const base::Value::List& args) {
+  AllowJavascript();
   RefreshUpdateStatus();
 }
 
diff --git a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks.mojom b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks.mojom
index 683d44d5..f1d68e2 100644
--- a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks.mojom
+++ b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks.mojom
@@ -118,6 +118,10 @@
   // the same logic as the native bookmarks context menu.
   ExecuteOpenInNewTabGroupCommand(array<int64> node_ids, ActionSource source);
 
+  // Opens the bookmarks specified by node_ids in a new split view, using
+  // the same logic as the native bookmarks context menu.
+  ExecuteOpenInSplitViewCommand(array<int64> node_ids, ActionSource source);
+
   // Moves bookmark specified by node_id to be a direct descendant of the
   // Bookmarks Bar folder, using the same logic as the native bookmarks context
   // menu.
diff --git a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.cc b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.cc
index 482a0871..d8d37ff 100644
--- a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.cc
@@ -9,6 +9,7 @@
 #include <optional>
 
 #include "base/check_is_test.h"
+#include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/user_metrics.h"
@@ -39,6 +40,7 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/chrome_pages.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/bookmarks/bookmark_prefs.h"
 #include "chrome/browser/ui/webui/commerce/shopping_list_context_menu_controller.h"
 #include "chrome/browser/ui/webui/side_panel/bookmarks/bookmarks.mojom.h"
@@ -99,6 +101,10 @@
       AddItem(IDC_BOOKMARK_BAR_OPEN_ALL);
       AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW);
       AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO);
+      if (bookmarks.size() == 1 && bookmarks.front()->is_url() &&
+          base::FeatureList::IsEnabled(features::kSideBySide)) {
+        AddItem(IDC_BOOKMARK_BAR_OPEN_SPLIT_VIEW);
+      }
       AddSeparator(ui::NORMAL_SEPARATOR);
       shopping_list_controller_->AddPriceTrackingItemForBookmark(
           this, bookmarks.front());
@@ -110,6 +116,10 @@
     AddItem(IDC_BOOKMARK_BAR_OPEN_ALL);
     AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW);
     AddItem(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO);
+    if (bookmarks.size() == 1 && bookmarks.front()->is_url() &&
+        base::FeatureList::IsEnabled(features::kSideBySide)) {
+      AddItem(IDC_BOOKMARK_BAR_OPEN_SPLIT_VIEW);
+    }
     AddSeparator(ui::NORMAL_SEPARATOR);
 
     AddItem(bookmarks.size() == 1 && bookmarks.front()->is_folder()
@@ -460,6 +470,13 @@
                             IDC_BOOKMARK_BAR_OPEN_ALL_NEW_TAB_GROUP);
 }
 
+void BookmarksPageHandler::ExecuteOpenInSplitViewCommand(
+    const std::vector<int64_t>& node_ids,
+    side_panel::mojom::ActionSource source) {
+  CHECK(base::FeatureList::IsEnabled(features::kSideBySide));
+  ExecuteContextMenuCommand(node_ids, source, IDC_BOOKMARK_BAR_OPEN_SPLIT_VIEW);
+}
+
 void BookmarksPageHandler::ExecuteEditCommand(
     const std::vector<int64_t>& node_ids,
     side_panel::mojom::ActionSource source) {
diff --git a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.h b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.h
index 5f316b3..d42b74f 100644
--- a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_page_handler.h
@@ -53,6 +53,9 @@
   void ExecuteOpenInNewTabGroupCommand(
       const std::vector<int64_t>& node_ids,
       side_panel::mojom::ActionSource source) override;
+  void ExecuteOpenInSplitViewCommand(
+      const std::vector<int64_t>& node_ids,
+      side_panel::mojom::ActionSource source) override;
   void ExecuteEditCommand(const std::vector<int64_t>& node_ids,
                           side_panel::mojom::ActionSource source) override;
   void ExecuteMoveCommand(const std::vector<int64_t>& node_ids,
diff --git a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
index d899d66fa..6ca0e85 100644
--- a/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/bookmarks/bookmarks_side_panel_ui.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/check_is_test.h"
+#include "base/feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/bookmarks/bookmark_merged_surface_service_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
@@ -148,6 +149,7 @@
       {"menuOpenNewTabGroup", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_TAB_GROUP},
       {"menuOpenNewTabGroupWithCount",
        IDS_BOOKMARK_MANAGER_MENU_OPEN_ALL_NEW_TAB_GROUP_WITH_COUNT},
+      {"menuOpenSplitView", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_SPLIT_VIEW},
       {"menuMoveToBookmarksBar", IDS_BOOKMARKS_MOVE_TO_BOOKMARKS_BAR},
       {"menuMoveToAllBookmarks", IDS_BOOKMARKS_MOVE_TO_ALL_BOOKMARKS},
       {"menuTrackPrice", IDS_SIDE_PANEL_TRACK_BUTTON},
@@ -210,6 +212,8 @@
   source->AddBoolean(
       "bookmarksTreeViewEnabled",
       base::FeatureList::IsEnabled(features::kBookmarksTreeView));
+  source->AddBoolean("splitViewEnabled",
+                     base::FeatureList::IsEnabled(features::kSideBySide));
   // TODO(crbug.com/380818698): Replace this with the flag which will be used to
   // launch account storage for bookmarks.
   source->AddBoolean("isBookmarksInTransportModeEnabled",
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
index 2781efa..1f55266e 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -906,8 +906,9 @@
   const std::string& language_code =
       (code.empty() || code == language_detection::kUnknownLanguageCode) ? ""
                                                                          : code;
-  // Only send the language code if it's a new language.
-  if (language_code != current_language_code_) {
+  // Only send the language code if it's a new language, unless it's an empty
+  // code. Always send an empty code so we know to use the tree language.
+  if (language_code.empty() || (language_code != current_language_code_)) {
     current_language_code_ = language_code;
     page_->SetLanguageCode(current_language_code_);
   }
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
index 61d0d83..5fd3323 100644
--- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler_unittest.cc
@@ -748,6 +748,19 @@
   EXPECT_CALL(page_, SetLanguageCode("")).Times(1);
 }
 
+TEST_F(ReadAnythingUntrustedPageHandlerTest,
+       OnLanguageDetermined_UnknownLanguageSendsEmptyEveryTime) {
+  handler_ = std::make_unique<TestReadAnythingUntrustedPageHandler>(
+      page_.BindAndGetRemote(), test_web_ui_.get());
+  EXPECT_CALL(page_, SetLanguageCode).Times(1);
+
+  OnLanguageDetermined(language_detection::kUnknownLanguageCode);
+  OnLanguageDetermined(language_detection::kUnknownLanguageCode);
+  OnLanguageDetermined(language_detection::kUnknownLanguageCode);
+
+  EXPECT_CALL(page_, SetLanguageCode("")).Times(3);
+}
+
 TEST_F(ReadAnythingUntrustedPageHandlerTest, AccessibilityEventReceived) {
   ui::AXUpdatesAndEvents details;
   details.events = {};
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index 00650302..c63d5b02 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -391,8 +391,8 @@
       "chromeos_web_app_experiments.h",
       "commands/install_app_from_verified_manifest_command.cc",
       "commands/install_app_from_verified_manifest_command.h",
-      "isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.cc",
-      "isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h",
+      "isolated_web_apps/commands/cleanup_bundle_cache_command.cc",
+      "isolated_web_apps/commands/cleanup_bundle_cache_command.h",
       "isolated_web_apps/policy/isolated_web_app_cache_client.cc",
       "isolated_web_apps/policy/isolated_web_app_cache_client.h",
       "os_integration/web_app_run_on_os_login_chromeos.cc",
@@ -972,7 +972,7 @@
   if (is_chromeos) {
     sources += [
       "chromeos_web_app_experiments_unittest.cc",
-      "isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command_unittest.cc",
+      "isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc",
       "isolated_web_apps/policy/isolated_web_app_cache_client_unittest.cc",
       "os_integration/web_app_run_on_os_login_chromeos_unittest.cc",
       "web_app_run_on_os_login_manager_unittest.cc",
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.cc b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.cc
similarity index 67%
rename from chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.cc
rename to chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.cc
index 06aad74..06a887c 100644
--- a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h"
 
 #include <string>
 #include <vector>
@@ -22,8 +22,8 @@
 namespace {
 
 // This function is blocking, should be called only by
-// `CleanupCacheForManagedGuestSessionCommand::StartWithLock`.
-CleanupCacheForManagedGuestSessionResult CleanupCacheForManagedGuestSessionImpl(
+// `CleanupBundleCacheCommand::StartWithLock`.
+CleanupBundleCacheResult CleanupBundleCacheImpl(
     const std::vector<web_package::SignedWebBundleId>& iwas_to_keep_in_cache) {
   const base::FilePath cache_dir = GetManagedGuestSessionBundleCacheDirectory();
 
@@ -53,24 +53,22 @@
     }
   }
   if (failed_to_cleaned_up_directories == 0) {
-    return base::ok(
-        CleanupCacheForManagedGuestSessionSuccess{dirs_to_delete.size()});
+    return base::ok(CleanupBundleCacheSuccess{dirs_to_delete.size()});
   }
 
-  return base::unexpected(CleanupCacheForManagedGuestSessionError{
-      CleanupCacheForManagedGuestSessionError::Type::kCouldNotDeleteAllBundles,
+  return base::unexpected(CleanupBundleCacheError{
+      CleanupBundleCacheError::Type::kCouldNotDeleteAllBundles,
       failed_to_cleaned_up_directories});
 }
 
 std::string CleanupCacheForManagedGuestSessionCommandErrorToString(
-    const CleanupCacheForManagedGuestSessionError& error) {
+    const CleanupBundleCacheError& error) {
   switch (error.type()) {
-    case CleanupCacheForManagedGuestSessionError::Type::
-        kCouldNotDeleteAllBundles:
+    case CleanupBundleCacheError::Type::kCouldNotDeleteAllBundles:
       return "Could not delete bundles, number of failed directories: " +
              base::NumberToString(
                  error.number_of_failed_to_cleaned_up_directories());
-    case CleanupCacheForManagedGuestSessionError::Type::kSystemShutdown:
+    case CleanupBundleCacheError::Type::kSystemShutdown:
       return "System is shutting down";
   }
 }
@@ -81,26 +79,23 @@
   return IsIwaBundleCacheEnabled() && chromeos::IsManagedGuestSession();
 }
 
-CleanupCacheForManagedGuestSessionCommand::
-    CleanupCacheForManagedGuestSessionCommand(
-        const std::vector<web_package::SignedWebBundleId>&
-            iwas_to_keep_in_cache,
-        Callback callback)
-    : WebAppCommand<AllAppsLock, CleanupCacheForManagedGuestSessionResult>(
+CleanupBundleCacheCommand::CleanupBundleCacheCommand(
+    const std::vector<web_package::SignedWebBundleId>& iwas_to_keep_in_cache,
+    Callback callback)
+    : WebAppCommand<AllAppsLock, CleanupBundleCacheResult>(
           "CleanupCacheForManagedGuestSessionCommand",
           AllAppsLockDescription(),
           std::move(callback),
           /*args_for_shutdown=*/
-          base::unexpected(CleanupCacheForManagedGuestSessionError{
-              CleanupCacheForManagedGuestSessionError::Type::kSystemShutdown})),
+          base::unexpected(CleanupBundleCacheError{
+              CleanupBundleCacheError::Type::kSystemShutdown})),
       iwas_to_keep_in_cache_(iwas_to_keep_in_cache) {
   CHECK(ShouldCleanupManagedGuestSessionCache());
 }
 
-CleanupCacheForManagedGuestSessionCommand::
-    ~CleanupCacheForManagedGuestSessionCommand() = default;
+CleanupBundleCacheCommand::~CleanupBundleCacheCommand() = default;
 
-void CleanupCacheForManagedGuestSessionCommand::StartWithLock(
+void CleanupBundleCacheCommand::StartWithLock(
     std::unique_ptr<AllAppsLock> lock) {
   CHECK(lock);
   lock_ = std::move(lock);
@@ -108,15 +103,13 @@
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE,
       {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
-      base::BindOnce(&CleanupCacheForManagedGuestSessionImpl,
-                     iwas_to_keep_in_cache_),
-      base::BindOnce(
-          &CleanupCacheForManagedGuestSessionCommand::CommandComplete,
-          weak_ptr_factory_.GetWeakPtr()));
+      base::BindOnce(&CleanupBundleCacheImpl, iwas_to_keep_in_cache_),
+      base::BindOnce(&CleanupBundleCacheCommand::CommandComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
-void CleanupCacheForManagedGuestSessionCommand::CommandComplete(
-    const CleanupCacheForManagedGuestSessionResult& result) {
+void CleanupBundleCacheCommand::CommandComplete(
+    const CleanupBundleCacheResult& result) {
   if (!result.has_value()) {
     LOG(ERROR) << "Cleanup cache for Managed Guest Session failed: "
                << CleanupCacheForManagedGuestSessionCommandErrorToString(
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h
new file mode 100644
index 0000000..7c93ceaf
--- /dev/null
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h
@@ -0,0 +1,105 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_CLEANUP_BUNDLE_CACHE_COMMAND_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_CLEANUP_BUNDLE_CACHE_COMMAND_H_
+
+#include "base/types/expected.h"
+#include "chrome/browser/web_applications/commands/web_app_command.h"
+#include "chrome/browser/web_applications/locks/all_apps_lock.h"
+#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
+
+namespace web_app {
+
+// Cache Cleanup should happen only if `IsIwaBundleCacheEnabled` is true and
+// inside the Managed Guest Session.
+bool ShouldCleanupManagedGuestSessionCache();
+
+class CleanupBundleCacheSuccess {
+ public:
+  explicit CleanupBundleCacheSuccess(size_t number_of_cleaned_up_directories)
+      : number_of_cleaned_up_directories_(number_of_cleaned_up_directories) {}
+
+  CleanupBundleCacheSuccess(const CleanupBundleCacheSuccess& other) = default;
+  ~CleanupBundleCacheSuccess() = default;
+
+  bool operator==(const CleanupBundleCacheSuccess& other) const = default;
+
+  size_t number_of_cleaned_up_directories() const {
+    return number_of_cleaned_up_directories_;
+  }
+
+ private:
+  size_t number_of_cleaned_up_directories_ = 0;
+};
+
+class CleanupBundleCacheError {
+ public:
+  enum class Type { kCouldNotDeleteAllBundles, kSystemShutdown };
+
+  explicit CleanupBundleCacheError(
+      Type type,
+      size_t number_of_failed_to_cleaned_up_directories = 0)
+      : type_(type),
+        number_of_failed_to_cleaned_up_directories_(
+            number_of_failed_to_cleaned_up_directories) {}
+
+  CleanupBundleCacheError(const CleanupBundleCacheError& other) = default;
+  ~CleanupBundleCacheError() = default;
+
+  bool operator==(const CleanupBundleCacheError& other) const = default;
+
+  Type type() const { return type_; }
+
+  size_t number_of_failed_to_cleaned_up_directories() const {
+    return number_of_failed_to_cleaned_up_directories_;
+  }
+
+ private:
+  Type type_;
+  // Valid only for `kCouldNotDeleteAllBundles` failure.
+  size_t number_of_failed_to_cleaned_up_directories_ = 0;
+};
+
+using CleanupBundleCacheResult =
+    base::expected<CleanupBundleCacheSuccess, CleanupBundleCacheError>;
+
+// Cleans all IWA cached bundles for Managed Guest Session which are not in
+// the `iwas_to_keep_in_cache`.
+// During the cleanup, this class iterates through all cached directories for
+// Managed Guest Session. To avoid adding new directories during the iteration,
+// this class takes `AllAppsLock`.
+// This command will CHECK that `ShouldCleanupManagedGuestSessionCache` is true.
+// TODO(crbug.com/388729037): rename or update this class to unify with kiosk
+// implementation.
+class CleanupBundleCacheCommand
+    : public WebAppCommand<AllAppsLock, CleanupBundleCacheResult> {
+ public:
+  using Callback = base::OnceCallback<void(CleanupBundleCacheResult)>;
+
+  CleanupBundleCacheCommand(
+      const std::vector<web_package::SignedWebBundleId>& iwas_to_keep_in_cache,
+      Callback callback);
+  CleanupBundleCacheCommand(const CleanupBundleCacheCommand&) = delete;
+  CleanupBundleCacheCommand& operator=(const CleanupBundleCacheCommand&) =
+      delete;
+
+  ~CleanupBundleCacheCommand() override;
+
+ protected:
+  // WebAppCommand:
+  void StartWithLock(std::unique_ptr<AllAppsLock> lock) override;
+
+ private:
+  void CommandComplete(const CleanupBundleCacheResult& result);
+
+  std::unique_ptr<AllAppsLock> lock_;
+  const std::vector<web_package::SignedWebBundleId> iwas_to_keep_in_cache_;
+
+  base::WeakPtrFactory<CleanupBundleCacheCommand> weak_ptr_factory_{this};
+};
+
+}  // namespace web_app
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_CLEANUP_BUNDLE_CACHE_COMMAND_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc
similarity index 83%
rename from chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command_unittest.cc
rename to chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc
index ac9f54c..a7819af 100644
--- a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h"
 
 #include "ash/constants/ash_paths.h"
 #include "base/files/file_util.h"
@@ -28,7 +28,7 @@
 using base::test::TestFuture;
 using base::test::ValueIs;
 using web_package::SignedWebBundleId;
-using CleanupResult = CleanupCacheForManagedGuestSessionResult;
+using CleanupResult = CleanupBundleCacheResult;
 using Callback = base::OnceCallback<void(CleanupResult)>;
 
 const SignedWebBundleId kMainBundleId = test::GetDefaultEd25519WebBundleId();
@@ -37,7 +37,7 @@
 
 }  // namespace
 
-class CleanupCacheForManagedGuestSessionCommandTest : public WebAppTest {
+class CleanupBundleCacheCommandTest : public WebAppTest {
  public:
   void SetUp() override {
     WebAppTest::SetUp();
@@ -103,16 +103,16 @@
       test_managed_guest_session_;
 };
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest, NoBundles) {
+TEST_F(CleanupBundleCacheCommandTest, NoBundles) {
   TestFuture<CleanupResult> cleanup_future;
   ScheduleCommand(/*iwas_to_keep_in_cache*/ {}, cleanup_future.GetCallback());
 
   EXPECT_THAT(cleanup_future.Get(),
-              ValueIs(CleanupCacheForManagedGuestSessionSuccess{
+              ValueIs(CleanupBundleCacheSuccess{
                   /*number_of_cleaned_up_directories=*/0}));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest, KeepTheOnlyApp) {
+TEST_F(CleanupBundleCacheCommandTest, KeepTheOnlyApp) {
   const base::FilePath bundle_path =
       CreateBundleInCacheDir(kMainBundleId, kVersion);
 
@@ -121,12 +121,12 @@
                   cleanup_future.GetCallback());
 
   EXPECT_THAT(cleanup_future.Get(),
-              ValueIs(CleanupCacheForManagedGuestSessionSuccess{
+              ValueIs(CleanupBundleCacheSuccess{
                   /*number_of_cleaned_up_directories=*/0}));
   EXPECT_TRUE(base::PathExists(bundle_path));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest, KeepTwoApps) {
+TEST_F(CleanupBundleCacheCommandTest, KeepTwoApps) {
   const base::FilePath bundle_path1 =
       CreateBundleInCacheDir(kMainBundleId, kVersion);
   const base::FilePath bundle_path2 =
@@ -137,13 +137,13 @@
                   cleanup_future.GetCallback());
 
   EXPECT_THAT(cleanup_future.Get(),
-              ValueIs(CleanupCacheForManagedGuestSessionSuccess{
+              ValueIs(CleanupBundleCacheSuccess{
                   /*number_of_cleaned_up_directories=*/0}));
   EXPECT_TRUE(base::PathExists(bundle_path1));
   EXPECT_TRUE(base::PathExists(bundle_path2));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest, RemoveTheOnlyApp) {
+TEST_F(CleanupBundleCacheCommandTest, RemoveTheOnlyApp) {
   const base::FilePath bundle_path =
       CreateBundleInCacheDir(kMainBundleId, kVersion);
 
@@ -151,12 +151,12 @@
   ScheduleCommand(/*iwas_to_keep_in_cache*/ {}, cleanup_future.GetCallback());
 
   EXPECT_THAT(cleanup_future.Get(),
-              ValueIs(CleanupCacheForManagedGuestSessionSuccess{
+              ValueIs(CleanupBundleCacheSuccess{
                   /*number_of_cleaned_up_directories=*/1}));
   EXPECT_FALSE(base::PathExists(bundle_path));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest, RemoveCorrectBundle) {
+TEST_F(CleanupBundleCacheCommandTest, RemoveCorrectBundle) {
   const base::FilePath bundle_path1 =
       CreateBundleInCacheDir(kMainBundleId, kVersion);
   const base::FilePath bundle_path2 =
@@ -167,24 +167,24 @@
                   cleanup_future.GetCallback());
 
   EXPECT_THAT(cleanup_future.Get(),
-              ValueIs(CleanupCacheForManagedGuestSessionSuccess{
+              ValueIs(CleanupBundleCacheSuccess{
                   /*number_of_cleaned_up_directories=*/1}));
   EXPECT_FALSE(base::PathExists(bundle_path1));
   EXPECT_TRUE(base::PathExists(bundle_path2));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest, IwaNotCached) {
+TEST_F(CleanupBundleCacheCommandTest, IwaNotCached) {
   TestFuture<CleanupResult> cleanup_future;
   ScheduleCommand(/*iwas_to_keep_in_cache*/ {kMainBundleId},
                   cleanup_future.GetCallback());
 
   // `kMainBundleId` is not cached, but it still should finish with success.
   EXPECT_THAT(cleanup_future.Get(),
-              ValueIs(CleanupCacheForManagedGuestSessionSuccess{
+              ValueIs(CleanupBundleCacheSuccess{
                   /*number_of_cleaned_up_directories=*/0}));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest, FailedToDeleteOneDir) {
+TEST_F(CleanupBundleCacheCommandTest, FailedToDeleteOneDir) {
   const base::FilePath bundle_path =
       CreateBundleInCacheDir(kMainBundleId, kVersion);
   const base::FilePath bundle_dir = GetBundleDir(kMainBundleId);
@@ -196,15 +196,13 @@
   ScheduleCommand(/*iwas_to_keep_in_cache*/ {}, cleanup_future.GetCallback());
 
   EXPECT_THAT(cleanup_future.Get(),
-              ErrorIs(CleanupCacheForManagedGuestSessionError{
-                  CleanupCacheForManagedGuestSessionError::Type::
-                      kCouldNotDeleteAllBundles,
+              ErrorIs(CleanupBundleCacheError{
+                  CleanupBundleCacheError::Type::kCouldNotDeleteAllBundles,
                   /*number_of_failed_to_cleaned_up_directories=*/1}));
   EXPECT_TRUE(base::PathExists(bundle_path));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest,
-       FailedToDeleteMultipleDirs) {
+TEST_F(CleanupBundleCacheCommandTest, FailedToDeleteMultipleDirs) {
   const base::FilePath bundle_path1 =
       CreateBundleInCacheDir(kMainBundleId, kVersion);
   const base::FilePath bundle_dir1 = GetBundleDir(kMainBundleId);
@@ -220,9 +218,8 @@
   ScheduleCommand(/*iwas_to_keep_in_cache*/ {}, cleanup_future.GetCallback());
 
   ASSERT_FALSE(cleanup_future.Get().has_value());
-  EXPECT_THAT(
-      cleanup_future.Get().error().type(),
-      CleanupCacheForManagedGuestSessionError::Type::kCouldNotDeleteAllBundles);
+  EXPECT_THAT(cleanup_future.Get().error().type(),
+              CleanupBundleCacheError::Type::kCouldNotDeleteAllBundles);
   EXPECT_THAT(
       cleanup_future.Get().error().number_of_failed_to_cleaned_up_directories(),
       2);
@@ -230,8 +227,7 @@
   EXPECT_TRUE(base::PathExists(bundle_path2));
 }
 
-TEST_F(CleanupCacheForManagedGuestSessionCommandTest,
-       PartiallyFailedToDeleteDirs) {
+TEST_F(CleanupBundleCacheCommandTest, PartiallyFailedToDeleteDirs) {
   const base::FilePath bundle_path1 =
       CreateBundleInCacheDir(kMainBundleId, kVersion);
   const base::FilePath bundle_dir1 = GetBundleDir(kMainBundleId);
@@ -246,9 +242,8 @@
   ScheduleCommand(/*iwas_to_keep_in_cache*/ {}, cleanup_future.GetCallback());
 
   EXPECT_THAT(cleanup_future.Get(),
-              ErrorIs(CleanupCacheForManagedGuestSessionError{
-                  CleanupCacheForManagedGuestSessionError::Type::
-                      kCouldNotDeleteAllBundles,
+              ErrorIs(CleanupBundleCacheError{
+                  CleanupBundleCacheError::Type::kCouldNotDeleteAllBundles,
                   /*number_of_failed_to_cleaned_up_directories=*/1}));
   EXPECT_FALSE(base::PathExists(bundle_path1));
   EXPECT_TRUE(base::PathExists(bundle_path2));
diff --git a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h b/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h
deleted file mode 100644
index 2da77ce..0000000
--- a/chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_CLEANUP_CACHE_FOR_MANAGED_GUEST_SESSION_COMMAND_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_CLEANUP_CACHE_FOR_MANAGED_GUEST_SESSION_COMMAND_H_
-
-#include "base/types/expected.h"
-#include "chrome/browser/web_applications/commands/web_app_command.h"
-#include "chrome/browser/web_applications/locks/all_apps_lock.h"
-#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
-
-namespace web_app {
-
-// Cache Cleanup should happen only if `IsIwaBundleCacheEnabled` is true and
-// inside the Managed Guest Session.
-bool ShouldCleanupManagedGuestSessionCache();
-
-class CleanupCacheForManagedGuestSessionSuccess {
- public:
-  explicit CleanupCacheForManagedGuestSessionSuccess(
-      size_t number_of_cleaned_up_directories)
-      : number_of_cleaned_up_directories_(number_of_cleaned_up_directories) {}
-
-  CleanupCacheForManagedGuestSessionSuccess(
-      const CleanupCacheForManagedGuestSessionSuccess& other) = default;
-  ~CleanupCacheForManagedGuestSessionSuccess() = default;
-
-  bool operator==(
-      const CleanupCacheForManagedGuestSessionSuccess& other) const = default;
-
-  size_t number_of_cleaned_up_directories() const {
-    return number_of_cleaned_up_directories_;
-  }
-
- private:
-  size_t number_of_cleaned_up_directories_ = 0;
-};
-
-class CleanupCacheForManagedGuestSessionError {
- public:
-  enum class Type { kCouldNotDeleteAllBundles, kSystemShutdown };
-
-  explicit CleanupCacheForManagedGuestSessionError(
-      Type type,
-      size_t number_of_failed_to_cleaned_up_directories = 0)
-      : type_(type),
-        number_of_failed_to_cleaned_up_directories_(
-            number_of_failed_to_cleaned_up_directories) {}
-
-  CleanupCacheForManagedGuestSessionError(
-      const CleanupCacheForManagedGuestSessionError& other) = default;
-  ~CleanupCacheForManagedGuestSessionError() = default;
-
-  bool operator==(const CleanupCacheForManagedGuestSessionError& other) const =
-      default;
-
-  Type type() const { return type_; }
-
-  size_t number_of_failed_to_cleaned_up_directories() const {
-    return number_of_failed_to_cleaned_up_directories_;
-  }
-
- private:
-  Type type_;
-  // Valid only for `kCouldNotDeleteAllBundles` failure.
-  size_t number_of_failed_to_cleaned_up_directories_ = 0;
-};
-
-using CleanupCacheForManagedGuestSessionResult =
-    base::expected<CleanupCacheForManagedGuestSessionSuccess,
-                   CleanupCacheForManagedGuestSessionError>;
-
-// Cleans all IWA cached bundles for Managed Guest Session which are not in
-// the `iwas_to_keep_in_cache`.
-// During the cleanup, this class iterates through all cached directories for
-// Managed Guest Session. To avoid adding new directories during the iteration,
-// this class takes `AllAppsLock`.
-// This command will CHECK that `ShouldCleanupManagedGuestSessionCache` is true.
-// TODO(crbug.com/388729037): rename or update this class to unify with kiosk
-// implementation.
-class CleanupCacheForManagedGuestSessionCommand
-    : public WebAppCommand<AllAppsLock,
-                           CleanupCacheForManagedGuestSessionResult> {
- public:
-  using Callback =
-      base::OnceCallback<void(CleanupCacheForManagedGuestSessionResult)>;
-
-  CleanupCacheForManagedGuestSessionCommand(
-      const std::vector<web_package::SignedWebBundleId>& iwas_to_keep_in_cache,
-      Callback callback);
-  CleanupCacheForManagedGuestSessionCommand(
-      const CleanupCacheForManagedGuestSessionCommand&) = delete;
-  CleanupCacheForManagedGuestSessionCommand& operator=(
-      const CleanupCacheForManagedGuestSessionCommand&) = delete;
-
-  ~CleanupCacheForManagedGuestSessionCommand() override;
-
- protected:
-  // WebAppCommand:
-  void StartWithLock(std::unique_ptr<AllAppsLock> lock) override;
-
- private:
-  void CommandComplete(const CleanupCacheForManagedGuestSessionResult& result);
-
-  std::unique_ptr<AllAppsLock> lock_;
-  const std::vector<web_package::SignedWebBundleId> iwas_to_keep_in_cache_;
-
-  base::WeakPtrFactory<CleanupCacheForManagedGuestSessionCommand>
-      weak_ptr_factory_{this};
-};
-
-}  // namespace web_app
-
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_COMMANDS_CLEANUP_CACHE_FOR_MANAGED_GUEST_SESSION_COMMAND_H_
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
index 2ba80d4..4109f39 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
@@ -60,7 +60,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
 #include "ash/constants/ash_pref_names.h"
-#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h"
 #include "chromeos/components/mgs/managed_guest_session_utils.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -636,7 +636,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
 void IsolatedWebAppPolicyManager::
     OnCleanIsolatedWebAppCacheForManagedGuestSession(
-        CleanupCacheForManagedGuestSessionResult result) {
+        CleanupBundleCacheResult result) {
   // TODO(crbug.com/388728155): add result to log.
 }
 #endif  // BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h
index 29c3e07e..f0332d86 100644
--- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h
+++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.h
@@ -26,8 +26,8 @@
 namespace web_app {
 
 #if BUILDFLAG(IS_CHROMEOS)
-class CleanupCacheForManagedGuestSessionSuccess;
-class CleanupCacheForManagedGuestSessionError;
+class CleanupBundleCacheSuccess;
+class CleanupBundleCacheError;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 // Controls whether we attempt to fetch latest component data before processing
@@ -74,8 +74,8 @@
 
 #if BUILDFLAG(IS_CHROMEOS)
   void OnCleanIsolatedWebAppCacheForManagedGuestSession(
-      base::expected<CleanupCacheForManagedGuestSessionSuccess,
-                     CleanupCacheForManagedGuestSessionError> result);
+      base::expected<CleanupBundleCacheSuccess, CleanupBundleCacheError>
+          result);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
   void LogAddPolicyInstallSourceResult(
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc
index ad0d5b6..318a4af 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.cc
+++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -87,7 +87,7 @@
 #include "content/public/browser/web_contents.h"
 
 #if BUILDFLAG(IS_CHROMEOS)
-#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_cache_for_managed_guest_session_command.h"
+#include "chrome/browser/web_applications/isolated_web_apps/commands/cleanup_bundle_cache_command.h"
 #else  // !BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/web_applications/jobs/link_capturing.h"
 #endif
@@ -359,12 +359,12 @@
 #if BUILDFLAG(IS_CHROMEOS)
 void WebAppCommandScheduler::CleanupIsolatedWebAppCacheForManagedGuestSession(
     const std::vector<web_package::SignedWebBundleId>& iwas_to_keep_in_cache,
-    base::OnceCallback<void(CleanupCacheForManagedGuestSessionResult)> callback,
+    base::OnceCallback<void(CleanupBundleCacheResult)> callback,
     const base::Location& call_location) {
   CHECK(ShouldCleanupManagedGuestSessionCache());
   provider_->command_manager().ScheduleCommand(
-      std::make_unique<CleanupCacheForManagedGuestSessionCommand>(
-          iwas_to_keep_in_cache, std::move(callback)),
+      std::make_unique<CleanupBundleCacheCommand>(iwas_to_keep_in_cache,
+                                                  std::move(callback)),
       call_location);
 }
 #endif  // BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h
index 4ad0230..2ebb489 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.h
+++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -74,8 +74,8 @@
 struct WebAppInstallInfo;
 
 #if BUILDFLAG(IS_CHROMEOS)
-class CleanupCacheForManagedGuestSessionSuccess;
-class CleanupCacheForManagedGuestSessionError;
+class CleanupBundleCacheSuccess;
+class CleanupBundleCacheError;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 // The command scheduler is the main API to access the web app system. The
@@ -287,8 +287,8 @@
   void CleanupIsolatedWebAppCacheForManagedGuestSession(
       const std::vector<web_package::SignedWebBundleId>& iwas_to_keep_in_cache,
       base::OnceCallback<void(
-          base::expected<CleanupCacheForManagedGuestSessionSuccess,
-                         CleanupCacheForManagedGuestSessionError>)> callback,
+          base::expected<CleanupBundleCacheSuccess, CleanupBundleCacheError>)>
+          callback,
       const base::Location& call_location = FROM_HERE);
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
diff --git a/chrome/browser/web_applications/web_app_utils.cc b/chrome/browser/web_applications/web_app_utils.cc
index 0d215af..1a3ac83 100644
--- a/chrome/browser/web_applications/web_app_utils.cc
+++ b/chrome/browser/web_applications/web_app_utils.cc
@@ -287,7 +287,7 @@
   auto* user_manager = user_manager::UserManager::Get();
 
   // Don't enable for Chrome App Kiosk sessions.
-  if (user_manager && user_manager->IsLoggedInAsKioskApp()) {
+  if (user_manager && user_manager->IsLoggedInAsKioskChromeApp()) {
     return false;
   }
 
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 6cc8482..e4e21f5 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1745603681-43df19a3fc5b182951a697dff118a7eda0d57ae1-4985c214a5d99bb709aa3d59dda04a3bdb883c5e.profdata
+chrome-android32-main-1745625292-beee6af4e4138b1fb04e10511ebdfc2f9c1bed32-cbe1710682e0cad0f691c773b718cccdaa096dc9.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 7822e94b..25a9ae4 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1745600425-b69182464eedc563976a07674dc2bf546f3ab95e-7cbf7c73195c6b9d7a1d0ac1747e8e41dbebe2a6.profdata
+chrome-android64-main-1745621434-4fd109b368b1d345b8662031109ba5f766bb291b-b4ab43b1f5d0313948cf4920fc0b5d14f891396e.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index c3cf1529..5557039b 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1745582325-1ce377e0ed62c188b22bb0612d73c3b375ae51c2-a0039c24ffa93e9bc6b9966fd076450d79b52310.profdata
+chrome-linux-main-1745603681-8436018dc8ac7db66bb1c254c2c9ecfd60b5653e-4985c214a5d99bb709aa3d59dda04a3bdb883c5e.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index d3320fd9..983c20e 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1745603681-68c3cd00f65a53f6cd3fa18f4bf1023c5f99b774-4985c214a5d99bb709aa3d59dda04a3bdb883c5e.profdata
+chrome-mac-arm-main-1745625292-e7c5a538ef2be343d24b693211621620c976f9e9-cbe1710682e0cad0f691c773b718cccdaa096dc9.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index ae0dad9e..13dd7f7 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1745582325-0b63dcb8bf03071347ecbf24c53df44e9c31c5a2-a0039c24ffa93e9bc6b9966fd076450d79b52310.profdata
+chrome-mac-main-1745603681-91bc2f674b1dc04f1cf76b0b24b5a90676cde9b4-4985c214a5d99bb709aa3d59dda04a3bdb883c5e.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index d310afbc..9bdc4d3 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1745582325-466eb0bc55dde8b154720e6049e44c1815d41215-a0039c24ffa93e9bc6b9966fd076450d79b52310.profdata
+chrome-win-arm64-main-1745603681-ab6e7fae0cfca622f5d37aa50b8625bacb8c9382-4985c214a5d99bb709aa3d59dda04a3bdb883c5e.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 8171081..76ead33d 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1745560604-cf3a700b1f3d645b464cc1b0f8eeaa87ccadf1f2-035d05bd372ccf966bb8d6178c5bdaa14da93498.profdata
+chrome-win64-main-1745582325-15a4311f14b4902efd2e1574beadc18a98678b93-a0039c24ffa93e9bc6b9966fd076450d79b52310.profdata
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl
index d9f2550..a71b12354 100644
--- a/chrome/common/extensions/api/autofill_private.idl
+++ b/chrome/common/extensions/api/autofill_private.idl
@@ -425,6 +425,9 @@
 
     // The Pay Over Time issuer image source.
     DOMString? imageSrc;
+
+    // The Pay Over Time issuer image source for dark theme.
+    DOMString? imageSrcDark;
   };
 
   callback VoidCallback = void();
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index b147cb1..99aa117b 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -2365,6 +2365,7 @@
       "//components/certificate_transparency",
       "//components/certificate_transparency:proto",
       "//components/collaboration/public:empty_messaging_backend_service",
+      "//components/collaboration/public:prefs",
       "//components/commerce/content/browser",
       "//components/commerce/content/renderer",
       "//components/commerce/core:cart_db_content_proto",
@@ -3214,6 +3215,7 @@
       "../browser/policy/test/policy_browsertest.cc",
       "../browser/policy/test/policy_test_google_browsertest.cc",
       "../browser/policy/test/restore_on_startup_policy_browsertest.cc",
+      "../browser/policy/test/sharing_policy_browsertest.cc",
       "../browser/policy/test/url_blocklist_policy_browsertest.cc",
       "../browser/policy/test/v8_optimizer_policy_browsertest.cc",
       "../browser/predictors/loading_predictor_browsertest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/ThemeTestUtils.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/ThemeTestUtils.java
index 599d2f4..c538d90 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/ThemeTestUtils.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/browser/ThemeTestUtils.java
@@ -11,7 +11,7 @@
 import org.chromium.base.test.util.CriteriaHelper;
 import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.tab.Tab;
-import org.chromium.components.browser_ui.styles.ChromeColors;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
@@ -19,7 +19,7 @@
 /** Utility methods for tests which customize the tab's theme color. */
 public class ThemeTestUtils {
     public static int getDefaultThemeColor(Tab tab) {
-        return ChromeColors.getDefaultThemeColor(tab.getContext(), tab.isIncognito());
+        return SurfaceColorUpdateUtils.getDefaultThemeColor(tab.getContext(), tab.isIncognito());
     }
 
     /** Waits for the activity active tab's theme-color to change to the passed-in color. */
diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc
index 3dca905..bfc9b67 100644
--- a/chrome/test/base/testing_profile.cc
+++ b/chrome/test/base/testing_profile.cc
@@ -109,25 +109,25 @@
 #include "testing/gmock/include/gmock/gmock.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
-#include "chrome/browser/extensions/extension_special_storage_policy.h"
-#endif
-
-#if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "chrome/browser/extensions/chrome_extension_system_factory.h"
-#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_special_storage_policy.h"
 #include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
-#include "chrome/browser/web_applications/web_app_provider_factory.h"
-#include "components/guest_view/browser/guest_view_manager.h"
 #include "extensions/browser/extension_pref_value_map.h"
 #include "extensions/browser/extension_pref_value_map_factory.h"
 #include "extensions/browser/extension_prefs.h"
 #include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_prefs_observer.h"
-#include "extensions/browser/extension_system.h"
 #include "extensions/common/switches.h"
 #endif
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/web_applications/test/fake_web_app_provider.h"
+#include "chrome/browser/web_applications/web_app_provider_factory.h"
+#include "components/guest_view/browser/guest_view_manager.h"
+#include "extensions/browser/extension_system.h"
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ash/arc/session/arc_service_launcher.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
@@ -417,7 +417,7 @@
 
     extensions_path_ = profile_path_.AppendASCII("Extensions");
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
     // Note that the GetPrefs() creates a TestingPrefService, therefore
     // the extension controlled pref values set in ExtensionPrefs
     // are not reflected in the pref service. One would need to
@@ -436,7 +436,9 @@
 
     extensions::ChromeExtensionSystemFactory::GetInstance()->SetTestingFactory(
         this, base::BindRepeating(&extensions::TestExtensionSystem::Build));
+#endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 
+#if BUILDFLAG(ENABLE_EXTENSIONS)
     web_app::WebAppProviderFactory::GetInstance()->SetTestingFactory(
         this, base::BindRepeating(&web_app::FakeWebAppProvider::BuildDefault));
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar.html b/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar.html
index a72b5425..cf03b10 100644
--- a/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar.html
+++ b/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar.html
@@ -10,7 +10,7 @@
     <DL><p>
         <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F1</H3>
         <DL><p>
-            <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAy0lEQVR4nJRR0Q6CMAyEURASCYSE//9F4osvBvRmrnZjm3rJ0t7tOCiV6o3617Oua30cR73vu68yv4CGZxxHvbRnGIaA930fBnRdFxgst72IaN+27SeAF9bAvmka5fg6q/sAGlBhsBzVOaecPasPsOK3HrDcB1C0hpIGUNcAggaCxhx8APabA+YmUj6ZpmmmMTaAx3qs6RYooHIM28fB1HULOUMM6LxHlWVZZiukkAqkpv/gH9iX6QilTRSxbdV2Eq9n3yXx7ON+c08AAAD//wBjaPcAAAAGSURBVAMA4GCgUZqhPP0AAAAASUVORK5CYII=">url1</A>
+            <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAz0lEQVR4nJRS0Q6DIBBTRKfJjMbE//9Fs5e9LLCVpbcDgW1NyF1LrcJpmzfaX9e+7633vnXOhWrXF9BwzfMsm3pN0xTxcRzjgGEYIoPm6BGKL7DWit73/SeAG9rAvus64QjSegigARUGzVGNMcLZs4YALX7rAc1DAEVtqGkAdQkgaCBoLCEE4HZLwLmJnM8uy7LSmBrAUz3VZAoUUHkM3afB1GUKJUMK6NwPP9a2basWcsgFUpM7+Af6ZXKE2iSqOI7mOInXs++SefZxv5knAAAA//8OAqROAAAABklEQVQDAL++nVHkMHONAAAAAElFTkSuQmCC">url1</A>
             <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F2</H3>
             <DL><p>
                 <DT><A HREF="http://url2/" ADD_DATE="1234567890">url2</A>
diff --git a/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar_with_libpng.html b/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar_with_libpng.html
index 78149e9..cc917e8 100644
--- a/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar_with_libpng.html
+++ b/chrome/test/data/bookmark_html_writer/bookmarks_in_bookmarks_bar_with_libpng.html
@@ -10,7 +10,7 @@
     <DL><p>
         <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F1</H3>
         <DL><p>
-            <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA2klEQVQ4jZWR22qGQAyEM7vRKlQcBN//FaU3vSmbXpSk8fi3A8HJ7GdkDeRH+Gut6wozQ2sNZgaQpJnBa5qmOMw1juOuH4YBZgb1AX3f74DcZ6+q4buu+x3gBxlwX2uNvrW2y5UkHai1orWG3JsZSinRu/enkmQOX3kAu15J0sMMPGUAIo8BLgdcDt5JSVJEboFaa/grTud5poNHQERO+TGLLXggInGN7I+DPY8t3AFHlVLiXESgy7IwB1e6GuhZ/IP/KH8srvC0iUdtm2yn8P3MvV28+/X5Ub4ByMOgS1rApB8AAAAASUVORK5CYII=">url1</A>
+            <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA4ElEQVQ4jZWRzWrEMAyENbbjJrAhQyDv/4qhl16K1MMirfO72wERzeeJgiPIU/i0lmWBmUFVYWYASZoZvMZxjMO2hmHY+L7vYWYoPqDWugm0vtYKVYWIoJQSvOu61wA/aAPe55zDq+qGF5L0QM4ZqorWmxlSSuG992chyRa+6wFsfCFJh23gjgEIHgNcHnB58EqFJEXkMpBzjv4sV6Zpogf3ARE58D2LLTgQkbhG2+8HO48tXAX2SinFuYigzPPMFpzpbKCz+Af/UfuxuMLdJm61rrIe4OOY+zp59/fnO/0BqCGdS8jidiAAAAAASUVORK5CYII=">url1</A>
             <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F2</H3>
             <DL><p>
                 <DT><A HREF="http://url2/" ADD_DATE="1234567890">url2</A>
diff --git a/chrome/test/data/bookmark_html_writer/bookmarks_in_other.html b/chrome/test/data/bookmark_html_writer/bookmarks_in_other.html
index c05b232..dca154d 100644
--- a/chrome/test/data/bookmark_html_writer/bookmarks_in_other.html
+++ b/chrome/test/data/bookmark_html_writer/bookmarks_in_other.html
@@ -11,7 +11,7 @@
     </DL><p>
     <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F1</H3>
     <DL><p>
-        <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAy0lEQVR4nJRR0Q6CMAyEURASCYSE//9F4osvBvRmrnZjm3rJ0t7tOCiV6o3617Oua30cR73vu68yv4CGZxxHvbRnGIaA930fBnRdFxgst72IaN+27SeAF9bAvmka5fg6q/sAGlBhsBzVOaecPasPsOK3HrDcB1C0hpIGUNcAggaCxhx8APabA+YmUj6ZpmmmMTaAx3qs6RYooHIM28fB1HULOUMM6LxHlWVZZiukkAqkpv/gH9iX6QilTRSxbdV2Eq9n3yXx7ON+c08AAAD//wBjaPcAAAAGSURBVAMA4GCgUZqhPP0AAAAASUVORK5CYII=">url1</A>
+        <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAz0lEQVR4nJRS0Q6DIBBTRKfJjMbE//9Fs5e9LLCVpbcDgW1NyF1LrcJpmzfaX9e+7633vnXOhWrXF9BwzfMsm3pN0xTxcRzjgGEYIoPm6BGKL7DWit73/SeAG9rAvus64QjSegigARUGzVGNMcLZs4YALX7rAc1DAEVtqGkAdQkgaCBoLCEE4HZLwLmJnM8uy7LSmBrAUz3VZAoUUHkM3afB1GUKJUMK6NwPP9a2basWcsgFUpM7+Af6ZXKE2iSqOI7mOInXs++SefZxv5knAAAA//8OAqROAAAABklEQVQDAL++nVHkMHONAAAAAElFTkSuQmCC">url1</A>
         <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F2</H3>
         <DL><p>
             <DT><A HREF="http://url2/" ADD_DATE="1234567890">url2</A>
diff --git a/chrome/test/data/bookmark_html_writer/bookmarks_in_other_with_libpng.html b/chrome/test/data/bookmark_html_writer/bookmarks_in_other_with_libpng.html
index 2cb5f48..c68306e 100644
--- a/chrome/test/data/bookmark_html_writer/bookmarks_in_other_with_libpng.html
+++ b/chrome/test/data/bookmark_html_writer/bookmarks_in_other_with_libpng.html
@@ -11,7 +11,7 @@
     </DL><p>
     <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F1</H3>
     <DL><p>
-        <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA2klEQVQ4jZWR22qGQAyEM7vRKlQcBN//FaU3vSmbXpSk8fi3A8HJ7GdkDeRH+Gut6wozQ2sNZgaQpJnBa5qmOMw1juOuH4YBZgb1AX3f74DcZ6+q4buu+x3gBxlwX2uNvrW2y5UkHai1orWG3JsZSinRu/enkmQOX3kAu15J0sMMPGUAIo8BLgdcDt5JSVJEboFaa/grTud5poNHQERO+TGLLXggInGN7I+DPY8t3AFHlVLiXESgy7IwB1e6GuhZ/IP/KH8srvC0iUdtm2yn8P3MvV28+/X5Ub4ByMOgS1rApB8AAAAASUVORK5CYII=">url1</A>
+        <DT><A HREF="http://url1/" ADD_DATE="1234567890" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA4ElEQVQ4jZWRzWrEMAyENbbjJrAhQyDv/4qhl16K1MMirfO72wERzeeJgiPIU/i0lmWBmUFVYWYASZoZvMZxjMO2hmHY+L7vYWYoPqDWugm0vtYKVYWIoJQSvOu61wA/aAPe55zDq+qGF5L0QM4ZqorWmxlSSuG992chyRa+6wFsfCFJh23gjgEIHgNcHnB58EqFJEXkMpBzjv4sV6Zpogf3ARE58D2LLTgQkbhG2+8HO48tXAX2SinFuYigzPPMFpzpbKCz+Af/UfuxuMLdJm61rrIe4OOY+zp59/fnO/0BqCGdS8jidiAAAAAASUVORK5CYII=">url1</A>
         <DT><H3 ADD_DATE="1234567890" LAST_MODIFIED="1234567890">F2</H3>
         <DL><p>
             <DT><A HREF="http://url2/" ADD_DATE="1234567890">url2</A>
diff --git a/chrome/test/data/webui/settings/autofill_fake_data.ts b/chrome/test/data/webui/settings/autofill_fake_data.ts
index 9a2d70a..21ffbd0 100644
--- a/chrome/test/data/webui/settings/autofill_fake_data.ts
+++ b/chrome/test/data/webui/settings/autofill_fake_data.ts
@@ -141,6 +141,7 @@
     instrumentId: '123456',
     displayName: 'Issuer1',
     imageSrc: 'chrome://theme/IDR_AUTOFILL_METADATA_BNPL_GENERIC',
+    imageSrcDark: 'chrome://theme/IDR_AUTOFILL_METADATA_BNPL_GENERIC',
   };
 }
 
diff --git a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts
index 4bd8cc4..a360b58 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/power_bookmarks_context_menu_test.ts
@@ -101,6 +101,7 @@
       menuOpenIncognitoWithCount: 'Open all in Incognito window',
       menuOpenNewTabGroup: 'Open in new tab group',
       menuOpenNewTabGroupWithCount: 'Open all in new tab group',
+      menuOpenSplitView: 'Open all in split view',
       menuEdit: 'Edit…',
       menuMoveToBookmarksBar: 'Move to Bookmarks Bar folder',
       menuTrackPrice: 'Track price',
@@ -108,6 +109,7 @@
       menuRename: 'Rename',
       tooltipDelete: 'Delete',
       tooltipMove: 'Move',
+      splitViewEnabled: true,
     });
 
     powerBookmarksContextMenu =
@@ -126,7 +128,7 @@
 
     const menuItems = powerBookmarksContextMenu.shadowRoot!.querySelectorAll(
         '.dropdown-item');
-    assertEquals(menuItems.length, 6);
+    assertEquals(menuItems.length, 7);
     assertEquals(
         menuItems[0]!.textContent!.includes(
             loadTimeData.getString('menuOpenNewTab')),
@@ -140,14 +142,18 @@
             loadTimeData.getString('menuOpenIncognito')),
         true);
     assertEquals(
-        menuItems[3]!.textContent!.includes(loadTimeData.getString('menuEdit')),
+        menuItems[3]!.textContent!.includes(
+            loadTimeData.getString('menuOpenSplitView')),
         true);
     assertEquals(
-        menuItems[4]!.textContent!.includes(
-            loadTimeData.getString('menuMoveToBookmarksBar')),
+        menuItems[4]!.textContent!.includes(loadTimeData.getString('menuEdit')),
         true);
     assertEquals(
         menuItems[5]!.textContent!.includes(
+            loadTimeData.getString('menuMoveToBookmarksBar')),
+        true);
+    assertEquals(
+        menuItems[6]!.textContent!.includes(
             loadTimeData.getString('tooltipDelete')),
         true);
   });
@@ -238,7 +244,7 @@
 
     const menuItems = powerBookmarksContextMenu.shadowRoot!.querySelectorAll(
         '.dropdown-item');
-    assertEquals(menuItems.length, 7);
+    assertEquals(menuItems.length, 8);
     assertEquals(
         menuItems[0]!.textContent!.includes(
             loadTimeData.getString('menuOpenNewTab')),
@@ -252,18 +258,22 @@
             loadTimeData.getString('menuOpenIncognito')),
         true);
     assertEquals(
-        menuItems[3]!.textContent!.includes(loadTimeData.getString('menuEdit')),
+        menuItems[3]!.textContent!.includes(
+            loadTimeData.getString('menuOpenSplitView')),
         true);
     assertEquals(
-        menuItems[4]!.textContent!.includes(
-            loadTimeData.getString('menuMoveToBookmarksBar')),
+        menuItems[4]!.textContent!.includes(loadTimeData.getString('menuEdit')),
         true);
     assertEquals(
         menuItems[5]!.textContent!.includes(
-            loadTimeData.getString('menuUntrackPrice')),
+            loadTimeData.getString('menuMoveToBookmarksBar')),
         true);
     assertEquals(
         menuItems[6]!.textContent!.includes(
+            loadTimeData.getString('menuUntrackPrice')),
+        true);
+    assertEquals(
+        menuItems[7]!.textContent!.includes(
             loadTimeData.getString('tooltipDelete')),
         true);
   });
@@ -307,4 +317,44 @@
             loadTimeData.getString('tooltipDelete')),
         true);
   });
+
+  test('ShowsMenuItemsForUserWithSplitViewDisabled', async () => {
+    loadTimeData.overrideValues({
+      splitViewEnabled: false,
+      isIncognitoModeAvailable: true,
+    });
+
+    const selection = [service.findBookmarkWithId('3')!];
+    powerBookmarksContextMenu.showAtPosition(
+        new MouseEvent('click'), selection, false, false);
+
+    await waitAfterNextRender(powerBookmarksContextMenu);
+
+    const menuItems = powerBookmarksContextMenu.shadowRoot!.querySelectorAll(
+        '.dropdown-item');
+    assertEquals(menuItems.length, 6);
+    assertEquals(
+        menuItems[0]!.textContent!.includes(
+            loadTimeData.getString('menuOpenNewTab')),
+        true);
+    assertEquals(
+        menuItems[1]!.textContent!.includes(
+            loadTimeData.getString('menuOpenNewWindow')),
+        true);
+    assertEquals(
+        menuItems[2]!.textContent!.includes(
+            loadTimeData.getString('menuOpenIncognito')),
+        true);
+    assertEquals(
+        menuItems[3]!.textContent!.includes(loadTimeData.getString('menuEdit')),
+        true);
+    assertEquals(
+        menuItems[4]!.textContent!.includes(
+            loadTimeData.getString('menuMoveToBookmarksBar')),
+        true);
+    assertEquals(
+        menuItems[5]!.textContent!.includes(
+            loadTimeData.getString('tooltipDelete')),
+        true);
+  });
 });
diff --git a/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts b/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts
index 0276c437..1f3867e 100644
--- a/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts
+++ b/chrome/test/data/webui/side_panel/bookmarks/test_bookmarks_api_proxy.ts
@@ -29,6 +29,7 @@
       'contextMenuOpenBookmarkInNewWindow',
       'contextMenuOpenBookmarkInIncognitoWindow',
       'contextMenuOpenBookmarkInNewTabGroup',
+      'contextMenuOpenBookmarkInSplitView',
       'contextMenuEdit',
       'contextMenuMove',
       'contextMenuAddToBookmarksBar',
@@ -89,6 +90,10 @@
     this.methodCalled('contextMenuOpenBookmarkInNewTabGroup', ids, source);
   }
 
+  contextMenuOpenBookmarkInSplitView(ids: string[], source: ActionSource) {
+    this.methodCalled('contextMenuOpenBookmarkInSplitView', ids, source);
+  }
+
   contextMenuEdit(ids: string[], source: ActionSource) {
     this.methodCalled('contextMenuEdit', ids, source);
   }
diff --git a/chrome/test/data/webui/side_panel/read_anything/language_menu_test.ts b/chrome/test/data/webui/side_panel/read_anything/language_menu_test.ts
index 3faa507..28c06cc2 100644
--- a/chrome/test/data/webui/side_panel/read_anything/language_menu_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/language_menu_test.ts
@@ -182,9 +182,64 @@
             'English (United States)', getLanguageLineItems()[0]!);
         assertEquals(true, getNoResultsFoundMessage()!.hidden);
       });
+
+      test('it matches the language code', async () => {
+        getLanguageSearchField().value = 'en-us';
+        await microtasksFinished();
+
+        assertEquals(1, getLanguageLineItems().length);
+        assertLanguageLineWithTextAndSwitch(
+            'English (United States)', getLanguageLineItems()[0]!);
+        assertEquals(true, getNoResultsFoundMessage()!.hidden);
+      });
+    });
+
+    suite('with display names with accent', () => {
+      const portugueseDisplayName = 'Português (Brasil)';
+
+      setup(() => {
+        availableVoices = [
+          createSpeechSynthesisVoice(
+              {name: portugueseDisplayName, lang: 'pt-br'}),
+        ];
+        languageMenu.localeToDisplayName = {
+          'pt-br': portugueseDisplayName,
+        };
+        languageMenu.availableVoices = availableVoices;
+        return drawLanguageMenu();
+      });
+
+      test('it matches search with accent', async () => {
+        getLanguageSearchField().value = 'português';
+        await microtasksFinished();
+
+        assertEquals(1, getLanguageLineItems().length);
+        assertLanguageLineWithTextAndSwitch(
+            portugueseDisplayName, getLanguageLineItems()[0]!);
+        assertEquals(true, getNoResultsFoundMessage()!.hidden);
+      });
+
+      test('it matches search with no accent', async () => {
+        getLanguageSearchField().value = 'portugues';
+        await microtasksFinished();
+
+        assertEquals(1, getLanguageLineItems().length);
+        assertLanguageLineWithTextAndSwitch(
+            portugueseDisplayName, getLanguageLineItems()[0]!);
+        assertEquals(true, getNoResultsFoundMessage()!.hidden);
+      });
+
+      test('it matches the language code', async () => {
+        getLanguageSearchField().value = 'pt-';
+        await microtasksFinished();
+
+        assertEquals(1, getLanguageLineItems().length);
+        assertLanguageLineWithTextAndSwitch(
+            portugueseDisplayName, getLanguageLineItems()[0]!);
+        assertEquals(true, getNoResultsFoundMessage()!.hidden);
+      });
     });
   });
-
   suite('with multiple languages', () => {
     setup(() => {
       availableVoices = [
diff --git a/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.cc b/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.cc
index f3096b89..4b8cb49 100644
--- a/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.cc
+++ b/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <string_view>
 
+#include "ash/constants/ash_features.h"
 #include "base/functional/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/ash/components/boca/babelorca/pref_names.h"
@@ -78,6 +79,10 @@
   profile_prefs_->SetString(prefs::kTranslateTargetLanguageCode, language_code);
 }
 
+bool CaptionBubbleSettingsImpl::ShouldAdjustPositionOnExpand() {
+  return features::IsBocaAdjustCaptionBubbleOnExpandEnabled();
+}
+
 void CaptionBubbleSettingsImpl::SetLiveTranslateEnabled(bool enabled) {
   bool enabled_changed = translate_enabled_ != enabled;
   translate_enabled_ = enabled;
diff --git a/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.h b/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.h
index 29e463e4..610e5696 100644
--- a/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.h
+++ b/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.h
@@ -44,6 +44,7 @@
   void SetLiveCaptionBubbleExpanded(bool expanded) override;
   void SetLiveTranslateTargetLanguageCode(
       std::string_view language_code) override;
+  bool ShouldAdjustPositionOnExpand() override;
 
   void SetLiveTranslateEnabled(bool enabled);
 
diff --git a/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl_unittest.cc b/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl_unittest.cc
index 9612166..0925f59a 100644
--- a/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl_unittest.cc
+++ b/chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl_unittest.cc
@@ -4,9 +4,11 @@
 
 #include "chromeos/ash/components/boca/babelorca/caption_bubble_settings_impl.h"
 
+#include "ash/constants/ash_features.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/weak_ptr.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chromeos/ash/components/boca/babelorca/pref_names.h"
 #include "components/live_caption/caption_bubble_settings.h"
@@ -145,5 +147,26 @@
   EXPECT_FALSE(notified);
 }
 
+TEST_F(CaptionBubbleSettingsImplTest,
+       ShouldAdjustPositionOnExpandIfFeatureEnabled) {
+  base::test::ScopedFeatureList feature_list(
+      {features::kBocaAdjustCaptionBubbleOnExpand});
+  CaptionBubbleSettingsImpl caption_bubble_settings(
+      &pref_service_, kEnglishLanguage, base::DoNothing());
+
+  EXPECT_TRUE(caption_bubble_settings.ShouldAdjustPositionOnExpand());
+}
+
+TEST_F(CaptionBubbleSettingsImplTest,
+       ShouldAdjustPositionOnExpandIfFeatureDisabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      {features::kBocaAdjustCaptionBubbleOnExpand});
+  CaptionBubbleSettingsImpl caption_bubble_settings(
+      &pref_service_, kEnglishLanguage, base::DoNothing());
+
+  EXPECT_FALSE(caption_bubble_settings.ShouldAdjustPositionOnExpand());
+}
+
 }  // namespace
 }  // namespace ash::babelorca
diff --git a/chromeos/ash/components/boca/boca_metrics_util.cc b/chromeos/ash/components/boca/boca_metrics_util.cc
index b47592c..240010b 100644
--- a/chromeos/ash/components/boca/boca_metrics_util.cc
+++ b/chromeos/ash/components/boca/boca_metrics_util.cc
@@ -8,6 +8,8 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/metrics_hashes.h"
 #include "base/metrics/user_metrics.h"
+#include "base/strings/string_util.h"
+#include "google_apis/common/api_error_codes.h"
 
 namespace ash::boca {
 
@@ -92,4 +94,36 @@
   }
 }
 
+void RecordOnRegisterScreenRequestSentErrorCode(
+    google_apis::ApiErrorCode error_code) {
+  RecordSpotlightGoogleApiErrorCode(kBocaSpotlightOnRegisterScreenRequestSent,
+                                    error_code);
+}
+
+void RecordSpotlightGoogleApiErrorCode(const std::string& name,
+                                       google_apis::ApiErrorCode error_code) {
+  base::UmaHistogramSparse(
+      base::ReplaceStringPlaceholders(
+          kBocaSpotlightGoogleApiCallErrorCodeTemplate, {name},
+          /*=offsets*/ nullptr),
+      error_code);
+}
+
+void RecordUpdateStudentActivitiesErrorCode(
+    google_apis::ApiErrorCode error_code) {
+  RecordGoogleApiErrorCode(kBocaUpdateStudentActivities, error_code);
+}
+
+void RecordStudentHeartBeatErrorCode(google_apis::ApiErrorCode error_code) {
+  RecordGoogleApiErrorCode(kBocaStudentHeartbeat, error_code);
+}
+
+void RecordGoogleApiErrorCode(const std::string& name,
+                              google_apis::ApiErrorCode error_code) {
+  base::UmaHistogramSparse(base::ReplaceStringPlaceholders(
+                               kBocaGoogleApiCallErrorCodeTemplate, {name},
+                               /*=offsets*/ nullptr),
+                           error_code);
+}
+
 }  // namespace ash::boca
diff --git a/chromeos/ash/components/boca/boca_metrics_util.h b/chromeos/ash/components/boca/boca_metrics_util.h
index 55fd7c3d..090c1b0 100644
--- a/chromeos/ash/components/boca/boca_metrics_util.h
+++ b/chromeos/ash/components/boca/boca_metrics_util.h
@@ -6,6 +6,7 @@
 #define CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_METRICS_UTIL_H_
 
 #include "base/time/time.h"
+#include "google_apis/common/api_error_codes.h"
 
 namespace ash::boca {
 inline constexpr char kBocaActionOfStudentJoinedSession[] =
@@ -38,6 +39,15 @@
     "Ash.Boca.OnTask.NumberOfTabsWhenSessionEnded";
 inline constexpr char kBocaOnTaskMaxNumOfTabsDuringSession[] =
     "Ash.Boca.OnTask.MaxNumberOfTabsDuringSession";
+inline constexpr char kBocaSpotlightGoogleApiCallErrorCodeTemplate[] =
+    "Ash.Boca.Spotlight.$1.ErrorCode";
+inline constexpr char kBocaSpotlightOnRegisterScreenRequestSent[] =
+    "RegisterScreen";
+inline constexpr char kBocaGoogleApiCallErrorCodeTemplate[] =
+    "Ash.Boca.$1.ErrorCode";
+inline constexpr char kBocaUpdateStudentActivities[] =
+    "UpdateStudentActivities";
+inline constexpr char kBocaStudentHeartbeat[] = "StudentHeartbeat";
 
 // Records the percentage of the duration that a session was in a particular
 // locked or unlocked state.
@@ -85,6 +95,25 @@
 // OnTask pod.
 void RecordOnTaskPodSetSnapLocationClicked(bool is_left);
 
+// Records the error code of the spotlight OnRegisterScreenRequestSent calls.
+void RecordOnRegisterScreenRequestSentErrorCode(
+    google_apis::ApiErrorCode error_code);
+
+// Records the error code of the spotlight Google Api calls.
+void RecordSpotlightGoogleApiErrorCode(const std::string& name,
+                                       google_apis::ApiErrorCode error_code);
+
+// Records the error code of the UpdateStudentActivities calls.
+void RecordUpdateStudentActivitiesErrorCode(
+    google_apis::ApiErrorCode error_code);
+
+// Records the error code of the StudentHeartBeat calls.
+void RecordStudentHeartBeatErrorCode(google_apis::ApiErrorCode error_code);
+
+// Records the error code of the Google Api calls.
+void RecordGoogleApiErrorCode(const std::string& name,
+                              google_apis::ApiErrorCode error_code);
+
 }  // namespace ash::boca
 
 #endif  // CHROMEOS_ASH_COMPONENTS_BOCA_BOCA_METRICS_UTIL_H_
diff --git a/chromeos/ash/components/boca/boca_session_manager.cc b/chromeos/ash/components/boca/boca_session_manager.cc
index 927a54e2..6b246245 100644
--- a/chromeos/ash/components/boca/boca_session_manager.cc
+++ b/chromeos/ash/components/boca/boca_session_manager.cc
@@ -19,6 +19,7 @@
 #include "base/time/time.h"
 #include "chromeos/ash/components/boca/babelorca/soda_installer.h"
 #include "chromeos/ash/components/boca/boca_app_client.h"
+#include "chromeos/ash/components/boca/boca_metrics_util.h"
 #include "chromeos/ash/components/boca/boca_role_util.h"
 #include "chromeos/ash/components/boca/boca_session_util.h"
 #include "chromeos/ash/components/boca/notifications/boca_notification_handler.h"
@@ -289,8 +290,10 @@
       base::BindOnce(
           [](base::expected<bool, google_apis::ApiErrorCode> result) {
             if (!result.has_value()) {
-              // TODO: crbug.com/366316261 - Add metrics for update failure.
-              LOG(WARNING) << "[Boca]Failed to update student activity.";
+              boca::RecordUpdateStudentActivitiesErrorCode(result.error());
+              LOG(WARNING)
+                  << "[Boca]Failed to update student activity with error code: "
+                  << result.error();
             }
           }));
 
@@ -738,8 +741,8 @@
 void BocaSessionManager::OnStudentHeartbeat(
     base::expected<bool, google_apis::ApiErrorCode> result) {
   if (!result.has_value()) {
-    // TODO: crbug.com/366316261 - Add metrics for update failure.
-    LOG(WARNING) << "[Boca]Failed to call student heartbeat with error code: ."
+    boca::RecordStudentHeartBeatErrorCode(result.error());
+    LOG(WARNING) << "[Boca]Failed to call student heartbeat with error code: "
                  << result.error();
     if ((result.error() >= 500 && result.error() < 600) ||
         result.error() == 429) {
diff --git a/chromeos/ash/components/boca/boca_session_manager_unittest.cc b/chromeos/ash/components/boca/boca_session_manager_unittest.cc
index abbc146..292ccc7 100644
--- a/chromeos/ash/components/boca/boca_session_manager_unittest.cc
+++ b/chromeos/ash/components/boca/boca_session_manager_unittest.cc
@@ -150,6 +150,10 @@
 constexpr char kTestDefaultUrl[] = "https://test";
 constexpr char kDefaultLanguage[] = "en-US";
 constexpr char kBadLanguage[] = "unknown language";
+constexpr char kUpdateStudentActivitiesErrorCodeUmaPath[] =
+    "Ash.Boca.UpdateStudentActivities.ErrorCode";
+constexpr char kStudentHeartbeatErrorCodeUmaPath[] =
+    "Ash.Boca.StudentHeartbeat.ErrorCode";
 
 ::boca::Session GetInitialSession(base::Time inital_time) {
   ::boca::Session session_1;
@@ -936,6 +940,7 @@
 }
 
 TEST_F(BocaSessionManagerTest, UpdateTabActivity) {
+  base::HistogramTester histogram_tester;
   std::u16string kTab(u"google.com");
   ::boca::Session session = GetInitialSession(session_start_time_);
 
@@ -953,6 +958,32 @@
   boca_session_manager()->UpdateCurrentSession(
       std::make_unique<::boca::Session>(session), false);
   boca_session_manager()->UpdateTabActivity(kTab);
+  histogram_tester.ExpectTotalCount(kUpdateStudentActivitiesErrorCodeUmaPath,
+                                    0);
+}
+
+TEST_F(BocaSessionManagerTest, UpdateTabActivityFailed) {
+  base::HistogramTester histogram_tester;
+  std::u16string kTab(u"google.com");
+  ::boca::Session session = GetInitialSession(session_start_time_);
+
+  EXPECT_CALL(*session_client_impl(), UpdateStudentActivity(_))
+      .WillOnce(WithArg<0>(
+          // Unique pointer have ownership issue, have to do manual deep copy
+          // here instead of using SaveArg.
+          Invoke([&](auto request) {
+            request->callback().Run(base::unexpected<google_apis::ApiErrorCode>(
+                google_apis::ApiErrorCode::HTTP_INTERNAL_SERVER_ERROR));
+          })));
+
+  boca_session_manager()->UpdateCurrentSession(
+      std::make_unique<::boca::Session>(session), false);
+  boca_session_manager()->UpdateTabActivity(kTab);
+  histogram_tester.ExpectTotalCount(kUpdateStudentActivitiesErrorCodeUmaPath,
+                                    1);
+  histogram_tester.ExpectBucketCount(
+      kUpdateStudentActivitiesErrorCodeUmaPath,
+      google_apis::ApiErrorCode::HTTP_INTERNAL_SERVER_ERROR, 1);
 }
 
 TEST_F(BocaSessionManagerTest, UpdateTabActivityWithDummyDeviceId) {
@@ -1992,6 +2023,7 @@
 
 TEST_F(BocaSessionManagerStudentHeartbeatTest,
        StudentHeartbeatCalledWhenSessionIsActive) {
+  base::HistogramTester histogram_tester;
   ::boca::Session session_1;
   session_1.set_session_id(kInitialSessionId);
   session_1.set_session_state(::boca::Session::ACTIVE);
@@ -2004,6 +2036,8 @@
       std::make_unique<::boca::Session>(session_1), /*dispatch_event=*/true);
 
   task_environment()->FastForwardBy(kDefaultStudentHeartbeatInterval);
+
+  histogram_tester.ExpectTotalCount(kStudentHeartbeatErrorCodeUmaPath, 0);
 }
 
 TEST_F(BocaSessionManagerStudentHeartbeatTest,
@@ -2029,6 +2063,7 @@
 
 TEST_F(BocaSessionManagerStudentHeartbeatTest,
        StudentHeartbeatCallFailedWithRetryBackoff) {
+  base::HistogramTester histogram_tester;
   ::boca::Session session_1;
   session_1.set_session_id(kInitialSessionId);
   session_1.set_session_state(::boca::Session::ACTIVE);
@@ -2051,10 +2086,16 @@
       kDefaultStudentHeartbeatInterval +
       base::Seconds(30) +        // Initial backoff delay.
       base::Seconds(30 * 1.2));  // Second backoff delay.
+
+  histogram_tester.ExpectTotalCount(kStudentHeartbeatErrorCodeUmaPath, 3);
+  histogram_tester.ExpectBucketCount(
+      kStudentHeartbeatErrorCodeUmaPath,
+      google_apis::ApiErrorCode::HTTP_INTERNAL_SERVER_ERROR, 3);
 }
 
 TEST_F(BocaSessionManagerStudentHeartbeatTest,
        StudentHeartbeatCallFailedWithRetryBackoffThenSucceeded) {
+  base::HistogramTester histogram_tester;
   ::boca::Session session_1;
   session_1.set_session_id(kInitialSessionId);
   session_1.set_session_state(::boca::Session::ACTIVE);
@@ -2089,10 +2130,16 @@
       base::Seconds(30) +        // Initial backoff delay.
       base::Seconds(30 * 1.2) +  // Second backoff delay.
       base::Seconds(30));        // Default heartbeat interval.
+
+  histogram_tester.ExpectTotalCount(kStudentHeartbeatErrorCodeUmaPath, 2);
+  histogram_tester.ExpectBucketCount(
+      kStudentHeartbeatErrorCodeUmaPath,
+      google_apis::ApiErrorCode::HTTP_INTERNAL_SERVER_ERROR, 2);
 }
 
 TEST_F(BocaSessionManagerStudentHeartbeatTest,
        StudentHeartbeatCallFailedWithRetryBackoffWithNewSession) {
+  base::HistogramTester histogram_tester;
   ::boca::Session session_1;
   session_1.set_session_id(kInitialSessionId);
   session_1.set_session_state(::boca::Session::ACTIVE);
@@ -2127,6 +2174,10 @@
       std::make_unique<::boca::Session>(session_2), /*dispatch_event=*/true);
 
   task_environment()->FastForwardBy(base::Seconds(30 * 1.2));
+  histogram_tester.ExpectTotalCount(kStudentHeartbeatErrorCodeUmaPath, 3);
+  histogram_tester.ExpectBucketCount(
+      kStudentHeartbeatErrorCodeUmaPath,
+      google_apis::ApiErrorCode::HTTP_INTERNAL_SERVER_ERROR, 3);
 }
 
 class BocaSessionManagerStudentHeartbeatCustomPollingTest
diff --git a/chromeos/ash/components/boca/spotlight/spotlight_session_manager.cc b/chromeos/ash/components/boca/spotlight/spotlight_session_manager.cc
index 6618304a..49057a6 100644
--- a/chromeos/ash/components/boca/spotlight/spotlight_session_manager.cc
+++ b/chromeos/ash/components/boca/spotlight/spotlight_session_manager.cc
@@ -12,6 +12,7 @@
 #include "base/functional/bind.h"
 #include "base/sequence_checker.h"
 #include "chromeos/ash/components/boca/boca_app_client.h"
+#include "chromeos/ash/components/boca/boca_metrics_util.h"
 #include "chromeos/ash/components/boca/proto/session.pb.h"
 #include "chromeos/ash/components/boca/session_api/constants.h"
 #include "chromeos/ash/components/boca/spotlight/spotlight_crd_manager.h"
@@ -116,8 +117,10 @@
     base::expected<bool, google_apis::ApiErrorCode> result) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   if (!result.has_value()) {
-    // TODO: crbug.com/366316261 - Add metrics for Spotlight failure.
-    LOG(WARNING) << "[Boca]Failed to send Spotlight connection code.";
+    boca::RecordOnRegisterScreenRequestSentErrorCode(result.error());
+    LOG(WARNING)
+        << "[Boca]Failed to send Spotlight connection code with error code: "
+        << result.error();
   }
   request_in_progress_ = false;
 
diff --git a/chromeos/ash/components/boca/spotlight/spotlight_session_manager_unittest.cc b/chromeos/ash/components/boca/spotlight/spotlight_session_manager_unittest.cc
index a2dd34aa..30389a5 100644
--- a/chromeos/ash/components/boca/spotlight/spotlight_session_manager_unittest.cc
+++ b/chromeos/ash/components/boca/spotlight/spotlight_session_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
@@ -44,6 +45,8 @@
 constexpr char kUserEmail[] = "cat@gmail.com";
 constexpr char kUserFullName[] = "Best Teacher";
 constexpr char kTestBaseUrl[] = "https://test";
+constexpr char kOnRegisterScreenRequestSentErrorCodeUmaPath[] =
+    "Ash.Boca.Spotlight.RegisterScreen.ErrorCode";
 // Length of the notification duration and one extra interval for the
 // notification to start.
 constexpr base::TimeDelta kTestNotificationDuration =
@@ -190,6 +193,8 @@
 }
 
 TEST_F(SpotlightSessionManagerTest, IniatesSpotlightSessionWhenRequested) {
+  base::HistogramTester histograms;
+
   ::boca::StudentDevice device;
   device.mutable_view_screen_config()->set_view_screen_state(
       ::boca::ViewScreenConfig::REQUESTED);
@@ -221,6 +226,8 @@
   spotlight_session_manager_->OnSessionStarted(kSessionId, producer);
   spotlight_session_manager_->OnConsumerActivityUpdated(activities);
   task_environment_.FastForwardBy(kTestNotificationDuration);
+
+  histograms.ExpectTotalCount(kOnRegisterScreenRequestSentErrorCodeUmaPath, 0);
 }
 
 TEST_F(SpotlightSessionManagerTest, DoesNotStartSpotlightWithInactiveSession) {
@@ -279,6 +286,8 @@
 }
 
 TEST_F(SpotlightSessionManagerTest, OnlyProcessesOneRequestAtATime) {
+  base::HistogramTester histograms;
+
   ::boca::StudentDevice device;
   device.mutable_view_screen_config()->set_view_screen_state(
       ::boca::ViewScreenConfig::REQUESTED);
@@ -308,6 +317,8 @@
   spotlight_session_manager_->OnSessionEnded(kSessionId);
   spotlight_session_manager_->OnSessionStarted(kSessionId, producer);
   spotlight_session_manager_->OnConsumerActivityUpdated(activities);
+
+  histograms.ExpectTotalCount(kOnRegisterScreenRequestSentErrorCodeUmaPath, 0);
 }
 
 }  // namespace
diff --git a/chromeos/ash/components/demo_mode/utils/demo_session_utils.cc b/chromeos/ash/components/demo_mode/utils/demo_session_utils.cc
index c8f8c9e7..c51a2df 100644
--- a/chromeos/ash/components/demo_mode/utils/demo_session_utils.cc
+++ b/chromeos/ash/components/demo_mode/utils/demo_session_utils.cc
@@ -14,6 +14,10 @@
 
 namespace {
 bool g_force_enable_demo_account_sign_in = false;
+
+bool g_should_schedule_logout_for_mgs = false;
+
+bool g_is_24h_session_enabled = false;
 }
 
 bool IsDeviceInDemoMode() {
@@ -57,10 +61,19 @@
 void SetDoNothingWhenPowerIdle() {
   chromeos::PowerPolicyController::Get()
       ->SetShouldDoNothingWhenIdleInDemoMode();
+  g_is_24h_session_enabled = true;
 }
 
 bool ForceSessionLengthCountFromSessionStarts() {
-  return IsDemoAccountSignInEnabled();
+  return g_is_24h_session_enabled;
+}
+
+void TurnOnScheduleLogoutForMGS() {
+  g_should_schedule_logout_for_mgs = true;
+}
+
+bool GetShouldScheduleLogoutForMGS() {
+  return g_should_schedule_logout_for_mgs;
 }
 
 }  // namespace ash::demo_mode
diff --git a/chromeos/ash/components/demo_mode/utils/demo_session_utils.h b/chromeos/ash/components/demo_mode/utils/demo_session_utils.h
index d701606..3a1bf0b 100644
--- a/chromeos/ash/components/demo_mode/utils/demo_session_utils.h
+++ b/chromeos/ash/components/demo_mode/utils/demo_session_utils.h
@@ -42,13 +42,26 @@
 COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DEMO_MODE)
 // Set to power idle action policy to do nothing and use the DemoModeIdleHandler
 // when idle.
+// TODO(crbugs.com/355727308): This happens only when power policy override for
+// 24h session. Rename with `Enable24HSessionForDemoMode()`.
 void SetDoNothingWhenPowerIdle();
 
 COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DEMO_MODE)
 // Whether force the session length count from ChromeOS session instead of first
 // user activity.
+// TODO(crbugs.com/355727308): This happens only when power policy override for
+// 24h session. Rename with `ShouldEnable24HSessionForDemoMode()`.
 bool ForceSessionLengthCountFromSessionStarts();
 
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DEMO_MODE)
+// Call on setup demo account error failed by exceed QPS.Turn on "should
+// schedule logout" in current session.
+void TurnOnScheduleLogoutForMGS();
+
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_DEMO_MODE)
+// Whether should schedule a logout in current session to retry sign-in.
+bool GetShouldScheduleLogoutForMGS();
+
 }  // namespace ash::demo_mode
 
 #endif  // CHROMEOS_ASH_COMPONENTS_DEMO_MODE_UTILS_DEMO_SESSION_UTILS_H_
diff --git a/chromeos/services/assistant/public/shared/BUILD.gn b/chromeos/services/assistant/public/shared/BUILD.gn
index 67513de..888f0fb5 100644
--- a/chromeos/services/assistant/public/shared/BUILD.gn
+++ b/chromeos/services/assistant/public/shared/BUILD.gn
@@ -17,7 +17,9 @@
     "utils.h",
   ]
 
-  if (enable_cros_libassistant) {
+  # Some Quick Answers code depend on constants.
+  # TODO(crbug.com/406795923): move them out to a better place.
+  if (is_chrome_branded) {
     sources += [ "//chromeos/assistant/internal/constants.cc" ]
   } else {
     sources += [ "constants.cc" ]
diff --git a/clank b/clank
index 72d9d8f..26de4f3 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 72d9d8f0bcf477b826e8abd3986b2b24eff275ff
+Subproject commit 26de4f3045d4ffc355f172a228fce0a245976f37
diff --git a/components/autofill/content/browser/BUILD.gn b/components/autofill/content/browser/BUILD.gn
index dc1c948..903cad0 100644
--- a/components/autofill/content/browser/BUILD.gn
+++ b/components/autofill/content/browser/BUILD.gn
@@ -118,6 +118,7 @@
     "autofill_internals_log_router_unittest.cc",
     "content_autofill_driver_factory_unittest.cc",
     "content_autofill_driver_unittest.cc",
+    "content_identity_credential_delegate_unittest.cc",
     "scoped_autofill_managers_observation_unittest.cc",
   ]
 
diff --git a/components/autofill/content/browser/DEPS b/components/autofill/content/browser/DEPS
index 5b7c0bd0..0021f65 100644
--- a/components/autofill/content/browser/DEPS
+++ b/components/autofill/content/browser/DEPS
@@ -13,6 +13,7 @@
   "+services/service_manager/public/mojom",
   "+third_party/blink/public/common",
   "+third_party/blink/public/mojom/permissions_policy",
+  "+third_party/blink/public/mojom/webid",
 ]
 
 specific_include_rules = {
diff --git a/components/autofill/content/browser/content_identity_credential_delegate.cc b/components/autofill/content/browser/content_identity_credential_delegate.cc
index b2e4cb87..100fcb6 100644
--- a/components/autofill/content/browser/content_identity_credential_delegate.cc
+++ b/components/autofill/content/browser/content_identity_credential_delegate.cc
@@ -4,6 +4,7 @@
 
 #include "components/autofill/content/browser/content_identity_credential_delegate.h"
 
+#include "base/functional/callback.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_field.h"
@@ -17,20 +18,31 @@
 
 ContentIdentityCredentialDelegate::ContentIdentityCredentialDelegate(
     content::WebContents* web_contents)
-    : web_contents_(web_contents) {}
+    : ContentIdentityCredentialDelegate(base::BindRepeating(
+          [](content::WebContents* web_contents)
+              -> content::FederatedAuthAutofillSource* {
+            return content::FederatedAuthAutofillSource::FromPage(
+                web_contents->GetPrimaryPage());
+          },
+          web_contents)) {}
+
+ContentIdentityCredentialDelegate::ContentIdentityCredentialDelegate(
+    base::RepeatingCallback<content::FederatedAuthAutofillSource*()> source)
+    : source_(std::move(source)) {}
+
+ContentIdentityCredentialDelegate::~ContentIdentityCredentialDelegate() =
+    default;
 
 std::vector<Suggestion>
 ContentIdentityCredentialDelegate::GetVerifiedAutofillSuggestions(
     const FieldType& field_type) const {
-  if (!(field_type == EMAIL_ADDRESS || field_type == NAME_FIRST ||
-        field_type == NAME_FULL)) {
+  if (!(field_type == PASSWORD || field_type == EMAIL_ADDRESS ||
+        field_type == NAME_FIRST || field_type == NAME_FULL)) {
     return {};
   }
   // TODO(crbug.com/380367784): reproduce and add a test to make sure this
   // works properly when FedCM is called from inner frames.
-  content::FederatedAuthAutofillSource* source =
-      content::FederatedAuthAutofillSource::FromPage(
-          web_contents_->GetPrimaryPage());
+  content::FederatedAuthAutofillSource* source = source_.Run();
 
   if (!source) {
     return {};
@@ -48,7 +60,6 @@
     Suggestion suggestion(SuggestionType::kIdentityCredential);
     auto payload = Suggestion::IdentityCredentialPayload(
         account->identity_provider->idp_metadata.config_url, account->id);
-    suggestion.payload = payload;
 
     if (field_type == EMAIL_ADDRESS || field_type == NAME_FIRST ||
         field_type == NAME_FULL) {
@@ -66,6 +77,8 @@
       suggestion.labels.push_back({Suggestion::Text(l10n_util::GetStringUTF16(
           IDS_AUTOFILL_IDENTITY_CREDENTIAL_EMAIL_LABEL))});
     } else if (field_type == PASSWORD) {
+      suggestion.main_text =
+          Suggestion::Text(base::UTF8ToUTF16(account->email));
       suggestion.custom_icon = account->decoded_picture;
       // TODO(crbug.com/410421491): support more context.
       suggestion.labels.push_back({Suggestion::Text(l10n_util::GetStringFUTF16(
@@ -73,6 +86,7 @@
           base::UTF8ToUTF16(account->identity_provider->idp_for_display)))});
     }
 
+    suggestion.payload = payload;
     suggestions.push_back(std::move(suggestion));
   }
 
@@ -82,9 +96,7 @@
 void ContentIdentityCredentialDelegate::NotifySuggestionAccepted(
     const Suggestion& suggestion,
     OnFederatedTokenReceivedCallback callback) const {
-  content::FederatedAuthAutofillSource* source =
-      content::FederatedAuthAutofillSource::FromPage(
-          web_contents_->GetPrimaryPage());
+  content::FederatedAuthAutofillSource* source = source_.Run();
 
   if (!source) {
     return;
diff --git a/components/autofill/content/browser/content_identity_credential_delegate.h b/components/autofill/content/browser/content_identity_credential_delegate.h
index 08ad8e0..b202b7a 100644
--- a/components/autofill/content/browser/content_identity_credential_delegate.h
+++ b/components/autofill/content/browser/content_identity_credential_delegate.h
@@ -5,8 +5,10 @@
 #ifndef COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_IDENTITY_CREDENTIAL_DELEGATE_H_
 #define COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_IDENTITY_CREDENTIAL_DELEGATE_H_
 
+#include "base/functional/callback.h"
 #include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/integrators/identity_credential/identity_credential_delegate.h"
+#include "content/public/browser/federated_auth_autofill_source.h"
 #include "content/public/browser/web_contents.h"
 
 namespace autofill {
@@ -18,6 +20,13 @@
   explicit ContentIdentityCredentialDelegate(
       content::WebContents* web_contents);
 
+  // Exposed for tests to inject a mock `FederatedAuthAutofillSource` as a
+  // dependency.
+  explicit ContentIdentityCredentialDelegate(
+      base::RepeatingCallback<content::FederatedAuthAutofillSource*()> source);
+
+  ~ContentIdentityCredentialDelegate() override;
+
   std::vector<Suggestion> GetVerifiedAutofillSuggestions(
       const FieldType& field_type) const override;
 
@@ -26,7 +35,9 @@
       OnFederatedTokenReceivedCallback callback) const override;
 
  private:
-  raw_ptr<content::WebContents> web_contents_;
+  // Provides a `FederatedAuthAutofillSource`. Derived from `WebContents` in
+  // practice and mocked in tests.
+  base::RepeatingCallback<content::FederatedAuthAutofillSource*()> source_;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/content/browser/content_identity_credential_delegate_unittest.cc b/components/autofill/content/browser/content_identity_credential_delegate_unittest.cc
new file mode 100644
index 0000000..45af418
--- /dev/null
+++ b/components/autofill/content/browser/content_identity_credential_delegate_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/content/browser/content_identity_credential_delegate.h"
+
+#include "base/test/bind.h"
+#include "base/test/gtest_util.h"
+#include "components/autofill/core/browser/suggestions/suggestion.h"
+#include "content/public/browser/federated_auth_autofill_source.h"
+#include "content/public/browser/identity_request_dialog_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
+
+namespace autofill {
+
+namespace {
+
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+
+class MockFederatedAuthAutofillSource
+    : public content::FederatedAuthAutofillSource {
+ public:
+  MockFederatedAuthAutofillSource() = default;
+  ~MockFederatedAuthAutofillSource() override = default;
+
+  MOCK_METHOD(const std::optional<std::vector<IdentityRequestAccountPtr>>,
+              GetAutofillSuggestions,
+              (),
+              (const override));
+  MOCK_METHOD(void,
+              NotifyAutofillSuggestionAccepted,
+              (const GURL& idp,
+               const std::string& account_id,
+               OnFederatedTokenReceivedCallback callback),
+              (override));
+};
+
+class ContentIdentityCredentialDelegateTest : public ::testing::Test {};
+
+TEST_F(ContentIdentityCredentialDelegateTest, NoPendingRequest) {
+  ContentIdentityCredentialDelegate delegate(base::BindLambdaForTesting([]() {
+    content::FederatedAuthAutofillSource* result = nullptr;
+    return result;
+  }));
+  std::vector<Suggestion> suggestions =
+      delegate.GetVerifiedAutofillSuggestions(EMAIL_ADDRESS);
+  EXPECT_EQ(0ul, suggestions.size());
+}
+
+TEST_F(ContentIdentityCredentialDelegateTest, GetVerifiedEmailRequest) {
+  MockFederatedAuthAutofillSource mock;
+
+  ContentIdentityCredentialDelegate delegate(
+      base::BindLambdaForTesting([&mock]() {
+        content::FederatedAuthAutofillSource* result = &mock;
+        return result;
+      }));
+
+  IdentityRequestAccountPtr account =
+      base::MakeRefCounted<content::IdentityRequestAccount>(
+          "id", "display_identifier", "display_name", "john@email.com", "name",
+          "given_name", GURL(), "phone", "username",
+          /*login_hints=*/std::vector<std::string>(),
+          /*domain_hints=*/std::vector<std::string>(),
+          /*labels=*/std::vector<std::string>());
+  content::IdentityProviderMetadata metadata;
+  metadata.config_url = GURL("https://idp.example");
+
+  std::vector<content::IdentityRequestDialogDisclosureField> disclosures;
+
+  scoped_refptr<content::IdentityProviderData> identity_provider =
+      base::MakeRefCounted<content::IdentityProviderData>(
+          "idp.example", metadata,
+          content::ClientMetadata((GURL()), (GURL()), (GURL()), (gfx::Image())),
+          blink::mojom::RpContext::kSignIn, disclosures, false);
+
+  account->identity_provider = identity_provider;
+  std::vector<IdentityRequestAccountPtr> accounts = {account};
+
+  EXPECT_CALL(mock, GetAutofillSuggestions).WillOnce(Return(accounts));
+
+  std::vector<Suggestion> suggestions =
+      delegate.GetVerifiedAutofillSuggestions(EMAIL_ADDRESS);
+  EXPECT_EQ(1ul, suggestions.size());
+
+  Suggestion suggestion = suggestions[0];
+  EXPECT_EQ(suggestion.main_text.value, u"john@email.com");
+  EXPECT_EQ(suggestion.labels.size(), 1ul);
+  EXPECT_EQ(suggestion.minor_texts.size(), 1ul);
+
+  // Expect the payload to be populated properly.
+  Suggestion::IdentityCredentialPayload payload =
+      suggestion.GetPayload<Suggestion::IdentityCredentialPayload>();
+  EXPECT_EQ(payload.account_id, "id");
+  EXPECT_EQ(payload.config_url, GURL("https://idp.example"));
+  EXPECT_EQ(payload.fields.size(), 3ul);
+  EXPECT_TRUE(payload.fields.contains(EMAIL_ADDRESS));
+  EXPECT_EQ(payload.fields[EMAIL_ADDRESS], u"john@email.com");
+  EXPECT_TRUE(payload.fields.contains(NAME_FULL));
+  EXPECT_EQ(payload.fields[NAME_FULL], u"name");
+  EXPECT_TRUE(payload.fields.contains(NAME_FIRST));
+  EXPECT_EQ(payload.fields[NAME_FIRST], u"given_name");
+}
+
+TEST_F(ContentIdentityCredentialDelegateTest, GetSuggestionsForPassword) {
+  MockFederatedAuthAutofillSource mock;
+
+  ContentIdentityCredentialDelegate delegate(
+      base::BindLambdaForTesting([&mock]() {
+        content::FederatedAuthAutofillSource* result = &mock;
+        return result;
+      }));
+
+  IdentityRequestAccountPtr account =
+      base::MakeRefCounted<content::IdentityRequestAccount>(
+          "id", "display_identifier", "display_name", "john@email.com", "name",
+          "given_name", GURL(), "phone", "username",
+          /*login_hints=*/std::vector<std::string>(),
+          /*domain_hints=*/std::vector<std::string>(),
+          /*labels=*/std::vector<std::string>());
+  content::IdentityProviderMetadata metadata;
+  metadata.config_url = GURL("https://idp.example");
+
+  std::vector<content::IdentityRequestDialogDisclosureField> disclosures;
+
+  scoped_refptr<content::IdentityProviderData> identity_provider =
+      base::MakeRefCounted<content::IdentityProviderData>(
+          "idp.example", metadata,
+          content::ClientMetadata((GURL()), (GURL()), (GURL()), (gfx::Image())),
+          blink::mojom::RpContext::kSignIn, disclosures, false);
+
+  account->identity_provider = identity_provider;
+  std::vector<IdentityRequestAccountPtr> accounts = {account};
+
+  EXPECT_CALL(mock, GetAutofillSuggestions).WillOnce(Return(accounts));
+
+  std::vector<Suggestion> suggestions =
+      delegate.GetVerifiedAutofillSuggestions(PASSWORD);
+  EXPECT_EQ(1ul, suggestions.size());
+
+  Suggestion suggestion = suggestions[0];
+  EXPECT_EQ(suggestion.main_text.value, u"john@email.com");
+  EXPECT_EQ(suggestion.labels.size(), 1ul);
+  EXPECT_EQ(suggestion.minor_texts.size(), 0ul);
+}
+
+}  // namespace
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/payments/bnpl_manager.cc b/components/autofill/core/browser/payments/bnpl_manager.cc
index b0f734b..be1caad 100644
--- a/components/autofill/core/browser/payments/bnpl_manager.cc
+++ b/components/autofill/core/browser/payments/bnpl_manager.cc
@@ -41,8 +41,7 @@
                                    uint64_t extracted_amount_in_micros) {
   // For MVP, BNPL will only target US users and support USD.
   return bnpl_issuer.IsEligibleAmount(extracted_amount_in_micros,
-                                      /*currency=*/"USD") &&
-         base::FeatureList::IsEnabled(features::kAutofillEnableBuyNowPayLater);
+                                      /*currency=*/"USD");
 }
 
 bool ShouldShowPermanentErrorDialog(
@@ -105,6 +104,10 @@
 
 void BnplManager::NotifyOfSuggestionGeneration(
     const AutofillSuggestionTriggerSource trigger_source) {
+  if (!base::FeatureList::IsEnabled(features::kAutofillEnableBuyNowPayLater)) {
+    return;
+  }
+
   update_suggestions_barrier_callback_ = base::BarrierCallback<
       std::variant<SuggestionsShownResponse, std::optional<uint64_t>>>(
       2U, base::BindOnce(&BnplManager::MaybeUpdateSuggestionsWithBnpl,
@@ -114,6 +117,10 @@
 void BnplManager::OnSuggestionsShown(
     base::span<const Suggestion> suggestions,
     UpdateSuggestionsCallback update_suggestions_callback) {
+  if (!update_suggestions_barrier_callback_.has_value()) {
+    return;
+  }
+
   // Do not proceed to calling the barrier callback, if the suggestion list
   // already contains a buy-now-pay-later-entry (which is triggered after
   // updating the original suggestion list).
@@ -131,6 +138,10 @@
 
 void BnplManager::OnAmountExtractionReturned(
     const std::optional<uint64_t>& extracted_amount) {
+  if (!update_suggestions_barrier_callback_.has_value()) {
+    return;
+  }
+
   if (update_suggestions_barrier_callback_.has_value()) {
     update_suggestions_barrier_callback_->Run(extracted_amount);
   }
diff --git a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc
index 8e9ee8b..c5396d2 100644
--- a/components/autofill/core/browser/payments/bnpl_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/bnpl_manager_unittest.cc
@@ -1091,6 +1091,33 @@
       1);
 }
 
+// Tests that BnplSuggestionNotShownReason will not be logged if BNPL feature
+// flag is disabled and the amount extraction engine fails to pass in a valid
+// value.
+TEST_F(
+    BnplManagerTest,
+    AddBnplSuggestion_NoAmountPassedIn_BnplSuggestionNotShownReasonNotLogged_BnplDisabled) {
+  scoped_feature_list_.Reset();
+  scoped_feature_list_.InitWithFeatures(
+      /*enabled_features=*/{features::kAutofillEnableBuyNowPayLaterSyncing},
+      /*disabled_features=*/{features::kAutofillEnableBuyNowPayLater});
+
+  base::HistogramTester histogram_tester;
+
+  // Add one linked issuer to payments data manager.
+  SetUpLinkedBnplIssuer(
+      /*price_lower_bound_in_micros=*/40,
+      /*price_higher_bound_in_micros=*/1000, IssuerId::kBnplAffirm,
+      /*instrument_id=*/1234);
+
+  TriggerBnplUpdateSuggestionsFlow(/*expect_suggestions_are_updated=*/false,
+                                   /*extracted_amount=*/std::nullopt);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.Bnpl.SuggestionNotShownReason",
+      autofill_metrics::BnplSuggestionNotShownReason::kAmountExtractionFailure,
+      0);
+}
+
 // Tests that update suggestions callback will not be called if the extracted
 // amount is not supported by available BNPL issuers.
 TEST_F(BnplManagerTest, AddBnplSuggestion_AmountNotSupported) {
@@ -1174,6 +1201,35 @@
       1);
 }
 
+// Tests that BnplSuggestionNotShownReason will not be logged if BNPL feature
+// flag is disabled and the extracted amount is not supported by available
+// BNPL issuers.
+TEST_F(
+    BnplManagerTest,
+    AddBnplSuggestion_AmountNotSupported_BnplSuggestionNotShownReasonNotLogged_BnplDisabled) {
+  scoped_feature_list_.Reset();
+  scoped_feature_list_.InitWithFeatures(
+      /*enabled_features=*/{features::kAutofillEnableBuyNowPayLaterSyncing},
+      /*disabled_features=*/{features::kAutofillEnableBuyNowPayLater});
+
+  base::HistogramTester histogram_tester;
+
+  // Add one linked issuer to payments data manager.
+  SetUpLinkedBnplIssuer(
+      /*price_lower_bound_in_micros=*/40,
+      /*price_higher_bound_in_micros=*/1000, IssuerId::kBnplAffirm,
+      /*instrument_id=*/1234);
+
+  TriggerBnplUpdateSuggestionsFlow(
+      /*expect_suggestions_are_updated=*/false,
+      /*extracted_amount=*/30'000'000ULL);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.Bnpl.SuggestionNotShownReason",
+      autofill_metrics::BnplSuggestionNotShownReason::
+          kCheckoutAmountNotSupported,
+      0);
+}
+
 // Tests that update suggestions callback will not be called if the BNPL
 // feature flag is disabled.
 TEST_F(BnplManagerTest, AddBnplSuggestion_BnplFeatureDisabled) {
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 1188e20..cfc400c4 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -806,6 +806,9 @@
     <message name="IDS_AUTOFILL_PAYMENTS_MANUAL_FALLBACK_VIRTUAL_CARD_INSTRUCTION_TEXT" desc="Text displayed in the manual fallback instructing users on how to add their virtual card details to checkout.">
       Tap virtual card details to add them to checkout.
     </message>
+    <message name="IDS_AUTOFILL_PAYMENTS_MANUAL_FALLBACK_CARD_INFO_RETRIEVAL_INSTRUCTION_TEXT" desc="Text displayed in the manual fallback instructing users on how to add their card details to checkout.">
+      Tap card details to add them to checkout.
+    </message>
   </if>
   <message name="IDS_AUTOFILL_VIRTUAL_CARD_STANDALONE_CVC_SUGGESTION_IPH_BUBBLE_LABEL" desc="Text shown in the IPH bubble for filling in CVC on a virtual card that is saved on file of a merchant website.">
       Use CVC for this virtual card
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_PAYMENTS_MANUAL_FALLBACK_CARD_INFO_RETRIEVAL_INSTRUCTION_TEXT.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_PAYMENTS_MANUAL_FALLBACK_CARD_INFO_RETRIEVAL_INSTRUCTION_TEXT.png.sha1
new file mode 100644
index 0000000..0844e50
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_PAYMENTS_MANUAL_FALLBACK_CARD_INFO_RETRIEVAL_INSTRUCTION_TEXT.png.sha1
@@ -0,0 +1 @@
+77850637db706cc57e17364e90e04762ec2281bb
\ No newline at end of file
diff --git a/components/collaboration/DEPS b/components/collaboration/DEPS
index 1c4b472..4c91e3fe 100644
--- a/components/collaboration/DEPS
+++ b/components/collaboration/DEPS
@@ -2,6 +2,7 @@
   "+components/data_sharing/public",
   "+components/data_sharing/test_support",
   "+components/keyed_service",
+  "+components/prefs/pref_change_registrar.h",
   "+components/prefs/pref_service.h",
   "+components/prefs/pref_registry_simple.h",
   "+components/saved_tab_groups/public",
diff --git a/components/collaboration/internal/BUILD.gn b/components/collaboration/internal/BUILD.gn
index a4341423..b4fa432d6 100644
--- a/components/collaboration/internal/BUILD.gn
+++ b/components/collaboration/internal/BUILD.gn
@@ -45,6 +45,7 @@
   public_deps = [
     "//base",
     "//components/collaboration/public",
+    "//components/collaboration/public:prefs",
     "//components/data_sharing/public",
     "//components/keyed_service/core",
   ]
diff --git a/components/collaboration/internal/collaboration_service_impl.cc b/components/collaboration/internal/collaboration_service_impl.cc
index 16eaa45..7a7336b0 100644
--- a/components/collaboration/internal/collaboration_service_impl.cc
+++ b/components/collaboration/internal/collaboration_service_impl.cc
@@ -10,6 +10,7 @@
 #include "components/collaboration/internal/metrics.h"
 #include "components/collaboration/public/collaboration_flow_type.h"
 #include "components/collaboration/public/collaboration_utils.h"
+#include "components/collaboration/public/pref_names.h"
 #include "components/collaboration/public/service_status.h"
 #include "components/data_sharing/public/data_sharing_service.h"
 #include "components/data_sharing/public/features.h"
@@ -54,10 +55,16 @@
   identity_manager_observer_.Observe(identity_manager_);
 
   current_status_.collaboration_status = GetCollaborationStatus();
+
+  registrar_.Init(profile_prefs_);
+  registrar_.Add(prefs::kSharedTabGroupsManagedAccountSetting,
+                 base::BindRepeating(&CollaborationServiceImpl::OnPrefChanged,
+                                     base::Unretained(this)));
 }
 
 CollaborationServiceImpl::~CollaborationServiceImpl() {
   join_controllers_.clear();
+  registrar_.RemoveAll();
 }
 
 bool CollaborationServiceImpl::IsEmptyService() {
@@ -340,7 +347,7 @@
 
 CollaborationStatus CollaborationServiceImpl::GetCollaborationStatus() {
   // Check if device policy allow signin.
-  if (!profile_prefs_->GetBoolean(prefs::kSigninAllowed)) {
+  if (!profile_prefs_->GetBoolean(::prefs::kSigninAllowed)) {
     return CollaborationStatus::kDisabledForPolicy;
   }
 
@@ -365,9 +372,6 @@
     return status;
   }
 
-  // Figure out if collaboration feature is disabled by account policy. This
-  // early check allows to not disable collaboration feature when the user need
-  // to refresh their account (refresh tokens unavailable).
   CoreAccountInfo account =
       identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);
   if (!signin::AccountManagedStatusFinder::MayBeEnterpriseUserBasedOnEmail(
@@ -375,6 +379,7 @@
     return status;
   }
 
+  // Enterprise account handling.
   if (!account_managed_status_finder_) {
     account_managed_status_finder_ =
         std::make_unique<signin::AccountManagedStatusFinder>(
@@ -384,6 +389,30 @@
             base::Seconds(5));
   }
 
+  // Enterprise V2: Check enterprise policy to allow/disallow collaboration
+  // feature.
+  if (base::FeatureList::IsEnabled(
+          data_sharing::features::kCollaborationEntrepriseV2)) {
+    switch (account_managed_status_finder_->GetOutcome()) {
+      case Outcome::kConsumerGmail:
+      case Outcome::kConsumerWellKnown:
+      case Outcome::kConsumerNotWellKnown:
+        break;
+      default:
+        if (profile_prefs_->GetInteger(
+                collaboration::prefs::kSharedTabGroupsManagedAccountSetting) ==
+            static_cast<int>(
+                prefs::SharedTabGroupsManagedAccountSetting::kDisabled)) {
+          return CollaborationStatus::kDisabledForPolicy;
+        }
+    }
+
+    return status;
+  }
+
+  // Enterprise V1: Figure out if collaboration feature is disabled by account
+  // policy. This early check allows to not disable collaboration feature when
+  // the user need to refresh their account (refresh tokens unavailable).
   switch (account_managed_status_finder_->GetOutcome()) {
     case Outcome::kPending:
       status = CollaborationStatus::kDisabledPending;
@@ -464,4 +493,8 @@
   std::move(callback).Run(/*success=*/false);
 }
 
+void CollaborationServiceImpl::OnPrefChanged() {
+  RefreshServiceStatus();
+}
+
 }  // namespace collaboration
diff --git a/components/collaboration/internal/collaboration_service_impl.h b/components/collaboration/internal/collaboration_service_impl.h
index cd50143..f80432d 100644
--- a/components/collaboration/internal/collaboration_service_impl.h
+++ b/components/collaboration/internal/collaboration_service_impl.h
@@ -10,6 +10,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "components/collaboration/public/collaboration_service.h"
+#include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/identity_manager/account_managed_status_finder.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
@@ -115,6 +116,7 @@
       const data_sharing::GroupId& group_id,
       base::OnceCallback<void(bool)> callback,
       data_sharing::DataSharingService::PeopleGroupActionOutcome result);
+  void OnPrefChanged();
 
   ServiceStatus current_status_;
   base::ScopedObservation<syncer::SyncService, syncer::SyncServiceObserver>
@@ -138,7 +140,10 @@
   // Service providing information about sync.
   raw_ptr<syncer::SyncService> sync_service_;
 
-  const raw_ptr<PrefService> profile_prefs_;
+  // Used to listen for sharing policy pref change notification.
+  PrefChangeRegistrar registrar_;
+
+  raw_ptr<PrefService> profile_prefs_;
 
   // Started flows.
   // Join controllers: <GroupId, CollaborationController>
diff --git a/components/collaboration/internal/messaging/messaging_backend_service_impl.cc b/components/collaboration/internal/messaging/messaging_backend_service_impl.cc
index c565c7ca..feac915 100644
--- a/components/collaboration/internal/messaging/messaging_backend_service_impl.cc
+++ b/components/collaboration/internal/messaging/messaging_backend_service_impl.cc
@@ -609,8 +609,6 @@
     return;
   }
 
-  std::vector<base::Uuid> cleared_tab_ids;
-
   // Since the dirty bits are cleared from DB, hide any dirty dots from the tabs
   // and tab groups if they are already showing.
   for (auto& message : cleared_messages) {
@@ -622,11 +620,6 @@
     NotifyHidePersistentMessagesForTypes(
         persistent_message, {PersistentNotificationType::CHIP,
                              PersistentNotificationType::DIRTY_TAB});
-    if (persistent_message.attribution.tab_metadata.has_value() &&
-        persistent_message.attribution.tab_metadata->sync_tab_id.has_value()) {
-      cleared_tab_ids.emplace_back(
-          persistent_message.attribution.tab_metadata->sync_tab_id.value());
-    }
 
     if (persistent_message.attribution.tab_group_metadata &&
         persistent_message.attribution.tab_group_metadata->sync_tab_group_id) {
@@ -637,11 +630,6 @@
                                                tab_group_id);
     }
   }
-
-  for (const base::Uuid& tab_id : cleared_tab_ids) {
-    tab_group_sync_service_->UpdateTabLastSeenTime(
-        tab_group->saved_guid(), tab_id, tab_groups::TriggerSource::LOCAL);
-  }
 }
 
 void MessagingBackendServiceImpl::ClearDirtyTabMessagesForGroup(
diff --git a/components/collaboration/public/pref_names.h b/components/collaboration/public/pref_names.h
index 7c2e708..664d41c80 100644
--- a/components/collaboration/public/pref_names.h
+++ b/components/collaboration/public/pref_names.h
@@ -12,6 +12,11 @@
 // Whether to allow shared tab group features for managed account.
 extern const char kSharedTabGroupsManagedAccountSetting[];
 
+enum class SharedTabGroupsManagedAccountSetting {
+  kEnabled = 0,
+  kDisabled = 1,
+};
+
 // Registers user preferences related to permissions.
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
diff --git a/components/ip_protection/common/BUILD.gn b/components/ip_protection/common/BUILD.gn
index fa5566f..306b11f8 100644
--- a/components/ip_protection/common/BUILD.gn
+++ b/components/ip_protection/common/BUILD.gn
@@ -795,6 +795,7 @@
     ":probabilistic_reveal_token_registry",
     "//base",
     "//base/test:test_support",
+    "//net",
     "//testing/gmock",
     "//testing/gtest",
     "//url",
diff --git a/components/ip_protection/common/ip_protection_core_impl.cc b/components/ip_protection/common/ip_protection_core_impl.cc
index b42d29c..3941c388 100644
--- a/components/ip_protection/common/ip_protection_core_impl.cc
+++ b/components/ip_protection/common/ip_protection_core_impl.cc
@@ -96,7 +96,9 @@
                                                : MdlType::kRegularBrowsing)
                     : MdlType::kIncognito) {
   net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
-  if (ip_protection_incognito && ipp_prt_manager_) {
+  bool should_request_prts = ip_protection_incognito ||
+       !net::features::kProbabilisticRevealTokensOnlyInIncognito.Get();
+  if (ipp_prt_manager_ && should_request_prts) {
     ipp_prt_manager_->RequestTokens();
   }
 }
@@ -258,8 +260,7 @@
           net::features::kEnableProbabilisticRevealTokens)) {
     return false;
   }
-  if (net::features::kAttachProbabilisticRevealTokensOnAllProxiedRequests
-          .Get()) {
+  if (net::features::kBypassProbabilisticRevealTokenRegistry.Get()) {
     return true;
   }
   return probabilistic_reveal_token_registry_->IsRegistered(request_url);
diff --git a/components/ip_protection/common/ip_protection_core_impl_unittest.cc b/components/ip_protection/common/ip_protection_core_impl_unittest.cc
index 207d9cd..af5baa4 100644
--- a/components/ip_protection/common/ip_protection_core_impl_unittest.cc
+++ b/components/ip_protection/common/ip_protection_core_impl_unittest.cc
@@ -161,9 +161,8 @@
                                                     std::nullopt) {}
   ~FakePRTManager() override = default;
   bool IsTokenAvailable() override { return response_.has_value(); }
-  std::optional<std::string> GetToken(
-      const std::string& top_level,
-      const std::string& third_party) override {
+  std::optional<std::string> GetToken(const std::string& top_level,
+                                      const std::string& third_party) override {
     return response_;
   }
   void SetMockResponse(std::optional<std::string> mock_response) {
@@ -818,10 +817,14 @@
       net::NetworkAnonymizationKey()));
 }
 
-TEST_F(IpProtectionCoreImplTest, IncognitoCoreCallsPRTRequest) {
+TEST_F(IpProtectionCoreImplTest,
+       IncognitoCoreCallsPRTRequestWhenIncognitoOnlyFeatureParamIsTrue) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      net::features::kEnableIpProtectionProxy);
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      net::features::kEnableProbabilisticRevealTokens,
+      {{net::features::kProbabilisticRevealTokensOnlyInIncognito.name,
+        base::ToString(true)}});
+
   auto ipp_prt_manager =
       std::make_unique<FakePRTManager>(std::make_unique<FakePRTFetcher>());
   std::string expected_token = "expected_token";
@@ -840,10 +843,14 @@
   EXPECT_EQ(maybe_token.value(), expected_token);
 }
 
-TEST_F(IpProtectionCoreImplTest, RegularCoreDoesNotCallPRTRequest) {
+TEST_F(IpProtectionCoreImplTest,
+       RegularCoreDoesNotCallPRTRequesWhenIncognitoOnlyFeatureParamIsTrue) {
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      net::features::kEnableIpProtectionProxy);
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      net::features::kEnableProbabilisticRevealTokens,
+      {{net::features::kProbabilisticRevealTokensOnlyInIncognito.name,
+        base::ToString(true)}});
+
   auto ipp_prt_manager =
       std::make_unique<FakePRTManager>(std::make_unique<FakePRTFetcher>());
   std::string expected_token = "expected_token";
@@ -861,6 +868,31 @@
   EXPECT_FALSE(maybe_token.has_value());
 }
 
+TEST_F(IpProtectionCoreImplTest,
+       RegularCoreCallsPRTRequestWhenIncognitoOnlyFeatureParamIsFalse) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      net::features::kEnableProbabilisticRevealTokens,
+      {{net::features::kProbabilisticRevealTokensOnlyInIncognito.name,
+        base::ToString(false)}});
+
+  auto ipp_prt_manager =
+      std::make_unique<FakePRTManager>(std::make_unique<FakePRTFetcher>());
+  std::string expected_token = "expected_token";
+  ipp_prt_manager->SetMockResponse(expected_token);
+
+  auto core = std::make_unique<IpProtectionCoreImpl>(
+      /*masked_domain_list_manager=*/nullptr,
+      /*ip_protection_proxy_config_manager=*/nullptr,
+      std::map<ProxyLayer, std::unique_ptr<IpProtectionTokenManager>>(),
+      /*probabilistic_reveal_token_registry=*/nullptr,
+      std::move(ipp_prt_manager),
+      /*is_ip_protection_enabled=*/true, /*ip_protection_incognito=*/false);
+  auto maybe_token = core->GetProbabilisticRevealToken("a", "b");
+  ASSERT_TRUE(maybe_token.has_value());
+  EXPECT_EQ(maybe_token.value(), expected_token);
+}
+
 TEST_F(IpProtectionCoreImplTest, GetPrtReturnsNulloptWhenNoManager) {
   auto core = std::make_unique<IpProtectionCoreImpl>(
       /*masked_domain_list_manager=*/nullptr,
@@ -937,14 +969,12 @@
   EXPECT_FALSE(core->ShouldRequestIncludeProbabilisticRevealToken(other_com));
 }
 
-TEST_F(
-    IpProtectionCoreImplTest,
-    RequestShouldIncludePRTWhenFeatureEnabledToAttachPRTsOnAllProxiedRequests) {
+TEST_F(IpProtectionCoreImplTest,
+       RequestShouldIncludePRTWhenFeatureEnabledToBypassRegistry) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       net::features::kEnableProbabilisticRevealTokens,
-      {{net::features::kAttachProbabilisticRevealTokensOnAllProxiedRequests
-            .name,
+      {{net::features::kBypassProbabilisticRevealTokenRegistry.name,
         base::ToString(true)}});
   FakeProbabilisticRevealTokenRegistry ipp_prt_registry;
   GURL example_com = GURL("https://example.com");
diff --git a/components/ip_protection/common/ip_protection_proxy_delegate.cc b/components/ip_protection/common/ip_protection_proxy_delegate.cc
index 1659f35..852b680 100644
--- a/components/ip_protection/common/ip_protection_proxy_delegate.cc
+++ b/components/ip_protection/common/ip_protection_proxy_delegate.cc
@@ -71,15 +71,6 @@
 
   result->set_is_mdl_match(true);
 
-  if (const std::optional<net::SchemefulSite>& maybe_top_frame_site =
-          network_anonymization_key.GetTopFrameSite();
-      maybe_top_frame_site.has_value()) {
-    result->SetPRTHeaderValue(
-        GetPRTHeaderValue(url, maybe_top_frame_site.value()));
-  } else {
-    result->SetPRTHeaderValue(std::nullopt);
-  }
-
   // Check availability. We do not proxy requests if:
   // - The allow list has not been populated.
   // - The request doesn't match the allow list.
@@ -130,6 +121,21 @@
   ProxyResolutionResult resolution_result =
       ClassifyRequest(url, network_anonymization_key, result);
   Telemetry().ProxyResolution(resolution_result);
+
+  const std::optional<net::SchemefulSite>& top_frame_site =
+      network_anonymization_key.GetTopFrameSite();
+  if (bool is_prt_eligible =
+          resolution_result == ProxyResolutionResult::kAttemptProxy ||
+          net::features::kEnableProbabilisticRevealTokensForNonProxiedRequests
+              .Get();
+      is_prt_eligible &&
+      !net::features::kProbabilisticRevealTokenFetchOnly.Get() &&
+      top_frame_site.has_value()) {
+    result->SetPRTHeaderValue(GetPRTHeaderValue(url, top_frame_site.value()));
+  } else {
+    result->SetPRTHeaderValue(std::nullopt);
+  }
+
   if (resolution_result != ProxyResolutionResult::kAttemptProxy) {
     return;
   }
@@ -154,8 +160,6 @@
   }
 
   if (VLOG_IS_ON(3)) {
-    std::optional<net::SchemefulSite> top_frame_site =
-        network_anonymization_key.GetTopFrameSite();
     VLOG(3) << "IPPD::OnResolveProxy(" << url << ", "
             << (top_frame_site.has_value() ? top_frame_site.value()
                                            : net::SchemefulSite())
diff --git a/components/ip_protection/common/ip_protection_proxy_delegate_unittest.cc b/components/ip_protection/common/ip_protection_proxy_delegate_unittest.cc
index f7fad01e..7b38860 100644
--- a/components/ip_protection/common/ip_protection_proxy_delegate_unittest.cc
+++ b/components/ip_protection/common/ip_protection_proxy_delegate_unittest.cc
@@ -1069,6 +1069,154 @@
   auto ipp_core = std::make_unique<MockIpProtectionCore>(
       &masked_domain_list_manager, &registry);
   ipp_core->SetPRT("serialized-prt");
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
+  auto delegate = CreateDelegate(ipp_core.get());
+  net::ProxyInfo result;
+  delegate->OnResolveProxy(destination_url,
+                           net::NetworkAnonymizationKey::CreateCrossSite(
+                               net::SchemefulSite(top_level_url)),
+                           "GET", net::ProxyRetryInfoMap(), &result);
+  std::optional<std::string> maybe_header_value = result.PRTHeaderValue();
+  ASSERT_TRUE(maybe_header_value.has_value());
+  EXPECT_EQ(maybe_header_value.value(),
+            ":" + base::Base64Encode("serialized-prt") + ":");
+  const auto maybe_item =
+      net::structured_headers::ParseBareItem(maybe_header_value.value());
+  ASSERT_TRUE(maybe_item.has_value());
+  EXPECT_EQ(maybe_item.value(),
+            net::structured_headers::Item(
+                "serialized-prt",
+                net::structured_headers::Item::ItemType::kByteSequenceType));
+}
+
+TEST_F(IpProtectionProxyDelegateTest, NoPRTHeaderWhenFetchOnlyFeatureEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeaturesAndParameters(
+      {{net::features::kEnableProbabilisticRevealTokens,
+        {{"ProbabilisticRevealTokenFetchOnly", "true"}}}},
+      {});
+
+  const GURL top_level_url =
+      GURL("https://sub.top.com:27272/another/path/arbitrary");
+  const GURL destination_url =
+      GURL("https://foo.example.com:1234/some/arbitrary/path/");
+  std::map<std::string, std::set<std::string>> mdl_map;
+  mdl_map["example.com"] = {};
+  auto masked_domain_list_manager = CreateMdlManager(mdl_map);
+  ProbabilisticRevealTokenRegistry registry;
+  registry.UpdateRegistry(CreateRegistryFromJson(R"json({
+    "domains": [
+      "example.com",
+    ]
+  })json"));
+  auto ipp_core = std::make_unique<MockIpProtectionCore>(
+      &masked_domain_list_manager, &registry);
+  ipp_core->SetPRT("serialized-prt");
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
+  auto delegate = CreateDelegate(ipp_core.get());
+  net::ProxyInfo result;
+  delegate->OnResolveProxy(destination_url,
+                           net::NetworkAnonymizationKey::CreateCrossSite(
+                               net::SchemefulSite(top_level_url)),
+                           "GET", net::ProxyRetryInfoMap(), &result);
+  std::optional<std::string> maybe_header_value = result.PRTHeaderValue();
+  ASSERT_FALSE(maybe_header_value.has_value());
+}
+
+TEST_F(IpProtectionProxyDelegateTest,
+       PRTHeaderNotAddedToNonProxiedRequestsByDefault) {
+  const GURL top_level_url =
+      GURL("https://sub.top.com:27272/another/path/arbitrary");
+  const GURL destination_url =
+      GURL("https://foo.example.com:1234/some/arbitrary/path/");
+  // Empty MDL.
+  std::map<std::string, std::set<std::string>> mdl_map;
+  auto masked_domain_list_manager = CreateMdlManager(mdl_map);
+  ProbabilisticRevealTokenRegistry registry;
+  registry.UpdateRegistry(CreateRegistryFromJson(R"json({
+    "domains": [
+      "example.com",
+    ]
+  })json"));
+  auto ipp_core = std::make_unique<MockIpProtectionCore>(
+      &masked_domain_list_manager, &registry);
+  ipp_core->SetPRT("serialized-prt");
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
+  auto delegate = CreateDelegate(ipp_core.get());
+  net::ProxyInfo result;
+  delegate->OnResolveProxy(destination_url,
+                           net::NetworkAnonymizationKey::CreateCrossSite(
+                               net::SchemefulSite(top_level_url)),
+                           "GET", net::ProxyRetryInfoMap(), &result);
+  std::optional<std::string> maybe_header_value = result.PRTHeaderValue();
+  ASSERT_FALSE(maybe_header_value.has_value());
+}
+
+TEST_F(IpProtectionProxyDelegateTest,
+       PRTHeaderNotAddedToNonProxiedRequestsWhenFeatureDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeaturesAndParameters(
+      {{net::features::kEnableProbabilisticRevealTokens,
+        {{"EnableProbabilisticRevealTokensForNonProxiedRequests", "false"}}}},
+      {});
+
+  const GURL top_level_url =
+      GURL("https://sub.top.com:27272/another/path/arbitrary");
+  const GURL destination_url =
+      GURL("https://foo.example.com:1234/some/arbitrary/path/");
+  // Empty MDL.
+  std::map<std::string, std::set<std::string>> mdl_map;
+  auto masked_domain_list_manager = CreateMdlManager(mdl_map);
+  ProbabilisticRevealTokenRegistry registry;
+  registry.UpdateRegistry(CreateRegistryFromJson(R"json({
+    "domains": [
+      "example.com",
+    ]
+  })json"));
+  auto ipp_core = std::make_unique<MockIpProtectionCore>(
+      &masked_domain_list_manager, &registry);
+  ipp_core->SetPRT("serialized-prt");
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
+  auto delegate = CreateDelegate(ipp_core.get());
+  net::ProxyInfo result;
+  delegate->OnResolveProxy(destination_url,
+                           net::NetworkAnonymizationKey::CreateCrossSite(
+                               net::SchemefulSite(top_level_url)),
+                           "GET", net::ProxyRetryInfoMap(), &result);
+  std::optional<std::string> maybe_header_value = result.PRTHeaderValue();
+  ASSERT_FALSE(maybe_header_value.has_value());
+}
+
+TEST_F(IpProtectionProxyDelegateTest,
+       PRTHeaderAddedToNonProxiedRequestsWhenFeatureEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeaturesAndParameters(
+      {{net::features::kEnableProbabilisticRevealTokens,
+        {{"EnableProbabilisticRevealTokensForNonProxiedRequests", "true"}}}},
+      {});
+
+  const GURL top_level_url =
+      GURL("https://sub.top.com:27272/another/path/arbitrary");
+  const GURL destination_url =
+      GURL("https://foo.example.com:1234/some/arbitrary/path/");
+  // Empty MDL.
+  std::map<std::string, std::set<std::string>> mdl_map;
+  auto masked_domain_list_manager = CreateMdlManager(mdl_map);
+  ProbabilisticRevealTokenRegistry registry;
+  registry.UpdateRegistry(CreateRegistryFromJson(R"json({
+    "domains": [
+      "example.com",
+    ]
+  })json"));
+  auto ipp_core = std::make_unique<MockIpProtectionCore>(
+      &masked_domain_list_manager, &registry);
+  ipp_core->SetPRT("serialized-prt");
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
   auto delegate = CreateDelegate(ipp_core.get());
   net::ProxyInfo result;
   delegate->OnResolveProxy(destination_url,
@@ -1104,7 +1252,9 @@
   })json"));
   auto ipp_core = std::make_unique<MockIpProtectionCore>(
       &masked_domain_list_manager, &registry);
-  // `ipp_core` does not have any tokens.
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
+  // `ipp_core` does not have any PRTs.
   auto delegate = CreateDelegate(ipp_core.get());
   net::ProxyInfo result;
   delegate->OnResolveProxy(destination_url,
@@ -1127,6 +1277,8 @@
   auto ipp_core = std::make_unique<MockIpProtectionCore>(
       &masked_domain_list_manager, &registry);
   ipp_core->SetPRT("prt-serialized");
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
   auto delegate = CreateDelegate(ipp_core.get());
   net::ProxyInfo result;
   delegate->OnResolveProxy(destination_url,
@@ -1206,6 +1358,8 @@
   auto ipp_core = std::make_unique<MockIpProtectionCore>(
       &masked_domain_list_manager, &registry,
       /*ip_protection_incognito=*/true, manager.get());
+  ipp_core->SetNextAuthToken(MakeAuthToken("Bearer: a-token"));
+  ipp_core->SetProxyList({MakeChain({"proxya", "proxyb"})});
 
   // Advance time for PRT manager to fetch PRTs.
   RunForTheSmallestTimeDelta();
diff --git a/components/ip_protection/common/probabilistic_reveal_token_registry.cc b/components/ip_protection/common/probabilistic_reveal_token_registry.cc
index 96c133a5..a64a8242 100644
--- a/components/ip_protection/common/probabilistic_reveal_token_registry.cc
+++ b/components/ip_protection/common/probabilistic_reveal_token_registry.cc
@@ -9,6 +9,7 @@
 #include "base/containers/flat_set.h"
 #include "base/strings/string_tokenizer.h"
 #include "base/values.h"
+#include "net/base/features.h"
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "url/gurl.h"
 
@@ -18,7 +19,16 @@
 constexpr char kDomainsFieldName[] = "domains";
 }  // namespace
 
-ProbabilisticRevealTokenRegistry::ProbabilisticRevealTokenRegistry() = default;
+ProbabilisticRevealTokenRegistry::ProbabilisticRevealTokenRegistry() {
+  if (net::features::kUseCustomProbabilisticRevealTokenRegistry.Get()) {
+    std::string custom_registry_csv =
+        net::features::kCustomProbabilisticRevealTokenRegistry.Get();
+    base::StringTokenizer registry_tokenizer(custom_registry_csv, ",");
+    while (registry_tokenizer.GetNext()) {
+      custom_domains_.insert(base::ToLowerASCII(registry_tokenizer.token()));
+    }
+  }
+}
 
 ProbabilisticRevealTokenRegistry::~ProbabilisticRevealTokenRegistry() = default;
 
@@ -40,6 +50,10 @@
     request_domain = request_domain.substr(0, request_domain.length() - 1);
   }
 
+  if (net::features::kUseCustomProbabilisticRevealTokenRegistry.Get()) {
+    return custom_domains_.contains(base::ToLowerASCII(request_domain));
+  }
+
   return domains_.contains(base::ToLowerASCII(request_domain));
 }
 
diff --git a/components/ip_protection/common/probabilistic_reveal_token_registry.h b/components/ip_protection/common/probabilistic_reveal_token_registry.h
index c5e77d88..36b27cc 100644
--- a/components/ip_protection/common/probabilistic_reveal_token_registry.h
+++ b/components/ip_protection/common/probabilistic_reveal_token_registry.h
@@ -31,6 +31,11 @@
 
  private:
   base::flat_set<std::string> domains_;
+
+  // This set of domains will be used instead of `domains_` if the
+  // `UseCustomProbabilisticRevealTokenRegistry` feature param is true.
+  // See `kCustomProbabilisticRevealTokenRegistry` in net/base/features.h.
+  base::flat_set<std::string> custom_domains_;
 };
 
 }  // namespace ip_protection
diff --git a/components/ip_protection/common/probabilistic_reveal_token_registry_unittest.cc b/components/ip_protection/common/probabilistic_reveal_token_registry_unittest.cc
index 524d31c..79f6a50f 100644
--- a/components/ip_protection/common/probabilistic_reveal_token_registry_unittest.cc
+++ b/components/ip_protection/common/probabilistic_reveal_token_registry_unittest.cc
@@ -9,7 +9,9 @@
 
 #include "base/check.h"
 #include "base/json/json_reader.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/values.h"
+#include "net/base/features.h"
 #include "testing/gmock/include/gmock/gmock-matchers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -114,4 +116,30 @@
   EXPECT_FALSE(registry_.IsRegistered(GURL("https://foo.com")));
 }
 
+TEST_F(ProbabilisticRevealTokenRegistryTest, CustomRegistryTest) {
+  // Set the custom registry feature param.
+  std::map<std::string, std::string> parameters;
+  parameters[net::features::kUseCustomProbabilisticRevealTokenRegistry.name] =
+      "true";
+  parameters[net::features::kCustomProbabilisticRevealTokenRegistry.name] =
+      "test.com,other.com";
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeatureWithParameters(
+      net::features::kEnableProbabilisticRevealTokens, std::move(parameters));
+
+  // Set the standard registry.
+  ProbabilisticRevealTokenRegistry registry;
+  registry.UpdateRegistry(CreateRegistryFromJson(R"json({
+    "domains": [
+      "example.com"
+    ]
+  })json"));
+
+  EXPECT_FALSE(registry.IsRegistered(GURL("https://example.com")));
+  EXPECT_TRUE(registry.IsRegistered(GURL("https://test.com")));
+  EXPECT_TRUE(registry.IsRegistered(GURL("https://foo.test.com")));
+  EXPECT_TRUE(registry.IsRegistered(GURL("https://other.com")));
+  EXPECT_FALSE(registry.IsRegistered(GURL("https://foo.com")));
+}
+
 }  // namespace ip_protection
diff --git a/components/live_caption/caption_bubble_settings.h b/components/live_caption/caption_bubble_settings.h
index 6dcc9c64..789d7a8 100644
--- a/components/live_caption/caption_bubble_settings.h
+++ b/components/live_caption/caption_bubble_settings.h
@@ -52,6 +52,8 @@
   virtual void SetLiveTranslateTargetLanguageCode(
       std::string_view language_code) = 0;
 
+  virtual bool ShouldAdjustPositionOnExpand() = 0;
+
  protected:
   CaptionBubbleSettings() = default;
 };
diff --git a/components/live_caption/live_caption_bubble_settings.cc b/components/live_caption/live_caption_bubble_settings.cc
index 18d61f9..85e10ab 100644
--- a/components/live_caption/live_caption_bubble_settings.cc
+++ b/components/live_caption/live_caption_bubble_settings.cc
@@ -81,4 +81,8 @@
                             language_code);
 }
 
+bool LiveCaptionBubbleSettings::ShouldAdjustPositionOnExpand() {
+  return false;
+}
+
 }  // namespace captions
diff --git a/components/live_caption/live_caption_bubble_settings.h b/components/live_caption/live_caption_bubble_settings.h
index b5376a9f..b8b1e04 100644
--- a/components/live_caption/live_caption_bubble_settings.h
+++ b/components/live_caption/live_caption_bubble_settings.h
@@ -44,6 +44,8 @@
   void SetLiveTranslateTargetLanguageCode(
       std::string_view language_code) override;
 
+  bool ShouldAdjustPositionOnExpand() override;
+
  private:
   const raw_ptr<PrefService> profile_prefs_;
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index facd9a6..8be9cd9c 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -1012,6 +1012,12 @@
   // The change of expanded state may cause the title to change visibility, and
   // it surely causes the content height to change, so redraw the bubble.
   Redraw();
+  if (caption_bubble_settings_->ShouldAdjustPositionOnExpand() && model_ &&
+      is_expanded_) {
+    model_->GetContext()->GetBounds(
+        base::BindOnce(&CaptionBubble::AdjustPosition,
+                       weak_ptr_factory_.GetWeakPtr(), model_->unique_id()));
+  }
 }
 
 void CaptionBubble::SwapButtons(views::Button* first_button,
@@ -1582,6 +1588,22 @@
   GetWidget()->SetBounds(target_bounds);
 }
 
+void CaptionBubble::AdjustPosition(CaptionBubbleModel::Id model_id,
+                                   const gfx::Rect& context_rect) {
+  // We shouldn't reposition ourselves into the context rect of a model that is
+  // no longer active.
+  if (model_ == nullptr || model_->unique_id() != model_id) {
+    return;
+  }
+  gfx::Rect inset_rect = context_rect;
+  inset_rect.Inset(gfx::Insets(kMinAnchorMarginDip));
+  gfx::Rect bubble_bounds = GetBubbleBounds();
+  if (!inset_rect.Contains(bubble_bounds)) {
+    bubble_bounds.AdjustToFit(inset_rect);
+    GetWidget()->SetBounds(bubble_bounds);
+  }
+}
+
 void CaptionBubble::UpdateContentSize() {
   double text_scale_factor = GetTextScaleFactor();
   int width = kMaxWidthDip * text_scale_factor;
diff --git a/components/live_caption/views/caption_bubble.h b/components/live_caption/views/caption_bubble.h
index 01a359bb..2171fcd 100644
--- a/components/live_caption/views/caption_bubble.h
+++ b/components/live_caption/views/caption_bubble.h
@@ -241,6 +241,9 @@
   void RepositionInContextRect(CaptionBubbleModel::Id model_id,
                                const gfx::Rect& context_rect);
 
+  void AdjustPosition(CaptionBubbleModel::Id model_id,
+                      const gfx::Rect& context_rect);
+
   void MediaFoundationErrorCheckboxPressed();
   bool HasMediaFoundationError();
 
diff --git a/components/omnibox/browser/autocomplete_match.cc b/components/omnibox/browser/autocomplete_match.cc
index b86ebdb8..5524c20 100644
--- a/components/omnibox/browser/autocomplete_match.cc
+++ b/components/omnibox/browser/autocomplete_match.cc
@@ -932,10 +932,6 @@
   if (type == AutocompleteMatchType::HISTORY_CLUSTER)
     return omnibox::GROUP_HISTORY_CLUSTER;
 
-  if (type == AutocompleteMatchType::NULL_RESULT_MESSAGE) {
-    return omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP;
-  }
-
   return omnibox::GROUP_OTHER_NAVS;
 }
 
diff --git a/components/omnibox/browser/featured_search_provider.cc b/components/omnibox/browser/featured_search_provider.cc
index cbf1ebe..64969c3 100644
--- a/components/omnibox/browser/featured_search_provider.cc
+++ b/components/omnibox/browser/featured_search_provider.cc
@@ -58,6 +58,18 @@
 // # of enterprise suggestions in these 2 cases.
 constexpr int kMaxEnterpriseSuggestions = 4;
 
+// Scored higher than history URL provider suggestions since inputs like '@b'
+// would default 'bing.com' instead (history URL provider seems to ignore '@'
+// prefix in the input). Featured Enterprise search ranks higher than "ask
+// google" suggestions, which ranks higher than the other starter pack
+// suggestions.
+constexpr int kFeaturedEnterpriseSearchRelevance = 1470;
+constexpr int kGeminiRelevance = 1460;
+constexpr int kStarterPackRelevance = 1450;
+// IPH suggestions are grouped after all other suggestions. But they still
+// need to score within top N suggestions to be shown.
+constexpr int kIPHRelevance = 5000;
+
 std::string GetIphDismissedPrefNameFor(IphType iph_type) {
   switch (iph_type) {
     case IphType::kNone:
@@ -122,15 +134,6 @@
 
 }  // namespace
 
-// Scored higher than history URL provider suggestions since inputs like '@b'
-// would default 'bing.com' instead (history URL provider seems to ignore '@'
-// prefix in the input). Featured Enterprise search ranks higher than "ask
-// google" suggestions, which ranks higher than the other starter pack
-// suggestions.
-const int FeaturedSearchProvider::kGeminiRelevance = 1460;
-const int FeaturedSearchProvider::kFeaturedEnterpriseSearchRelevance = 1470;
-const int FeaturedSearchProvider::kStarterPackRelevance = 1450;
-
 FeaturedSearchProvider::FeaturedSearchProvider(
     AutocompleteProviderClient* client)
     : AutocompleteProvider(AutocompleteProvider::TYPE_FEATURED_SEARCH),
@@ -161,16 +164,16 @@
     return;
   }
 
-  if (ShouldShowEnterpriseFeaturedSearchIPHMatch(input)) {
-    AddFeaturedEnterpriseSearchIPHMatch();
-  } else if (ShouldShowGeminiIPHMatch(input)) {
-    AddIPHMatch(IphType::kGemini,
-                l10n_util::GetStringUTF16(IDS_OMNIBOX_GEMINI_IPH), u"@gemini",
-                u"", {}, true);
-  } else if (ShouldShowHistoryScopePromoIphMatch(input)) {
-    AddHistoryScopePromoIphMatch();
-  } else if (ShouldShowHistoryEmbeddingsScopePromoIphMatch(input)) {
-    AddHistoryEmbeddingsScopePromoIphMatch();
+  if (input.IsZeroSuggest()) {
+    if (ShouldShowEnterpriseFeaturedSearchIPHMatch()) {
+      AddFeaturedEnterpriseSearchIPHMatch();
+    } else if (ShouldShowGeminiIPHMatch()) {
+      AddGeminiIPHMatch();
+    } else if (ShouldShowHistoryScopePromoIphMatch()) {
+      AddHistoryScopePromoIphMatch();
+    } else if (ShouldShowHistoryEmbeddingsScopePromoIphMatch()) {
+      AddHistoryEmbeddingsScopePromoIphMatch();
+    }
   }
 
   AddFeaturedKeywordMatches(input);
@@ -192,6 +195,29 @@
   });
 }
 
+void FeaturedSearchProvider::RegisterDisplayedMatches(
+    const AutocompleteResult& result) {
+  auto iph_match = std::ranges::find_if(result, [](const auto& match) {
+    return match.iph_type != IphType::kNone;
+  });
+  IphType iph_type =
+      iph_match == result.end() ? IphType::kNone : iph_match->iph_type;
+
+  // `kHistoryEmbeddingsDisclaimer` has no shown limit.
+  if (!iph_shown_in_omnibox_session_ && iph_type != IphType::kNone &&
+      iph_type != IphType::kHistoryEmbeddingsDisclaimer) {
+    PrefService* prefs = client_->GetPrefs();
+    // `ShouldShowIPH()` shouldn't allow adding IPH matches if there is no
+    // `prefs`.
+    CHECK(prefs);
+    prefs->SetInteger(
+        GetIphShownCountPrefNameFor(iph_type),
+        prefs->GetInteger(GetIphShownCountPrefNameFor(iph_type)) + 1);
+    iph_shown_in_browser_session_count_++;
+    iph_shown_in_omnibox_session_ = true;
+  }
+}
+
 FeaturedSearchProvider::~FeaturedSearchProvider() = default;
 
 void FeaturedSearchProvider::AddFeaturedKeywordMatches(
@@ -312,11 +338,9 @@
                                          const std::u16string& matched_term,
                                          const std::u16string& iph_link_text,
                                          const GURL& iph_link_url,
+                                         int relevance,
                                          bool deletable) {
-  // IPH suggestions are grouped after all other suggestions. But they still
-  // need to score within top N suggestions to be shown.
-  constexpr int kRelevanceScore = 5000;
-  AutocompleteMatch match(this, kRelevanceScore, /*deletable=*/deletable,
+  AutocompleteMatch match(this, relevance, deletable,
                           AutocompleteMatchType::NULL_RESULT_MESSAGE);
 
   // Use this suggestion's contents field to display a message to the user that
@@ -326,6 +350,7 @@
   match.iph_type = iph_type;
   match.iph_link_text = iph_link_text;
   match.iph_link_url = iph_link_url;
+  match.suggestion_group_id = omnibox::GROUP_ZERO_SUGGEST_IN_PRODUCT_HELP;
   match.RecordAdditionalInfo("iph type", IphTypeDebugString(iph_type));
   match.RecordAdditionalInfo("trailing iph link text", iph_link_text);
   match.RecordAdditionalInfo("trailing iph link url", iph_link_url.spec());
@@ -342,29 +367,6 @@
   matches_.push_back(match);
 }
 
-void FeaturedSearchProvider::RegisterDisplayedMatches(
-    const AutocompleteResult& result) {
-  auto iph_match = std::ranges::find_if(result, [](const auto& match) {
-    return match.iph_type != IphType::kNone;
-  });
-  IphType iph_type =
-      iph_match == result.end() ? IphType::kNone : iph_match->iph_type;
-
-  // `kHistoryEmbeddingsDisclaimer` has no shown limit.
-  if (!iph_shown_in_omnibox_session_ && iph_type != IphType::kNone &&
-      iph_type != IphType::kHistoryEmbeddingsDisclaimer) {
-    PrefService* prefs = client_->GetPrefs();
-    // `ShouldShowIPH()` shouldn't allow adding IPH matches if there is no
-    // `prefs`.
-    CHECK(prefs);
-    prefs->SetInteger(
-        GetIphShownCountPrefNameFor(iph_type),
-        prefs->GetInteger(GetIphShownCountPrefNameFor(iph_type)) + 1);
-    iph_shown_in_browser_session_count_++;
-    iph_shown_in_omnibox_session_ = true;
-  }
-}
-
 void FeaturedSearchProvider::AddFeaturedEnterpriseSearchMatch(
     const TemplateURL& template_url,
     const AutocompleteInput& input) {
@@ -402,10 +404,31 @@
   matches_.push_back(match);
 }
 
-bool FeaturedSearchProvider::ShouldShowGeminiIPHMatch(
-    const AutocompleteInput& input) const {
-  // The IPH suggestion should only be shown in Zero prefix state.
-  if (!OmniboxFieldTrial::IsStarterPackIPHEnabled() || !input.IsZeroSuggest() ||
+bool FeaturedSearchProvider::ShouldShowIPH(IphType iph_type) const {
+  PrefService* prefs = client_->GetPrefs();
+  // Check the IPH hasn't been dismissed.
+  if (!prefs || prefs->GetBoolean(GetIphDismissedPrefNameFor(iph_type))) {
+    return false;
+  }
+
+  // The limit only applies once per session. E.g., when the user types
+  // '@history a', the `kHistoryEmbeddingsSettingsPromo` IPH might be shown.
+  // When they then type '@history abcdefg', the IPH should continue to be
+  // shown, and not disappear at '@history abcd', and only count as 1 shown.
+  if (iph_shown_in_omnibox_session_) {
+    return true;
+  }
+
+  // Check the IPH hasn't reached its show limit. Check too many IPHs haven't
+  // been shown this session; don't want to show 3 of type 1, then 3 of type 2
+  // immediately after.
+  size_t iph_shown_count =
+      prefs->GetInteger(GetIphShownCountPrefNameFor(iph_type));
+  return iph_shown_count < 3 && iph_shown_in_browser_session_count_ < 3;
+}
+
+bool FeaturedSearchProvider::ShouldShowGeminiIPHMatch() const {
+  if (!OmniboxFieldTrial::IsStarterPackIPHEnabled() ||
       !ShouldShowIPH(IphType::kGemini)) {
     return false;
   }
@@ -421,11 +444,21 @@
   return true;
 }
 
-bool FeaturedSearchProvider::ShouldShowEnterpriseFeaturedSearchIPHMatch(
-    const AutocompleteInput& input) const {
+void FeaturedSearchProvider::AddGeminiIPHMatch() {
+  AddIPHMatch(
+      IphType::kGemini,
+      /*iph_contents=*/l10n_util::GetStringUTF16(IDS_OMNIBOX_GEMINI_IPH),
+      /*matched_term=*/u"@gemini",
+      /*iph_link_text=*/u"",
+      /*iph_link_url=*/{},
+      /*relevance=*/kIPHRelevance,
+      /*deletable=*/true);
+}
+
+bool FeaturedSearchProvider::ShouldShowEnterpriseFeaturedSearchIPHMatch()
+    const {
   // Conditions to show the IPH for featured Enterprise search:
   // - The feature is enabled.
-  // - This is a Zero prefix state.
   // - There is at least one featured search engine set by policy, excluding
   //   featured search engines created by enterprise search aggregator policy
   //   when in incognito mode.
@@ -440,34 +473,13 @@
       featured_engines.push_back(turl);
     }
   }
-  return input.IsZeroSuggest() && !featured_engines.empty() &&
+  return !featured_engines.empty() &&
          ShouldShowIPH(IphType::kFeaturedEnterpriseSearch) &&
          std::ranges::all_of(featured_engines, [](auto turl) {
            return turl->usage_count() == 0;
          });
 }
 
-bool FeaturedSearchProvider::ShouldShowIPH(IphType iph_type) const {
-  PrefService* prefs = client_->GetPrefs();
-  // Check the IPH hasn't been dismissed.
-  if (!prefs || prefs->GetBoolean(GetIphDismissedPrefNameFor(iph_type)))
-    return false;
-
-  // The limit only applies once per session. E.g., when the user types
-  // '@history a', the `kHistoryEmbeddingsSettingsPromo` IPH might be shown.
-  // When they then type '@history abcdefg', the IPH should continue to be
-  // shown, and not disappear at '@history abcd', and only count as 1 shown.
-  if (iph_shown_in_omnibox_session_)
-    return true;
-
-  // Check the IPH hasn't reached its show limit. Check too many IPHs haven't
-  // been shown this session; don't want to show 3 of type 1, then 3 of type 2
-  // immediately after.
-  size_t iph_shown_count =
-      prefs->GetInteger(GetIphShownCountPrefNameFor(iph_type));
-  return iph_shown_count < 3 && iph_shown_in_browser_session_count_ < 3;
-}
-
 void FeaturedSearchProvider::AddFeaturedEnterpriseSearchIPHMatch() {
   std::vector<std::string> sites;
   for (const TemplateURL* turl :
@@ -479,10 +491,15 @@
   }
   std::ranges::sort(sites);
   AddIPHMatch(IphType::kFeaturedEnterpriseSearch,
+              /*iph_contents=*/
               l10n_util::GetStringFUTF16(
                   IDS_OMNIBOX_FEATURED_ENTERPRISE_SITE_SEARCH_IPH,
                   base::UTF8ToUTF16(base::JoinString(sites, ", "))),
-              u"", u"", {}, true);
+              /*matched_term=*/u"",
+              /*iph_link_text=*/u"",
+              /*iph_link_url=*/{},
+              /*relevance=*/kIPHRelevance,
+              /*deletable=*/true);
 }
 
 bool FeaturedSearchProvider::ShouldShowHistoryEmbeddingsSettingsPromoIphMatch()
@@ -511,8 +528,13 @@
       GURL(optimization_guide::features::IsAiSettingsPageRefreshEnabled()
                ? "chrome://settings/ai/historySearch"
                : "chrome://settings/historySearch");
-  AddIPHMatch(IphType::kHistoryEmbeddingsSettingsPromo, text, u"", link_text,
-              link_url, true);
+  AddIPHMatch(IphType::kHistoryEmbeddingsSettingsPromo,
+              /*iph_contents=*/text,
+              /*matched_term=*/u"",
+              /*iph_link_text=*/link_text,
+              /*iph_link_url=*/link_url,
+              /*relevance=*/kIPHRelevance,
+              /*deletable=*/true);
 }
 
 bool FeaturedSearchProvider::ShouldShowHistoryEmbeddingsDisclaimerIphMatch()
@@ -534,41 +556,54 @@
       GURL(optimization_guide::features::IsAiSettingsPageRefreshEnabled()
                ? "chrome://settings/ai/historySearch"
                : "chrome://settings/historySearch");
-  AddIPHMatch(IphType::kHistoryEmbeddingsDisclaimer, text, u"", link_text,
-              link_url, false);
+  AddIPHMatch(IphType::kHistoryEmbeddingsDisclaimer,
+              /*iph_contents=*/text,
+              /*matched_term=*/u"",
+              /*iph_link_text=*/link_text,
+              /*iph_link_url=*/link_url,
+              /*relevance=*/kIPHRelevance,
+              /*deletable=*/false);
 }
 
-bool FeaturedSearchProvider::ShouldShowHistoryScopePromoIphMatch(
-    const AutocompleteInput& input) const {
+bool FeaturedSearchProvider::ShouldShowHistoryScopePromoIphMatch() const {
   // Shown in the zero state when history embeddings is disabled (not opted-in),
   // but the embeddings is enabled for the omnibox. Doesn't check if the setting
   // is visible. We want to guard this behind some meaningful param but it's not
   // directly related to embeddings so it's ok to show to users who can't opt-in
   // to embeddings.
-  return input.IsZeroSuggest() && !client_->IsHistoryEmbeddingsEnabled() &&
+  return !client_->IsHistoryEmbeddingsEnabled() &&
          history_embeddings::GetFeatureParameters().omnibox_scoped &&
          !client_->IsOffTheRecord() &&
          ShouldShowIPH(IphType::kHistoryScopePromo);
 }
 
 void FeaturedSearchProvider::AddHistoryScopePromoIphMatch() {
-  std::u16string text =
-      l10n_util::GetStringUTF16(IDS_OMNIBOX_HISTORY_SCOPE_PROMO_IPH);
-  AddIPHMatch(IphType::kHistoryScopePromo, text, u"@history", u"", {}, true);
+  AddIPHMatch(IphType::kHistoryScopePromo,
+              /*iph_contents=*/
+              l10n_util::GetStringUTF16(IDS_OMNIBOX_HISTORY_SCOPE_PROMO_IPH),
+              /*matched_term=*/u"@history",
+              /*iph_link_text=*/u"",
+              /*iph_link_url=*/{},
+              /*relevance=*/kIPHRelevance,
+              /*deletable=*/true);
 }
 
-bool FeaturedSearchProvider::ShouldShowHistoryEmbeddingsScopePromoIphMatch(
-    const AutocompleteInput& input) const {
-  // Shown in the zero state when history embeddings is enabled (& opted-in) for
-  // the omnibox.
-  return input.IsZeroSuggest() && client_->IsHistoryEmbeddingsEnabled() &&
+bool FeaturedSearchProvider::ShouldShowHistoryEmbeddingsScopePromoIphMatch()
+    const {
+  // Shown when history embeddings is enabled (& opted-in) for the omnibox.
+  return client_->IsHistoryEmbeddingsEnabled() &&
          history_embeddings::GetFeatureParameters().omnibox_scoped &&
          ShouldShowIPH(IphType::kHistoryEmbeddingsScopePromo);
 }
 
 void FeaturedSearchProvider::AddHistoryEmbeddingsScopePromoIphMatch() {
-  std::u16string text =
-      l10n_util::GetStringUTF16(IDS_OMNIBOX_HISTORY_EMBEDDINGS_SCOPE_PROMO_IPH);
-  AddIPHMatch(IphType::kHistoryEmbeddingsScopePromo, text, u"@history", u"", {},
-              true);
+  AddIPHMatch(
+      IphType::kHistoryEmbeddingsScopePromo,
+      /*iph_contents=*/
+      l10n_util::GetStringUTF16(IDS_OMNIBOX_HISTORY_EMBEDDINGS_SCOPE_PROMO_IPH),
+      /*matched_term=*/u"@history",
+      /*iph_link_text=*/u"",
+      /*iph_link_url=*/{},
+      /*relevance=*/kIPHRelevance,
+      /*deletable=*/true);
 }
diff --git a/components/omnibox/browser/featured_search_provider.h b/components/omnibox/browser/featured_search_provider.h
index 411c240..670c624 100644
--- a/components/omnibox/browser/featured_search_provider.h
+++ b/components/omnibox/browser/featured_search_provider.h
@@ -38,10 +38,6 @@
  private:
   ~FeaturedSearchProvider() override;
 
-  static const int kGeminiRelevance;
-  static const int kFeaturedEnterpriseSearchRelevance;
-  static const int kStarterPackRelevance;
-
   // Populates `matches_` with matching starter pack keywords such as @history,
   // and @bookmarks
   void AddFeaturedKeywordMatches(const AutocompleteInput& input);
@@ -59,42 +55,37 @@
                    const std::u16string& matched_term,
                    const std::u16string& iph_link_text,
                    const GURL& iph_link_url,
+                   int relevance,
                    bool deletable);
 
   void AddFeaturedEnterpriseSearchMatch(const TemplateURL& template_url,
                                         const AutocompleteInput& input);
 
-  // Whether to show the @gemini IPH row. This takes into account factors like
-  // feature flags, zero suggest state, how many times its been shown, and past
-  // user behavior.
-  bool ShouldShowGeminiIPHMatch(const AutocompleteInput& input) const;
-
-  // Whether to show the Enterprise featured Search IPH row. This takes into
-  // account factors like feature flags, zero suggest state, how many times it's
-  // been shown, and past user behavior.
-  bool ShouldShowEnterpriseFeaturedSearchIPHMatch(
-      const AutocompleteInput& input) const;
-
-  // Returns whether Chrome should show the IPH for `iph_type`, meaning that:
-  // - It has been shown fewer times than the session limit;
-  // - The user has not manually deleted it.
-  // If the limit is set to INT_MAX, it is not limited.
+  // Returns whether to show the IPH match for `iph_type`.
   bool ShouldShowIPH(IphType iph_type) const;
 
+  // Whether to show the @gemini keyword promo row in zero-state.
+  bool ShouldShowGeminiIPHMatch() const;
+  void AddGeminiIPHMatch();
+
+  // Whether to show the Enterprise Featured keyword promo row in zero-state.
+  bool ShouldShowEnterpriseFeaturedSearchIPHMatch() const;
   void AddFeaturedEnterpriseSearchIPHMatch();
 
+  // Whether to show the History Embeddings promo row in @history scope.
   bool ShouldShowHistoryEmbeddingsSettingsPromoIphMatch() const;
   void AddHistoryEmbeddingsSettingsPromoIphMatch();
 
+  // Whether to show the History Embeddings disclaimer row in @history scope.
   bool ShouldShowHistoryEmbeddingsDisclaimerIphMatch() const;
   void AddHistoryEmbeddingsDisclaimerIphMatch();
 
-  bool ShouldShowHistoryScopePromoIphMatch(
-      const AutocompleteInput& input) const;
+  // Whether to show the @history keyword promo row in zero-state.
+  bool ShouldShowHistoryScopePromoIphMatch() const;
   void AddHistoryScopePromoIphMatch();
 
-  bool ShouldShowHistoryEmbeddingsScopePromoIphMatch(
-      const AutocompleteInput& input) const;
+  // Whether to show the @history (embeddings) keyword promo row in zero-state.
+  bool ShouldShowHistoryEmbeddingsScopePromoIphMatch() const;
   void AddHistoryEmbeddingsScopePromoIphMatch();
 
   raw_ptr<AutocompleteProviderClient> client_;
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 7d0a3c2..ec81c25 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 7d0a3c2435fe79279ba2a404cecb24a8b6015e4a
+Subproject commit ec81c2567f8bfb29d84641673c86dbe24a335d94
diff --git a/components/passage_embeddings/internal/scheduling_embedder.cc b/components/passage_embeddings/internal/scheduling_embedder.cc
index 44b20544..6e099bc 100644
--- a/components/passage_embeddings/internal/scheduling_embedder.cc
+++ b/components/passage_embeddings/internal/scheduling_embedder.cc
@@ -29,6 +29,8 @@
   switch (priority) {
     case PassagePriority::kUserInitiated:
       return "UserInitiated";
+    case PassagePriority::kUrgent:
+      return "Urgent";
     case PassagePriority::kPassive:
       return "Passive";
     case PassagePriority::kLatent:
@@ -213,8 +215,10 @@
 
 bool SchedulingEmbedder::IsPerformanceScenarioReady() {
   if (!jobs_.empty() &&
-      jobs_.front().priority == PassagePriority::kUserInitiated) {
-    // Do not block on performance scenario if user initiated a query.
+      (jobs_.front().priority == PassagePriority::kUserInitiated ||
+       jobs_.front().priority == PassagePriority::kUrgent)) {
+    // Do not block on performance scenario if user initiated a query or it's
+    // urgent.
     return true;
   }
 
@@ -229,6 +233,19 @@
          input_scenario == InputScenario::kNoInput;
 }
 
+void SchedulingEmbedder::ReprioritizeTasks(PassagePriority priority,
+                                           const std::set<TaskId>& tasks) {
+  for (Job& job : jobs_) {
+    const auto loc = tasks.find(job.task_id);
+    if (loc != tasks.end()) {
+      job.priority = priority;
+    }
+  }
+
+  // Note: the jobs will be reordered to account for the new priorities on the
+  // next call to SubmitWorkToEmbedder().
+}
+
 bool SchedulingEmbedder::TryCancel(TaskId task_id) {
   // No Job should have an invalid task Id.
   CHECK_NE(task_id, kInvalidTaskId);
diff --git a/components/passage_embeddings/internal/scheduling_embedder.h b/components/passage_embeddings/internal/scheduling_embedder.h
index 86304ef..674923c 100644
--- a/components/passage_embeddings/internal/scheduling_embedder.h
+++ b/components/passage_embeddings/internal/scheduling_embedder.h
@@ -53,6 +53,8 @@
       PassagePriority priority,
       std::vector<std::string> passages,
       ComputePassagesEmbeddingsCallback callback) override;
+  void ReprioritizeTasks(PassagePriority priority,
+                         const std::set<TaskId>& tasks) override;
   bool TryCancel(TaskId task_id) override;
 
  private:
diff --git a/components/passage_embeddings/passage_embeddings_features.cc b/components/passage_embeddings/passage_embeddings_features.cc
index c36a13c..83497a2 100644
--- a/components/passage_embeddings/passage_embeddings_features.cc
+++ b/components/passage_embeddings/passage_embeddings_features.cc
@@ -19,6 +19,9 @@
     "UserInitiatedPriorityNumThreads",
     4);
 
+const base::FeatureParam<int>
+    kUrgentPriorityNumThreads(&kPassageEmbedder, "UrgentPriorityNumThreads", 4);
+
 const base::FeatureParam<int> kPassivePriorityNumThreads(
     &kPassageEmbedder,
     "PassivePriorityNumThreads",
diff --git a/components/passage_embeddings/passage_embeddings_features.h b/components/passage_embeddings/passage_embeddings_features.h
index 62f0dc3..6154116 100644
--- a/components/passage_embeddings/passage_embeddings_features.h
+++ b/components/passage_embeddings/passage_embeddings_features.h
@@ -20,6 +20,10 @@
 extern const base::FeatureParam<int> kUserInitiatedPriorityNumThreads;
 
 // The number of threads to use for embeddings generation with
+// mojom::PassagePriority::kUrgent.
+extern const base::FeatureParam<int> kUrgentPriorityNumThreads;
+
+// The number of threads to use for embeddings generation with
 // mojom::PassagePriority::kPassive.
 extern const base::FeatureParam<int> kPassivePriorityNumThreads;
 
diff --git a/components/passage_embeddings/passage_embeddings_service_controller.cc b/components/passage_embeddings/passage_embeddings_service_controller.cc
index 492e842..e0ceddb 100644
--- a/components/passage_embeddings/passage_embeddings_service_controller.cc
+++ b/components/passage_embeddings/passage_embeddings_service_controller.cc
@@ -40,6 +40,7 @@
   auto params = mojom::PassageEmbedderParams::New();
   params->user_initiated_priority_num_threads =
       kUserInitiatedPriorityNumThreads.Get();
+  params->urgent_priority_num_threads = kUrgentPriorityNumThreads.Get();
   params->passive_priority_num_threads = kPassivePriorityNumThreads.Get();
   params->embedder_cache_size = kEmbedderCacheSize.Get();
   params->allow_gpu_execution = kAllowGpuExecution.Get();
@@ -50,6 +51,8 @@
   switch (priority) {
     case kUserInitiated:
       return mojom::PassagePriority::kUserInitiated;
+    case kUrgent:
+      return mojom::PassagePriority::kUrgent;
     case kPassive:
     case kLatent:
       return mojom::PassagePriority::kPassive;
diff --git a/components/passage_embeddings/passage_embeddings_test_util.cc b/components/passage_embeddings/passage_embeddings_test_util.cc
index fff5151..115e8cb4 100644
--- a/components/passage_embeddings/passage_embeddings_test_util.cc
+++ b/components/passage_embeddings/passage_embeddings_test_util.cc
@@ -82,6 +82,9 @@
   return kInvalidTaskId;
 }
 
+void TestEmbedder::ReprioritizeTasks(PassagePriority priority,
+                                     const std::set<TaskId>& tasks) {}
+
 bool TestEmbedder::TryCancel(TaskId task_id) {
   return false;
 }
diff --git a/components/passage_embeddings/passage_embeddings_test_util.h b/components/passage_embeddings/passage_embeddings_test_util.h
index 43dc4934..3a34ac1 100644
--- a/components/passage_embeddings/passage_embeddings_test_util.h
+++ b/components/passage_embeddings/passage_embeddings_test_util.h
@@ -39,6 +39,8 @@
       PassagePriority priority,
       std::vector<std::string> passages,
       ComputePassagesEmbeddingsCallback callback) override;
+  void ReprioritizeTasks(PassagePriority priority,
+                         const std::set<TaskId>& tasks) override;
   bool TryCancel(TaskId task_id) override;
 };
 
diff --git a/components/passage_embeddings/passage_embeddings_types.h b/components/passage_embeddings/passage_embeddings_types.h
index 270a9cf..3a67fea 100644
--- a/components/passage_embeddings/passage_embeddings_types.h
+++ b/components/passage_embeddings/passage_embeddings_types.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_PASSAGE_EMBEDDINGS_PASSAGE_EMBEDDINGS_TYPES_H_
 
 #include <optional>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -46,11 +47,14 @@
   // Executed as quickly as possible, runs faster and costs more resources.
   kUserInitiated = 0,
 
+  // Executes quickly but possibly at lower cost than kUserInitiated.
+  kUrgent = 1,
+
   // Execution is deprioritized and runs more slowly but more economically.
-  kPassive = 1,
+  kPassive = 2,
 
   // Execution may be delayed indefinitely and runs economically.
-  kLatent = 2,
+  kLatent = 3,
 };
 
 // The status of an embeddings generation attempt.
@@ -172,6 +176,10 @@
       std::vector<std::string> passages,
       ComputePassagesEmbeddingsCallback callback) = 0;
 
+  // Updates all pending tasks to have the specified priority.
+  virtual void ReprioritizeTasks(PassagePriority priority,
+                                 const std::set<TaskId>& tasks) = 0;
+
   // Cancels computation of embeddings iff none of the passages given to
   // `ComputePassagesEmbeddings()` has been submitted for embedding yet.
   // If successful, the callback for the canceled task will be invoked with
diff --git a/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiSettings.yaml b/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiSettings.yaml
index 30751e67..f94b53b 100644
--- a/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiSettings.yaml
+++ b/components/policy/resources/templates/policy_definitions/GenerativeAI/GeminiSettings.yaml
@@ -1,6 +1,10 @@
 caption: Settings for Gemini integration
 desc: |-
-  Enables Gemini app integration
+  This setting enables Gemini app integrations.
+
+  If the policy is unset, its behavior is determined by the GenAiDefaultSettings policy.
+
+  For more information, please check the Enterprise Release Notes (https://support.google.com/chrome/a/answer/7679408).
 
   0/unset = Gemini integration will be available for users
 
@@ -12,7 +16,7 @@
 example_value: 1
 future_on:
 - android
-- chrome.*
+- chrome.linux
 - chrome_os
 features:
   dynamic_refresh: true
@@ -31,4 +35,7 @@
   name: Disabled
   value: 1
 default: 0
-tags: []
\ No newline at end of file
+supported_on:
+- chrome.win:137-
+- chrome.mac:137-
+tags: []
diff --git a/components/saved_tab_groups/internal/saved_tab_group_model.cc b/components/saved_tab_groups/internal/saved_tab_group_model.cc
index 5b7c218..366722c 100644
--- a/components/saved_tab_groups/internal/saved_tab_group_model.cc
+++ b/components/saved_tab_groups/internal/saved_tab_group_model.cc
@@ -526,10 +526,6 @@
   SavedTabGroup* group = GetMutableGroup(group_id);
   CHECK(group);
 
-  if (!group->is_shared_tab_group()) {
-    return;
-  }
-
   SavedTabGroupTab* tab = group->GetTab(tab_id);
   CHECK(tab);
 
diff --git a/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc b/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc
index cc13527..83ac552a 100644
--- a/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc
+++ b/components/saved_tab_groups/internal/saved_tab_group_model_unittest.cc
@@ -980,7 +980,6 @@
 
 TEST_F(SavedTabGroupModelTest, SetsLastSeenTime) {
   SavedTabGroup saved_group = test::CreateTestSavedTabGroup();
-  saved_group.SetCollaborationId(tab_groups::CollaborationId("collab_id"));
   saved_tab_group_model_->AddedLocally(saved_group);
   const base::Uuid group_id = saved_group.saved_guid();
 
@@ -1357,7 +1356,6 @@
 TEST_F(SavedTabGroupModelObserverTest,
        TriggersObserverWhenSettingTabLastSeenTime) {
   SavedTabGroup saved_group = test::CreateTestSavedTabGroup();
-  saved_group.SetCollaborationId(tab_groups::CollaborationId("collab_id"));
   saved_tab_group_model_->AddedLocally(saved_group);
   const base::Uuid group_id = saved_group.saved_guid();
 
diff --git a/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc b/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc
index b30e26e..ac8a437 100644
--- a/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc
+++ b/components/saved_tab_groups/internal/tab_group_sync_service_impl.cc
@@ -1068,12 +1068,6 @@
   model_->UpdateArchivalStatus(sync_id, archival_status);
 }
 
-void TabGroupSyncServiceImpl::UpdateTabLastSeenTime(const base::Uuid& group_id,
-                                                    const base::Uuid& tab_id,
-                                                    TriggerSource source) {
-  model_->UpdateTabLastSeenTime(group_id, tab_id, base::Time::Now(), source);
-}
-
 TabGroupSyncMetricsLogger*
 TabGroupSyncServiceImpl::GetTabGroupSyncMetricsLogger() {
   return metrics_logger_.get();
diff --git a/components/saved_tab_groups/internal/tab_group_sync_service_impl.h b/components/saved_tab_groups/internal/tab_group_sync_service_impl.h
index d9ef7e7..1d95257 100644
--- a/components/saved_tab_groups/internal/tab_group_sync_service_impl.h
+++ b/components/saved_tab_groups/internal/tab_group_sync_service_impl.h
@@ -163,10 +163,6 @@
   void UpdateArchivalStatus(const base::Uuid& sync_id,
                             bool archival_status) override;
 
-  void UpdateTabLastSeenTime(const base::Uuid& group_id,
-                             const base::Uuid& tab_id,
-                             TriggerSource source) override;
-
   TabGroupSyncMetricsLogger* GetTabGroupSyncMetricsLogger() override;
 
   base::WeakPtr<syncer::DataTypeControllerDelegate>
diff --git a/components/saved_tab_groups/public/tab_group_sync_service.h b/components/saved_tab_groups/public/tab_group_sync_service.h
index bb235095..8f4796d3 100644
--- a/components/saved_tab_groups/public/tab_group_sync_service.h
+++ b/components/saved_tab_groups/public/tab_group_sync_service.h
@@ -349,11 +349,6 @@
   virtual void UpdateArchivalStatus(const base::Uuid& sync_id,
                                     bool archival_status) = 0;
 
-  // Update the last seen timestamp for a tab.
-  virtual void UpdateTabLastSeenTime(const base::Uuid& group_id,
-                                     const base::Uuid& tab_id,
-                                     TriggerSource source) = 0;
-
   // For accessing the centralized metrics logger.
   virtual TabGroupSyncMetricsLogger* GetTabGroupSyncMetricsLogger() = 0;
 
diff --git a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc
index 58f2227..9571eca8 100644
--- a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc
+++ b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.cc
@@ -409,12 +409,6 @@
   // No op.
 }
 
-void FakeTabGroupSyncService::UpdateTabLastSeenTime(const base::Uuid& group_id,
-                                                    const base::Uuid& tab_id,
-                                                    TriggerSource source) {
-  // No op.
-}
-
 TabGroupSyncMetricsLogger*
 FakeTabGroupSyncService::GetTabGroupSyncMetricsLogger() {
   return nullptr;
diff --git a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h
index a0875df..5013444 100644
--- a/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h
+++ b/components/saved_tab_groups/test_support/fake_tab_group_sync_service.h
@@ -96,9 +96,6 @@
   void RecordTabGroupEvent(const EventDetails& event_details) override;
   void UpdateArchivalStatus(const base::Uuid& sync_id,
                             bool archival_status) override;
-  void UpdateTabLastSeenTime(const base::Uuid& group_id,
-                             const base::Uuid& tab_id,
-                             TriggerSource source) override;
   TabGroupSyncMetricsLogger* GetTabGroupSyncMetricsLogger() override;
   base::WeakPtr<syncer::DataTypeControllerDelegate>
   GetSavedTabGroupControllerDelegate() override;
diff --git a/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h b/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h
index 2814a3e..c1699a0 100644
--- a/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h
+++ b/components/saved_tab_groups/test_support/mock_tab_group_sync_service.h
@@ -118,9 +118,6 @@
               (const));
   MOCK_METHOD(void, RecordTabGroupEvent, (const EventDetails&));
   MOCK_METHOD(void, UpdateArchivalStatus, (const base::Uuid&, bool));
-  MOCK_METHOD(void,
-              UpdateTabLastSeenTime,
-              (const base::Uuid&, const base::Uuid&, TriggerSource));
   MOCK_METHOD(TabGroupSyncMetricsLogger*, GetTabGroupSyncMetricsLogger, ());
 
   MOCK_METHOD(syncer::DataTypeSyncBridge*, bridge, ());
diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h
index 198a29e..baf715f 100644
--- a/components/user_manager/user_manager.h
+++ b/components/user_manager/user_manager.h
@@ -453,8 +453,8 @@
   // Returns true if we're logged in as a Guest.
   virtual bool IsLoggedInAsGuest() const = 0;
 
-  // Returns true if we're logged in as a kiosk app.
-  virtual bool IsLoggedInAsKioskApp() const = 0;
+  // Returns true if we're logged in as a kiosk Chrome app.
+  virtual bool IsLoggedInAsKioskChromeApp() const = 0;
 
   // Returns true if we're logged in as a Web kiosk app.
   virtual bool IsLoggedInAsWebKioskApp() const = 0;
diff --git a/components/user_manager/user_manager_impl.cc b/components/user_manager/user_manager_impl.cc
index 4a97658..2839165 100644
--- a/components/user_manager/user_manager_impl.cc
+++ b/components/user_manager/user_manager_impl.cc
@@ -990,7 +990,7 @@
   return IsUserLoggedIn() && active_user_->GetType() == UserType::kGuest;
 }
 
-bool UserManagerImpl::IsLoggedInAsKioskApp() const {
+bool UserManagerImpl::IsLoggedInAsKioskChromeApp() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return IsUserLoggedIn() && active_user_->GetType() == UserType::kKioskApp;
 }
diff --git a/components/user_manager/user_manager_impl.h b/components/user_manager/user_manager_impl.h
index 3d37ee1..4c1b843 100644
--- a/components/user_manager/user_manager_impl.h
+++ b/components/user_manager/user_manager_impl.h
@@ -208,7 +208,7 @@
   bool IsLoggedInAsChildUser() const override;
   bool IsLoggedInAsManagedGuestSession() const override;
   bool IsLoggedInAsGuest() const override;
-  bool IsLoggedInAsKioskApp() const override;
+  bool IsLoggedInAsKioskChromeApp() const override;
   bool IsLoggedInAsWebKioskApp() const override;
   bool IsLoggedInAsKioskIWA() const override;
   bool IsLoggedInAsAnyKioskApp() const override;
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn
index c13ad2d..39a87db 100644
--- a/components/vector_icons/BUILD.gn
+++ b/components/vector_icons/BUILD.gn
@@ -2,315 +2,331 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/util/branding.gni")
 import("//components/vector_icons/vector_icons.gni")
 
-aggregate_vector_icons("components_vector_icons") {
-  icon_directory = "."
+_icons_gen_dir = "$root_build_dir/gen/components/vector_icons"
 
-  generate_unittests = true
+if (current_toolchain == default_toolchain) {
+  import("//build/util/branding.gni")
+  aggregate_vector_icons("components_vector_icons") {
+    icon_directory = "."
 
-  sources = [
-    "${branding_path_component}/product.icon",
-    "${branding_path_component}/product_refresh.icon",
-    "account_circle.icon",
-    "account_circle_chrome_refresh.icon",
-    "account_circle_off_chrome_refresh.icon",
-    "add.icon",
-    "add_link.icon",
-    "ads.icon",
-    "ads_chrome_refresh.icon",
-    "ads_click.icon",
-    "ads_off_chrome_refresh.icon",
-    "arrow_back.icon",
-    "arrow_back_chrome_refresh.icon",
-    "arrow_right_alt.icon",
-    "back_arrow.icon",
-    "back_arrow_chrome_refresh.icon",
-    "back_to_tab.icon",
-    "back_to_tab_chrome_refresh.icon",
-    "blocked_badge.icon",
-    "bluetooth.icon",
-    "bluetooth_chrome_refresh.icon",
-    "bluetooth_connected.icon",
-    "bluetooth_off_chrome_refresh.icon",
-    "bluetooth_scanning.icon",
-    "bluetooth_scanning_chrome_refresh.icon",
-    "business.icon",
-    "business_chrome_refresh.icon",
-    "call.icon",
-    "call_end.icon",
-    "call_refresh.icon",
-    "cancel.icon",
-    "cancel_chrome_refresh.icon",
-    "capture.icon",
-    "cardboard.icon",
-    "caret_down.icon",
-    "caret_up.icon",
-    "cast.icon",
-    "cast_warning.icon",
-    "celebration.icon",
-    "certificate.icon",
-    "certificate_off.icon",
-    "chat.icon",
-    "chat_spark.icon",
-    "check_circle.icon",
-    "checklist.icon",
-    "close.icon",
-    "close_chrome_refresh.icon",
-    "close_rounded.icon",
-    "close_small.icon",
-    "code.icon",
-    "code_chrome_refresh.icon",
-    "code_off_chrome_refresh.icon",
-    "content_copy.icon",
-    "content_paste.icon",
-    "content_paste_off.icon",
-    "cookie.icon",
-    "cookie_chrome_refresh.icon",
-    "cookie_off_chrome_refresh.icon",
-    "dangerous.icon",
-    "dangerous_chrome_refresh.icon",
-    "database.icon",
-    "database_off.icon",
-    "description.icon",
-    "devices.icon",
-    "devices_off.icon",
-    "directions_car.icon",
-    "document_scanner.icon",
-    "dogfood.icon",
-    "edit.icon",
-    "edit_chrome_refresh.icon",
-    "email.icon",
-    "email_outline.icon",
-    "error.icon",
-    "error_outline.icon",
-    "ethernet.icon",
-    "expand_more.icon",
-    "extension.icon",
-    "extension_chrome_refresh.icon",
-    "extension_off.icon",
-    "extension_on.icon",
-    "family_link.icon",
-    "feedback.icon",
-    "file_download.icon",
-    "file_download_chrome_refresh.icon",
-    "file_download_off_chrome_refresh.icon",
-    "fingerprint.icon",
-    "fingerprint_off.icon",
-    "folder.icon",
-    "folder_chrome_refresh.icon",
-    "folder_managed_refresh.icon",
-    "folder_open.icon",
-    "font_download.icon",
-    "font_download_chrome_refresh.icon",
-    "font_download_off_chrome_refresh.icon",
-    "forward_10.icon",
-    "forward_arrow.icon",
-    "forward_arrow_chrome_refresh.icon",
-    "globe.icon",
-    "google_color.icon",
-    "gpp_maybe.icon",
-    "hand_gesture.icon",
-    "hand_gesture_off.icon",
-    "headset.icon",
-    "help.icon",
-    "help_outline.icon",
-    "history.icon",
-    "history_chrome_refresh.icon",
-    "home.icon",
-    "https_valid.icon",
-    "id_card.icon",
-    "iframe.icon",
-    "iframe_off.icon",
-    "image_search.icon",
-    "info_outline.icon",
-    "info_refresh.icon",
-    "insert_drive_file_outline.icon",
-    "install_desktop.icon",
-    "install_desktop_off.icon",
-    "keyboard.icon",
-    "keyboard_lock.icon",
-    "keyboard_lock_off.icon",
-    "launch.icon",
-    "launch_chrome_refresh.icon",
-    "launch_off_chrome_refresh.icon",
-    "lightbulb_outline.icon",
-    "lightbulb_outline_chrome_refresh.icon",
-    "link.icon",
-    "live_caption_on.icon",
-    "location_off_chrome_refresh.icon",
-    "location_on.icon",
-    "location_on_chrome_refresh.icon",
-    "lock.icon",
-    "loyalty.icon",
-    "magic_button.icon",
-    "media_next_track.icon",
-    "media_previous_track.icon",
-    "media_router_active_chrome_refresh.icon",
-    "media_router_error.icon",
-    "media_router_idle.icon",
-    "media_router_idle_chrome_refresh.icon",
-    "media_router_paused.icon",
-    "media_router_warning_chrome_refresh.icon",
-    "media_seek_backward.icon",
-    "media_seek_forward.icon",
-    "mic.icon",
-    "mic_chrome_refresh.icon",
-    "mic_off.icon",
-    "mic_off_chrome_refresh.icon",
-    "midi.icon",
-    "midi_chrome_refresh.icon",
-    "midi_off_chrome_refresh.icon",
-    "not_secure_warning.icon",
-    "not_secure_warning_chrome_refresh.icon",
-    "not_secure_warning_off_chrome_refresh.icon",
-    "not_uploaded.icon",
-    "notification_warning.icon",
-    "notifications.icon",
-    "notifications_chrome_refresh.icon",
-    "notifications_off_chrome_refresh.icon",
-    "page_info_content_paste.icon",
-    "passkey.icon",
-    "password_manager.icon",
-    "pause.icon",
-    "pause_chrome_refresh.icon",
-    "photo.icon",
-    "photo_chrome_refresh.icon",
-    "photo_off_chrome_refresh.icon",
-    "picture_in_picture.icon",
-    "picture_in_picture_alt.icon",
-    "pip_exit.icon",
-    "play_arrow.icon",
-    "play_arrow_chrome_refresh.icon",
-    "pointer_lock.icon",
-    "pointer_lock_off.icon",
-    "printer.icon",
-    "protected_content.icon",
-    "protocol_handler.icon",
-    "protocol_handler_chrome_refresh.icon",
-    "protocol_handler_off_chrome_refresh.icon",
-    "radio_button_checked.icon",
-    "receipt_long.icon",
-    "reload.icon",
-    "reload_chrome_refresh.icon",
-    "replay.icon",
-    "replay_10.icon",
-    "save_cloud.icon",
-    "science.icon",
-    "screen_share.icon",
-    "screen_share_old.icon",
-    "search.icon",
-    "search_chrome_refresh.icon",
-    "select_window.icon",
-    "select_window_chrome_refresh.icon",
-    "select_window_off_chrome_refresh.icon",
-    "send.icon",
-    "sensors.icon",
-    "sensors_chrome_refresh.icon",
-    "sensors_off_chrome_refresh.icon",
-    "serial_port.icon",
-    "serial_port_chrome_refresh.icon",
-    "serial_port_off_chrome_refresh.icon",
-    "settings.icon",
-    "settings_chrome_refresh.icon",
-    "settings_outline.icon",
-    "shopping_bag.icon",
-    "shopping_bag_refresh.icon",
-    "shoppingmode.icon",
-    "skip_next.icon",
-    "skip_previous.icon",
-    "smart_card_reader.icon",
-    "smart_card_reader_off.icon",
-    "sms.icon",
-    "star.icon",
-    "star_half.icon",
-    "sticky_note_2.icon",
-    "stop_circle.icon",
-    "storage_access.icon",
-    "storage_access_off.icon",
-    "storefront.icon",
-    "submenu_arrow.icon",
-    "submenu_arrow_chrome_refresh.icon",
-    "sync.icon",
-    "sync_chrome_refresh.icon",
-    "sync_off_chrome_refresh.icon",
-    "sync_problem_chrome_refresh.icon",
-    "tab_search.icon",
-    "tenancy.icon",
-    "thumb_down.icon",
-    "thumb_down_filled.icon",
-    "thumb_up.icon",
-    "thumb_up_filled.icon",
-    "touchpad_mouse.icon",
-    "touchpad_mouse_off.icon",
-    "translate.icon",
-    "undo.icon",
-    "usb.icon",
-    "usb_chrome_refresh.icon",
-    "usb_off_chrome_refresh.icon",
-    "video_library.icon",
-    "videocam.icon",
-    "videocam_chrome_refresh.icon",
-    "videocam_off.icon",
-    "videocam_off_chrome_refresh.icon",
-    "videogame_asset.icon",
-    "videogame_asset_chrome_refresh.icon",
-    "videogame_asset_off_chrome_refresh.icon",
-    "view_in_ar_chrome_refresh.icon",
-    "view_in_ar_off_chrome_refresh.icon",
-    "visibility.icon",
-    "visibility_off.icon",
-    "volume_off_chrome_refresh.icon",
-    "volume_up.icon",
-    "volume_up_chrome_refresh.icon",
-    "vr_headset.icon",
-    "vr_headset_chrome_refresh.icon",
-    "vr_headset_off_chrome_refresh.icon",
-    "warning.icon",
-    "warning_outline.icon",
-    "work.icon",
-  ]
+    generate_unittests = true
 
-  if (is_chromeos) {
-    sources += [
-      "notification_download.icon",
-      "videogame_asset_outline.icon",
+    sources = [
+      "${branding_path_component}/product.icon",
+      "${branding_path_component}/product_refresh.icon",
+      "account_circle.icon",
+      "account_circle_chrome_refresh.icon",
+      "account_circle_off_chrome_refresh.icon",
+      "add.icon",
+      "add_link.icon",
+      "ads.icon",
+      "ads_chrome_refresh.icon",
+      "ads_click.icon",
+      "ads_off_chrome_refresh.icon",
+      "arrow_back.icon",
+      "arrow_back_chrome_refresh.icon",
+      "arrow_right_alt.icon",
+      "back_arrow.icon",
+      "back_arrow_chrome_refresh.icon",
+      "back_to_tab.icon",
+      "back_to_tab_chrome_refresh.icon",
+      "blocked_badge.icon",
+      "bluetooth.icon",
+      "bluetooth_chrome_refresh.icon",
+      "bluetooth_connected.icon",
+      "bluetooth_off_chrome_refresh.icon",
+      "bluetooth_scanning.icon",
+      "bluetooth_scanning_chrome_refresh.icon",
+      "business.icon",
+      "business_chrome_refresh.icon",
+      "call.icon",
+      "call_end.icon",
+      "call_refresh.icon",
+      "cancel.icon",
+      "cancel_chrome_refresh.icon",
+      "capture.icon",
+      "cardboard.icon",
+      "caret_down.icon",
+      "caret_up.icon",
+      "cast.icon",
+      "cast_warning.icon",
+      "celebration.icon",
+      "certificate.icon",
+      "certificate_off.icon",
+      "chat.icon",
+      "chat_spark.icon",
+      "check_circle.icon",
+      "checklist.icon",
+      "close.icon",
+      "close_chrome_refresh.icon",
+      "close_rounded.icon",
+      "close_small.icon",
+      "code.icon",
+      "code_chrome_refresh.icon",
+      "code_off_chrome_refresh.icon",
+      "content_copy.icon",
+      "content_paste.icon",
+      "content_paste_off.icon",
+      "cookie.icon",
+      "cookie_chrome_refresh.icon",
+      "cookie_off_chrome_refresh.icon",
+      "dangerous.icon",
+      "dangerous_chrome_refresh.icon",
+      "database.icon",
+      "database_off.icon",
+      "description.icon",
+      "devices.icon",
+      "devices_off.icon",
+      "directions_car.icon",
+      "document_scanner.icon",
+      "dogfood.icon",
+      "edit.icon",
+      "edit_chrome_refresh.icon",
+      "email.icon",
+      "email_outline.icon",
+      "error.icon",
+      "error_outline.icon",
+      "ethernet.icon",
+      "expand_more.icon",
+      "extension.icon",
+      "extension_chrome_refresh.icon",
+      "extension_off.icon",
+      "extension_on.icon",
+      "family_link.icon",
+      "feedback.icon",
+      "file_download.icon",
+      "file_download_chrome_refresh.icon",
+      "file_download_off_chrome_refresh.icon",
+      "fingerprint.icon",
+      "fingerprint_off.icon",
+      "folder.icon",
+      "folder_chrome_refresh.icon",
+      "folder_managed_refresh.icon",
+      "folder_open.icon",
+      "font_download.icon",
+      "font_download_chrome_refresh.icon",
+      "font_download_off_chrome_refresh.icon",
+      "forward_10.icon",
+      "forward_arrow.icon",
+      "forward_arrow_chrome_refresh.icon",
+      "globe.icon",
+      "google_color.icon",
+      "gpp_maybe.icon",
+      "hand_gesture.icon",
+      "hand_gesture_off.icon",
+      "headset.icon",
+      "help.icon",
+      "help_outline.icon",
+      "history.icon",
+      "history_chrome_refresh.icon",
+      "home.icon",
+      "https_valid.icon",
+      "id_card.icon",
+      "iframe.icon",
+      "iframe_off.icon",
+      "image_search.icon",
+      "info_outline.icon",
+      "info_refresh.icon",
+      "insert_drive_file_outline.icon",
+      "install_desktop.icon",
+      "install_desktop_off.icon",
+      "keyboard.icon",
+      "keyboard_lock.icon",
+      "keyboard_lock_off.icon",
+      "launch.icon",
+      "launch_chrome_refresh.icon",
+      "launch_off_chrome_refresh.icon",
+      "lightbulb_outline.icon",
+      "lightbulb_outline_chrome_refresh.icon",
+      "link.icon",
+      "live_caption_on.icon",
+      "location_off_chrome_refresh.icon",
+      "location_on.icon",
+      "location_on_chrome_refresh.icon",
+      "lock.icon",
+      "loyalty.icon",
+      "magic_button.icon",
+      "media_next_track.icon",
+      "media_previous_track.icon",
+      "media_router_active_chrome_refresh.icon",
+      "media_router_error.icon",
+      "media_router_idle.icon",
+      "media_router_idle_chrome_refresh.icon",
+      "media_router_paused.icon",
+      "media_router_warning_chrome_refresh.icon",
+      "media_seek_backward.icon",
+      "media_seek_forward.icon",
+      "mic.icon",
+      "mic_chrome_refresh.icon",
+      "mic_off.icon",
+      "mic_off_chrome_refresh.icon",
+      "midi.icon",
+      "midi_chrome_refresh.icon",
+      "midi_off_chrome_refresh.icon",
+      "not_secure_warning.icon",
+      "not_secure_warning_chrome_refresh.icon",
+      "not_secure_warning_off_chrome_refresh.icon",
+      "not_uploaded.icon",
+      "notification_warning.icon",
+      "notifications.icon",
+      "notifications_chrome_refresh.icon",
+      "notifications_off_chrome_refresh.icon",
+      "page_info_content_paste.icon",
+      "passkey.icon",
+      "password_manager.icon",
+      "pause.icon",
+      "pause_chrome_refresh.icon",
+      "photo.icon",
+      "photo_chrome_refresh.icon",
+      "photo_off_chrome_refresh.icon",
+      "picture_in_picture.icon",
+      "picture_in_picture_alt.icon",
+      "pip_exit.icon",
+      "play_arrow.icon",
+      "play_arrow_chrome_refresh.icon",
+      "pointer_lock.icon",
+      "pointer_lock_off.icon",
+      "printer.icon",
+      "protected_content.icon",
+      "protocol_handler.icon",
+      "protocol_handler_chrome_refresh.icon",
+      "protocol_handler_off_chrome_refresh.icon",
+      "radio_button_checked.icon",
+      "receipt_long.icon",
+      "reload.icon",
+      "reload_chrome_refresh.icon",
+      "replay.icon",
+      "replay_10.icon",
+      "save_cloud.icon",
+      "science.icon",
+      "screen_share.icon",
+      "screen_share_old.icon",
+      "search.icon",
+      "search_chrome_refresh.icon",
+      "select_window.icon",
+      "select_window_chrome_refresh.icon",
+      "select_window_off_chrome_refresh.icon",
+      "send.icon",
+      "sensors.icon",
+      "sensors_chrome_refresh.icon",
+      "sensors_off_chrome_refresh.icon",
+      "serial_port.icon",
+      "serial_port_chrome_refresh.icon",
+      "serial_port_off_chrome_refresh.icon",
+      "settings.icon",
+      "settings_chrome_refresh.icon",
+      "settings_outline.icon",
+      "shopping_bag.icon",
+      "shopping_bag_refresh.icon",
+      "shoppingmode.icon",
+      "skip_next.icon",
+      "skip_previous.icon",
+      "smart_card_reader.icon",
+      "smart_card_reader_off.icon",
+      "sms.icon",
+      "star.icon",
+      "star_half.icon",
+      "sticky_note_2.icon",
+      "stop_circle.icon",
+      "storage_access.icon",
+      "storage_access_off.icon",
+      "storefront.icon",
+      "submenu_arrow.icon",
+      "submenu_arrow_chrome_refresh.icon",
+      "sync.icon",
+      "sync_chrome_refresh.icon",
+      "sync_off_chrome_refresh.icon",
+      "sync_problem_chrome_refresh.icon",
+      "tab_search.icon",
+      "tenancy.icon",
+      "thumb_down.icon",
+      "thumb_down_filled.icon",
+      "thumb_up.icon",
+      "thumb_up_filled.icon",
+      "touchpad_mouse.icon",
+      "touchpad_mouse_off.icon",
+      "translate.icon",
+      "undo.icon",
+      "usb.icon",
+      "usb_chrome_refresh.icon",
+      "usb_off_chrome_refresh.icon",
+      "video_library.icon",
+      "videocam.icon",
+      "videocam_chrome_refresh.icon",
+      "videocam_off.icon",
+      "videocam_off_chrome_refresh.icon",
+      "videogame_asset.icon",
+      "videogame_asset_chrome_refresh.icon",
+      "videogame_asset_off_chrome_refresh.icon",
+      "view_in_ar_chrome_refresh.icon",
+      "view_in_ar_off_chrome_refresh.icon",
+      "visibility.icon",
+      "visibility_off.icon",
+      "volume_off_chrome_refresh.icon",
+      "volume_up.icon",
+      "volume_up_chrome_refresh.icon",
+      "vr_headset.icon",
+      "vr_headset_chrome_refresh.icon",
+      "vr_headset_off_chrome_refresh.icon",
+      "warning.icon",
+      "warning_outline.icon",
+      "work.icon",
     ]
+
+    if (is_chromeos) {
+      sources += [
+        "notification_download.icon",
+        "videogame_asset_outline.icon",
+      ]
+    }
+
+    if (is_chrome_branded) {
+      sources += [
+        "google_chrome/google_calendar.icon",
+        "google_chrome/google_chrome_webstore.icon",
+        "google_chrome/google_docs.icon",
+        "google_chrome/google_drive.icon",
+        "google_chrome/google_g_logo.icon",
+        "google_chrome/google_g_logo_monochrome.icon",
+        "google_chrome/google_keep_note.icon",
+        "google_chrome/google_lens_full_logo.icon",
+        "google_chrome/google_lens_full_logo_dark.icon",
+        "google_chrome/google_lens_logo.icon",
+        "google_chrome/google_lens_monochrome_logo.icon",
+        "google_chrome/google_password_manager.icon",
+        "google_chrome/google_pay_logo.icon",
+        "google_chrome/google_search_companion_logo.icon",
+        "google_chrome/google_search_companion_monochrome_logo.icon",
+        "google_chrome/google_search_companion_monochrome_logo_chrome_refresh.icon",
+        "google_chrome/google_sheets.icon",
+        "google_chrome/google_sites.icon",
+        "google_chrome/google_slides.icon",
+        "google_chrome/google_super_g.icon",
+        "google_chrome/gshield.icon",
+        "google_chrome/page_insights.icon",
+        "google_chrome/page_insights_color.icon",
+        "google_chrome/pen_spark.icon",
+      ]
+    }
   }
-
-  if (is_chrome_branded) {
-    sources += [
-      "google_chrome/google_calendar.icon",
-      "google_chrome/google_chrome_webstore.icon",
-      "google_chrome/google_docs.icon",
-      "google_chrome/google_drive.icon",
-      "google_chrome/google_g_logo.icon",
-      "google_chrome/google_g_logo_monochrome.icon",
-      "google_chrome/google_keep_note.icon",
-      "google_chrome/google_lens_full_logo.icon",
-      "google_chrome/google_lens_full_logo_dark.icon",
-      "google_chrome/google_lens_logo.icon",
-      "google_chrome/google_lens_monochrome_logo.icon",
-      "google_chrome/google_password_manager.icon",
-      "google_chrome/google_pay_logo.icon",
-      "google_chrome/google_search_companion_logo.icon",
-      "google_chrome/google_search_companion_monochrome_logo.icon",
-      "google_chrome/google_search_companion_monochrome_logo_chrome_refresh.icon",
-      "google_chrome/google_sheets.icon",
-      "google_chrome/google_sites.icon",
-      "google_chrome/google_slides.icon",
-      "google_chrome/google_super_g.icon",
-      "google_chrome/gshield.icon",
-      "google_chrome/page_insights.icon",
-      "google_chrome/page_insights_color.icon",
-      "google_chrome/pen_spark.icon",
-    ]
+} else {
+  # Copy the header to the toolchain's gen/ directory so that files can
+  # #include it without needing to add the root toolchain's gen/ as an
+  # include_dir.
+  copy("components_vector_icons") {
+    public_deps = [ ":components_vector_icons($default_toolchain)" ]
+    sources = [ "$_icons_gen_dir/vector_icons.h" ]
+    outputs = [ "$target_gen_dir/vector_icons.h" ]
   }
 }
 
 static_library("vector_icons") {
-  sources = get_target_outputs(":components_vector_icons")
+  sources = [
+    "$_icons_gen_dir/vector_icons.cc",
+    "$target_gen_dir/vector_icons.h",
+  ]
 
   defines = [ "COMPONENTS_VECTOR_ICONS_IMPL" ]
 
@@ -324,9 +340,10 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = filter_include(get_target_outputs(":components_vector_icons"),
-                           [ "*unittest.h" ])
-  sources += [ "vector_icons_unittest.cc" ]
+  sources = [
+    "$_icons_gen_dir/vector_icons_unittest.h",
+    "vector_icons_unittest.cc",
+  ]
 
   deps = [
     ":components_vector_icons",
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index 48363b9..24f6f89cf 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -662,18 +662,7 @@
 
   MainFunctionParams main_params(command_line);
   main_params.zygote_child = true;
-
-  if (process_type == switches::kGpuProcess) {
-    // Once Zygote forks and feature list initializes we can start a thread to
-    // begin tracing immediately.
-    // TODO(https://crbug.com/380411640): Enable for more processes other than
-    // GPU process.
-    tracing::EnableStartupTracingIfNeeded(/*with_thread=*/true);
-    tracing::InitTracingPostFeatureList(/*enable_consumer=*/false);
-    main_params.needs_startup_tracing_after_mojo_init = false;
-  } else {
-    main_params.needs_startup_tracing_after_mojo_init = true;
-  }
+  main_params.needs_startup_tracing_after_mojo_init = true;
 
   // The hang watcher needs to be created once the feature list is available
   // but before the IO thread is started.
@@ -903,27 +892,15 @@
 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC)
   // A sandboxed process won't be able to allocate the SMB needed for startup
   // tracing until Mojo IPC support is brought up, at which point the Mojo
-  // broker will transparently broker the SMB creation. Unless the sandboxed
-  // process stops the trace threads when entering sandbox.
-  // TODO(https://crbug.com/380411640): Implement for other processes other than
-  // GPU process.
-  if (process_type != switches::kGpuProcess &&
-      !sandbox::policy::IsUnsandboxedSandboxType(
+  // broker will transparently broker the SMB creation.
+  if (!sandbox::policy::IsUnsandboxedSandboxType(
           sandbox::policy::SandboxTypeFromCommandLine(command_line))) {
     enable_startup_tracing = false;
     needs_startup_tracing_after_mojo_init_ = true;
   }
 #endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_MAC)
   if (enable_startup_tracing) {
-    if (process_type == switches::kGpuProcess) {
-      // Without Zygote and posix sandbox we can start a thread to begin tracing
-      // immediately.
-      // TODO(https://crbug.com/380411640): Enable for more processes other than
-      // GPU process.
-      tracing::EnableStartupTracingIfNeeded(/*with_thread=*/true);
-    } else {
-      tracing::EnableStartupTracingIfNeeded(/*with_thread=*/false);
-    }
+    tracing::EnableStartupTracingIfNeeded();
   }
   TRACE_EVENT0("startup,benchmark,rail", "ContentMainRunnerImpl::Initialize");
 
@@ -1139,9 +1116,6 @@
               ContentMainDelegate::InvokedInChildProcess())) {
         InitializeFieldTrialAndFeatureList();
       }
-      if (process_type == switches::kGpuProcess) {
-        tracing::InitTracingPostFeatureList(/*enable_consumer=*/false);
-      }
       if (delegate_->ShouldInitializeMojo(
               ContentMainDelegate::InvokedInChildProcess())) {
         InitializeMojoCore();
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index 502464e..440b4086 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -216,47 +216,6 @@
   }
 }
 
-void RecordPrefetchMatchingBlockedNavigationWithPrefetchHistogram(
-    const PrefetchType& prefetch_type,
-    bool blocked_until_head) {
-  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
-    base::UmaHistogramBoolean(
-        base::StrCat({"PrefetchProxy.AfterClick."
-                      "PrefetchMatchingBlockedNavigationWithPrefetch.",
-                      GetPrefetchEagernessHistogramSuffix(
-                          prefetch_type.GetEagerness())}),
-        blocked_until_head);
-  } else {
-    // TODO(crbug.com/40946257, crbug.com/40898833): Extend the metrics for
-    // embedder triggers.
-  }
-}
-
-void MaybeRecordBlockUntilHeadDuration2Histogram(
-    const PrefetchType& prefetch_type,
-    const std::optional<base::TimeDelta>& blocked_duration,
-    bool served) {
-  if (IsSpeculationRuleType(prefetch_type.trigger_type())) {
-    base::UmaHistogramTimes(
-        base::StrCat({"PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.",
-                      served ? "Served." : "NotServed.",
-                      GetPrefetchEagernessHistogramSuffix(
-                          prefetch_type.GetEagerness())}),
-        blocked_duration.value_or(base::Seconds(0)));
-    if (blocked_duration.has_value()) {
-      base::UmaHistogramTimes(
-          base::StrCat({"PrefetchProxy.AfterClick.BlockUntilHeadDuration2.",
-                        served ? "Served." : "NotServed.",
-                        GetPrefetchEagernessHistogramSuffix(
-                            prefetch_type.GetEagerness())}),
-          blocked_duration.value());
-    }
-  } else {
-    // TODO(crbug.com/40946257, crbug.com/40898833): Extend the metrics for
-    // embedder triggers.
-  }
-}
-
 ukm::SourceId GetUkmSourceId(RenderFrameHostImpl& rfhi) {
   // Prerendering page should not trigger prefetches.
   CHECK(
@@ -2060,11 +2019,10 @@
                              redirect_chain_.size());
   }
 
-  RecordPrefetchMatchingBlockedNavigationWithPrefetchHistogram(
-      prefetch_type_, blocked_duration.has_value());
+  RecordPrefetchMatchingBlockedNavigationHistogram(
+      blocked_duration.has_value());
 
-  MaybeRecordBlockUntilHeadDuration2Histogram(prefetch_type_, blocked_duration,
-                                              is_served);
+  RecordBlockUntilHeadDurationHistogram(blocked_duration, is_served);
 
   // Note that `PreloadingAttemptImpl::SetIsAccurateTriggering()` is called for
   // prefetch in
@@ -2245,46 +2203,7 @@
   }
 }
 
-const char* PrefetchContainer::GetMetricsSuffixTriggerTypeAndEagerness() {
-  switch (prefetch_type_.trigger_type()) {
-    case PreloadingTriggerType::kSpeculationRule:
-      switch (prefetch_type_.GetEagerness()) {
-        case blink::mojom::SpeculationEagerness::kEager:
-          return "SpeculationRule_Eager";
-        case blink::mojom::SpeculationEagerness::kModerate:
-          return "SpeculationRule_Moderate";
-        case blink::mojom::SpeculationEagerness::kConservative:
-          return "SpeculationRule_Conservative";
-      }
-    case PreloadingTriggerType::kSpeculationRuleFromIsolatedWorld:
-      switch (prefetch_type_.GetEagerness()) {
-        case blink::mojom::SpeculationEagerness::kEager:
-          return "SpeculationRuleFromIsolatedWorld_Eager";
-        case blink::mojom::SpeculationEagerness::kModerate:
-          return "SpeculationRuleFromIsolatedWorld_Moderate";
-        case blink::mojom::SpeculationEagerness::kConservative:
-          return "SpeculationRuleFromIsolatedWorld_Conservative";
-      }
-    case PreloadingTriggerType::kSpeculationRuleFromAutoSpeculationRules:
-      switch (prefetch_type_.GetEagerness()) {
-        case blink::mojom::SpeculationEagerness::kEager:
-          return "SpeculationRuleFromAutoSpeculationRules_Eager";
-        case blink::mojom::SpeculationEagerness::kModerate:
-          return "SpeculationRuleFromAutoSpeculationRules_Moderate";
-        case blink::mojom::SpeculationEagerness::kConservative:
-          return "SpeculationRuleFromAutoSpeculationRules_Conservative";
-      }
-    case PreloadingTriggerType::kEmbedder:
-      // TODO(crrev.com/c/6367815): Add "_<embedder_histogram_suffix>".
-      return "Embedder";
-  }
-}
-
 void PrefetchContainer::RecordDurationFromAdded() {
-  // TODO(crrev.com/c/6367815): Update
-  // `GetMetricsSuffixTriggerTypeAndEagerness()` and remove suffix
-  // `.NoEmbedderSuffix`.
-
   if (!time_added_to_prefetch_service_.has_value()) {
     return;
   }
@@ -2296,8 +2215,8 @@
   base::UmaHistogramTimes(
       base::StrCat({
           "Prefetch.PrefetchContainer.AddedToInitialEligibility.",
-          GetMetricsSuffixTriggerTypeAndEagerness(),
-          ".NoEmbedderSuffix",
+          GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type_,
+                                                  embedder_histogram_suffix_),
       }),
       time_initial_eligibility_got_.value() -
           time_added_to_prefetch_service_.value());
@@ -2309,8 +2228,8 @@
   base::UmaHistogramTimes(
       base::StrCat({
           "Prefetch.PrefetchContainer.AddedToPrefetchStarted.",
-          GetMetricsSuffixTriggerTypeAndEagerness(),
-          ".NoEmbedderSuffix",
+          GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type_,
+                                                  embedder_histogram_suffix_),
       }),
       time_prefetch_started_.value() - time_added_to_prefetch_service_.value());
 
@@ -2321,8 +2240,8 @@
   base::UmaHistogramTimes(base::StrCat({
                               "Prefetch.PrefetchContainer."
                               "AddedToHeaderDeterminedSuccessfully.",
-                              GetMetricsSuffixTriggerTypeAndEagerness(),
-                              ".NoEmbedderSuffix",
+                              GetMetricsSuffixTriggerTypeAndEagerness(
+                                  prefetch_type_, embedder_histogram_suffix_),
                           }),
                           time_header_determined_successfully_.value() -
                               time_added_to_prefetch_service_.value());
@@ -2334,11 +2253,31 @@
   base::UmaHistogramTimes(base::StrCat({
                               "Prefetch.PrefetchContainer."
                               "AddedToPrefetchCompletedSuccessfully.",
-                              GetMetricsSuffixTriggerTypeAndEagerness(),
-                              ".NoEmbedderSuffix",
+                              GetMetricsSuffixTriggerTypeAndEagerness(
+                                  prefetch_type_, embedder_histogram_suffix_),
                           }),
                           time_prefetch_completed_successfully_.value() -
                               time_added_to_prefetch_service_.value());
 }
 
+void PrefetchContainer::RecordPrefetchMatchingBlockedNavigationHistogram(
+    bool blocked_until_head) {
+  base::UmaHistogramBoolean(
+      base::StrCat(
+          {"Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.",
+           GetMetricsSuffixTriggerTypeAndEagerness(
+               prefetch_type_, embedder_histogram_suffix_)}),
+      blocked_until_head);
+}
+
+void PrefetchContainer::RecordBlockUntilHeadDurationHistogram(
+    const std::optional<base::TimeDelta>& blocked_duration,
+    bool served) {
+  base::UmaHistogramTimes(
+      base::StrCat({"Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.",
+                    served ? "Served." : "NotServed.",
+                    GetMetricsSuffixTriggerTypeAndEagerness(
+                        prefetch_type_, embedder_histogram_suffix_)}),
+      blocked_duration.value_or(base::Seconds(0)));
+}
 }  // namespace content
diff --git a/content/browser/preloading/prefetch/prefetch_container.h b/content/browser/preloading/prefetch/prefetch_container.h
index d473788..c25a7c5 100644
--- a/content/browser/preloading/prefetch/prefetch_container.h
+++ b/content/browser/preloading/prefetch/prefetch_container.h
@@ -872,11 +872,15 @@
   // be updated to the latest value when this method is called.
   void MaybeRecordPrefetchStatusToUMA(PrefetchStatus prefetch_status);
 
-  // Returns a suffix for UMAs.
-  const char* GetMetricsSuffixTriggerTypeAndEagerness();
-
   // Records `Prefetch.PrefetchContainer.DurationAdded*` UMAs.
   void RecordDurationFromAdded();
+  // Records `Prefetch.PrefetchMatchingBlockedNavigationWithPrefetch.*` UMAs.
+  void RecordPrefetchMatchingBlockedNavigationHistogram(
+      bool blocked_until_head);
+  // Records `Prefetch.BlockUntilHeadDuration.*` UMAs.
+  void RecordBlockUntilHeadDurationHistogram(
+      const std::optional<base::TimeDelta>& blocked_duration,
+      bool served);
 
   // The ID of the RenderFrameHost/Document that triggered the prefetch.
   // This will be empty when browser-initiated prefetch.
diff --git a/content/browser/preloading/prefetch/prefetch_container_unittest.cc b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
index 4916344..0d5f46d 100644
--- a/content/browser/preloading/prefetch/prefetch_container_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
@@ -69,6 +69,7 @@
   }
 
   struct SpeculationRulesPrefetchContainerOptions {
+    blink::mojom::SpeculationEagerness eagerness;
     SpeculationRulesTags speculation_rules_tags;
     base::WeakPtr<PrefetchDocumentManager> prefetch_document_manager;
   };
@@ -79,8 +80,7 @@
     return std::make_unique<PrefetchContainer>(
         *main_rfhi(), blink::DocumentToken(), prefetch_url,
         PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                     /*use_prefetch_proxy=*/true,
-                     blink::mojom::SpeculationEagerness::kEager),
+                     /*use_prefetch_proxy=*/true, options.eagerness),
         blink::mojom::Referrer(),
         std::make_optional(std::move(options.speculation_rules_tags)),
         /*no_vary_search_hint=*/std::nullopt, options.prefetch_document_manager,
@@ -927,99 +927,132 @@
             PrefetchStatus::kPrefetchFailedIneligibleRedirect);
 }
 
-TEST_P(PrefetchContainerTest, BlockUntilHeadHistograms2) {
+TEST_P(PrefetchContainerTest, BlockUntilHeadHistograms) {
   struct TestCase {
-    blink::mojom::SpeculationEagerness eagerness;
+    PrefetchType prefetch_type;
     bool is_served;
     std::optional<base::TimeDelta> prefetch_match_resolver_wait_duration;
   };
 
   std::vector<TestCase> test_cases{
-      {blink::mojom::SpeculationEagerness::kEager, true, std::nullopt},
-      {blink::mojom::SpeculationEagerness::kModerate, true,
-       base::Milliseconds(10)},
-      {blink::mojom::SpeculationEagerness::kConservative, false,
-       base::Milliseconds(20)}};
+      {PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                    /*use_prefetch_proxy=*/true,
+                    blink::mojom::SpeculationEagerness::kEager),
+       true, std::nullopt},
+      {PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                    /*use_prefetch_proxy=*/true,
+                    blink::mojom::SpeculationEagerness::kModerate),
+       true, base::Milliseconds(10)},
+      {PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                    /*use_prefetch_proxy=*/true,
+                    blink::mojom::SpeculationEagerness::kConservative),
+       false, std::nullopt},
+      {PrefetchType(PreloadingTriggerType::kEmbedder,
+                    /*use_prefetch_proxy=*/true),
+       false, base::Milliseconds(20)}};
+
+  const GURL prefetch_url = GURL("https://test.com/?nvsparam=1");
+  const GURL navigated_url = GURL("https://test.com/");
 
   base::HistogramTester histogram_tester;
   for (const auto& test_case : test_cases) {
-    PrefetchContainer prefetch_container(
-        *main_rfhi(), blink::DocumentToken(),
-        GURL("https://test.com/?nvsparam=1"),
-        PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                     /*use_prefetch_proxy=*/true, test_case.eagerness),
-        blink::mojom::Referrer(), std::make_optional(SpeculationRulesTags()),
-        /*no_vary_search_hint=*/std::nullopt,
-        /*prefetch_document_manager=*/nullptr,
-        PreloadPipelineInfo::Create(
-            /*planned_max_preloading_type=*/PreloadingType::kPrefetch));
+    auto prefetch_container = [&] {
+      if (test_case.prefetch_type.IsRendererInitiated()) {
+        return CreateSpeculationRulesPrefetchContainer(
+            prefetch_url,
+            {.eagerness = test_case.prefetch_type.GetEagerness()});
+      } else {
+        return CreateEmbedderPrefetchContainer(prefetch_url);
+      }
+    }();
 
-    prefetch_container.OnUnregisterCandidate(
-        GURL("https://test.com/"), test_case.is_served,
+    prefetch_container->OnUnregisterCandidate(
+        navigated_url, test_case.is_served,
         test_case.prefetch_match_resolver_wait_duration);
   }
 
   histogram_tester.ExpectBucketCount(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Eager",
       true, 0);
   histogram_tester.ExpectBucketCount(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Eager",
       false, 1);
   histogram_tester.ExpectUniqueTimeSample(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.Eager",
+      "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served."
+      "SpeculationRule_"
+      "Eager",
       base::Milliseconds(0), 1);
   histogram_tester.ExpectTotalCount(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.Eager",
+      "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed."
+      "SpeculationRule_"
+      "Eager",
       0);
-  histogram_tester.ExpectTotalCount(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.Eager", 0);
-  histogram_tester.ExpectTotalCount(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.Eager", 0);
 
   histogram_tester.ExpectBucketCount(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Moderate",
       true, 1);
   histogram_tester.ExpectBucketCount(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Moderate",
       false, 0);
   histogram_tester.ExpectUniqueTimeSample(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.Moderate",
+      "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served."
+      "SpeculationRule_"
+      "Moderate",
       base::Milliseconds(10), 1);
   histogram_tester.ExpectTotalCount(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed."
+      "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed."
+      "SpeculationRule_"
       "Moderate",
       0);
-  histogram_tester.ExpectUniqueTimeSample(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.Moderate",
-      base::Milliseconds(10), 1);
-  histogram_tester.ExpectTotalCount(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.Moderate", 0);
 
   histogram_tester.ExpectBucketCount(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Conservative",
+      true, 0);
+  histogram_tester.ExpectBucketCount(
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
+      "Conservative",
+      false, 1);
+  histogram_tester.ExpectTotalCount(
+      "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served."
+      "SpeculationRule_"
+      "Conservative",
+      0);
+  histogram_tester.ExpectUniqueTimeSample(
+      "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed."
+      "SpeculationRule_"
+      "Conservative",
+      base::Milliseconds(0), 1);
+
+  histogram_tester.ExpectBucketCount(
+      base::StrCat({"Prefetch.PrefetchMatchingBlockedNavigation."
+                    "PerMatchingCandidate.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       true, 1);
   histogram_tester.ExpectBucketCount(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
-      "Conservative",
+      base::StrCat({"Prefetch.PrefetchMatchingBlockedNavigation."
+                    "PerMatchingCandidate.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       false, 0);
   histogram_tester.ExpectTotalCount(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served."
-      "Conservative",
+      base::StrCat({"Prefetch.BlockUntilHeadDuration.PerMatchingCandidate."
+                    "Served.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       0);
   histogram_tester.ExpectUniqueTimeSample(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed."
-      "Conservative",
-      base::Milliseconds(20), 1);
-  histogram_tester.ExpectTotalCount(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.Conservative",
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.Conservative",
+      base::StrCat({"Prefetch.BlockUntilHeadDuration.PerMatchingCandidate."
+                    "NotServed.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       base::Milliseconds(20), 1);
 }
 
diff --git a/content/browser/preloading/prefetch/prefetch_params.cc b/content/browser/preloading/prefetch/prefetch_params.cc
index 9175bf0e..6e94e6a3 100644
--- a/content/browser/preloading/prefetch/prefetch_params.cc
+++ b/content/browser/preloading/prefetch/prefetch_params.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/no_destructor.h"
 #include "base/rand_util.h"
+#include "base/strings/strcat.h"
 #include "content/browser/preloading/prefetch/prefetch_features.h"
 #include "content/browser/preloading/preloading_trigger_type_impl.h"
 #include "content/browser/preloading/prerender/prerender_features.h"
@@ -232,17 +233,45 @@
   return base::Milliseconds(timeout_in_milliseconds);
 }
 
-std::string GetPrefetchEagernessHistogramSuffix(
-    blink::mojom::SpeculationEagerness eagerness) {
-  switch (eagerness) {
-    case blink::mojom::SpeculationEagerness::kEager:
-      return "Eager";
-    case blink::mojom::SpeculationEagerness::kModerate:
-      return "Moderate";
-    case blink::mojom::SpeculationEagerness::kConservative:
-      return "Conservative";
+// These strings (including `embedder_histogram_suffix`) are persisted to logs.
+// LINT.IfChange
+std::string GetMetricsSuffixTriggerTypeAndEagerness(
+    const PrefetchType prefetch_type,
+    const std::optional<std::string>& embedder_histogram_suffix) {
+  switch (prefetch_type.trigger_type()) {
+    case PreloadingTriggerType::kSpeculationRule:
+      switch (prefetch_type.GetEagerness()) {
+        case blink::mojom::SpeculationEagerness::kEager:
+          return "SpeculationRule_Eager";
+        case blink::mojom::SpeculationEagerness::kModerate:
+          return "SpeculationRule_Moderate";
+        case blink::mojom::SpeculationEagerness::kConservative:
+          return "SpeculationRule_Conservative";
+      }
+    case PreloadingTriggerType::kSpeculationRuleFromIsolatedWorld:
+      switch (prefetch_type.GetEagerness()) {
+        case blink::mojom::SpeculationEagerness::kEager:
+          return "SpeculationRuleFromIsolatedWorld_Eager";
+        case blink::mojom::SpeculationEagerness::kModerate:
+          return "SpeculationRuleFromIsolatedWorld_Moderate";
+        case blink::mojom::SpeculationEagerness::kConservative:
+          return "SpeculationRuleFromIsolatedWorld_Conservative";
+      }
+    case PreloadingTriggerType::kSpeculationRuleFromAutoSpeculationRules:
+      switch (prefetch_type.GetEagerness()) {
+        case blink::mojom::SpeculationEagerness::kEager:
+          return "SpeculationRuleFromAutoSpeculationRules_Eager";
+        case blink::mojom::SpeculationEagerness::kModerate:
+          return "SpeculationRuleFromAutoSpeculationRules_Moderate";
+        case blink::mojom::SpeculationEagerness::kConservative:
+          return "SpeculationRuleFromAutoSpeculationRules_Conservative";
+      }
+    case PreloadingTriggerType::kEmbedder:
+      CHECK(!embedder_histogram_suffix.value().empty());
+      return base::StrCat({"Embedder_", embedder_histogram_suffix.value()});
   }
 }
+// LINT.ThenChange(//tools/metrics/histograms/metadata/prefetch/histograms.xml:TriggerTypeAndEagerness)
 
 size_t MaxNumberOfEagerPrefetchesPerPage() {
   int max = base::GetFieldTrialParamByFeatureAsInt(features::kPrefetchNewLimits,
diff --git a/content/browser/preloading/prefetch/prefetch_params.h b/content/browser/preloading/prefetch/prefetch_params.h
index e492ac1c..22b82921 100644
--- a/content/browser/preloading/prefetch/prefetch_params.h
+++ b/content/browser/preloading/prefetch/prefetch_params.h
@@ -103,9 +103,15 @@
     const PrefetchType& prefetch_type,
     bool is_nav_prerender);
 
-// Gets the histogram suffix to use for the given eagerness parameter.
-CONTENT_EXPORT std::string GetPrefetchEagernessHistogramSuffix(
-    blink::mojom::SpeculationEagerness eagerness);
+// Gets the histogram suffix for the given `prefetch_type` and
+// `embedder_histogram_suffix`.
+// `embedder_histogram_suffix` will be utilized directly to generate the
+// histogram names. `TriggerTypeAndEagerness` in
+// //tools/metrics/histograms/metadata/prefetch/histograms.xml should be updated
+// if we start using a new one.
+CONTENT_EXPORT std::string GetMetricsSuffixTriggerTypeAndEagerness(
+    const PrefetchType prefetch_type,
+    const std::optional<std::string>& embedder_histogram_suffix);
 
 // Returns the max number of eager prefetches allowed.
 size_t MaxNumberOfEagerPrefetchesPerPage();
diff --git a/content/browser/preloading/prefetch/prefetch_service_unittest.cc b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
index 9ef8017..bcbd629 100644
--- a/content/browser/preloading/prefetch/prefetch_service_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
@@ -1245,11 +1245,10 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
 
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
-      PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true,
-                   blink::mojom::SpeculationEagerness::kEager));
+  const PrefetchType prefetch_type = PrefetchType(
+      PreloadingTriggerType::kSpeculationRule,
+      /*use_prefetch_proxy=*/true, blink::mojom::SpeculationEagerness::kEager);
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(GURL("https://example.com"),
@@ -1268,13 +1267,12 @@
   histogram_tester.ExpectUniqueSample(
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
 
-    histogram_tester.ExpectUniqueSample(
-        base::StringPrintf("PrefetchProxy.AfterClick."
-                           "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                           GetPrefetchEagernessHistogramSuffix(
-                               blink::mojom::SpeculationEagerness::kEager)
-                               .c_str()),
-        false, 1);
+  histogram_tester.ExpectUniqueSample(
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          GetMetricsSuffixTriggerTypeAndEagerness(
+              prefetch_type, /*embedder_histogram_suffix=*/std::nullopt)),
+      false, 1);
 }
 
 TEST_P(PrefetchServiceTest, SuccessCase_Browser) {
@@ -1342,6 +1340,15 @@
   ExpectServingReaderSuccess(serveable_reader);
   EXPECT_EQ(serveable_reader.GetPrefetchContainer()->GetURL(),
             GURL("https://example.com/?b=1"));
+
+  histogram_tester.ExpectUniqueSample(
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          GetMetricsSuffixTriggerTypeAndEagerness(
+              PrefetchType(PreloadingTriggerType::kEmbedder,
+                           /*use_prefetch_proxy=*/false),
+              test::kPreloadingEmbedderHistgramSuffixForTesting)),
+      false, 1);
 }
 
 TEST_P(PrefetchServiceTest, SuccessCase_Browser_NoVarySearch) {
@@ -1640,11 +1647,10 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
           /*num_on_prefetch_likely_calls=*/std::nullopt));
-
+  const PrefetchType prefetch_type = PrefetchType(
+      PreloadingTriggerType::kEmbedder, /*use_prefetch_proxy=*/false);
   auto handle =
-      MakePrefetchFromEmbedder(GURL("https://example.com"),
-                               PrefetchType(PreloadingTriggerType::kEmbedder,
-                                            /*use_prefetch_proxy=*/false));
+      MakePrefetchFromEmbedder(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestStateForEmbedders(GURL("https://example.com"),
@@ -1676,6 +1682,13 @@
 
   histogram_tester.ExpectUniqueSample(
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
+  histogram_tester.ExpectUniqueSample(
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          GetMetricsSuffixTriggerTypeAndEagerness(
+              prefetch_type,
+              test::kPreloadingEmbedderHistgramSuffixForTesting)),
+      false, 1);
 }
 
 TEST_P(PrefetchServiceTest,
@@ -4162,10 +4175,10 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
 
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()));
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -4205,31 +4218,21 @@
   ExpectServingMetricsSuccess();
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       base::Milliseconds(500), 1);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      base::Milliseconds(500), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -4247,10 +4250,11 @@
   no_vary_search_hint->search_variance =
       network::mojom::SearchParamsVariance::NewNoVaryParams(
           std::vector<std::string>({"a"}));
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com/index.html?a=5"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()),
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+  MakePrefetchOnMainFrame(
+      GURL("https://example.com/index.html?a=5"), prefetch_type,
       /* referrer */ blink::mojom::Referrer(), std::move(no_vary_search_hint));
   task_environment()->RunUntilIdle();
 
@@ -4292,31 +4296,26 @@
   ExpectServingMetricsSuccess();
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       base::Milliseconds(600), 1);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      base::Milliseconds(600), 1);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -4334,10 +4333,11 @@
   no_vary_search_hint->search_variance =
       network::mojom::SearchParamsVariance::NewNoVaryParams(
           std::vector<std::string>({"a"}));
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com/index.html?a=5"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()),
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+  MakePrefetchOnMainFrame(
+      GURL("https://example.com/index.html?a=5"), prefetch_type,
       /* referrer */ blink::mojom::Referrer(),
       /* no_vary_search_hint */ std::move(no_vary_search_hint));
   task_environment()->RunUntilIdle();
@@ -4378,31 +4378,21 @@
   ExpectServingMetricsSuccess();
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(700), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(700), 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -4420,10 +4410,11 @@
   no_vary_search_hint->search_variance =
       network::mojom::SearchParamsVariance::NewNoVaryParams(
           std::vector<std::string>({"a"}));
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com/index.html?a=5"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()),
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+  MakePrefetchOnMainFrame(
+      GURL("https://example.com/index.html?a=5"), prefetch_type,
       /* referrer */ blink::mojom::Referrer(),
       /* no_vary_search_hint */ std::move(no_vary_search_hint));
   task_environment()->RunUntilIdle();
@@ -4465,31 +4456,21 @@
   ExpectServingMetricsSuccess();
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(kHeaderLatency), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(kHeaderLatency), 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -4500,11 +4481,10 @@
 
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
-
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()));
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -4563,31 +4543,21 @@
                         .eagerness = GetEagernessParam()});
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(800), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(800), 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -4630,33 +4600,23 @@
   ExpectServingMetrics(PrefetchStatus::kPrefetchFailedNetError);
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   base::TimeDelta block_until_head_timeout =
       PrefetchBlockUntilHeadTimeout(prefetch_type, /*is_nav_prerender=*/false);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      block_until_head_timeout, 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       block_until_head_timeout, 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -4667,11 +4627,10 @@
 
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
-
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/false, GetEagernessParam()));
+                   /*use_prefetch_proxy=*/false, GetEagernessParam());
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -4717,31 +4676,21 @@
   // navigation start but decided not to be used later (after
   // `kBlockUntilHeadTimeout` msec) due to timeout.
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(kBlockUntilHeadTimeout), 2);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(kBlockUntilHeadTimeout), 2);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 2);
 
   // The third navigation is started after the PrefetchContainer became not
@@ -4758,28 +4707,18 @@
   // thus shouldn't be considered as a candidate in the first place.
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(kBlockUntilHeadTimeout), 2);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(kBlockUntilHeadTimeout), 2);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 2);
 }
 
@@ -4790,11 +4729,10 @@
 
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
-
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/false, GetEagernessParam()));
+                   /*use_prefetch_proxy=*/false, GetEagernessParam());
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -4827,31 +4765,21 @@
                        /*required_private_prefetch_proxy=*/false);
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(300), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(300), 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -4879,6 +4807,10 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(2));
 
+  const PrefetchType prefetch_type =
+      PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                   /*use_prefetch_proxy=*/false, GetEagernessParam());
+
   {
     network::mojom::NoVarySearchPtr no_vary_search_hint =
         network::mojom::NoVarySearch::New();
@@ -4887,9 +4819,7 @@
         network::mojom::SearchParamsVariance::NewNoVaryParams(
             std::vector<std::string>({"a"}));
     MakePrefetchOnMainFrame(
-        GURL(kTestUrl + "?a=5"),
-        PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                     /*use_prefetch_proxy=*/false, GetEagernessParam()),
+        GURL(kTestUrl + "?a=5"), prefetch_type,
         /* referrer */ blink::mojom::Referrer(),
         /* no_vary_search_hint */ std::move(no_vary_search_hint));
     task_environment()->RunUntilIdle();
@@ -4906,9 +4836,7 @@
         network::mojom::SearchParamsVariance::NewNoVaryParams(
             std::vector<std::string>({"b"}));
     MakePrefetchOnMainFrame(
-        GURL(kTestUrl + "?b=3"),
-        PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                     /*use_prefetch_proxy=*/false, GetEagernessParam()),
+        GURL(kTestUrl + "?b=3"), prefetch_type,
         /* referrer */ blink::mojom::Referrer(),
         /* no_vary_search_hint */ std::move(no_vary_search_hint));
     task_environment()->RunUntilIdle();
@@ -4965,31 +4893,21 @@
   ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       base::Milliseconds(kHeaderLatency), 1);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      base::Milliseconds(kHeaderLatency), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -5193,6 +5111,10 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(2));
 
+  const PrefetchType prefetch_type =
+      PrefetchType(PreloadingTriggerType::kSpeculationRule,
+                   /*use_prefetch_proxy=*/false, GetEagernessParam());
+
   {
     network::mojom::NoVarySearchPtr no_vary_search_hint =
         network::mojom::NoVarySearch::New();
@@ -5202,9 +5124,7 @@
             std::vector<std::string>({"a"}));
     GURL not_matched_url = GURL(kTestUrl + "?a=5");
     MakePrefetchOnMainFrame(
-        not_matched_url,
-        PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                     /*use_prefetch_proxy=*/false, GetEagernessParam()),
+        not_matched_url, prefetch_type,
         /* referrer */ blink::mojom::Referrer(),
         /* no_vary_search_hint */ std::move(no_vary_search_hint));
     task_environment()->RunUntilIdle();
@@ -5222,9 +5142,7 @@
             std::vector<std::string>({"b"}));
     GURL matched_url = GURL(kTestUrl + "?b=3");
     MakePrefetchOnMainFrame(
-        matched_url,
-        PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                     /*use_prefetch_proxy=*/false, GetEagernessParam()),
+        matched_url, prefetch_type,
         /* referrer */ blink::mojom::Referrer(),
         /* no_vary_search_hint */ std::move(no_vary_search_hint));
     task_environment()->RunUntilIdle();
@@ -5274,31 +5192,21 @@
   EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 2);
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(0), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(0), 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -5309,10 +5217,11 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
 
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()));
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -5347,31 +5256,21 @@
   EXPECT_FALSE(serveable_reader);
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(1000), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(1000), 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -5382,10 +5281,10 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
 
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()));
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -5420,31 +5319,21 @@
   EXPECT_FALSE(serveable_reader);
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(1000), 1);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(1000), 1);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 1);
 }
 
@@ -5456,10 +5345,10 @@
   MakePrefetchService(
       std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
 
-  MakePrefetchOnMainFrame(
-      GURL("https://example.com"),
+  const PrefetchType prefetch_type =
       PrefetchType(PreloadingTriggerType::kSpeculationRule,
-                   /*use_prefetch_proxy=*/true, GetEagernessParam()));
+                   /*use_prefetch_proxy=*/true, GetEagernessParam());
+  MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
   task_environment()->RunUntilIdle();
 
   VerifyCommonRequestState(
@@ -5501,31 +5390,21 @@
   ExpectServingMetricsSuccess();
 
   std::string histogram_suffix =
-      GetPrefetchEagernessHistogramSuffix(GetEagernessParam());
+      GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
   histogram_tester.ExpectTotalCount(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.Served.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
           histogram_suffix),
       0);
   histogram_tester.ExpectUniqueTimeSample(
       base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.NotServed.%s",
-          histogram_suffix),
-      base::Milliseconds(1000), 2);
-  histogram_tester.ExpectTotalCount(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.Served.%s",
-          histogram_suffix),
-      0);
-  histogram_tester.ExpectUniqueTimeSample(
-      base::StringPrintf(
-          "PrefetchProxy.AfterClick.BlockUntilHeadDuration2.NotServed.%s",
+          "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
           histogram_suffix),
       base::Milliseconds(1000), 2);
   histogram_tester.ExpectUniqueSample(
-      base::StringPrintf("PrefetchProxy.AfterClick."
-                         "PrefetchMatchingBlockedNavigationWithPrefetch.%s",
-                         histogram_suffix),
+      base::StringPrintf(
+          "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
+          histogram_suffix),
       true, 2);
 }
 
@@ -7090,7 +6969,8 @@
   histogram_tester.ExpectUniqueSample(
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 2);
   histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Eager",
       false, 2);
 }
@@ -7160,7 +7040,8 @@
   histogram_tester.ExpectUniqueSample(
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 2);
   histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Eager",
       true, 2);
 }
@@ -7236,7 +7117,8 @@
   histogram_tester.ExpectUniqueSample(
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
   histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Eager",
       true, 2);
 }
@@ -7296,7 +7178,8 @@
   histogram_tester.ExpectTotalCount(
       "PrefetchProxy.AfterClick.RedirectChainSize", 0);
   histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Eager",
       true, 2);
 }
@@ -7384,7 +7267,8 @@
   histogram_tester.ExpectUniqueSample(
       "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
   histogram_tester.ExpectUniqueSample(
-      "PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch."
+      "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
+      "SpeculationRule_"
       "Eager",
       true, 1);
 
@@ -8119,20 +8003,24 @@
   handle.reset();
 
   histogram_tester.ExpectUniqueSample(
-      "Prefetch.PrefetchContainer.AddedToInitialEligibility.Embedder."
-      "NoEmbedderSuffix",
+      base::StrCat(
+          {"Prefetch.PrefetchContainer.AddedToInitialEligibility.Embedder_",
+           test::kPreloadingEmbedderHistgramSuffixForTesting}),
       0, 1);
   histogram_tester.ExpectUniqueSample(
-      "Prefetch.PrefetchContainer.AddedToPrefetchStarted.Embedder."
-      "NoEmbedderSuffix",
+      base::StrCat(
+          {"Prefetch.PrefetchContainer.AddedToPrefetchStarted.Embedder_",
+           test::kPreloadingEmbedderHistgramSuffixForTesting}),
       0, 1);
   histogram_tester.ExpectUniqueSample(
-      "Prefetch.PrefetchContainer.AddedToHeaderDeterminedSuccessfully."
-      "Embedder.NoEmbedderSuffix",
+      base::StrCat({"Prefetch.PrefetchContainer."
+                    "AddedToHeaderDeterminedSuccessfully.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       kHeaderLatency, 1);
   histogram_tester.ExpectUniqueSample(
-      "Prefetch.PrefetchContainer.AddedToPrefetchCompletedSuccessfully."
-      "Embedder.NoEmbedderSuffix",
+      base::StrCat({"Prefetch.PrefetchContainer."
+                    "AddedToPrefetchCompletedSuccessfully.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       kHeaderLatency, 1);
 }
 
@@ -8155,20 +8043,24 @@
   handle.reset();
 
   histogram_tester.ExpectUniqueSample(
-      "Prefetch.PrefetchContainer.AddedToInitialEligibility.Embedder."
-      "NoEmbedderSuffix",
+      base::StrCat(
+          {"Prefetch.PrefetchContainer.AddedToInitialEligibility.Embedder_",
+           test::kPreloadingEmbedderHistgramSuffixForTesting}),
       0, 1);
   histogram_tester.ExpectUniqueSample(
-      "Prefetch.PrefetchContainer.AddedToPrefetchStarted.Embedder."
-      "NoEmbedderSuffix",
+      base::StrCat(
+          {"Prefetch.PrefetchContainer.AddedToPrefetchStarted.Embedder_",
+           test::kPreloadingEmbedderHistgramSuffixForTesting}),
       0, 1);
   histogram_tester.ExpectTotalCount(
-      "Prefetch.PrefetchContainer.AddedToHeaderDeterminedSuccesfully."
-      "Embedder.NoEmbedderSuffix",
+      base::StrCat({"Prefetch.PrefetchContainer."
+                    "AddedToHeaderDeterminedSuccesfully.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       0);
   histogram_tester.ExpectTotalCount(
-      "Prefetch.PrefetchContainer.AddedToPrefetchCompletedSuccessfully."
-      "Embedder.NoEmbedderSuffix",
+      base::StrCat({"Prefetch.PrefetchContainer."
+                    "AddedToPrefetchCompletedSuccessfully.Embedder_",
+                    test::kPreloadingEmbedderHistgramSuffixForTesting}),
       0);
 }
 
diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc
index cadb96f..f78e6732 100644
--- a/content/gpu/gpu_main.cc
+++ b/content/gpu/gpu_main.cc
@@ -28,7 +28,6 @@
 #include "base/task/single_thread_task_executor.h"
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "base/threading/platform_thread.h"
-#include "base/threading/thread.h"
 #include "base/time/time.h"
 #include "base/timer/hi_res_timer_manager.h"
 #include "base/trace_event/trace_event.h"
@@ -45,7 +44,6 @@
 #include "content/public/common/content_switches.h"
 #include "content/public/common/main_function_params.h"
 #include "content/public/common/result_codes.h"
-#include "content/public/common/zygote/zygote_buildflags.h"
 #include "content/public/gpu/content_gpu_client.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/config/gpu_driver_bug_list.h"
@@ -61,7 +59,6 @@
 #include "media/gpu/buildflags.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
-#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
 #include "services/tracing/public/cpp/trace_startup.h"
 #include "services/tracing/public/cpp/trace_startup_config.h"
 #include "third_party/angle/src/gpu_info_util/SystemInfo.h"
@@ -199,8 +196,11 @@
 
 // Main function for starting the Gpu process.
 int GpuMain(MainFunctionParams parameters) {
-  TRACE_EVENT("gpu,startup", "GpuMain");
+  if (tracing::TraceStartupConfig::GetInstance().IsEnabled()) {
+    gl::StartupTrace::Startup();
+  }
 
+  TRACE_EVENT0("gpu", "GpuMain");
   base::CurrentProcess::GetInstance().SetProcessType(
       base::CurrentProcessType::PROCESS_GPU);
 
@@ -310,6 +310,7 @@
             base::MessagePumpType::DEFAULT);
 #endif
   }
+  gl::StartupTrace::GetInstance()->BindToCurrentThread();
 
   base::PlatformThread::SetName("CrGpuMain");
   mojo::InterfaceEndpointClient::SetThreadNameSuffixForMetrics("GpuMain");
@@ -435,6 +436,7 @@
   }
 
   DCHECK(tracing::IsTracingInitialized());
+  gl::StartupTrace::StarupDone();
 
   {
     TRACE_EVENT0("gpu", "Run Message Loop");
@@ -458,14 +460,6 @@
     sandbox::policy::SandboxLinux::GetInstance()->StopThread(watchdog_thread);
   }
 
-  base::Thread* trace_thread =
-      tracing::IsTracingInitialized()
-          ? tracing::PerfettoTracedProcess::GetTraceThread()
-          : nullptr;
-  if (trace_thread) {
-    sandbox::policy::SandboxLinux::GetInstance()->StopThread(trace_thread);
-  }
-
   // SandboxLinux::InitializeSandbox() must always be called
   // with only one thread.
   sandbox::policy::SandboxLinux::Options sandbox_options;
@@ -515,10 +509,6 @@
     watchdog_thread->Start();
   }
 
-  if (trace_thread) {
-    tracing::PerfettoTracedProcess::Get().RestartThreadInSandbox();
-  }
-
   return res;
 }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
diff --git a/infra/config/generated/builder-owners/chrome-product-engprod@google.com.txt b/infra/config/generated/builder-owners/chrome-product-engprod@google.com.txt
index c86f587..7649402 100644
--- a/infra/config/generated/builder-owners/chrome-product-engprod@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-product-engprod@google.com.txt
@@ -1,4 +1,3 @@
-ci/android-15-chrome-stable-wpt-fyi-rel
 ci/android-15-chrome-wpt-fyi-rel
 ci/android-15-webview-wpt-fyi-rel
 try/android-15-chrome-wpt-fyi-rel
diff --git a/infra/config/generated/builders/ci/GPU FYI Mac Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Mac Builder/targets/chromium.gpu.fyi.json
index ed298b14..c9bf0e4 100644
--- a/infra/config/generated/builders/ci/GPU FYI Mac Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Mac Builder/targets/chromium.gpu.fyi.json
@@ -3411,53 +3411,6 @@
       },
       {
         "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --disable-features=SkiaGraphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "pixel_skia_gold_gl_passthrough_ganesh_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "x86-64",
-            "display_attached": "1",
-            "gpu": "10de:0fe9",
-            "hidpi": "1",
-            "os": "Mac-11.7.9",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "screenshot_sync",
           "--show-stdout",
           "--browser=release",
diff --git "a/infra/config/generated/builders/ci/Mac FYI Retina Release \050NVIDIA\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Mac FYI Retina Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
index 00e4a955..211f264 100644
--- "a/infra/config/generated/builders/ci/Mac FYI Retina Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Mac FYI Retina Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
@@ -366,53 +366,6 @@
       },
       {
         "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --disable-features=SkiaGraphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "pixel_skia_gold_gl_passthrough_ganesh_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "x86-64",
-            "display_attached": "1",
-            "gpu": "10de:0fe9",
-            "hidpi": "1",
-            "os": "Mac-11.7.9",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "screenshot_sync",
           "--show-stdout",
           "--browser=release",
diff --git a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/gn-args.json b/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/gn-args.json
deleted file mode 100644
index 382c163..0000000
--- a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/gn-args.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "gn_args": {
-    "android_static_analysis": "off",
-    "dcheck_always_on": false,
-    "debuggable_apks": false,
-    "ffmpeg_branding": "Chrome",
-    "is_component_build": false,
-    "is_debug": false,
-    "proprietary_codecs": true,
-    "strip_debug_info": true,
-    "symbol_level": 1,
-    "system_webview_package_name": "com.google.android.webview.debug",
-    "system_webview_shell_package_name": "org.chromium.my_webview_shell",
-    "target_cpu": "x64",
-    "target_os": "android",
-    "use_reclient": false,
-    "use_remoteexec": true,
-    "use_siso": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/properties.json b/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/properties.json
deleted file mode 100644
index 117d9f13..0000000
--- a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/properties.json
+++ /dev/null
@@ -1,73 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "android-15-chrome-stable-wpt-fyi-rel",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-android-archive",
-              "builder_group": "chromium.android.fyi",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_android_config": {
-                "config": "base_config"
-              },
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "main_builder",
-                "target_arch": "intel",
-                "target_bits": 64,
-                "target_platform": "android"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "android"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "android-15-chrome-stable-wpt-fyi-rel",
-          "project": "chromium"
-        }
-      ],
-      "targets_spec_directory": "src/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/targets"
-    }
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_monitoring": true,
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "metrics_project": "chromium-reclient-metrics",
-    "project": "rbe-chromium-trusted",
-    "remote_jobs": 250
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.android.fyi",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/shadow-properties.json b/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/shadow-properties.json
deleted file mode 100644
index 78dedff8..0000000
--- a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/shadow-properties.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_monitoring": true,
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "metrics_project": "chromium-reclient-metrics",
-    "project": "rbe-chromium-untrusted",
-    "remote_jobs": 250
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/targets/chromium.android.fyi.json
deleted file mode 100644
index 3706339..0000000
--- a/infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/targets/chromium.android.fyi.json
+++ /dev/null
@@ -1,54 +0,0 @@
-{
-  "android-15-chrome-stable-wpt-fyi-rel": {
-    "isolated_scripts": [
-      {
-        "args": [
-          "--avd-config=../../tools/android/avd/proto/android_35_google_apis_x64.textpb",
-          "--use-upstream-wpt",
-          "--stable",
-          "--timeout-multiplier=4"
-        ],
-        "description": "Run with android_35_google_apis_x64",
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "chrome_public_wpt",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "dimensions": {
-            "cores": "8",
-            "cpu": "x86-64",
-            "device_os": null,
-            "device_type": null,
-            "os": "Ubuntu-22.04",
-            "pool": "chromium.tests.avd"
-          },
-          "expiration": 18000,
-          "hard_timeout": 14400,
-          "named_caches": [
-            {
-              "name": "android_35_google_apis_x64",
-              "path": ".android_emulator/android_35_google_apis_x64"
-            }
-          ],
-          "optional_dimensions": {
-            "60": {
-              "caches": "android_35_google_apis_x64"
-            }
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 36
-        },
-        "test": "chrome_public_wpt",
-        "test_id_prefix": "ninja://chrome/android:chrome_public_wpt/"
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index 2a21d59a..4043fde8 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -84,7 +84,6 @@
     "android-13-x64-fyi-rel": "ci/android-13-x64-fyi-rel/gn-args.json",
     "android-14-arm64-fyi-rel": "ci/android-14-arm64-fyi-rel/gn-args.json",
     "android-14-x64-fyi-rel": "ci/android-14-x64-fyi-rel/gn-args.json",
-    "android-15-chrome-stable-wpt-fyi-rel": "ci/android-15-chrome-stable-wpt-fyi-rel/gn-args.json",
     "android-15-chrome-wpt-fyi-rel": "ci/android-15-chrome-wpt-fyi-rel/gn-args.json",
     "android-15-webview-wpt-fyi-rel": "ci/android-15-webview-wpt-fyi-rel/gn-args.json",
     "android-15-x64-fyi-rel": "ci/android-15-x64-fyi-rel/gn-args.json",
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-mac-nvidia-retina-rel/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-mac-nvidia-retina-rel/targets/chromium.gpu.fyi.json
index ee481fd..10949d2 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-mac-nvidia-retina-rel/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-mac-nvidia-retina-rel/targets/chromium.gpu.fyi.json
@@ -367,53 +367,6 @@
       },
       {
         "args": [
-          "pixel",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--stable-jobs",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=gl --disable-features=SkiaGraphite",
-          "--enforce-browser-version",
-          "--git-revision=${got_revision}",
-          "--dont-restore-color-profile-after-test",
-          "--test-machine-name",
-          "${buildername}",
-          "--jobs=1"
-        ],
-        "merge": {
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "pixel_skia_gold_gl_passthrough_ganesh_test",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "containment_type": "AUTO",
-          "dimensions": {
-            "cpu": "x86-64",
-            "display_attached": "1",
-            "gpu": "10de:0fe9",
-            "hidpi": "1",
-            "os": "Mac-11.7.9",
-            "pool": "chromium.tests.gpu"
-          },
-          "hard_timeout": 1800,
-          "idempotent": false,
-          "io_timeout": 1800,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "telemetry_gpu_integration_test",
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
-      },
-      {
-        "args": [
           "screenshot_sync",
           "--show-stdout",
           "--browser=release",
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index d718e59e..68d72246 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -6773,27 +6773,6 @@
           }
         ]
       },
-      "android-15-chrome-stable-wpt-fyi-rel": {
-        "contact_team_email": "chrome-product-engprod@google.com",
-        "problem_specs": [
-          {
-            "name": "Unhealthy",
-            "period_days": 7,
-            "score": 5,
-            "thresholds": {
-              "_default": "_default"
-            }
-          },
-          {
-            "name": "Low Value",
-            "period_days": 90,
-            "score": 1,
-            "thresholds": {
-              "_default": "_default"
-            }
-          }
-        ]
-      },
       "android-15-chrome-wpt-fyi-rel": {
         "contact_team_email": "chrome-product-engprod@google.com",
         "problem_specs": [
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 5c13343..30abc4c1 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -38447,118 +38447,6 @@
       }
     }
     builders {
-      name: "android-15-chrome-stable-wpt-fyi-rel"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/properties.json",'
-        '    "shadow_properties_file": "infra/config/generated/builders/ci/android-15-chrome-stable-wpt-fyi-rel/shadow-properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.android.fyi",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      priority: 35
-      execution_timeout_secs: 14400
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.use_per_builder_build_dir_name"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      description_html: "This builder runs upstream web platform tests for reporting results to wpt.fyi.<br/>Builder owner: <a href=mailto:chrome-product-engprod@google.com>chrome-product-engprod@google.com</a>"
-      shadow_builder_adjustments {
-        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-        pool: "luci.chromium.try"
-        dimensions: "free_space:"
-        dimensions: "pool:luci.chromium.try"
-      }
-      contact_team_email: "chrome-product-engprod@google.com"
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/cached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"true\""
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
-        predicates: "has(build.output.properties.ran_tests_retry_shard)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
-        predicates: "has(build.output.properties.ran_tests_without_patch)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/uncached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"false\""
-      }
-    }
-    builders {
       name: "android-15-chrome-wpt-fyi-rel"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index dae4cd5..34eb4722 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -9971,11 +9971,6 @@
     short_name: "15"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/android-15-chrome-stable-wpt-fyi-rel"
-    category: "wpt|chrome"
-    short_name: "15-stable"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/android-webview-13-x64-wpt-android-specific"
     category: "wpt|webview"
     short_name: "13-x64"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index e14b260f..ddbaecf 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -3400,16 +3400,6 @@
   }
 }
 job {
-  id: "android-15-chrome-stable-wpt-fyi-rel"
-  realm: "ci"
-  schedule: "0 9 * * *"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "android-15-chrome-stable-wpt-fyi-rel"
-  }
-}
-job {
   id: "android-15-chrome-wpt-fyi-rel"
   realm: "ci"
   buildbucket {
@@ -6804,7 +6794,6 @@
   triggers: "android-14-automotive-landscape-x64-rel"
   triggers: "android-14-tablet-landscape-arm64-rel"
   triggers: "android-14-x64-rel"
-  triggers: "android-15-chrome-stable-wpt-fyi-rel"
   triggers: "android-15-chrome-wpt-fyi-rel"
   triggers: "android-15-tablet-landscape-x64-rel"
   triggers: "android-15-tablet-x64-rel"
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
index d80d825..99722d5 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -103,71 +103,6 @@
 )
 
 ci.builder(
-    name = "android-15-chrome-stable-wpt-fyi-rel",
-    description_html = "This builder runs upstream web platform tests for reporting results to wpt.fyi.",
-    schedule = "0 9 * * *",
-    builder_spec = builder_config.builder_spec(
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["android"],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "main_builder",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_arch = builder_config.target_arch.INTEL,
-            target_bits = 64,
-            target_platform = builder_config.target_platform.ANDROID,
-        ),
-        android_config = builder_config.android_config(config = "base_config"),
-        build_gs_bucket = "chromium-android-archive",
-    ),
-    gn_args = gn_args.config(
-        configs = [
-            "android_builder",
-            "release_builder",
-            "remoteexec",
-            "minimal_symbols",
-            "x64",
-            "strip_debug_info",
-            "android_fastbuild",
-            "webview_trichrome",
-            "webview_shell",
-        ],
-    ),
-    targets = targets.bundle(
-        targets = [
-            "chrome_public_wpt_suite",
-        ],
-        mixins = [
-            "15-x64-emulator",
-            "emulator-8-cores",
-            "has_native_resultdb_integration",
-            "linux-jammy",
-            "x86-64",
-        ],
-        per_test_modifications = {
-            "chrome_public_wpt": targets.mixin(
-                args = [
-                    "--use-upstream-wpt",
-                    "--stable",
-                    "--timeout-multiplier=4",
-                ],
-            ),
-        },
-    ),
-    targets_settings = targets.settings(
-        os_type = targets.os_type.ANDROID,
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "wpt|chrome",
-        short_name = "15-stable",
-    ),
-    contact_team_email = "chrome-product-engprod@google.com",
-    execution_timeout = 4 * time.hour,
-)
-
-ci.builder(
     name = "android-chrome-13-x64-wpt-android-specific",
     description_html = "Run wpt tests on Chrome Android in Android 13 emulators.",
     builder_spec = builder_config.builder_spec(
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index 44fd271b..da2f24e 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -2381,6 +2381,11 @@
         mixins = [
             "mac_retina_nvidia_gpu_stable",
         ],
+        per_test_modifications = {
+            "pixel_skia_gold_gl_passthrough_ganesh_test": targets.remove(
+                reason = "Tests timeout too often, also bot will be decommissioned. See crbug.com/407800772",
+            ),
+        },
     ),
     targets_settings = targets.settings(
         browser_config = targets.browser_config.RELEASE,
diff --git a/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_egtest.mm
index 18a0fc5..93e7c8e 100644
--- a/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_egtest.mm
@@ -186,6 +186,10 @@
                      IDS_AUTOFILL_CARD_UNMASK_OTP_INPUT_DIALOG_TITLE)]
       assertWithMatcher:grey_sufficientlyVisible()];
 
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::
+    StaticTextWithAccessibilityLabel(@"Enter 6-digit verification code")]
+      assertWithMatcher:grey_sufficientlyVisible()];
+
   [[EarlGrey selectElementWithMatcher:OtpTextfield()]
       assertWithMatcher:grey_sufficientlyVisible()];
 
diff --git a/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_view_controller.mm b/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_view_controller.mm
index ac48ef3..7be3e1c 100644
--- a/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_view_controller.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/authentication/otp_input_dialog_view_controller.mm
@@ -215,6 +215,8 @@
                       itemIdentifier:(ItemIdentifier)itemIdentifier {
   TableViewTextEditCell* cell =
       DequeueTableViewCell<TableViewTextEditCell>(self.tableView);
+  cell.accessibilityLabelValue = _content.textFieldPlaceholder;
+
   [cell setIdentifyingIcon:nil];
   if (_errorTitle) {
     [cell setIcon:TableViewTextEditItemIconTypeError];
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm
index e3bfd1b1..aeea0993 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_card_cell.mm
@@ -181,8 +181,9 @@
 // The menu button displayed in the cell's header.
 @property(nonatomic, strong) UIButton* overflowMenuButton;
 
-// The text view with instructions for how to use virtual cards.
-@property(nonatomic, strong) UITextView* virtualCardInstructionTextView;
+// The text view with instructions for how to use virtual or cardInfo retrieval
+// enrolled cards.
+@property(nonatomic, strong) UITextView* cardInstructionTextView;
 
 // A labeled chip showing the card number.
 @property(nonatomic, strong) ManualFillLabeledChip* cardNumberLabeledChip;
@@ -213,7 +214,7 @@
 
 // Separator line. Used to delimit the virtual card instruction text view from
 // the rest of the cell.
-@property(nonatomic, strong) UIView* virtualCardInstructionsSeparator;
+@property(nonatomic, strong) UIView* cardInstructionsSeparator;
 
 // Button to autofill the current form with the card's data.
 @property(nonatomic, strong) UIButton* autofillFormButton;
@@ -242,8 +243,8 @@
 
   self.cardLabel.text = @"";
 
-  self.virtualCardInstructionTextView.text = @"";
-  self.virtualCardInstructionTextView.hidden = NO;
+  self.cardInstructionTextView.text = @"";
+  self.cardInstructionTextView.hidden = NO;
 
   [self.cardNumberLabeledChip prepareForReuse];
   [self.expirationDateLabeledChip prepareForReuse];
@@ -335,14 +336,13 @@
 
   UILabel* expirationDateSeparatorLabel;
 
-  // Virtual card instruction textview is always created, but hidden for
-  // non-virtual cards.
-  self.virtualCardInstructionTextView =
-      [self createVirtualCardInstructionTextView];
-  [self.contentView addSubview:self.virtualCardInstructionTextView];
-  [accessibilityElements addObject:self.virtualCardInstructionTextView];
+  // Card instruction textview is always created, but is only visible for
+  // virtual and CardInfoRetrieval enrolled cards and hidden for rest.
+  self.cardInstructionTextView = [self createCardInstructionTextView];
+  [self.contentView addSubview:self.cardInstructionTextView];
+  [accessibilityElements addObject:self.cardInstructionTextView];
 
-  self.virtualCardInstructionsSeparator =
+  self.cardInstructionsSeparator =
       CreateGraySeparatorForContainer(self.contentView);
 
   self.cardNumberLabeledChip = [[ManualFillLabeledChip alloc]
@@ -395,9 +395,8 @@
   self.gPayIcon = [self createGPayIcon];
   [self.contentView addSubview:self.gPayIcon];
 
-  AppendHorizontalConstraintsForViews(staticConstraints,
-                                      @[ self.virtualCardInstructionTextView ],
-                                      self.layoutGuide);
+  AppendHorizontalConstraintsForViews(
+      staticConstraints, @[ self.cardInstructionTextView ], self.layoutGuide);
   AppendHorizontalConstraintsForViews(
       staticConstraints, @[ self.cardNumberLabeledChip ], self.layoutGuide,
       kChipsHorizontalMargin,
@@ -454,9 +453,13 @@
   self.cardLabel.attributedText = attributedString;
   self.cardLabel.accessibilityIdentifier = attributedString.string;
   if (card.recordType == kVirtualCard) {
-    self.virtualCardInstructionTextView.attributedText =
+    self.cardInstructionTextView.attributedText =
         [self createvirtualCardInstructionTextViewAttributedText];
-    self.virtualCardInstructionTextView.backgroundColor = UIColor.clearColor;
+  } else if (card.cardInfoRetrievalEnrollmentState ==
+             autofill::CreditCard::CardInfoRetrievalEnrollmentState::
+                 kRetrievalEnrolled) {
+    self.cardInstructionTextView.text = l10n_util::GetNSString(
+        IDS_AUTOFILL_PAYMENTS_MANUAL_FALLBACK_CARD_INFO_RETRIEVAL_INSTRUCTION_TEXT);
   }
   [self.cardNumberLabeledChip
       setLabelText:
@@ -527,23 +530,26 @@
   NSMutableArray<UIView*>* cardInfoGroupVerticalLeadChips =
       [[NSMutableArray alloc] init];
 
-  // Virtual card instruction.
-  if (card.recordType == kVirtualCard) {
+  // Card instruction.
+  if (card.recordType == kVirtualCard ||
+      card.cardInfoRetrievalEnrollmentState ==
+          autofill::CreditCard::CardInfoRetrievalEnrollmentState::
+              kRetrievalEnrolled) {
     AddViewToVerticalLeadViews(
-        self.virtualCardInstructionTextView,
+        self.cardInstructionTextView,
         ManualFillCellView::ElementType::kVirtualCardInstructions,
         verticalLeadViews);
     if (IsKeyboardAccessoryUpgradeEnabled()) {
       AddViewToVerticalLeadViews(
-          self.virtualCardInstructionsSeparator,
-          ManualFillCellView::ElementType::kVirtualCardInstructionsSeparator,
+          self.cardInstructionsSeparator,
+          ManualFillCellView::ElementType::kCardInstructionsSeparator,
           verticalLeadViews);
-      self.virtualCardInstructionsSeparator.hidden = NO;
+      self.cardInstructionsSeparator.hidden = NO;
     }
-    self.virtualCardInstructionTextView.hidden = NO;
+    self.cardInstructionTextView.hidden = NO;
   } else {
-    self.virtualCardInstructionTextView.hidden = YES;
-    self.virtualCardInstructionsSeparator.hidden = YES;
+    self.cardInstructionTextView.hidden = YES;
+    self.cardInstructionsSeparator.hidden = YES;
   }
 
   // Card number labeled chip button.
@@ -754,20 +760,20 @@
   return virtualCardInstructionAttributedString;
 }
 
-- (UITextView*)createVirtualCardInstructionTextView {
-  UITextView* virtualCardInstructionTextView =
+- (UITextView*)createCardInstructionTextView {
+  UITextView* cardInstructionTextView =
       [[UITextView alloc] initWithFrame:self.contentView.frame];
-  virtualCardInstructionTextView.scrollEnabled = NO;
-  virtualCardInstructionTextView.editable = NO;
-  virtualCardInstructionTextView.delegate = self;
-  virtualCardInstructionTextView.translatesAutoresizingMaskIntoConstraints = NO;
-  virtualCardInstructionTextView.textColor =
-      [UIColor colorNamed:kTextSecondaryColor];
-  virtualCardInstructionTextView.backgroundColor = UIColor.clearColor;
-  virtualCardInstructionTextView.textContainerInset =
-      UIEdgeInsetsMake(0, 0, 0, 0);
-  virtualCardInstructionTextView.textContainer.lineFragmentPadding = 0;
-  return virtualCardInstructionTextView;
+  cardInstructionTextView.scrollEnabled = NO;
+  cardInstructionTextView.editable = NO;
+  cardInstructionTextView.delegate = self;
+  cardInstructionTextView.translatesAutoresizingMaskIntoConstraints = NO;
+  cardInstructionTextView.textColor = [UIColor colorNamed:kTextSecondaryColor];
+  cardInstructionTextView.backgroundColor = UIColor.clearColor;
+  cardInstructionTextView.font =
+            [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  cardInstructionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
+  cardInstructionTextView.textContainer.lineFragmentPadding = 0;
+  return cardInstructionTextView;
 }
 
 // Creates and configures the card icon image view.
@@ -803,7 +809,7 @@
     shouldInteractWithURL:(NSURL*)URL
                   inRange:(NSRange)characterRange
               interaction:(UITextItemInteraction)interaction {
-  if (textView == self.virtualCardInstructionTextView) {
+  if (textView == self.cardInstructionTextView) {
     // The learn more link was clicked.
     [self.navigationDelegate
           openURL:[[CrURL alloc]
@@ -818,7 +824,7 @@
 - (UIAction*)textView:(UITextView*)textView
     primaryActionForTextItem:(UITextItem*)textItem
                defaultAction:(UIAction*)defaultAction API_AVAILABLE(ios(17.0)) {
-  if (textView != self.virtualCardInstructionTextView) {
+  if (textView != self.cardInstructionTextView) {
     return defaultAction;
   }
 
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.h
index 754cd8ee..ddc41c1 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.h
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.h
@@ -46,9 +46,9 @@
     kHeaderSeparator,
     // The view presenting the instructions on how to use virtual cards.
     kVirtualCardInstructions,
-    // A grey line to separate the virtual card instruction view from the rest
+    // A grey line to separate the card instruction view from the rest
     // of the cell.
-    kVirtualCardInstructionsSeparator,
+    kCardInstructionsSeparator,
     // Any other element not falling into one of the above types.
     kOther,
   };
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.mm
index 7634aca..8223ff7 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_cell_utils.mm
@@ -73,8 +73,8 @@
 // left.
 constexpr CGFloat kTrailingViewMinLeadingSpacing = 8;
 
-// Top and bottom padding for the virtual card instruction view.
-constexpr CGFloat kVirtualCardInstructionsVerticalPadding = 8;
+// Top and bottom padding for the card instruction view.
+constexpr CGFloat kCardInstructionsVerticalPadding = 8;
 
 // Adds all baseline anchor constraints for the given `views` to match the first
 // one. Constraints are not activated.
@@ -120,8 +120,8 @@
     case ManualFillCellView::ElementType::kOtherChipButton:
       return kSmallSpacingBetweenViews;
     case ManualFillCellView::ElementType::kVirtualCardInstructions:
-    case ManualFillCellView::ElementType::kVirtualCardInstructionsSeparator:
-      return kVirtualCardInstructionsVerticalPadding;
+    case ManualFillCellView::ElementType::kCardInstructionsSeparator:
+      return kCardInstructionsVerticalPadding;
   }
 }
 
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard.mm
index 70645e4..d8bd2ee 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard.mm
@@ -81,18 +81,20 @@
   }
 
   return [self initWithGUID:GUID
-                       network:network
-                          icon:icon
-                      bankName:bankName
-                    cardHolder:cardHolder
-                        number:number
-              obfuscatedNumber:obfuscatedNumber
-      networkAndLastFourDigits:networkAndLastFourDigits
-                expirationYear:expirationYear
-               expirationMonth:expirationMonth
-                           CVC:CVC
-                    recordType:creditCard.record_type()
-               canFillDirectly:canFillDirectly];
+                               network:network
+                                  icon:icon
+                              bankName:bankName
+                            cardHolder:cardHolder
+                                number:number
+                      obfuscatedNumber:obfuscatedNumber
+              networkAndLastFourDigits:networkAndLastFourDigits
+                        expirationYear:expirationYear
+                       expirationMonth:expirationMonth
+                                   CVC:CVC
+                            recordType:creditCard.record_type()
+      cardInfoRetrievalEnrollmentState:
+          creditCard.card_info_retrieval_enrollment_state()
+                       canFillDirectly:canFillDirectly];
 }
 
 @end
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard_unittest.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard_unittest.mm
index 142abf3..0836474 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard_unittest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card+CreditCard_unittest.mm
@@ -179,9 +179,9 @@
 }
 
 // Tests that the manual fill credit card build from CardInfoRetrieval enrolled
-// card has CVC.
+// card has CVC and the enrollment state set.
 TEST_F(ManualFillCreditCardFormAutofilliOSTest,
-       ManualFillFromCardInfoRetrievalHasCVC) {
+       ManualFillFromCardInfoRetrieval) {
   NSString* GUID = @"1234-5678-abcd";
   NSString* number = @"1234";
   NSString* CVC = @"123";
@@ -200,4 +200,7 @@
                                                   icon:nil];
 
   EXPECT_NSEQ(manualFillCard.CVC, CVC);
+  EXPECT_EQ(manualFillCard.cardInfoRetrievalEnrollmentState,
+            autofill::CreditCard::CardInfoRetrievalEnrollmentState::
+                kRetrievalEnrolled);
 }
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.h b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.h
index 60db99d..063e4a3 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.h
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.h
@@ -50,6 +50,11 @@
 // The type of card: masked, local, virtual, etc.
 @property(nonatomic, readonly) autofill::CreditCard::RecordType recordType;
 
+// The credit card enrollment state for CardInfoRetrieval.
+@property(nonatomic, readonly)
+    autofill::CreditCard::CardInfoRetrievalEnrollmentState
+        cardInfoRetrievalEnrollmentState;
+
 // YES if the card can be filled directly from the ManualFillCreditCard NO if it
 // needs to be requested.
 @property(nonatomic, readonly) BOOL canFillDirectly;
@@ -57,18 +62,23 @@
 // Default init. `GUID` and `number` are the only fields considered for
 // equality, so we can differentiate between an obfuscated and a complete one.
 - (instancetype)initWithGUID:(NSString*)GUID
-                     network:(NSString*)network
-                        icon:(UIImage*)icon
-                    bankName:(NSString*)bankName
-                  cardHolder:(NSString*)cardHolder
-                      number:(NSString*)number
-            obfuscatedNumber:(NSString*)obfuscatedNumber
-    networkAndLastFourDigits:(NSString*)networkAndLastFourDigits
-              expirationYear:(NSString*)expirationYear
-             expirationMonth:(NSString*)expirationMonth
-                         CVC:(NSString*)CVC
-                  recordType:(autofill::CreditCard::RecordType)recordType
-             canFillDirectly:(BOOL)canFillDirecly NS_DESIGNATED_INITIALIZER;
+                             network:(NSString*)network
+                                icon:(UIImage*)icon
+                            bankName:(NSString*)bankName
+                          cardHolder:(NSString*)cardHolder
+                              number:(NSString*)number
+                    obfuscatedNumber:(NSString*)obfuscatedNumber
+            networkAndLastFourDigits:(NSString*)networkAndLastFourDigits
+                      expirationYear:(NSString*)expirationYear
+                     expirationMonth:(NSString*)expirationMonth
+                                 CVC:(NSString*)CVC
+                          recordType:
+                              (autofill::CreditCard::RecordType)recordType
+    cardInfoRetrievalEnrollmentState:
+        (autofill::CreditCard::CardInfoRetrievalEnrollmentState)
+            cardInfoRetrievalEnrollmentState
+                     canFillDirectly:(BOOL)canFillDirecly
+    NS_DESIGNATED_INITIALIZER;
 
 // Unavailable. Please use `initWithGuid:network:bankName:cardholder:number:
 // obfuscatedNumber:expirationYear:expirationMonth:`.
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.mm
index e2b5afcf..61199b2 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card.mm
@@ -7,18 +7,22 @@
 @implementation ManualFillCreditCard
 
 - (instancetype)initWithGUID:(NSString*)GUID
-                     network:(NSString*)network
-                        icon:(UIImage*)icon
-                    bankName:(NSString*)bankName
-                  cardHolder:(NSString*)cardHolder
-                      number:(NSString*)number
-            obfuscatedNumber:(NSString*)obfuscatedNumber
-    networkAndLastFourDigits:(NSString*)networkAndLastFourDigits
-              expirationYear:(NSString*)expirationYear
-             expirationMonth:(NSString*)expirationMonth
-                         CVC:(NSString*)CVC
-                  recordType:(autofill::CreditCard::RecordType)recordType
-             canFillDirectly:(BOOL)canFillDirectly {
+                             network:(NSString*)network
+                                icon:(UIImage*)icon
+                            bankName:(NSString*)bankName
+                          cardHolder:(NSString*)cardHolder
+                              number:(NSString*)number
+                    obfuscatedNumber:(NSString*)obfuscatedNumber
+            networkAndLastFourDigits:(NSString*)networkAndLastFourDigits
+                      expirationYear:(NSString*)expirationYear
+                     expirationMonth:(NSString*)expirationMonth
+                                 CVC:(NSString*)CVC
+                          recordType:
+                              (autofill::CreditCard::RecordType)recordType
+    cardInfoRetrievalEnrollmentState:
+        (autofill::CreditCard::CardInfoRetrievalEnrollmentState)
+            cardInfoRetrievalEnrollmentState
+                     canFillDirectly:(BOOL)canFillDirectly {
   self = [super init];
   if (self) {
     _GUID = [GUID copy];
@@ -33,6 +37,7 @@
     _expirationMonth = [expirationMonth copy];
     _CVC = [CVC copy];
     _recordType = recordType;
+    _cardInfoRetrievalEnrollmentState = cardInfoRetrievalEnrollmentState;
     _canFillDirectly = canFillDirectly;
   }
   return self;
diff --git a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card_unittest.mm b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card_unittest.mm
index f1f67120..7364449 100644
--- a/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card_unittest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/manual_fill/manual_fill_credit_card_unittest.mm
@@ -12,6 +12,12 @@
 
 autofill::CreditCard::RecordType LOCAL_CARD_RECORD_TYPE =
     autofill::CreditCard::RecordType::kLocalCard;
+autofill::CreditCard::CardInfoRetrievalEnrollmentState
+    CARD_INFO_RETRIEVAL_ENROLLED_STATE = autofill::CreditCard::
+        CardInfoRetrievalEnrollmentState::kRetrievalEnrolled;
+autofill::CreditCard::CardInfoRetrievalEnrollmentState
+    CARD_INFO_RETRIEVAL_UNENROLLED_STATE = autofill::CreditCard::
+        CardInfoRetrievalEnrollmentState::kRetrievalUnenrolledAndNotEligible;
 
 // Tests that a credential is correctly created.
 TEST_F(ManualFillCreditCardiOSTest, Creation) {
@@ -25,20 +31,21 @@
   NSString* expirationYear = @"19";
   NSString* expirationMonth = @"10";
   NSString* CVC = @"123";
-  ManualFillCreditCard* card =
-      [[ManualFillCreditCard alloc] initWithGUID:GUID
-                                         network:network
-                                            icon:icon
-                                        bankName:bankName
-                                      cardHolder:cardHolder
-                                          number:number
-                                obfuscatedNumber:obfuscatedNumber
-                        networkAndLastFourDigits:nil
-                                  expirationYear:expirationYear
-                                 expirationMonth:expirationMonth
-                                             CVC:CVC
-                                      recordType:LOCAL_CARD_RECORD_TYPE
-                                 canFillDirectly:true];
+  ManualFillCreditCard* card = [[ManualFillCreditCard alloc]
+                          initWithGUID:GUID
+                               network:network
+                                  icon:icon
+                              bankName:bankName
+                            cardHolder:cardHolder
+                                number:number
+                      obfuscatedNumber:obfuscatedNumber
+              networkAndLastFourDigits:nil
+                        expirationYear:expirationYear
+                       expirationMonth:expirationMonth
+                                   CVC:CVC
+                            recordType:LOCAL_CARD_RECORD_TYPE
+      cardInfoRetrievalEnrollmentState:CARD_INFO_RETRIEVAL_ENROLLED_STATE
+                       canFillDirectly:true];
   EXPECT_TRUE(card);
   EXPECT_NSEQ(GUID, card.GUID);
   EXPECT_NSEQ(network, card.network);
@@ -50,6 +57,8 @@
   EXPECT_NSEQ(expirationMonth, card.expirationMonth);
   EXPECT_NSEQ(CVC, card.CVC);
   EXPECT_EQ(card.recordType, LOCAL_CARD_RECORD_TYPE);
+  EXPECT_EQ(card.cardInfoRetrievalEnrollmentState,
+            CARD_INFO_RETRIEVAL_ENROLLED_STATE);
 }
 
 // Test equality between credit cards.
@@ -65,52 +74,55 @@
   NSString* expirationMonth = @"10";
   NSString* CVC = @"123";
 
-  ManualFillCreditCard* card =
-      [[ManualFillCreditCard alloc] initWithGUID:GUID
-                                         network:network
-                                            icon:icon
-                                        bankName:bankName
-                                      cardHolder:cardHolder
-                                          number:number
-                                obfuscatedNumber:obfuscatedNumber
-                        networkAndLastFourDigits:nil
-                                  expirationYear:expirationYear
-                                 expirationMonth:expirationMonth
-                                             CVC:CVC
-                                      recordType:LOCAL_CARD_RECORD_TYPE
-                                 canFillDirectly:true];
+  ManualFillCreditCard* card = [[ManualFillCreditCard alloc]
+                          initWithGUID:GUID
+                               network:network
+                                  icon:icon
+                              bankName:bankName
+                            cardHolder:cardHolder
+                                number:number
+                      obfuscatedNumber:obfuscatedNumber
+              networkAndLastFourDigits:nil
+                        expirationYear:expirationYear
+                       expirationMonth:expirationMonth
+                                   CVC:CVC
+                            recordType:LOCAL_CARD_RECORD_TYPE
+      cardInfoRetrievalEnrollmentState:CARD_INFO_RETRIEVAL_UNENROLLED_STATE
+                       canFillDirectly:true];
 
-  ManualFillCreditCard* equalCard =
-      [[ManualFillCreditCard alloc] initWithGUID:GUID
-                                         network:network
-                                            icon:icon
-                                        bankName:bankName
-                                      cardHolder:cardHolder
-                                          number:number
-                                obfuscatedNumber:obfuscatedNumber
-                        networkAndLastFourDigits:nil
-                                  expirationYear:expirationYear
-                                 expirationMonth:expirationMonth
-                                             CVC:nil
-                                      recordType:LOCAL_CARD_RECORD_TYPE
-                                 canFillDirectly:true];
+  ManualFillCreditCard* equalCard = [[ManualFillCreditCard alloc]
+                          initWithGUID:GUID
+                               network:network
+                                  icon:icon
+                              bankName:bankName
+                            cardHolder:cardHolder
+                                number:number
+                      obfuscatedNumber:obfuscatedNumber
+              networkAndLastFourDigits:nil
+                        expirationYear:expirationYear
+                       expirationMonth:expirationMonth
+                                   CVC:nil
+                            recordType:LOCAL_CARD_RECORD_TYPE
+      cardInfoRetrievalEnrollmentState:CARD_INFO_RETRIEVAL_UNENROLLED_STATE
+                       canFillDirectly:true];
 
   EXPECT_TRUE([card isEqual:equalCard]);
 
-  ManualFillCreditCard* differentGuidCredential =
-      [[ManualFillCreditCard alloc] initWithGUID:@"wxyz-8765-4321"
-                                         network:network
-                                            icon:icon
-                                        bankName:bankName
-                                      cardHolder:cardHolder
-                                          number:number
-                                obfuscatedNumber:obfuscatedNumber
-                        networkAndLastFourDigits:nil
-                                  expirationYear:expirationYear
-                                 expirationMonth:expirationMonth
-                                             CVC:CVC
-                                      recordType:LOCAL_CARD_RECORD_TYPE
-                                 canFillDirectly:true];
+  ManualFillCreditCard* differentGuidCredential = [[ManualFillCreditCard alloc]
+                          initWithGUID:@"wxyz-8765-4321"
+                               network:network
+                                  icon:icon
+                              bankName:bankName
+                            cardHolder:cardHolder
+                                number:number
+                      obfuscatedNumber:obfuscatedNumber
+              networkAndLastFourDigits:nil
+                        expirationYear:expirationYear
+                       expirationMonth:expirationMonth
+                                   CVC:CVC
+                            recordType:LOCAL_CARD_RECORD_TYPE
+      cardInfoRetrievalEnrollmentState:CARD_INFO_RETRIEVAL_UNENROLLED_STATE
+                       canFillDirectly:true];
   EXPECT_FALSE([card isEqual:differentGuidCredential]);
 
   // Guid is the main differentiator, and as long as the guids are equal,
diff --git a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.mm b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.mm
index f4eb283..320db09 100644
--- a/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.mm
+++ b/ios/chrome/browser/intelligence/enhanced_calendar/coordinator/enhanced_calendar_mediator.mm
@@ -20,19 +20,15 @@
 // The string template to use for parsing the end and start date/time.
 NSString* kDateTimeTemplate = @"dd/MM/yyyy HH:mm";
 
-// The string template to use for the calendar event description. This should be
-// equivalent to:
-// ```
-// {eventSummaryString}
-//
-// Location: {locationString}
-// URL: {URLString}
-// ```
-constexpr std::string kCalendarEventDescriptionTemplate = "{}\n\n{} {}\n{} {}";
+// String template to use for adding additional information to the calendar
+// event summary.
+constexpr std::string kCalendarEventSummaryAdditionalInfoTemplate = "\n{} {}";
 
-// The string template to use for the calendar event title. This can include an
-// optional prefix.
-constexpr std::string kCalendarEventTitleTemplate = "{}{}";
+// The string template to use for the calendar event summary.
+constexpr std::string kCalendarEventSummaryTemplate = "{}\n";
+
+// The string template to use for the calendar event title.
+constexpr std::string kCalendarEventTitleTemplate = "{} {}";
 
 }  // namespace
 
@@ -159,45 +155,24 @@
     _enhancedCalendarConfig.calendarEventConfig.endDateTime = endDateTime;
   }
 
-  // Add an optional `BOOKED` prefix before the title of the event.
-  BOOL isEventBooked = enhancedCalendarResponse.is_event_booked();
+  // The expected string template for the calendar event should be equivalent
+  // to:
+  // ```
+  // {eventSummaryString}
+  //
+  // Location: {optional locationString}
+  // URL: {URLString}
+  // Confirmation code: {optional confirmationCode}
+  // ```
 
-  std::string prefix =
-      isEventBooked ? l10n_util::GetStringUTF8(
-                          IDS_IOS_ENHANCED_CALENDAR_EVENT_TITLE_BOOKED_PREFIX) +
-                          " "
-                    : "";
-
-  _enhancedCalendarConfig.calendarEventConfig.eventTitle =
-      base::SysUTF8ToNSString(
-          std::format(kCalendarEventTitleTemplate, prefix,
-                      enhancedCalendarResponse.event_title()));
-
-  // Set the templated description.
-  _enhancedCalendarConfig.calendarEventConfig.eventDescription =
-      base::SysUTF8ToNSString(
-          std::format(kCalendarEventDescriptionTemplate,
-                      enhancedCalendarResponse.event_summary(),
-                      l10n_util::GetStringUTF8(
-                          IDS_IOS_ENHANCED_CALENDAR_EVENT_DESCRIPTION_LOCATION),
-                      enhancedCalendarResponse.event_location(),
-                      l10n_util::GetStringUTF8(
-                          IDS_IOS_ENHANCED_CALENDAR_EVENT_DESCRIPTION_URL),
-                      _enhancedCalendarConfig.URL));
-
-  // Optionally add the confirmation code if it exists.
-  NSString* confirmationCode = base::SysUTF8ToNSString(
-      enhancedCalendarResponse.event_confirmation_code());
-  if ([confirmationCode length] != 0) {
-    _enhancedCalendarConfig.calendarEventConfig
-        .eventDescription = [_enhancedCalendarConfig.calendarEventConfig
-                                 .eventDescription
-        stringByAppendingFormat:
-            @"\n%@ %@",
-            l10n_util::GetNSString(
-                IDS_IOS_ENHANCED_CALENDAR_EVENT_DESCRIPTION_CONFIRMATION_CODE),
-            confirmationCode];
-  }
+  _enhancedCalendarConfig.calendarEventConfig.eventTitle = [self
+      formattedCalendarEventTitle:enhancedCalendarResponse.event_title()
+                    isEventBooked:enhancedCalendarResponse.is_event_booked()];
+  _enhancedCalendarConfig.calendarEventConfig.eventDescription = [self
+      formattedCalendarEventSummary:enhancedCalendarResponse.event_summary()
+                      eventLocation:enhancedCalendarResponse.event_location()
+                   confirmationCode:enhancedCalendarResponse
+                                        .event_confirmation_code()];
 
   // Set `isAllDay`.
   _enhancedCalendarConfig.calendarEventConfig.isAllDay =
@@ -221,4 +196,71 @@
   return [dateFormatter dateFromString:dateTimeString];
 }
 
+// Get optional location field.
+- (NSString*)optionalLocationField:(std::string)eventLocation {
+  if (eventLocation.empty()) {
+    return @"";
+  }
+
+  return base::SysUTF8ToNSString(
+      std::format(kCalendarEventSummaryAdditionalInfoTemplate,
+                  l10n_util::GetStringUTF8(
+                      IDS_IOS_ENHANCED_CALENDAR_EVENT_DESCRIPTION_LOCATION),
+                  eventLocation));
+}
+
+// Get description URL field.
+- (NSString*)descriptionURL {
+  CHECK(_enhancedCalendarConfig.URL.empty());
+
+  return base::SysUTF8ToNSString(std::format(
+      kCalendarEventSummaryAdditionalInfoTemplate,
+      l10n_util::GetStringUTF8(IDS_IOS_ENHANCED_CALENDAR_EVENT_DESCRIPTION_URL),
+      _enhancedCalendarConfig.URL));
+}
+
+// Get optional confirmation code field.
+- (NSString*)optionalConfirmationCodeField:(std::string)confirmationCode {
+  if (confirmationCode.empty()) {
+    return @"";
+  }
+
+  return base::SysUTF8ToNSString(std::format(
+      kCalendarEventSummaryAdditionalInfoTemplate,
+      l10n_util::GetStringUTF8(
+          IDS_IOS_ENHANCED_CALENDAR_EVENT_DESCRIPTION_CONFIRMATION_CODE),
+      confirmationCode));
+}
+
+// Get the event title.
+- (NSString*)formattedCalendarEventTitle:(std::string)eventTitle
+                           isEventBooked:(BOOL)isEventBooked {
+  // Add an optional `BOOKED` prefix before the title of the event.
+  if (!isEventBooked) {
+    return base::SysUTF8ToNSString(eventTitle);
+  }
+
+  std::string prefix = l10n_util::GetStringUTF8(
+      IDS_IOS_ENHANCED_CALENDAR_EVENT_TITLE_BOOKED_PREFIX);
+  return base::SysUTF8ToNSString(
+      std::format(kCalendarEventTitleTemplate, prefix, eventTitle));
+}
+
+// Get the event summary.
+- (NSString*)formattedCalendarEventSummary:(std::string)eventSummary
+                             eventLocation:(std::string)eventLocation
+                          confirmationCode:(std::string)confirmationCode {
+  // Set the templated description.
+  NSString* summary = base::SysUTF8ToNSString(
+      std::format(kCalendarEventSummaryTemplate, eventSummary));
+
+  summary = [summary
+      stringByAppendingString:[self optionalLocationField:eventLocation]];
+  summary = [summary stringByAppendingString:[self descriptionURL]];
+  summary = [summary stringByAppendingString:
+                         [self optionalConfirmationCodeField:confirmationCode]];
+
+  return summary;
+}
+
 @end
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 9da7b56..236390b 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-447330cd4c96d8104c05399b29fdee38d7a49112
\ No newline at end of file
+1e1ce583ed6148aa9a1ee46aaa9e2d5afd770ac1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index 3d60f2b9..8c2c8cff 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-a1d6800ed768d0f3110115f37cce9a11eac35415
\ No newline at end of file
+551705248fcd8cad5036ed529a6faadbfba71dc2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index a899480..6858556 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-f2eb88084692042f7a319a3c95e9c50764dac8e6
\ No newline at end of file
+c387612811a419a2292976db51af3250b3a1228b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index d3d9b81..dd80163 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-d5237ddb9a4ece6eb915f9bff57189d5975f85eb
\ No newline at end of file
+c8f73bf756b9614699ccae912333f17884801641
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 501d7762..493ec9a 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-a2e30bc6b17737e02a14a0415435e0d071cdbfd0
\ No newline at end of file
+ea95aa31f60ed14731857d470a9554a585d415f4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index 72515c5..0ca45601 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-92bc57e4459cf48b4a4cc3b2deeba7690407aba0
\ No newline at end of file
+10e5b359dc6494b847f1ae44422cd3ade2c68d88
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index ec7ea9b4..cb62174 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-5e5e4835df1ed01a69a8f1f6150bf88d9486b59e
\ No newline at end of file
+cbce32e54584020aa3997666911a071cd5d62687
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index d110f7b..a5f56220 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-06dfe75ddcc937ad7bb30b666aee763c92276464
\ No newline at end of file
+e0c2f8251cfd8cbc065f99f2d56d20509bbe3c51
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index 9b24135..163f2c6 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-e11d79df15ba3e1444b36f30ba3081c2794e9faf
\ No newline at end of file
+90a21e6ed8fd60221845c829e37a41b47d676329
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index b1b2035..33eb24d 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-211637af2db6644b28dadd3cf09371b8fdd6e463
\ No newline at end of file
+d29da3a4c9f87b84c6fb6446e12ff59bfce82abb
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index d05a851..65f022d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-9ab1338645eddf73f87b0e10f6b7ecb1d8fec9b3
\ No newline at end of file
+724fdc75fd40d3f77231c4276bfc9f92eb3c3f61
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index 257eabc..b066127 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-02da09dcef9de9123064c271c155360b470fe260
\ No newline at end of file
+65729b9204d1b8651d0660bad6bac03be1de0158
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 6f2c76d..0061dff 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-825e1cc54ec49e0dbea83012162f80f5cbc1d0c9
\ No newline at end of file
+417cd01d5545dff80234302388ce8d877b676419
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 8494c05..2b4706d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-f6f8187387ae6ef2a67f35ca1b78ed2e0c3c69f3
\ No newline at end of file
+4d8f085ee93bbec7112c3aa797cf090eb8e87d0e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 2d78b4a..cb8e511 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-18e4ed53537f641e4e4183e4c01f9295a1126da3
\ No newline at end of file
+8affed71b3e2ac3e963bf1b170bebeca3c692ddd
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index 2fd09c0..8adf4eb 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-b783273ba392b64b26c5b7561b916e4a5d9b1325
\ No newline at end of file
+2c8eaa84e7a1e7f8637aac1e24381be60641f993
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index d03a385..139a824 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-8c157da852389bc912d712067d3ae9d615b6beca
\ No newline at end of file
+f2f09c3fa26c1d105365270a90807827d8e9911d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 186acb0..280f022 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-2ebbbd1fd23cd96a4ff0df0db5d35d85b0cface6
\ No newline at end of file
+edff6e42009a2a494862380d9f25396139a2f32f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index 1e518fd..763694f 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-98dcfaad27f42ac9427b293dd3fd86ae7cf73844
\ No newline at end of file
+3f4c6a5017a89ee3603dc5b42ba75e76ef610af8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index deb9b65..523cd23 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-042c98caaa3308dd830230f02e17530f4053f3d1
\ No newline at end of file
+f3e2f2bbcb08819358fb146974d5adc59d691c13
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 9461c9d..4f171a19 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-8d59db6e7f61e54b2e12f49a9fdaa967a6286b34
\ No newline at end of file
+13af92a7bdeb60860851add9060c7f72b8e67d1c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
index a9a473c..f57a057 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-c5e47f0c91640a933adbdab160af8d16aa0f7904
\ No newline at end of file
+c7ef642c5832c8bb02465aa14afff93945519de4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 978c93d..0582dd0 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-af9507b00925750d57ab75015b5f2f88c1f25f3c
\ No newline at end of file
+13d85d08e4d24d1bdc12e7cf2f3ca23605f25f57
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
index f31950e9..294ea78 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-5fb941f3a34795aeabf7d298f38135f726635a39
\ No newline at end of file
+59d002ee2b2b3eca030c2ae16b6fa31b267086c1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index b7ecb1cb..afdd756 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-2d0d806484eab78708d5e5fd6a5c5d2d45099404
\ No newline at end of file
+da2968e8c3212e025c467e9d02aa673f6f7b6d12
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 4bdb2be..a506a00 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-8ba4305aaf4122517528c997f12ba8dd63775bf2
\ No newline at end of file
+d91b364efc4d513a8c9749336b8c33824bfd0881
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 2c2ca66..e0b9a22 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-a53befefbdb07244d3350daf37484f865ef03f35
\ No newline at end of file
+9fadc1e9a65a46de5439a186362f1cf143f2e8df
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index f490e39..3bb70ff 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-636da13b39efd15ea561d480528eff5aee68f502
\ No newline at end of file
+8321a94e5ffcbf469ac19eca8312ae8313280aa1
\ No newline at end of file
diff --git a/ios/third_party/material_components_ios/src b/ios/third_party/material_components_ios/src
index 91a227c..a801760 160000
--- a/ios/third_party/material_components_ios/src
+++ b/ios/third_party/material_components_ios/src
@@ -1 +1 @@
-Subproject commit 91a227c283a463f49d0b55474f9686b67aa2d9da
+Subproject commit a801760a5838d0ad87a1ca107efc3d447f8b867f
diff --git a/ios_internal b/ios_internal
index 09a4552..5077d2b6 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 09a455234a0af038336d80a2f116dc4602e49a5c
+Subproject commit 5077d2b6c74926b472eddb9c2cc581f485b75654
diff --git a/media/filters/hls_manifest_demuxer_engine_unittest.cc b/media/filters/hls_manifest_demuxer_engine_unittest.cc
index d4fccf1..cee73532 100644
--- a/media/filters/hls_manifest_demuxer_engine_unittest.cc
+++ b/media/filters/hls_manifest_demuxer_engine_unittest.cc
@@ -138,6 +138,13 @@
     "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.05\",AUDIO=\"aac\"\n"
     "main/english-audio.m3u8\n";
 
+const std::string kMultivariantPlaylistWithEmbeddedAlts =
+    "#EXTM3U\n"
+    "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Eng\",DEFAULT=YES,"
+    "AUTOSELECT=YES,LANGUAGE=\"en\"\n"
+    "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"avc1.420000\",AUDIO=\"aac\"\n"
+    "hi/video-only.m3u8\n";
+
 const std::string kLiveFullEncryptedMediaPlaylist =
     "#EXTM3U\n"
     "#EXT-X-VERSION:4\n"
@@ -604,6 +611,30 @@
   task_environment_.RunUntilIdle();
 }
 
+TEST_F(HlsManifestDemuxerEngineTest, TestMultivariantPlaylistWithNoUrlAlts) {
+  EXPECT_CALL(*mock_mdeh_, SetSequenceMode("audio-override", true)).Times(0);
+  EXPECT_CALL(*mock_mdeh_, SetSequenceMode("primary", true));
+  EXPECT_CALL(*mock_mdeh_, SetDuration(21.021));
+  EXPECT_CALL(*mock_mdeh_,
+              AddRole("audio-override", RelaxedParserSupportedType::kMP2T))
+      .Times(0);
+  EXPECT_CALL(*mock_mdeh_,
+              AddRole("primary", RelaxedParserSupportedType::kMP2T));
+
+  // URL queries in order:
+  //  - manifest.m3u8: root manifest
+  //  - video-only.m3u8: primary rendition
+  //  - first.ts: check container/codecs for the primary rendition
+  BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
+      "http://media.example.com/manifest.m3u8",
+      kMultivariantPlaylistWithEmbeddedAlts);
+  BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
+      "http://media.example.com/hi/video-only.m3u8", kSimpleMediaPlaylist);
+  EXPECT_CALL(*this, MockInitComplete(HasStatusCode(PIPELINE_OK)));
+  InitializeEngine();
+  task_environment_.RunUntilIdle();
+}
+
 TEST_F(HlsManifestDemuxerEngineTest, TestMultivariantWithNoSupportedCodecs) {
   EXPECT_CALL(*mock_mdeh_, AddRole(_, _)).Times(0);
   EXPECT_CALL(*mock_mdeh_, SetSequenceMode(_, _)).Times(0);
diff --git a/media/formats/hls/rendition_manager.cc b/media/formats/hls/rendition_manager.cc
index 14b1c75..e202e920 100644
--- a/media/formats/hls/rendition_manager.cc
+++ b/media/formats/hls/rendition_manager.cc
@@ -181,6 +181,14 @@
     if (!extra_rendition.has_value()) {
       extra_rendition = audio_renditions->MostSimilar(selected_extra_);
     }
+    if (extra_rendition.has_value() &&
+        !std::get<1>(extra_rendition.value())->GetUri().has_value()) {
+      // An audio rendition with no uri just plays the content from the
+      // selected variant. See section 4.4.6.2.1 of the HLS spec for details.
+      // The URI attribute is OPTIONAL unless the TYPE is CLOSED-CAPTIONS, in
+      // which case the URI attribute must not be present.
+      extra_rendition = std::nullopt;
+    }
   }
 
   if (!IsSameRendition(extra_rendition, selected_extra_)) {
diff --git a/media/formats/hls/rendition_manager_unittest.cc b/media/formats/hls/rendition_manager_unittest.cc
index 8742ec8..adbd4e3 100644
--- a/media/formats/hls/rendition_manager_unittest.cc
+++ b/media/formats/hls/rendition_manager_unittest.cc
@@ -632,6 +632,34 @@
   rm.SetPreferredExtraRendition(std::nullopt);
 }
 
+TEST_F(HlsRenditionManagerTest, CantSelectRenditionWithNoURI) {
+  {
+    auto rm = GetRenditionManager(
+        "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"G\",NAME=\"A\",URI=\"A.m3u8\"",
+        "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"G\",NAME=\"B\",URI=\"B.m3u8\"",
+        "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"G\",NAME=\"C\",AUTOSELECT=YES",
+        "#EXT-X-STREAM-INF:BANDWIDTH=100,CODECS=\"audio.codec,video.codec\","
+        "AUDIO=\"G\"",
+        "100.m3u8",
+        "#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS=\"audio.codec,video.codec\","
+        "AUDIO=\"G\"",
+        "200.m3u8");
+
+    // The C rendition is autoselectable, but has no URL
+    EXPECT_CALL(*this, VariantSelected("/200.m3u8", "NONE"));
+    rm.Reselect(GetVariantCb());
+
+    // The user has selected B explicitly, so we use B as the primary rendition.
+    const auto renditions = rm.GetSelectableExtraRenditions();
+    EXPECT_CALL(*this, VariantSelected("/200.m3u8", "/B.m3u8"));
+    rm.SetPreferredExtraRendition(renditions[1].track_id());
+
+    // The user has selected C explicitly, but too bad, it has no URI.
+    EXPECT_CALL(*this, VariantSelected("/200.m3u8", "NONE"));
+    rm.SetPreferredExtraRendition(renditions[2].track_id());
+  }
+}
+
 TEST_F(HlsRenditionManagerTest, AudioOnlyRenditionSelectionOverrides) {
   {
     auto rm = GetRenditionManager(
diff --git a/media/renderers/video_renderer_impl.cc b/media/renderers/video_renderer_impl.cc
index d99ad146..e57c7c8b 100644
--- a/media/renderers/video_renderer_impl.cc
+++ b/media/renderers/video_renderer_impl.cc
@@ -384,6 +384,12 @@
           buffering_state, reason});
 
   client_->OnBufferingStateChange(buffering_state, reason);
+
+  if (buffering_state == BUFFERING_HAVE_ENOUGH &&
+      buffering_state_ == BUFFERING_HAVE_ENOUGH && time_progressing_ &&
+      !sink_started_) {
+    OnTimeProgressing();
+  }
 }
 
 void VideoRendererImpl::OnWaiting(WaitingReason reason) {
diff --git a/media/renderers/video_renderer_impl_unittest.cc b/media/renderers/video_renderer_impl_unittest.cc
index 107e157..b1f0f75 100644
--- a/media/renderers/video_renderer_impl_unittest.cc
+++ b/media/renderers/video_renderer_impl_unittest.cc
@@ -1015,6 +1015,63 @@
   Destroy();
 }
 
+// Tests the case where underflow evicts all frames then good frames come in.
+TEST_F(VideoRendererImplTest, UnderflowResume) {
+  Initialize();
+  QueueFrames("0 30 60 90 120");
+
+  EXPECT_CALL(mock_cb_, OnVideoFrameRateChange(_));
+
+  {
+    SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH");
+    WaitableMessageLoopEvent event;
+    EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _))
+        .WillOnce(RunOnceClosure(event.GetClosure()));
+    EXPECT_CALL(mock_cb_, FrameReceived(_)).Times(AnyNumber());
+    EXPECT_CALL(mock_cb_, OnVideoNaturalSizeChange(_)).Times(1);
+    EXPECT_CALL(mock_cb_, OnVideoOpacityChange(_)).Times(1);
+    EXPECT_CALL(mock_cb_, OnStatisticsUpdate(_)).Times(AnyNumber());
+    StartPlayingFrom(0);
+    event.RunAndWait();
+  }
+
+  {
+    SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING");
+    WaitableMessageLoopEvent event;
+    EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING, _))
+        .WillOnce(RunOnceClosure(event.GetClosure()));
+    renderer_->OnTimeProgressing();
+    time_source_.StartTicking();
+    // Jump time far enough forward that no frames are valid.
+    AdvanceTimeInMs(1000);
+    event.RunAndWait();
+  }
+
+  WaitForPendingDecode();
+
+  renderer_->OnTimeStopped();
+  time_source_.StopTicking();
+  EXPECT_FALSE(null_video_sink_->is_started());
+
+  renderer_->OnTimeProgressing();
+  time_source_.StartTicking();
+  EXPECT_FALSE(null_video_sink_->is_started());
+
+  QueueFrames("1000 1030 1060 1090 1120 1150");
+  SatisfyPendingDecode();
+
+  // Providing the end of stream packet should remove all frames and exit.
+  {
+    SCOPED_TRACE("Waiting for BUFFERING_HAVE_ENOUGH");
+    WaitableMessageLoopEvent event;
+    EXPECT_CALL(mock_cb_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH, _))
+        .WillOnce(RunOnceClosure(event.GetClosure()));
+    event.RunAndWait();
+  }
+  EXPECT_TRUE(null_video_sink_->is_started());
+  Destroy();
+}
+
 // Tests the case where underflow evicts all frames in the HAVE_ENOUGH state.
 TEST_F(VideoRendererImplTest, UnderflowEvictionWhileHaveEnough) {
   Initialize();
diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h
index 586dbca..f5f804b2 100644
--- a/mojo/public/cpp/bindings/lib/array_internal.h
+++ b/mojo/public/cpp/bindings/lib/array_internal.h
@@ -13,15 +13,10 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <limits>
-#include <new>
-
 #include "base/check.h"
 #include "base/component_export.h"
 #include "base/memory/raw_ptr.h"
-#include "mojo/public/c/system/macros.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
-#include "mojo/public/cpp/bindings/lib/buffer.h"
 #include "mojo/public/cpp/bindings/lib/message_fragment.h"
 #include "mojo/public/cpp/bindings/lib/template_util.h"
 #include "mojo/public/cpp/bindings/lib/validate_params.h"
diff --git a/mojo/public/cpp/bindings/lib/array_serialization.h b/mojo/public/cpp/bindings/lib/array_serialization.h
index 55cd14d..30e2b5a 100644
--- a/mojo/public/cpp/bindings/lib/array_serialization.h
+++ b/mojo/public/cpp/bindings/lib/array_serialization.h
@@ -13,12 +13,10 @@
 #include <stddef.h>
 #include <string.h>  // For |memcpy()|.
 
-#include <limits>
 #include <type_traits>
 #include <utility>
-#include <vector>
 
-#include "base/logging.h"
+#include "base/check.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "mojo/public/cpp/bindings/array_data_view.h"
 #include "mojo/public/cpp/bindings/lib/array_internal.h"
diff --git a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
index c76a345..05a33a8 100644
--- a/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h
@@ -7,25 +7,22 @@
 
 #include <stdint.h>
 
-#include <algorithm>  // For |std::swap()|.
 #include <memory>
 #include <string>
 #include <utility>
 
+#include "base/check.h"
 #include "base/component_export.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
 #include "base/location.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "mojo/public/cpp/bindings/associated_group.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
 #include "mojo/public/cpp/bindings/connection_error_callback.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
-#include "mojo/public/cpp/bindings/interface_id.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
-#include "mojo/public/cpp/system/message_pipe.h"
 
 namespace mojo {
 namespace internal {
diff --git a/mojo/public/cpp/bindings/lib/associated_receiver.cc b/mojo/public/cpp/bindings/lib/associated_receiver.cc
index 4a0fbb2..7134c119 100644
--- a/mojo/public/cpp/bindings/lib/associated_receiver.cc
+++ b/mojo/public/cpp/bindings/lib/associated_receiver.cc
@@ -4,9 +4,15 @@
 
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 
+#include <stdint.h>
+
 #include <memory>
 #include <string_view>
+#include <utility>
 
+#include "base/check.h"
+#include "base/containers/span.h"
+#include "base/functional/bind.h"
 #include "base/task/sequenced_task_runner.h"
 #include "mojo/public/cpp/bindings/lib/multiplex_router.h"
 #include "mojo/public/cpp/bindings/lib/task_runner_helper.h"
diff --git a/mojo/public/cpp/bindings/lib/binding_state.h b/mojo/public/cpp/bindings/lib/binding_state.h
index 3de514b2..60544ea4c 100644
--- a/mojo/public/cpp/bindings/lib/binding_state.h
+++ b/mojo/public/cpp/bindings/lib/binding_state.h
@@ -6,7 +6,6 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_BINDING_STATE_H_
 
 #include <stdint.h>
-
 #include <memory>
 #include <string_view>
 #include <utility>
@@ -16,18 +15,14 @@
 #include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "mojo/public/cpp/bindings/async_flusher.h"
 #include "mojo/public/cpp/bindings/connection_error_callback.h"
-#include "mojo/public/cpp/bindings/connection_group.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
-#include "mojo/public/cpp/bindings/interface_id.h"
 #include "mojo/public/cpp/bindings/lib/multiplex_router.h"
 #include "mojo/public/cpp/bindings/lib/pending_receiver_state.h"
 #include "mojo/public/cpp/bindings/lib/sync_method_traits.h"
-#include "mojo/public/cpp/bindings/message_header_validator.h"
 #include "mojo/public/cpp/bindings/pending_flush.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
diff --git a/mojo/public/cpp/bindings/lib/bindings_internal.h b/mojo/public/cpp/bindings/lib/bindings_internal.h
index d6d1115..0d05e1e 100644
--- a/mojo/public/cpp/bindings/lib/bindings_internal.h
+++ b/mojo/public/cpp/bindings/lib/bindings_internal.h
@@ -17,10 +17,8 @@
 #include <type_traits>
 #include <utility>
 
-#include "base/logging.h"
+#include "base/check.h"
 #include "mojo/public/cpp/bindings/enum_traits.h"
-#include "mojo/public/cpp/bindings/interface_id.h"
-#include "mojo/public/cpp/bindings/lib/template_util.h"
 #include "mojo/public/cpp/platform/platform_handle.h"
 #include "mojo/public/cpp/system/handle.h"
 
diff --git a/mojo/public/cpp/bindings/lib/buffer.cc b/mojo/public/cpp/bindings/lib/buffer.cc
index 64056b6f..cbd82fe 100644
--- a/mojo/public/cpp/bindings/lib/buffer.cc
+++ b/mojo/public/cpp/bindings/lib/buffer.cc
@@ -10,12 +10,10 @@
 #include "mojo/public/cpp/bindings/lib/buffer.h"
 
 #include <cstring>
-#include <tuple>
 
+#include "base/check.h"
 #include "base/check_op.h"
 #include "base/notreached.h"
-#include "base/numerics/safe_math.h"
-#include "mojo/public/c/system/message_pipe.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
 
 namespace mojo {
diff --git a/mojo/public/cpp/bindings/lib/buffer.h b/mojo/public/cpp/bindings/lib/buffer.h
index bc5451b..142fe5a 100644
--- a/mojo/public/cpp/bindings/lib/buffer.h
+++ b/mojo/public/cpp/bindings/lib/buffer.h
@@ -15,6 +15,7 @@
 
 #include <vector>
 
+#include "base/check_op.h"
 #include "base/component_export.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "mojo/public/cpp/system/handle.h"
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index 6127288..d2606ac 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -8,17 +8,15 @@
 
 #include <memory>
 
+#include "base/check.h"
 #include "base/check_op.h"
-#include "base/compiler_specific.h"
 #include "base/debug/alias.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
-#include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
diff --git a/mojo/public/cpp/bindings/lib/generated_code_util.cc b/mojo/public/cpp/bindings/lib/generated_code_util.cc
index c71edaa..320baca 100644
--- a/mojo/public/cpp/bindings/lib/generated_code_util.cc
+++ b/mojo/public/cpp/bindings/lib/generated_code_util.cc
@@ -4,8 +4,6 @@
 
 #include "mojo/public/cpp/bindings/lib/generated_code_util.h"
 
-#include <cstring>
-
 #include "mojo/public/cpp/bindings/lib/control_message_handler.h"
 #include "mojo/public/cpp/bindings/lib/validation_context.h"
 #include "mojo/public/cpp/bindings/lib/validation_util.h"
diff --git a/mojo/public/cpp/bindings/lib/generated_code_util.h b/mojo/public/cpp/bindings/lib/generated_code_util.h
index 7cb7cd6..2cf3b07 100644
--- a/mojo/public/cpp/bindings/lib/generated_code_util.h
+++ b/mojo/public/cpp/bindings/lib/generated_code_util.h
@@ -5,7 +5,6 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_GENERATED_CODE_UTIL_H_
 
-#include <utility>
 #include "base/component_export.h"
 #include "base/containers/span.h"
 
diff --git a/mojo/public/cpp/bindings/lib/hash_util.h b/mojo/public/cpp/bindings/lib/hash_util.h
index fbd63734..b39c0e0 100644
--- a/mojo/public/cpp/bindings/lib/hash_util.h
+++ b/mojo/public/cpp/bindings/lib/hash_util.h
@@ -5,7 +5,6 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_HASH_UTIL_H_
 
-#include <cstring>
 #include <functional>
 #include <optional>
 #include <type_traits>
diff --git a/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
index 860de777..0cff2435 100644
--- a/mojo/public/cpp/bindings/lib/interface_ptr_state.h
+++ b/mojo/public/cpp/bindings/lib/interface_ptr_state.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include <algorithm>  // For |std::swap()|.
 #include <memory>
 #include <string>
 #include <utility>
diff --git a/mojo/public/cpp/bindings/lib/message.cc b/mojo/public/cpp/bindings/lib/message.cc
index 6ccb715..33936322 100644
--- a/mojo/public/cpp/bindings/lib/message.cc
+++ b/mojo/public/cpp/bindings/lib/message.cc
@@ -11,12 +11,8 @@
 
 #include <stddef.h>
 #include <stdint.h>
-#include <stdlib.h>
 
-#include <algorithm>
-#include <atomic>
 #include <string_view>
-#include <tuple>
 #include <utility>
 
 #include "base/check_op.h"
diff --git a/mojo/public/cpp/bindings/lib/message_fragment.h b/mojo/public/cpp/bindings/lib/message_fragment.h
index 2e206a1d..f60f4008 100644
--- a/mojo/public/cpp/bindings/lib/message_fragment.h
+++ b/mojo/public/cpp/bindings/lib/message_fragment.h
@@ -6,14 +6,11 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_FRAGMENT_H_
 
 #include <stddef.h>
-
-#include <limits>
 #include <type_traits>
 
 #include "base/bits.h"
 #include "base/check_op.h"
 #include "base/component_export.h"
-#include "base/logging.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
 #include "mojo/public/cpp/bindings/message.h"
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index c7d6a0e7..09ca485 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -19,7 +19,6 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/location.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_util.h"
diff --git a/mojo/public/cpp/bindings/lib/native_struct_serialization.h b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
index 00ba53d..9664966 100644
--- a/mojo/public/cpp/bindings/lib/native_struct_serialization.h
+++ b/mojo/public/cpp/bindings/lib/native_struct_serialization.h
@@ -8,8 +8,6 @@
 #include <stddef.h>
 #include <stdint.h>
 
-#include <limits>
-
 #include "base/check_op.h"
 #include "base/component_export.h"
 #include "base/pickle.h"
diff --git a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
index b0e9dca0..6e930185 100644
--- a/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
+++ b/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc
@@ -4,9 +4,6 @@
 
 #include "mojo/public/cpp/bindings/pipe_control_message_proxy.h"
 
-#include <stddef.h>
-
-#include <tuple>
 #include <utility>
 
 #include "mojo/public/cpp/bindings/lib/message_fragment.h"
diff --git a/mojo/public/cpp/bindings/lib/proxy_to_responder.cc b/mojo/public/cpp/bindings/lib/proxy_to_responder.cc
index ae70722..828a6657 100644
--- a/mojo/public/cpp/bindings/lib/proxy_to_responder.cc
+++ b/mojo/public/cpp/bindings/lib/proxy_to_responder.cc
@@ -4,8 +4,6 @@
 
 #include "mojo/public/cpp/bindings/lib/proxy_to_responder.h"
 
-#include <cstring>
-
 #include "mojo/public/cpp/bindings/message.h"
 
 namespace mojo {
diff --git a/mojo/public/cpp/bindings/lib/send_message_helper.cc b/mojo/public/cpp/bindings/lib/send_message_helper.cc
index c045d45f..fd73841 100644
--- a/mojo/public/cpp/bindings/lib/send_message_helper.cc
+++ b/mojo/public/cpp/bindings/lib/send_message_helper.cc
@@ -4,7 +4,6 @@
 
 #include "mojo/public/cpp/bindings/lib/send_message_helper.h"
 
-#include <cstring>
 #include <tuple>
 
 #include "base/trace_event/typed_macros.h"
diff --git a/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc b/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc
index c3e31c0d0..b8d59ce 100644
--- a/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc
+++ b/mojo/public/cpp/bindings/lib/sequence_local_sync_event_watcher.cc
@@ -5,12 +5,9 @@
 #include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h"
 
 #include <map>
-#include <memory>
-#include <set>
 
 #include "base/containers/flat_set.h"
 #include "base/functional/bind.h"
-#include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
diff --git a/mojo/public/cpp/bindings/lib/serialization.h b/mojo/public/cpp/bindings/lib/serialization.h
index bf43112..9a077b72 100644
--- a/mojo/public/cpp/bindings/lib/serialization.h
+++ b/mojo/public/cpp/bindings/lib/serialization.h
@@ -10,11 +10,8 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_H_
 
-#include <string.h>
-
 #include <type_traits>
 
-#include "base/numerics/safe_math.h"
 #include "mojo/public/cpp/bindings/array_traits_span.h"
 #include "mojo/public/cpp/bindings/array_traits_stl.h"
 #include "mojo/public/cpp/bindings/lib/array_serialization.h"
diff --git a/mojo/public/cpp/bindings/lib/serialization_forward.h b/mojo/public/cpp/bindings/lib/serialization_forward.h
index 9af6f9c..97653f0 100644
--- a/mojo/public/cpp/bindings/lib/serialization_forward.h
+++ b/mojo/public/cpp/bindings/lib/serialization_forward.h
@@ -15,7 +15,6 @@
 #include "mojo/public/cpp/bindings/lib/message_fragment.h"
 #include "mojo/public/cpp/bindings/lib/template_util.h"
 #include "mojo/public/cpp/bindings/map_traits.h"
-#include "mojo/public/cpp/bindings/optional_as_pointer.h"
 #include "mojo/public/cpp/bindings/string_traits.h"
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "mojo/public/cpp/bindings/union_traits.h"
@@ -31,13 +30,6 @@
 template <typename MojomType, typename MaybeConstUserType>
 struct Serializer;
 
-template <typename T>
-using IsAbslOptional = IsSpecializationOf<std::optional, std::decay_t<T>>;
-
-template <typename T>
-using IsOptionalAsPointer =
-    IsSpecializationOf<mojo::OptionalAsPointer, std::decay_t<T>>;
-
 template <typename MojomType, typename InputUserType, typename... Args>
 void Serialize(InputUserType&& input, Args&&... args) {
   if constexpr (IsAbslOptional<InputUserType>::value) {
diff --git a/mojo/public/cpp/bindings/lib/serialization_util.h b/mojo/public/cpp/bindings/lib/serialization_util.h
index 7ab236c..6dca9957 100644
--- a/mojo/public/cpp/bindings/lib/serialization_util.h
+++ b/mojo/public/cpp/bindings/lib/serialization_util.h
@@ -5,10 +5,7 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_UTIL_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_SERIALIZATION_UTIL_H_
 
-#include <stddef.h>
-
 #include <type_traits>
-#include <utility>
 
 #include "mojo/public/cpp/bindings/lib/bindings_internal.h"
 #include "mojo/public/cpp/bindings/lib/serialization_forward.h"
diff --git a/mojo/public/cpp/bindings/lib/string_serialization.h b/mojo/public/cpp/bindings/lib/string_serialization.h
index 41a70b8..8f3bf4d 100644
--- a/mojo/public/cpp/bindings/lib/string_serialization.h
+++ b/mojo/public/cpp/bindings/lib/string_serialization.h
@@ -10,7 +10,6 @@
 #ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_STRING_SERIALIZATION_H_
 
-#include <stddef.h>
 #include <string.h>
 
 #include <string_view>
diff --git a/mojo/public/cpp/bindings/lib/sync_event_watcher.cc b/mojo/public/cpp/bindings/lib/sync_event_watcher.cc
index f6a638d..faf6ac5 100644
--- a/mojo/public/cpp/bindings/lib/sync_event_watcher.cc
+++ b/mojo/public/cpp/bindings/lib/sync_event_watcher.cc
@@ -9,7 +9,6 @@
 
 #include "mojo/public/cpp/bindings/sync_event_watcher.h"
 
-#include <algorithm>
 #include <utility>
 
 #include "base/check_op.h"
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
index ac150b5..e5e5c00 100644
--- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc
@@ -9,8 +9,6 @@
 
 #include "mojo/public/cpp/bindings/sync_handle_registry.h"
 
-#include <algorithm>
-#include <map>
 #include <utility>
 
 #include "base/auto_reset.h"
diff --git a/mojo/public/cpp/bindings/lib/template_util.h b/mojo/public/cpp/bindings/lib/template_util.h
index cfc35c07..738d4f5 100644
--- a/mojo/public/cpp/bindings/lib/template_util.h
+++ b/mojo/public/cpp/bindings/lib/template_util.h
@@ -7,6 +7,8 @@
 
 #include <type_traits>
 
+#include "mojo/public/cpp/bindings/optional_as_pointer.h"
+
 namespace mojo::internal {
 
 // A helper template to determine if given type is non-const move-only-type,
@@ -29,6 +31,13 @@
   static const bool value = false;
 };
 
+template <typename T>
+using IsAbslOptional = IsSpecializationOf<std::optional, std::decay_t<T>>;
+
+template <typename T>
+using IsOptionalAsPointer =
+    IsSpecializationOf<mojo::OptionalAsPointer, std::decay_t<T>>;
+
 }  // namespace mojo::internal
 
 #endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_TEMPLATE_UTIL_H_
diff --git a/mojo/public/cpp/bindings/lib/thread_safe_forwarder_base.h b/mojo/public/cpp/bindings/lib/thread_safe_forwarder_base.h
index 1e918c4..ae6b9d5f 100644
--- a/mojo/public/cpp/bindings/lib/thread_safe_forwarder_base.h
+++ b/mojo/public/cpp/bindings/lib/thread_safe_forwarder_base.h
@@ -6,16 +6,10 @@
 #define MOJO_PUBLIC_CPP_BINDINGS_LIB_THREAD_SAFE_FORWARDER_BASE_H_
 
 #include <memory>
-#include <vector>
 
 #include "base/component_export.h"
-#include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/synchronization/lock.h"
-#include "base/synchronization/waitable_event.h"
 #include "base/task/sequenced_task_runner.h"
-#include "mojo/public/cpp/bindings/associated_group.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/thread_safe_proxy.h"
 
diff --git a/mojo/public/cpp/bindings/lib/tracing_helpers.cc b/mojo/public/cpp/bindings/lib/tracing_helpers.cc
deleted file mode 100644
index 66b5a608..0000000
--- a/mojo/public/cpp/bindings/lib/tracing_helpers.cc
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/tracing_helpers.h"
-
-namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/unserialized_message_context.cc b/mojo/public/cpp/bindings/lib/unserialized_message_context.cc
index 3194413..b4a93e33 100644
--- a/mojo/public/cpp/bindings/lib/unserialized_message_context.cc
+++ b/mojo/public/cpp/bindings/lib/unserialized_message_context.cc
@@ -4,9 +4,6 @@
 
 #include "mojo/public/cpp/bindings/lib/unserialized_message_context.h"
 
-#include "base/memory/raw_ptr.h"
-#include "mojo/public/cpp/bindings/lib/message_internal.h"
-
 namespace mojo {
 namespace internal {
 
diff --git a/mojo/public/cpp/bindings/lib/validation_util.cc b/mojo/public/cpp/bindings/lib/validation_util.cc
index 001a8e7..253851e6 100644
--- a/mojo/public/cpp/bindings/lib/validation_util.cc
+++ b/mojo/public/cpp/bindings/lib/validation_util.cc
@@ -6,8 +6,6 @@
 
 #include <stdint.h>
 
-#include <limits>
-
 #include "base/containers/adapters.h"
 #include "base/strings/stringprintf.h"
 #include "mojo/public/cpp/bindings/lib/message_internal.h"
diff --git a/net/base/features.cc b/net/base/features.cc
index c5decbc..bd039523 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -363,10 +363,35 @@
     /*name=*/"ProbabilisticRevealTokenServerPath",
     /*default_value=*/"/v1/issueprts"};
 
+const base::FeatureParam<bool> kBypassProbabilisticRevealTokenRegistry{
+    &kEnableProbabilisticRevealTokens,
+    /*name=*/"BypassProbabilisticRevealTokenRegistry",
+    /*default_value=*/false};
+
+const base::FeatureParam<bool> kUseCustomProbabilisticRevealTokenRegistry{
+    &kEnableProbabilisticRevealTokens,
+    /*name=*/"UseCustomProbabilisticRevealTokenRegistry",
+    /*default_value=*/false};
+
+const base::FeatureParam<std::string> kCustomProbabilisticRevealTokenRegistry{
+    &kEnableProbabilisticRevealTokens,
+    /*name=*/"CustomProbabilisticRevealTokenRegistry",
+    /*default_value=*/""};
+
+const base::FeatureParam<bool> kProbabilisticRevealTokensOnlyInIncognito{
+    &kEnableProbabilisticRevealTokens,
+    /*name=*/"ProbabilisticRevealTokensOnlyInIncognito",
+    /*default_value=*/false};
+
+const base::FeatureParam<bool> kProbabilisticRevealTokenFetchOnly{
+    &kEnableProbabilisticRevealTokens,
+    /*name=*/"ProbabilisticRevealTokenFetchOnly",
+    /*default_value=*/false};
+
 const base::FeatureParam<bool>
-    kAttachProbabilisticRevealTokensOnAllProxiedRequests{
+    kEnableProbabilisticRevealTokensForNonProxiedRequests{
         &kEnableProbabilisticRevealTokens,
-        /*name=*/"AttachProbabilisticRevealTokensOnAllProxiedRequests",
+        /*name=*/"EnableProbabilisticRevealTokensForNonProxiedRequests",
         /*default_value=*/false};
 
 const base::FeatureParam<bool>
diff --git a/net/base/features.h b/net/base/features.h
index ef08866..5f4c680 100644
--- a/net/base/features.h
+++ b/net/base/features.h
@@ -378,10 +378,48 @@
 NET_EXPORT extern const base::FeatureParam<std::string>
     kProbabilisticRevealTokenServerPath;
 
-// If true, probabilistic reveal tokens will be attached to all proxied requests
-// regardless of whether the request domain is registered.
+// If true, the probabilistic reveal token registration check will be skipped
+// and we will consider every domain as being eligible to receive PRTs. In order
+// for PRTs to be attached to requests, the
+// `ProbabilisticRevealTokensAddHeaderToProxiedRequests` flag must also be true.
 NET_EXPORT extern const base::FeatureParam<bool>
-    kAttachProbabilisticRevealTokensOnAllProxiedRequests;
+    kBypassProbabilisticRevealTokenRegistry;
+
+// If true, the standard probabilistic reveal token registry will be ignored and
+// the custom registry will be used instead. The custom registry can be set with
+// the `CustomProbabilisticRevealTokenRegistry` flag. This will only be used if
+// `BypassProbabilisticRevealTokenRegistry` is false. This is intended to be
+// used for developer testing only.
+NET_EXPORT extern const base::FeatureParam<bool>
+    kUseCustomProbabilisticRevealTokenRegistry;
+
+// A comma-separated list of domains (eTLD+1) which will be considered eligible
+// to receive PRTs. This will override the default PRT registry and will only be
+// used if `UseCustomProbabilisticRevealTokenRegistry` is true and
+// `BypassProbabilisticRevealTokenRegistry` is false. This is intended to be
+// used for developer testing only.
+NET_EXPORT extern const base::FeatureParam<std::string>
+    kCustomProbabilisticRevealTokenRegistry;
+
+// If true, probabilistic reveal tokens will only be enabled in Incognito mode.
+NET_EXPORT extern const base::FeatureParam<bool>
+    kProbabilisticRevealTokensOnlyInIncognito;
+
+// If true, probabilistic reveal tokens will only be fetched. PRTs will not be
+// randomized at request time or attached to any requests. This is intended to
+// be used for measuring issuer server load before the feature is fully enabled.
+NET_EXPORT extern const base::FeatureParam<bool>
+    kProbabilisticRevealTokenFetchOnly;
+
+// If true, probabilistic reveal tokens can be attached to non-proxied requests
+// as well. PRTs will still only be attached to requests if the
+// `ProbabilisticRevealTokensAddHeaderToProxiedRequests` flag is true and the
+// request is being sent to a registered domain, but this flag can be used in
+//  combination with `BypassProbabilisticRevealTokenRegistry` or
+// `CustomProbabilisticRevealTokenRegistry`. This is intended to be used for
+// developer testing only.
+NET_EXPORT extern const base::FeatureParam<bool>
+    kEnableProbabilisticRevealTokensForNonProxiedRequests;
 
 // If true, probabilistic reveal tokens header will be added to proxied
 // requests.
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 77f9b08..490636fd 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -1293,15 +1293,19 @@
     auth_controllers_[HttpAuth::AUTH_SERVER]->AddAuthorizationHeader(
         &request_headers_);
 
+  bool is_proxied_request =
+      proxy_info_.is_for_ip_protection() && !proxy_info_.is_direct();
   if (features::kIpPrivacyAddHeaderToProxiedRequests.Get() &&
-      proxy_info_.is_for_ip_protection()) {
-    if (!proxy_info_.is_direct()) {
-      request_headers_.SetHeader("IP-Protection", "1");
-    }
+      is_proxied_request) {
+    request_headers_.SetHeader("IP-Protection", "1");
   }
 
-  if (features::kProbabilisticRevealTokensAddHeaderToProxiedRequests.Get() &&
-      proxy_info_.is_for_ip_protection() && !proxy_info_.is_direct()) {
+  if (bool is_prt_eligible =
+          features::kEnableProbabilisticRevealTokensForNonProxiedRequests
+              .Get() ||
+          is_proxied_request;
+      features::kProbabilisticRevealTokensAddHeaderToProxiedRequests.Get() &&
+      is_prt_eligible) {
     if (std::optional<std::string> maybe_prt_header_value =
             proxy_info_.PRTHeaderValue();
         maybe_prt_header_value.has_value()) {
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 03408fe6..541fba9 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -28207,6 +28207,152 @@
   EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
 }
 
+// Test that the 'Sec-Probabilistic-Reveal-Token' header is not sent for
+// requests that are not sent through an IP Protection proxy.
+TEST_P(
+    HttpNetworkTransactionTest,
+    HttpsNestedProxyProbabilisticRevealTokenRequestHeaderNotAddedForNonProxiedRequests) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeaturesAndParameters(
+      /*enabled_features=*/
+      {{features::kEnableIpProtectionProxy,
+        {{features::kIpPrivacyDirectOnly.name, "true"}}},
+       {features::kEnableProbabilisticRevealTokens,
+        {{features::kProbabilisticRevealTokensAddHeaderToProxiedRequests.name,
+          "true"},
+         {features::kEnableProbabilisticRevealTokensForNonProxiedRequests.name,
+          "false"}}}},
+      /*disabled_features=*/{});
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  const auto kIpProtectionDirectChain =
+      ProxyChain::ForIpProtection(std::vector<ProxyServer>());
+
+  session_deps_.proxy_resolution_service =
+      ConfiguredProxyResolutionService::CreateFixedForTest(
+          "https://not-used:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+  session_deps_.proxy_delegate = std::make_unique<IpProtectionProxyDelegate>();
+  auto* proxy_delegate = static_cast<IpProtectionProxyDelegate*>(
+      session_deps_.proxy_delegate.get());
+  proxy_delegate->set_proxy_chain(kIpProtectionDirectChain);
+  session_deps_.proxy_resolution_service->SetProxyDelegate(proxy_delegate);
+  session_deps_.net_log = NetLog::Get();
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  MockWrite data_writes[] = {
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n\r\n"),
+  };
+
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200\r\n\r\n"),
+      MockRead(SYNCHRONOUS, OK),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  TestCompletionCallback callback;
+
+  HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans.Start(&request, callback.callback(),
+                       NetLogWithSource::Make(NetLogSourceType::NONE));
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans.GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
+}
+
+// Test that the 'Sec-Probabilistic-Reveal-Token' header is sent for
+// requests that are not sent through an IP Protection proxy if the
+// corresponding feature is enabled.
+TEST_P(
+    HttpNetworkTransactionTest,
+    HttpsNestedProxyProbabilisticRevealTokenRequestHeaderAddedForNonProxiedRequestsWhenFeatureEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeaturesAndParameters(
+      /*enabled_features=*/
+      {{features::kEnableIpProtectionProxy,
+        {{features::kIpPrivacyDirectOnly.name, "true"}}},
+       {features::kEnableProbabilisticRevealTokens,
+        {{features::kProbabilisticRevealTokensAddHeaderToProxiedRequests.name,
+          "true"},
+         {features::kEnableProbabilisticRevealTokensForNonProxiedRequests.name,
+          "true"}}}},
+      /*disabled_features=*/{});
+
+  HttpRequestInfo request;
+  request.method = "GET";
+  request.url = GURL("https://www.example.org/");
+  request.traffic_annotation =
+      MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+  const auto kIpProtectionDirectChain =
+      ProxyChain::ForIpProtection(std::vector<ProxyServer>());
+
+  session_deps_.proxy_resolution_service =
+      ConfiguredProxyResolutionService::CreateFixedForTest(
+          "https://not-used:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+  session_deps_.proxy_delegate = std::make_unique<IpProtectionProxyDelegate>();
+  auto* proxy_delegate = static_cast<IpProtectionProxyDelegate*>(
+      session_deps_.proxy_delegate.get());
+  proxy_delegate->set_proxy_chain(kIpProtectionDirectChain);
+  session_deps_.proxy_resolution_service->SetProxyDelegate(proxy_delegate);
+  session_deps_.net_log = NetLog::Get();
+  std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+  MockWrite data_writes[] = {
+      MockWrite("GET / HTTP/1.1\r\n"
+                "Host: www.example.org\r\n"
+                "Connection: keep-alive\r\n"
+                // IpProtectionProxyDelegate defined above hard
+                // codes following serialized PRT value
+                "Sec-Probabilistic-Reveal-Token: :serializedPRT:\r\n\r\n"),
+  };
+
+  MockRead data_reads[] = {
+      MockRead("HTTP/1.1 200\r\n\r\n"),
+      MockRead(SYNCHRONOUS, OK),
+  };
+
+  StaticSocketDataProvider data(data_reads, data_writes);
+  session_deps_.socket_factory->AddSocketDataProvider(&data);
+
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
+
+  TestCompletionCallback callback;
+
+  HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
+
+  int rv = trans.Start(&request, callback.callback(),
+                       NetLogWithSource::Make(NetLogSourceType::NONE));
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+  rv = callback.WaitForResult();
+  EXPECT_THAT(rv, IsOk());
+
+  const HttpResponseInfo* response = trans.GetResponseInfo();
+  ASSERT_TRUE(response);
+  ASSERT_TRUE(response->headers);
+  EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
+}
+
 // Tests specific to the HappyEyeballsV3 feature.
 // TODO(crbug.com/346835898): Find ways to run more tests with the
 // HappyEyeballsV3 feature enabled.
diff --git a/printing/backend/mojom/print_backend_mojom_traits.cc b/printing/backend/mojom/print_backend_mojom_traits.cc
index 02cd2d3..c44e560 100644
--- a/printing/backend/mojom/print_backend_mojom_traits.cc
+++ b/printing/backend/mojom/print_backend_mojom_traits.cc
@@ -4,7 +4,7 @@
 
 #include "printing/backend/mojom/print_backend_mojom_traits.h"
 
-#include <map>
+#include <set>
 
 #include "base/containers/contains.h"
 #include "base/debug/crash_logging.h"
@@ -78,20 +78,18 @@
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 namespace {
+
 template <class Key>
-class DuplicateChecker {
- public:
-  bool HasDuplicates(const std::vector<Key>& items) {
-    std::map<Key, bool> items_encountered;
-    for (auto it = items.begin(); it != items.end(); ++it) {
-      auto found = items_encountered.find(*it);
-      if (found != items_encountered.end())
-        return true;
-      items_encountered[*it] = true;
+bool HasDuplicateItems(const std::vector<Key>& items) {
+  std::set<Key> items_encountered;
+  for (const Key& item : items) {
+    bool inserted = items_encountered.insert(item).second;
+    if (!inserted) {
+      return true;
     }
-    return false;
   }
-};
+  return false;
+}
 
 }  // namespace
 
@@ -349,15 +347,12 @@
   }
 
   // There should not be duplicates in certain arrays.
-  DuplicateChecker<printing::mojom::DuplexMode> duplex_modes_dup_checker;
-  if (duplex_modes_dup_checker.HasDuplicates(out->duplex_modes)) {
+  if (HasDuplicateItems(out->duplex_modes)) {
     DLOG(ERROR) << "Duplicate duplex_modes detected.";
     return false;
   }
 
-  DuplicateChecker<printing::PrinterSemanticCapsAndDefaults::Paper>
-      user_defined_papers_dup_checker;
-  if (user_defined_papers_dup_checker.HasDuplicates(out->user_defined_papers)) {
+  if (HasDuplicateItems(out->user_defined_papers)) {
     DLOG(ERROR) << "Duplicate user_defined_papers detected.";
     // TODO(crbug.com/372062459): Remove debug code when done.
     std::string names;
@@ -373,16 +368,11 @@
   }
 
 #if BUILDFLAG(IS_CHROMEOS)
-  DuplicateChecker<printing::AdvancedCapability>
-      advanced_capabilities_dup_checker;
-  if (advanced_capabilities_dup_checker.HasDuplicates(
-          out->advanced_capabilities)) {
+  if (HasDuplicateItems(out->advanced_capabilities)) {
     DLOG(ERROR) << "Duplicate advanced_capabilities detected.";
     return false;
   }
-  DuplicateChecker<printing::mojom::PrintScalingType>
-      print_scaling_types_dup_checker;
-  if (print_scaling_types_dup_checker.HasDuplicates(out->print_scaling_types)) {
+  if (HasDuplicateItems(out->print_scaling_types)) {
     DLOG(ERROR) << "Duplicate print_scaling_types detected.";
     return false;
   }
@@ -392,8 +382,6 @@
   if (!data.ReadPageOutputQuality(&out->page_output_quality)) {
     return false;
   }
-  DuplicateChecker<printing::PageOutputQualityAttribute>
-      page_output_quality_dup_checker;
   if (out->page_output_quality) {
     printing::PageOutputQualityAttributes qualities =
         out->page_output_quality->qualities;
@@ -412,7 +400,7 @@
     }
 
     // There should be no duplicates in `qualities` array.
-    if (page_output_quality_dup_checker.HasDuplicates(qualities)) {
+    if (HasDuplicateItems(qualities)) {
       DLOG(ERROR) << "Duplicate page output qualities detected.";
       return false;
     }
diff --git a/remoting/base/oauth_token_info.cc b/remoting/base/oauth_token_info.cc
index 05250628..a5f8fe1 100644
--- a/remoting/base/oauth_token_info.cc
+++ b/remoting/base/oauth_token_info.cc
@@ -6,11 +6,11 @@
 
 namespace remoting {
 
-OAuthTokenInfo::OAuthTokenInfo(const std::string& token)
-    : access_token_(token), user_email_("") {}
+OAuthTokenInfo::OAuthTokenInfo(const std::string& access_token)
+    : access_token_(access_token), user_email_("") {}
 
-OAuthTokenInfo::OAuthTokenInfo(const std::string& token,
-                               const std::string& email)
-    : access_token_(token), user_email_(email) {}
+OAuthTokenInfo::OAuthTokenInfo(const std::string& access_token,
+                               const std::string& user_email)
+    : access_token_(access_token), user_email_(user_email) {}
 
 }  // namespace remoting
diff --git a/remoting/base/oauth_token_info.h b/remoting/base/oauth_token_info.h
index b94c2f7..4d125cb 100644
--- a/remoting/base/oauth_token_info.h
+++ b/remoting/base/oauth_token_info.h
@@ -12,11 +12,12 @@
 // OAuthTokenInfo contains relevant info for a given OAuth token.
 struct OAuthTokenInfo {
   OAuthTokenInfo() = default;
-  explicit OAuthTokenInfo(const std::string& token);
-  OAuthTokenInfo(const std::string& token, const std::string& email);
+  explicit OAuthTokenInfo(const std::string& access_token);
+  OAuthTokenInfo(const std::string& access_token,
+                 const std::string& user_email);
 
-  std::string access_token() const { return access_token_; }
-  std::string user_email() const { return user_email_; }
+  const std::string& access_token() const { return access_token_; }
+  const std::string& user_email() const { return user_email_; }
 
   void set_access_token(const std::string& access_token) {
     access_token_ = access_token;
diff --git a/remoting/client/cli/remoting_client_main.cc b/remoting/client/cli/remoting_client_main.cc
index 06d36ae..5aa6105 100644
--- a/remoting/client/cli/remoting_client_main.cc
+++ b/remoting/client/cli/remoting_client_main.cc
@@ -33,17 +33,24 @@
 
   constexpr std::string_view kSupportIdSwitch = "support_id";
   constexpr std::string_view kAccessTokenSwitch = "access_token";
+  constexpr std::string_view kUserEmailSwitch = "user_email";
 
   if (!command_line->HasSwitch(kSupportIdSwitch)) {
     LOG(ERROR) << kSupportIdSwitch << " arg is missing";
   } else if (!command_line->HasSwitch(kAccessTokenSwitch)) {
     LOG(ERROR) << kAccessTokenSwitch << " arg is missing";
+  } else if (!command_line->HasSwitch(kUserEmailSwitch)) {
+    LOG(ERROR) << kUserEmailSwitch << " arg is missing";
   }
 
   auto support_id = command_line->GetSwitchValueASCII(kSupportIdSwitch);
   auto access_token = command_line->GetSwitchValueASCII(kAccessTokenSwitch);
+  auto user_email = command_line->GetSwitchValueASCII(kUserEmailSwitch);
 
-  if (support_id.empty() || access_token.empty()) {
+  if (support_id.empty() || access_token.empty() || user_email.empty()) {
+    return -1;
+  } else if (support_id.length() != 12) {
+    LOG(ERROR) << kSupportIdSwitch << " value must be 12 digits long.";
     return -1;
   }
 
@@ -67,7 +74,7 @@
                          run_loop.QuitClosure()),
       url_loader_factory_owner.GetURLLoaderFactory());
 
-  remoting_client.StartSession(support_id, access_token);
+  remoting_client.StartSession(support_id, {access_token, user_email});
 
   run_loop.Run();
 
diff --git a/remoting/client/common/BUILD.gn b/remoting/client/common/BUILD.gn
index cd84f1f..52aa7b2 100644
--- a/remoting/client/common/BUILD.gn
+++ b/remoting/client/common/BUILD.gn
@@ -17,6 +17,9 @@
       "//net",
       "//remoting/base",
       "//remoting/proto/remoting/v1:remote_support_host_messages",
+      "//remoting/protocol",
+      "//remoting/signaling",
+      "//third_party/protobuf:protobuf_lite",
     ]
   }
 }
diff --git a/remoting/client/common/DEPS b/remoting/client/common/DEPS
index ea8149d..c12b68d 100644
--- a/remoting/client/common/DEPS
+++ b/remoting/client/common/DEPS
@@ -1,3 +1,6 @@
 include_rules = [
+  "+components/webrtc",
+  "+remoting/protocol",
+  "+remoting/signaling",
   "+services/network/public/cpp",
 ]
diff --git a/remoting/client/common/remoting_client.cc b/remoting/client/common/remoting_client.cc
index adfe8555..f4dc1e3 100644
--- a/remoting/client/common/remoting_client.cc
+++ b/remoting/client/common/remoting_client.cc
@@ -13,13 +13,23 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/scoped_refptr.h"
-#include "base/task/thread_pool.h"
+#include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
+#include "components/webrtc/thread_wrapper.h"
 #include "remoting/base/directory_service_client.h"
 #include "remoting/base/oauth_token_info.h"
 #include "remoting/base/passthrough_oauth_token_getter.h"
 #include "remoting/client/common/logging.h"
 #include "remoting/proto/remoting/v1/remote_support_host_messages.pb.h"
+#include "remoting/protocol/chromium_port_allocator_factory.h"
+#include "remoting/protocol/chromium_socket_factory.h"
+#include "remoting/protocol/ice_config_fetcher_default.h"
+#include "remoting/protocol/jingle_session_manager.h"
+#include "remoting/protocol/session_config.h"
+#include "remoting/protocol/transport.h"
+#include "remoting/protocol/transport_context.h"
+#include "remoting/signaling/ftl_client_uuid_device_id_provider.h"
+#include "remoting/signaling/ftl_signal_strategy.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 namespace remoting {
@@ -30,19 +40,34 @@
     : quit_closure_(std::move(quit_closure)),
       url_loader_factory_(url_loader_factory) {}
 
-RemotingClient::~RemotingClient() = default;
+RemotingClient::~RemotingClient() {
+  if (signal_strategy_) {
+    signal_strategy_->RemoveListener(this);
+  }
+}
 
 void RemotingClient::StartSession(std::string_view support_id,
-                                  std::string_view access_token) {
-  oauth_token_getter_ = std::make_unique<PassthroughOAuthTokenGetter>(
-      OAuthTokenInfo{std::string(access_token)});
+                                  OAuthTokenInfo oauth_token_info) {
+  CHECK(!session_manager_);
+  CHECK_EQ(support_id.length(), 12);
+  CHECK_GT(oauth_token_info.access_token().length(), 0);
+  CHECK_GT(oauth_token_info.user_email().length(), 0);
+
+  chrome_os_host_id_ = support_id;
+
+  // TODO: joedow - If we need to support sessions > 1 hour, we will need to
+  // provide a method for refreshing the access token.
+  oauth_token_info_ = std::move(oauth_token_info);
+  oauth_token_getter_ =
+      std::make_unique<PassthroughOAuthTokenGetter>(oauth_token_info_);
   directory_service_client_ = std::make_unique<DirectoryServiceClient>(
       oauth_token_getter_.get(), url_loader_factory_);
 
   // base::Unretained is sound because this instance owns the service client
   // and callbacks will not be run after destruction.
+  CLIENT_LOG << "Retrieving host information for id: " << chrome_os_host_id_;
   directory_service_client_->GetManagedChromeOsHost(
-      std::string(support_id),
+      chrome_os_host_id_.substr(0, 7),
       base::BindOnce(&RemotingClient::OnGetManagedChromeOsHostRetrieved,
                      base::Unretained(this)));
 }
@@ -50,10 +75,139 @@
 void RemotingClient::OnGetManagedChromeOsHostRetrieved(
     const HttpStatus& status,
     std::unique_ptr<apis::v1::GetManagedChromeOsHostResponse> response) {
-  CLIENT_LOG << "GetManagedChromeOsHost result: " << status.ok()
-             << ", error: " << status.error_message();
+  if (!status.ok()) {
+    LOG(ERROR) << "Failed to retrieve host information. code: "
+               << static_cast<int>(status.error_code())
+               << ", message: " << status.error_message();
+    std::move(quit_closure_).Run();
+    return;
+  }
 
-  std::move(quit_closure_).Run();
+  CLIENT_LOG << "Initializing signaling...";
+  signal_strategy_ = std::make_unique<FtlSignalStrategy>(
+      std::make_unique<PassthroughOAuthTokenGetter>(oauth_token_info_),
+      url_loader_factory_, std::make_unique<FtlClientUuidDeviceIdProvider>());
+  signal_strategy_->AddListener(this);
+  signal_strategy_->Connect();
+
+  CLIENT_LOG << "Creating transport context...";
+  webrtc::ThreadWrapper::EnsureForCurrentMessageLoop();
+  scoped_refptr<protocol::TransportContext> transport_context =
+      new protocol::TransportContext(
+          std::make_unique<protocol::ChromiumPortAllocatorFactory>(),
+          webrtc::ThreadWrapper::current()->SocketServer(),
+          std::make_unique<protocol::IceConfigFetcherDefault>(
+              url_loader_factory_, oauth_token_getter_.get()),
+          protocol::TransportRole::CLIENT);
+
+  CLIENT_LOG << "Creating session manager...";
+  auto protocol_config = protocol::CandidateSessionConfig::CreateDefault();
+  protocol_config->DisableAudioChannel();
+  protocol_config->set_webrtc_supported(true);
+  session_manager_ =
+      std::make_unique<protocol::JingleSessionManager>(signal_strategy_.get());
+  session_manager_->set_protocol_config(std::move(protocol_config));
+
+  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&SignalStrategy::Disconnect,
+                     base::Unretained(signal_strategy_.get())),
+      base::Seconds(5));
+}
+
+void RemotingClient::SetCapabilities(
+    const protocol::Capabilities& capabilities) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::SetPairingResponse(
+    const protocol::PairingResponse& pairing_response) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::DeliverHostMessage(
+    const protocol::ExtensionMessage& message) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::SetVideoLayout(const protocol::VideoLayout& layout) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::SetTransportInfo(
+    const protocol::TransportInfo& transport_info) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::SetActiveDisplay(
+    const protocol::ActiveDisplay& active_display) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::InjectClipboardEvent(
+    const protocol::ClipboardEvent& event) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::SetCursorShape(
+    const protocol::CursorShapeInfo& cursor_shape) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::SetKeyboardLayout(const protocol::KeyboardLayout& layout) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::OnConnectionState(protocol::ConnectionToHost::State state,
+                                       protocol::ErrorCode error) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::OnConnectionReady(bool ready) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::OnRouteChanged(const std::string& channel_name,
+                                    const protocol::TransportRoute& route) {
+  NOTIMPLEMENTED();
+}
+
+void RemotingClient::OnSignalStrategyStateChange(SignalStrategy::State state) {
+  switch (state) {
+    case SignalStrategy::CONNECTING:
+      CLIENT_LOG << "Signaling channel is being established.";
+      break;
+    case SignalStrategy::CONNECTED:
+      CLIENT_LOG << "Signaling channel has been established for: "
+                 << signal_strategy_->GetLocalAddress().id();
+      // TODO: joedow - Start the connection process.
+      break;
+    case SignalStrategy::DISCONNECTED:
+      auto error = signal_strategy_->GetError();
+      if (error != SignalStrategy::Error::OK) {
+        LOG(ERROR) << "Signaling channel has been closed due to error: "
+                   << error;
+      } else {
+        CLIENT_LOG << "Signaling channel has been closed.";
+      }
+
+      RunQuitClosure();
+      break;
+  }
+}
+
+bool RemotingClient::OnSignalStrategyIncomingStanza(
+    const jingle_xmpp::XmlElement* stanza) {
+  CLIENT_LOG << "OnSignalStrategyIncomingStanza";
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void RemotingClient::RunQuitClosure() {
+  if (quit_closure_) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, std::move(quit_closure_));
+  }
 }
 
 }  // namespace remoting
diff --git a/remoting/client/common/remoting_client.h b/remoting/client/common/remoting_client.h
index 75ac8ea..d6679fd9 100644
--- a/remoting/client/common/remoting_client.h
+++ b/remoting/client/common/remoting_client.h
@@ -6,11 +6,16 @@
 #define REMOTING_CLIENT_COMMON_REMOTING_CLIENT_H_
 
 #include <memory>
+#include <string>
 #include <string_view>
 
 #include "base/functional/callback.h"
 #include "base/memory/scoped_refptr.h"
 #include "remoting/base/http_status.h"
+#include "remoting/base/oauth_token_info.h"
+#include "remoting/protocol/client_stub.h"
+#include "remoting/protocol/connection_to_host.h"
+#include "remoting/signaling/signal_strategy.h"
 
 namespace network {
 class SharedURLLoaderFactory;
@@ -23,10 +28,17 @@
 }  // namespace apis::v1
 
 class DirectoryServiceClient;
-class PassthroughOAuthTokenGetter;
+class OAuthTokenGetter;
+
+namespace protocol {
+class SessionManager;
+class TransportContext;
+}  // namespace protocol
 
 // A simple, native chromoting client implementation.
-class RemotingClient {
+class RemotingClient : public SignalStrategy::Listener,
+                       public protocol::ConnectionToHost::HostEventCallback,
+                       public protocol::ClientStub {
  public:
   RemotingClient(
       base::OnceClosure quit_closure,
@@ -35,24 +47,60 @@
   RemotingClient(const RemotingClient&) = delete;
   RemotingClient& operator=(const RemotingClient&) = delete;
 
-  ~RemotingClient();
+  ~RemotingClient() override;
 
-  void StartSession(std::string_view support_id, std::string_view access_token);
+  void StartSession(std::string_view support_id,
+                    OAuthTokenInfo oauth_token_info);
 
  private:
+  // ClientStub implementation.
+  void SetCapabilities(const protocol::Capabilities& capabilities) override;
+  void SetPairingResponse(
+      const protocol::PairingResponse& pairing_response) override;
+  void DeliverHostMessage(const protocol::ExtensionMessage& message) override;
+  void SetVideoLayout(const protocol::VideoLayout& layout) override;
+  void SetTransportInfo(const protocol::TransportInfo& transport_info) override;
+  void SetActiveDisplay(const protocol::ActiveDisplay& active_display) override;
+  void InjectClipboardEvent(const protocol::ClipboardEvent& event) override;
+  void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override;
+  void SetKeyboardLayout(const protocol::KeyboardLayout& layout) override;
+
+  // ConnectionToHost::HostEventCallback implementation.
+  void OnConnectionState(protocol::ConnectionToHost::State state,
+                         protocol::ErrorCode error) override;
+  void OnConnectionReady(bool ready) override;
+  void OnRouteChanged(const std::string& channel_name,
+                      const protocol::TransportRoute& route) override;
+
+  // SignalStrategy::StatusObserver interface.
+  void OnSignalStrategyStateChange(SignalStrategy::State state) override;
+  bool OnSignalStrategyIncomingStanza(
+      const jingle_xmpp::XmlElement* stanza) override;
+
   void OnGetManagedChromeOsHostRetrieved(
       const HttpStatus& status,
       std::unique_ptr<apis::v1::GetManagedChromeOsHostResponse> response);
 
+  void RunQuitClosure();
+
+  std::string chrome_os_host_id_;
+  OAuthTokenInfo oauth_token_info_;
   base::OnceClosure quit_closure_;
 
   // Used to provide an OAuth access token for service requests. Since a raw *
   // is passed around, this field should be destroyed after the service clients.
-  std::unique_ptr<PassthroughOAuthTokenGetter> oauth_token_getter_;
+  std::unique_ptr<OAuthTokenGetter> oauth_token_getter_;
 
   // Used to retrieve details about the remote host to connect to.
   std::unique_ptr<DirectoryServiceClient> directory_service_client_;
 
+  // TODO: joedow - |Move FtlSignalingConnector| from //remoting/host into
+  // //remoting/signaling so it can be used in the client.
+  std::unique_ptr<SignalStrategy> signal_strategy_;
+
+  std::unique_ptr<protocol::SessionManager> session_manager_;
+  scoped_refptr<protocol::TransportContext> transport_context_;
+
   // Used to make service requests.
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 };
diff --git a/remoting/host/native_messaging/native_messaging_writer_unittest.cc b/remoting/host/native_messaging/native_messaging_writer_unittest.cc
index 83c5d182..59c7bde 100644
--- a/remoting/host/native_messaging/native_messaging_writer_unittest.cc
+++ b/remoting/host/native_messaging/native_messaging_writer_unittest.cc
@@ -53,7 +53,6 @@
   ASSERT_TRUE(header_read.has_value());
   ASSERT_EQ(sizeof(body_length), *header_read);
 
-  // Use std::string as buffer.
   std::string body_buffer(body_length, '\0');
   auto body_read =
       read_file_.ReadAtCurrentPos(base::as_writable_byte_span(body_buffer));
diff --git a/remoting/protocol/channel_socket_adapter_unittest.cc b/remoting/protocol/channel_socket_adapter_unittest.cc
index c5e8aa6..4fbd5b8c 100644
--- a/remoting/protocol/channel_socket_adapter_unittest.cc
+++ b/remoting/protocol/channel_socket_adapter_unittest.cc
@@ -90,11 +90,10 @@
 }
 
 // Verify that Send sends the packet and returns correct result.
-// TODO(bugs.webrtc.org/367395350): re-enable after webrtc roll.
-TEST_F(TransportChannelSocketAdapterTest, DISABLED_Send) {
+TEST_F(TransportChannelSocketAdapterTest, Send) {
   auto buffer = base::MakeRefCounted<IOBufferWithSize>(kTestDataSize);
 
-  // EXPECT_CALL(channel_, writable()).WillOnce(Return(true));
+  EXPECT_CALL(channel_, writable()).WillOnce(Return(true));
   EXPECT_CALL(channel_, SendPacket(buffer->data(), kTestDataSize, _, 0))
       .WillOnce(Return(kTestDataSize));
 
@@ -104,11 +103,10 @@
 
 // Verify that the message is still sent if Send() is called while
 // socket is not open yet. The result is the packet is lost.
-// TODO(bugs.webrtc.org/367395350): re-enable after webrtc roll.
-TEST_F(TransportChannelSocketAdapterTest, DISABLED_SendPending) {
+TEST_F(TransportChannelSocketAdapterTest, SendPending) {
   auto buffer = base::MakeRefCounted<IOBufferWithSize>(kTestDataSize);
 
-  // EXPECT_CALL(channel_, writable()).WillOnce(Return(true));
+  EXPECT_CALL(channel_, writable()).WillOnce(Return(true));
   EXPECT_CALL(channel_, SendPacket(buffer->data(), kTestDataSize, _, 0))
       .Times(1)
       .WillOnce(Return(SOCKET_ERROR));
diff --git a/remoting/signaling/ftl_signal_strategy.cc b/remoting/signaling/ftl_signal_strategy.cc
index 2606fa5..d8329d7 100644
--- a/remoting/signaling/ftl_signal_strategy.cc
+++ b/remoting/signaling/ftl_signal_strategy.cc
@@ -259,6 +259,11 @@
   }
 
   user_email_ = token_info.user_email();
+  if (user_email_.empty()) {
+    LOG(WARNING) << "No user email in the OAuth token response";
+    Disconnect();
+    return;
+  }
   StartReceivingMessages();
 }
 
diff --git a/services/passage_embeddings/passage_embedder.cc b/services/passage_embeddings/passage_embedder.cc
index ec40597..0adc947 100644
--- a/services/passage_embeddings/passage_embedder.cc
+++ b/services/passage_embeddings/passage_embedder.cc
@@ -66,6 +66,8 @@
       embeddings_cache_(embedder_params->embedder_cache_size),
       user_initiated_priority_num_threads_(
           embedder_params->user_initiated_priority_num_threads),
+      urgent_priority_num_threads_(
+          embedder_params->urgent_priority_num_threads),
       passive_priority_num_threads_(
           embedder_params->passive_priority_num_threads),
       allow_gpu_execution_(embedder_params->allow_gpu_execution) {
@@ -164,6 +166,9 @@
     case mojom::PassagePriority::kUserInitiated:
       num_threads = user_initiated_priority_num_threads_;
       break;
+    case mojom::PassagePriority::kUrgent:
+      num_threads = urgent_priority_num_threads_;
+      break;
     case mojom::PassagePriority::kPassive:
       num_threads = passive_priority_num_threads_;
       break;
diff --git a/services/passage_embeddings/passage_embedder.h b/services/passage_embeddings/passage_embedder.h
index 2f82ca7..fcb4fd45 100644
--- a/services/passage_embeddings/passage_embedder.h
+++ b/services/passage_embeddings/passage_embedder.h
@@ -86,6 +86,9 @@
   // The number of threads to use for PassagePriority::kUserInitiated.
   uint32_t user_initiated_priority_num_threads_;
 
+  // The number of threads to use for PassagePriority::kUrgent.
+  uint32_t urgent_priority_num_threads_;
+
   // The number of threads to use for PassagePriority::kPassive.
   uint32_t passive_priority_num_threads_;
 
diff --git a/services/passage_embeddings/public/mojom/passage_embeddings.mojom b/services/passage_embeddings/public/mojom/passage_embeddings.mojom
index 4a16afa..30c355f 100644
--- a/services/passage_embeddings/public/mojom/passage_embeddings.mojom
+++ b/services/passage_embeddings/public/mojom/passage_embeddings.mojom
@@ -21,6 +21,7 @@
 // execution of passive requests will be more efficient but slower.
 enum PassagePriority {
   kUnknown,
+  kUrgent,
   kUserInitiated,
   kPassive,
 };
@@ -53,6 +54,8 @@
 struct PassageEmbedderParams {
   // The number of threads to use for PassagePriority::kUserInitiated tasks.
   uint32 user_initiated_priority_num_threads;
+  // The number of threads to use for PassagePriority::kUrgent tasks.
+  uint32 urgent_priority_num_threads;
   // The number of threads to use for PassagePriority::kPassive tasks.
   uint32 passive_priority_num_threads;
   // The size of the cache to use to limit execution on the same passage.
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
index 2df9562..9090517f 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc
@@ -108,7 +108,7 @@
 
 // Wrapper for |ConnectProducerSocketViaMojo| to be used as a function pointer.
 void ConnectProducerSocketAsync(perfetto::CreateSocketCallback cb) {
-  PerfettoTracedProcess::Get().DeferOrConnectProducerSocket(std::move(cb));
+  ConnectProducerSocketViaMojo(std::move(cb), base::Milliseconds(100));
 }
 #endif
 
@@ -171,22 +171,19 @@
   GetDataSourceTaskRunner() = task_runner;
 }
 
+// static
 void PerfettoTracedProcess::RestartThreadInSandbox() {
-  CHECK(trace_process_thread_->StartWithOptions(
-      base::Thread::Options(base::MessagePumpType::IO, 0)));
-  DETACH_FROM_SEQUENCE(sequence_checker_);
-  task_runner_ = trace_process_thread_->task_runner();
-  platform_->ResetTaskRunner(trace_process_thread_->task_runner());
-  DataSourceBase::ResetTaskRunner(trace_process_thread_->task_runner());
-  tracing_backend_->DetachFromMuxerSequence();
-  CustomEventRecorder::GetInstance()->DetachFromSequence();
-  will_trace_thread_restart_ = false;
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-  if (system_tracing_producer_socket_cb_) {
-    task_runner_->PostTask(FROM_HERE,
-                           std::move(system_tracing_producer_socket_cb_));
+  base::Thread* trace_thread = PerfettoTracedProcess::GetTraceThread();
+  if (trace_thread->StartWithOptions(
+          base::Thread::Options(base::MessagePumpType::IO, 0))) {
+    DETACH_FROM_SEQUENCE(PerfettoTracedProcess::Get().sequence_checker_);
+    PerfettoTracedProcess::Get().task_runner_ = trace_thread->task_runner();
+    PerfettoTracedProcess::Get().platform_->ResetTaskRunner(
+        trace_thread->task_runner());
+    DataSourceBase::ResetTaskRunner(trace_thread->task_runner());
+    PerfettoTracedProcess::Get().tracing_backend_->DetachFromMuxerSequence();
+    CustomEventRecorder::GetInstance()->DetachFromSequence();
   }
-#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
 }
 
 // static
@@ -203,10 +200,8 @@
 }
 
 // static
-PerfettoTracedProcess& PerfettoTracedProcess::MaybeCreateInstanceWithThread(
-    bool will_trace_thread_restart) {
-  static base::NoDestructor<PerfettoTracedProcess> traced_process(
-      will_trace_thread_restart);
+PerfettoTracedProcess& PerfettoTracedProcess::MaybeCreateInstanceWithThread() {
+  static base::NoDestructor<PerfettoTracedProcess> traced_process{};
   return *traced_process;
 }
 
@@ -222,18 +217,15 @@
   return *g_instance;
 }
 
-PerfettoTracedProcess::PerfettoTracedProcess(bool will_trace_thread_restart)
+PerfettoTracedProcess::PerfettoTracedProcess()
     : trace_process_thread_(std::make_unique<base::Thread>("PerfettoTrace")),
       task_runner_(trace_process_thread_->StartWithOptions(
                        base::Thread::Options(base::MessagePumpType::IO, 0))
                        ? trace_process_thread_->task_runner()
                        : nullptr),
-      will_trace_thread_restart_(will_trace_thread_restart),
+      platform_(
+          std::make_unique<base::tracing::PerfettoPlatform>(task_runner_)),
       tracing_backend_(std::make_unique<PerfettoTracingBackend>()) {
-  base::tracing::PerfettoPlatform::Options options{
-      .defer_delayed_tasks = will_trace_thread_restart_};
-  platform_ =
-      std::make_unique<base::tracing::PerfettoPlatform>(task_runner_, options);
   DETACH_FROM_SEQUENCE(sequence_checker_);
   CHECK_EQ(g_instance, nullptr);
   CHECK(task_runner_);
@@ -374,20 +366,6 @@
   CustomEventRecorder::GetInstance();
 }
 
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-void PerfettoTracedProcess::DeferOrConnectProducerSocket(
-    perfetto::CreateSocketCallback cb) {
-  CHECK(!system_tracing_producer_socket_cb_);
-  // Hold off the attempts to get socket fd until trace thread restarts.
-  if (will_trace_thread_restart_) {
-    system_tracing_producer_socket_cb_ = base::BindOnce(
-        ConnectProducerSocketViaMojo, cb, base::Milliseconds(100));
-  } else {
-    ConnectProducerSocketViaMojo(cb, base::Milliseconds(100));
-  }
-}
-#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-
 void PerfettoTracedProcess::OnThreadPoolAvailable(bool enable_consumer) {
   thread_pool_started_ = true;
   SetupClientLibrary(enable_consumer);
diff --git a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
index a60a3ac..6901696 100644
--- a/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
+++ b/services/tracing/public/cpp/perfetto/perfetto_traced_process.h
@@ -97,9 +97,6 @@
     // By default, data source callbacks (e.g., Start/StopTracingImpl) are
     // called on PerfettoTracedProcess::GetTaskRunner()'s sequence. This method
     // allows overriding that task runner.
-    // Note: The task_runner's thread may stop and restart for Linux/ChromeOS
-    // sandboxing so the task_runner can change and delayed tasks posted to it
-    // may be silently dropped.
     virtual base::SequencedTaskRunner* GetTaskRunner();
 
     static void ResetTaskRunner(
@@ -155,8 +152,8 @@
     perfetto::DataSourceConfig data_source_config_;
   };
 
-  // Restarts the trace thread and replaces the task_runner for tracing.
-  void RestartThreadInSandbox();
+  // Restart the trace thread and replace the task_runner for tracing.
+  static void RestartThreadInSandbox();
 
   // Returns the process-wide ptr to the trace thread, returns nullptr if the
   // task_runner for tracing is from the thread-pool.
@@ -164,8 +161,7 @@
 
   // Creates the process-wide instance of the PerfettoTracedProcess.
   static PerfettoTracedProcess& MaybeCreateInstance();
-  static PerfettoTracedProcess& MaybeCreateInstanceWithThread(
-      bool will_trace_thread_restart);
+  static PerfettoTracedProcess& MaybeCreateInstanceWithThread();
   static PerfettoTracedProcess& MaybeCreateInstanceForTesting();
 
   // Returns the process-wide instance of the PerfettoTracedProcess.
@@ -232,15 +228,11 @@
       const perfetto::TraceConfig& config,
       const perfetto::Tracing::SetupStartupTracingOpts& opts);
 
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-  void DeferOrConnectProducerSocket(perfetto::CreateSocketCallback cb);
-#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-
  private:
   friend class base::NoDestructor<PerfettoTracedProcess>;
 
   // Default constructor would create a dedicated thread for tracing
-  explicit PerfettoTracedProcess(bool will_trace_thread_restart);
+  PerfettoTracedProcess();
   explicit PerfettoTracedProcess(
       scoped_refptr<base::SequencedTaskRunner> task_runner);
 
@@ -269,11 +261,6 @@
   std::unique_ptr<base::Thread> trace_process_thread_;
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
-  bool will_trace_thread_restart_ = false;
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-  base::OnceClosure system_tracing_producer_socket_cb_;
-#endif  // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
-
   // Platform implementation for the Perfetto client library.
   std::unique_ptr<base::tracing::PerfettoPlatform> platform_;
   std::unique_ptr<PerfettoTracingBackend> tracing_backend_;
diff --git a/services/tracing/public/cpp/trace_startup.cc b/services/tracing/public/cpp/trace_startup.cc
index fc94a93e..71edec3 100644
--- a/services/tracing/public/cpp/trace_startup.cc
+++ b/services/tracing/public/cpp/trace_startup.cc
@@ -46,15 +46,17 @@
 }  // namespace
 
 bool g_tracing_initialized_after_featurelist = false;
-bool g_tracing_with_thread = false;
 
 bool IsTracingInitialized() {
   return g_tracing_initialized_after_featurelist;
 }
 
-void EnableStartupTracingIfNeeded(bool with_thread) {
+void EnableStartupTracingIfNeeded() {
   RegisterTracedValueProtoWriter();
 
+  // Create the PerfettoTracedProcess.
+  PerfettoTracedProcess::MaybeCreateInstance();
+
   // Initialize the client library's TrackRegistry to support trace points
   // during startup tracing. We don't setup the client library completely here
   // yet, because we don't have field trials loaded yet (which influence which
@@ -63,20 +65,6 @@
   // setting up the client library?
   perfetto::internal::TrackRegistry::InitializeInstance();
 
-  // Create the PerfettoTracedProcess.
-  if (with_thread) {
-    g_tracing_with_thread = true;
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-    PerfettoTracedProcess::MaybeCreateInstanceWithThread(
-        /*will_trace_thread_restart=*/true);
-#else
-    PerfettoTracedProcess::MaybeCreateInstanceWithThread(
-        /*will_trace_thread_restart=*/false);
-#endif
-  } else {
-    PerfettoTracedProcess::MaybeCreateInstance();
-  }
-
   // Ensure TraceLog is initialized first.
   // https://crbug.com/764357
   TraceLog::GetInstance();
@@ -117,9 +105,7 @@
   DCHECK(base::FeatureList::GetInstance());
 
   // Create the PerfettoTracedProcess.
-  if (!g_tracing_with_thread) {
-    PerfettoTracedProcess::MaybeCreateInstance();
-  }
+  PerfettoTracedProcess::MaybeCreateInstance();
   PerfettoTracedProcess::Get().OnThreadPoolAvailable(enable_consumer);
 #if BUILDFLAG(IS_WIN)
   tracing::EnableETWExport();
diff --git a/services/tracing/public/cpp/trace_startup.h b/services/tracing/public/cpp/trace_startup.h
index 1fcf57b..15b4f83c 100644
--- a/services/tracing/public/cpp/trace_startup.h
+++ b/services/tracing/public/cpp/trace_startup.h
@@ -35,8 +35,7 @@
 // TODO(eseckler): Consider allocating the SMB in parent processes outside the
 // sandbox and supply it via the command line. Then, we can revert to call this
 // earlier and from fewer places again.
-void COMPONENT_EXPORT(TRACING_CPP)
-    EnableStartupTracingIfNeeded(bool with_thread = false);
+void COMPONENT_EXPORT(TRACING_CPP) EnableStartupTracingIfNeeded();
 
 // Enable startup tracing for the current process with the provided config. Sets
 // up ProducerClient and trace event and/or sampler profiler data sources, and
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 371c149a..481ef46e 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -67,6 +67,7 @@
     "SK_USER_CONFIG_HEADER=\"../../skia/config/SkUserConfig.h\"",
     "SK_WIN_FONTMGR_NO_SIMULATIONS",
     "SK_DISABLE_LEGACY_INIT_DECODERS",
+    "SK_CODEC_ENCODES_PNG_WITH_CONVERT_PIXELS",
   ]
 
   include_dirs = [
diff --git a/sql/database.cc b/sql/database.cc
index f2d4caa..469fb13 100644
--- a/sql/database.cc
+++ b/sql/database.cc
@@ -27,6 +27,7 @@
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/dcheck_is_on.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/format_macros.h"
@@ -62,6 +63,7 @@
 #include "sql/initialization.h"
 #include "sql/internal_api_token.h"
 #include "sql/meta_table.h"
+#include "sql/sql_features.h"
 #include "sql/sqlite_result_code.h"
 #include "sql/sqlite_result_code_values.h"
 #include "sql/statement.h"
@@ -872,12 +874,15 @@
 size_t Database::ComputeMmapSizeForOpen() {
   TRACE_EVENT0("sql", "Database::ComputeMmapSizeForOpen");
 
-  std::optional<base::ScopedBlockingCall> scoped_blocking_call;
-  InitScopedBlockingCall(FROM_HERE, &scoped_blocking_call);
-
   // How much to map if no errors are found.  50MB encompasses the 99th
   // percentile of Chrome databases in the wild, so this should be good.
   const size_t kMmapEverything = 256 * 1024 * 1024;
+  if (base::FeatureList::IsEnabled(sql::features::kSqlFixedMmapSize)) {
+    return kMmapEverything;
+  }
+
+  std::optional<base::ScopedBlockingCall> scoped_blocking_call;
+  InitScopedBlockingCall(FROM_HERE, &scoped_blocking_call);
 
   // Progress information is tracked in the [meta] table for databases which use
   // sql::MetaTable, otherwise it is tracked in a special view.
diff --git a/sql/sql_features.cc b/sql/sql_features.cc
index 0c17664e..929d787 100644
--- a/sql/sql_features.cc
+++ b/sql/sql_features.cc
@@ -18,6 +18,11 @@
              "PreOpenPreloadDatabase",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Use a fixed memory-map size instead of using the heuristic.
+BASE_FEATURE(kSqlFixedMmapSize,
+             "SqlFixedMmapSize",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Explicitly unlock the database on close to ensure lock is released.
 BASE_FEATURE(kUnlockDatabaseOnClose,
              "UnlockDatabaseOnClose",
diff --git a/sql/sql_features.h b/sql/sql_features.h
index c11338cc..decc37a 100644
--- a/sql/sql_features.h
+++ b/sql/sql_features.h
@@ -16,6 +16,7 @@
 // Alphabetical:
 COMPONENT_EXPORT(SQL) BASE_DECLARE_FEATURE(kEnableWALModeByDefault);
 COMPONENT_EXPORT(SQL) BASE_DECLARE_FEATURE(kPreOpenPreloadDatabase);
+COMPONENT_EXPORT(SQL) BASE_DECLARE_FEATURE(kSqlFixedMmapSize);
 COMPONENT_EXPORT(SQL) BASE_DECLARE_FEATURE(kUnlockDatabaseOnClose);
 
 }  // namespace sql::features
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 45191ad..a519666 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -264,6 +264,21 @@
             ]
         }
     ],
+    "AddSuggestStrongPasswordFromAddPassword": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SuggestStrongPasswordInAddPassword"
+                    ]
+                }
+            ]
+        }
+    ],
     "AddressInfobarDisplayLength": [
         {
             "platforms": [
@@ -23887,6 +23902,29 @@
             ]
         }
     ],
+    "SqlFixedMmapSize": [
+        {
+            "platforms": [
+                "android",
+                "android_weblayer",
+                "android_webview",
+                "chromeos",
+                "fuchsia",
+                "ios",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "SqlFixedMmapSize"
+                    ]
+                }
+            ]
+        }
+    ],
     "SqlScopedTransactionWebDatabase": [
         {
             "platforms": [
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index d2ed9df..53a3c4af 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -303,7 +303,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/13408113/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/13410607/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/angle b/third_party/angle
index 99eb1ce..84e3b8f 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 99eb1ceb5895d57a8859447411f4ccf68c2038d9
+Subproject commit 84e3b8f1bb235392502e2316f0045b941cd62c37
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_impl.cc b/third_party/blink/renderer/core/annotation/annotation_agent_impl.cc
index e6382373..bf636943 100644
--- a/third_party/blink/renderer/core/annotation/annotation_agent_impl.cc
+++ b/third_party/blink/renderer/core/annotation/annotation_agent_impl.cc
@@ -176,6 +176,11 @@
   }
 }
 
+bool AlmostEqual(const ScrollOffset& a, const ScrollOffset& b) {
+  float length = (a - b).Length();
+  return length <= 1.f;
+}
+
 }  // namespace
 
 AnnotationAgentImpl::AnnotationAgentImpl(
@@ -367,6 +372,30 @@
   // start the search to find the next focusable element from this element.
   document.SetSequentialFocusNavigationStartingPoint(&first_node);
 
+  if (type_ == mojom::blink::AnnotationType::kGlic) {
+    auto* scrollable_area =
+        first_node.GetLayoutObject()->GetFrameView()->GetScrollableArea();
+    CHECK(scrollable_area);
+    ScrollOffset scroll_offset = scroll_into_view_util::GetScrollOffsetToExpose(
+        *scrollable_area, bounding_box, PhysicalBoxStrut(), *params->align_x,
+        *params->align_y);
+    // Removes any negative offset from the `ScrollAlignment::CenterAlways()`.
+    scroll_offset = scrollable_area->ClampScrollOffset(scroll_offset);
+    ScrollOffset current_scroll_offset = scrollable_area->GetScrollOffset();
+    if (AlmostEqual(scroll_offset, current_scroll_offset)) {
+      document.Markers().StartGlicMarkerAnimationIfNeeded();
+    } else {
+      // Scroll is guaranteed to happen. `ScrollableArea::OnScrollFinished()`
+      // will call `StartGlicMarkerAnimation()`. This is a near-term solution
+      // due to the re-arch work in crbug.com/41406914. It means in the nested
+      // multiple scollers case, the first ever `OnScrollFinished()` starts the
+      // animation, regardless if the actual scroll has finished or not.
+      //
+      // TODO(https://crbug.com/41406914): Migrate from `OnScrollFinished()` to
+      // the scroll-promises.
+    }
+  }
+
   scroll_into_view_util::ScrollRectToVisible(*first_node.GetLayoutObject(),
                                              bounding_box, std::move(params));
 }
@@ -482,9 +511,6 @@
       }
       case mojom::blink::AnnotationType::kGlic: {
         document->Markers().AddGlicMarker(dom_range);
-        // TODO(crbug.com/407967372): Should only start the animation after the
-        // annotated target is scrolled into the viewport.
-        document->Markers().StartGlicMarkerAnimation();
         break;
       }
       case mojom::blink::AnnotationType::kTextFinder: {
diff --git a/third_party/blink/renderer/core/annotation/annotation_agent_impl_test.cc b/third_party/blink/renderer/core/annotation/annotation_agent_impl_test.cc
index 09a9ee9..57efb97 100644
--- a/third_party/blink/renderer/core/annotation/annotation_agent_impl_test.cc
+++ b/third_party/blink/renderer/core/annotation/annotation_agent_impl_test.cc
@@ -16,7 +16,9 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/annotation/annotation.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_font_face_descriptors.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_arraybuffer_arraybufferview_string.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_union_boolean_scrollintoviewoptions.h"
 #include "third_party/blink/renderer/core/annotation/annotation_agent_container_impl.h"
 #include "third_party/blink/renderer/core/annotation/annotation_test_utils.h"
 #include "third_party/blink/renderer/core/annotation/text_annotation_selector.h"
@@ -208,6 +210,29 @@
   DocumentMarkerVector GetAllMarkers() {
     return GetDocument().Markers().Markers();
   }
+
+  float GetAlphaForGlicMarkerAt(size_t index) {
+    const auto& markers = GetAllMarkers();
+    EXPECT_GE(markers.size(), index + 1);
+    DocumentMarker* marker = markers[index];
+    EXPECT_EQ(marker->GetType(), DocumentMarker::MarkerType::kGlic);
+    return To<GlicMarker>(marker)->BackgroundColor().Alpha();
+  }
+
+  bool GlicAnimationRunning() {
+    return GetDocument().Markers().glic_animation_state_ ==
+           DocumentMarkerController::GlicAnimationState::kRunning;
+  }
+
+  bool GlicAnimationFinished() {
+    return GetDocument().Markers().glic_animation_state_ ==
+           DocumentMarkerController::GlicAnimationState::kFinished;
+  }
+
+  bool GlicAnimationNotStarted() {
+    return GetDocument().Markers().glic_animation_state_ ==
+           DocumentMarkerController::GlicAnimationState::kNotStarted;
+  }
 };
 
 // Tests that the agent type is correctly set.
@@ -1704,10 +1729,18 @@
   host.BindToAgent(*agent);
   ASSERT_FALSE(host.did_finish_attachment_rect_);
 
-  // Set the first RequestAnimationFrame.
+  // Finish the attachment.
   Compositor().BeginFrame();
   host.FlushForTesting();
   ASSERT_TRUE(host.did_finish_attachment_rect_);
+  ASSERT_TRUE(agent->IsAttached());
+  EXPECT_TRUE(GlicAnimationNotStarted());
+
+  // The browser tells the renderer to scroll. Since we don't need to scroll,
+  // the highlight animation starts immediately, which queues the first
+  // RequestAnimationFrame.
+  agent->ScrollIntoView(/*applies_focus=*/false);
+  EXPECT_TRUE(GlicAnimationRunning());
 
   // Execute the first RequestAnimationFrame, which initializes the T=0.
   Compositor().BeginFrame();
@@ -1716,10 +1749,12 @@
   // `BeginFrame()`.
   task_environment().FastForwardBy(base::Milliseconds(500));
   Compositor().BeginFrame(0.5);
+  EXPECT_TRUE(GlicAnimationRunning());
 
   // T=1100ms. Submit the last frame with progress=1.
   task_environment().FastForwardBy(base::Milliseconds(600));
   Compositor().BeginFrame(0.6);
+  EXPECT_TRUE(GlicAnimationFinished());
 
   const auto& markers = GetAllMarkers();
   EXPECT_EQ(markers.size(), 1u);
@@ -1751,14 +1786,15 @@
   MockAnnotationAgentHost host;
   host.BindToAgent(*agent);
   ASSERT_FALSE(host.did_finish_attachment_rect_);
-
-  // Set the first RequestAnimationFrame.
   Compositor().BeginFrame();
   host.FlushForTesting();
   ASSERT_TRUE(host.did_finish_attachment_rect_);
+  ASSERT_TRUE(agent->IsAttached());
 
-  // Execute the first RequestAnimationFrame.
-  Compositor().BeginFrame();
+  // The browser tells the renderer to scroll. Since we don't need to scroll,
+  // the highlight animation starts immediately, which queues the first
+  // RequestAnimationFrame.
+  agent->ScrollIntoView(/*applies_focus=*/false);
 
   EXPECT_EQ(GetAllMarkers().size(), 1u);
 
@@ -1881,21 +1917,22 @@
   ASSERT_TRUE(agent1);
   MockAnnotationAgentHost host1;
   host1.BindToAgent(*agent1);
-  host1.FlushForTesting();
-
-  // Start the animation.
   Compositor().BeginFrame();
+  host1.FlushForTesting();
+  ASSERT_TRUE(agent1->IsAttached());
+
+  // The browser tells the renderer to scroll. Since we don't need to scroll,
+  // the highlight animation starts immediately, which queues the first
+  // RequestAnimationFrame.
+  agent1->ScrollIntoView(/*applies_focus=*/false);
   // Execute the first RequestAnimationFrame with T=0.
   Compositor().BeginFrame();
   // Execute the first RequestAnimationFrame with T=0.5.
   task_environment().FastForwardBy(base::Milliseconds(500));
   Compositor().BeginFrame(0.5);
 
-  EXPECT_EQ(GetAllMarkers().size(), 1u);
-  EXPECT_EQ(GetAllMarkers()[0]->GetType(), DocumentMarker::MarkerType::kGlic);
   // Greater than 0.
-  EXPECT_GT(To<GlicMarker>(GetAllMarkers()[0].Get())->BackgroundColor().Alpha(),
-            0.f);
+  EXPECT_GT(GetAlphaForGlicMarkerAt(0u), 0.f);
 
   // Simulate that glic highlights a different text. Currently only one text
   // (agent) is highlighted at a time.
@@ -1913,18 +1950,218 @@
   ASSERT_TRUE(agent2);
   MockAnnotationAgentHost host2;
   host2.BindToAgent(*agent2);
-  host2.FlushForTesting();
-
-  // Start the animation.
   Compositor().BeginFrame();
+  host2.FlushForTesting();
+  ASSERT_TRUE(agent2->IsAttached());
+
+  // No scroll. Queues the first RequestAnimationFrame.
+  agent2->ScrollIntoView(/*applies_focus=*/false);
   // Execute the first RequestAnimationFrame with T=0.
   task_environment().FastForwardBy(base::Milliseconds(16));
   Compositor().BeginFrame();
 
-  EXPECT_EQ(GetAllMarkers().size(), 1u);
-  EXPECT_EQ(GetAllMarkers()[0]->GetType(), DocumentMarker::MarkerType::kGlic);
-  EXPECT_EQ(To<GlicMarker>(GetAllMarkers()[0].Get())->BackgroundColor().Alpha(),
-            0.f);
+  EXPECT_EQ(GetAlphaForGlicMarkerAt(0u), 0.f);
+}
+
+TEST_F(AnnotationAgentImplTest,
+       GlicHighlight_HighLightStartsAfterScrollFinishes) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+      #foo {
+        position: absolute;
+        top: 1000px;
+      }
+      body {
+        height: 5000px;
+        margin: 0;
+      }
+    </style>
+    <p id='foo'>FOO<p>
+  )HTML");
+  Compositor().BeginFrame();
+
+  Element* element_foo = GetDocument().getElementById(AtomicString("foo"));
+
+  RangeInFlatTree* range_foo =
+      CreateRangeToExpectedText(element_foo->firstChild(), 0, 3, "FOO");
+  auto* agent_foo =
+      CreateAgentForRange(range_foo, mojom::blink::AnnotationType::kGlic);
+  ASSERT_TRUE(agent_foo);
+
+  MockAnnotationAgentHost host_foo;
+  host_foo.BindToAgent(*agent_foo);
+  Compositor().BeginFrame();
+  ASSERT_TRUE(agent_foo->IsAttached());
+
+  EXPECT_TRUE(GlicAnimationNotStarted());
+
+  // We need to scroll.
+  EXPECT_TRUE(ExpectNotInViewport(*element_foo));
+  host_foo.agent_->ScrollIntoView(/*applies_focus=*/false);
+  host_foo.FlushForTesting();
+
+  Compositor().BeginFrame();
+  Compositor().BeginFrame();
+
+  EXPECT_TRUE(GlicAnimationNotStarted());
+
+  // Smooth scrolling is guaranteed to finish within 750ms.
+  task_environment().FastForwardBy(base::Seconds(1));
+  Compositor().BeginFrame(1.0);
+  EXPECT_TRUE(ExpectInViewport(*element_foo));
+  // Since the text node is in the viewport, we must have queued the first
+  // RequestAnimationFrame.
+  EXPECT_TRUE(GlicAnimationRunning());
+  EXPECT_EQ(GetAlphaForGlicMarkerAt(0u), 0.f);
+
+  // Run the highlight animation.
+  task_environment().FastForwardBy(base::Milliseconds(16));
+  Compositor().BeginFrame();
+
+  EXPECT_TRUE(GlicAnimationRunning());
+  // The opacity is greater than 0.
+  EXPECT_GT(GetAlphaForGlicMarkerAt(0u), 0.f);
+}
+
+// Glic instant scrolls for more than 7000px of distance.
+TEST_F(AnnotationAgentImplTest, GlicHighlight_InstantStartForInstantScroll) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    #foo {
+      position: absolute;
+      top: 7500px;
+    }
+    body {
+      height: 10000px;
+      margin: 0;
+    }
+    </style>
+    <p id='foo'>FOO<p>
+  )HTML");
+  Compositor().BeginFrame();
+
+  Element* element_foo = GetDocument().getElementById(AtomicString("foo"));
+
+  RangeInFlatTree* range_foo =
+      CreateRangeToExpectedText(element_foo->firstChild(), 0, 3, "FOO");
+  auto* agent_foo =
+      CreateAgentForRange(range_foo, mojom::blink::AnnotationType::kGlic);
+  ASSERT_TRUE(agent_foo);
+
+  MockAnnotationAgentHost host_foo;
+  host_foo.BindToAgent(*agent_foo);
+  Compositor().BeginFrame();
+  ASSERT_TRUE(agent_foo->IsAttached());
+
+  EXPECT_TRUE(GlicAnimationNotStarted());
+
+  // We need to scroll.
+  EXPECT_TRUE(ExpectNotInViewport(*element_foo));
+  host_foo.agent_->ScrollIntoView(/*applies_focus=*/false);
+  host_foo.FlushForTesting();
+
+  // Instant scroll.
+  EXPECT_TRUE(ExpectInViewport(*element_foo));
+  EXPECT_TRUE(GlicAnimationRunning());
+
+  // Queue the first RequestAnimationFrame.
+  task_environment().FastForwardBy(base::Milliseconds(16));
+  Compositor().BeginFrame();
+  // Run the first RequestAnimationFrame.
+  task_environment().FastForwardBy(base::Milliseconds(16));
+  Compositor().BeginFrame();
+
+  EXPECT_TRUE(GlicAnimationRunning());
+  // The opacity is greater than 0.
+  EXPECT_GT(GetAlphaForGlicMarkerAt(0u), 0.f);
+}
+
+// Test that the highlight doesn't restart after subsequent scrollEnd events.
+TEST_F(AnnotationAgentImplTest,
+       GlicHighlight_AnimationDoesNotRestartAfterSubsequentScroll) {
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(R"HTML(
+    <!DOCTYPE html>
+    <style>
+    #foo {
+      position: absolute;
+      top: 2000px;
+    }
+    #bar {
+      position: absolute;
+      top: 3000px;
+    }
+    body {
+      height: 4000px;
+      margin: 0;
+    }
+    </style>
+    <p id='foo'>FOO<p>
+    <p id='bar'>BAR<p>
+  )HTML");
+  Compositor().BeginFrame();
+
+  Element* element_foo = GetDocument().getElementById(AtomicString("foo"));
+
+  RangeInFlatTree* range_foo =
+      CreateRangeToExpectedText(element_foo->firstChild(), 0, 3, "FOO");
+  auto* agent_foo =
+      CreateAgentForRange(range_foo, mojom::blink::AnnotationType::kGlic);
+  ASSERT_TRUE(agent_foo);
+
+  MockAnnotationAgentHost host_foo;
+  host_foo.BindToAgent(*agent_foo);
+  Compositor().BeginFrame();
+  ASSERT_TRUE(agent_foo->IsAttached());
+
+  EXPECT_TRUE(GlicAnimationNotStarted());
+
+  // We need to scroll.
+  EXPECT_TRUE(ExpectNotInViewport(*element_foo));
+  host_foo.agent_->ScrollIntoView(/*applies_focus=*/false);
+  host_foo.FlushForTesting();
+
+  Compositor().BeginFrame();
+  Compositor().BeginFrame();
+  EXPECT_TRUE(GlicAnimationNotStarted());
+
+  // Max smooth scrolling is capped at 750ms.
+  task_environment().FastForwardBy(base::Seconds(1));
+  Compositor().BeginFrame(1.0);
+  EXPECT_TRUE(ExpectInViewport(*element_foo));
+  // Since the text node is in the viewport, we must have queued the first
+  // RequestAnimationFrame.
+  EXPECT_TRUE(GlicAnimationRunning());
+
+  // Run the highlight animation, and finish it.
+  task_environment().FastForwardBy(base::Seconds(1));
+  Compositor().BeginFrame(/*time_delta_in_seconds=*/1);
+  EXPECT_TRUE(GlicAnimationFinished());
+  EXPECT_EQ(GetAlphaForGlicMarkerAt(0u), 1.f);
+
+  // Scroll BAR into the viewport.
+  Element* element_bar = GetDocument().getElementById(AtomicString("bar"));
+  EXPECT_TRUE(ExpectNotInViewport(*element_bar));
+  ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
+  options->setBlock("start");
+  options->setBehavior("instant");
+  auto* arg =
+      MakeGarbageCollected<V8UnionBooleanOrScrollIntoViewOptions>(options);
+  element_bar->scrollIntoView(arg);
+  Compositor().BeginFrame();
+  EXPECT_TRUE(ExpectInViewport(*element_bar));
+
+  // If we started another highlight animation for scrolling BAR, the animation
+  // should not have restarted.
+  EXPECT_TRUE(GlicAnimationFinished());
+  EXPECT_EQ(GetAlphaForGlicMarkerAt(0u), 1.f);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index bb2fc91..6788239b 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -8050,11 +8050,11 @@
     }
   }
 
-  DCHECK(!IsScheduledForTopLayerRemoval(element));
-  DCHECK(!before || top_layer_elements_.Contains(before));
+  CHECK(!IsScheduledForTopLayerRemoval(element));
+  CHECK(!before || top_layer_elements_.Contains(before));
 
   if (before) {
-    DCHECK(element->IsBackdropPseudoElement())
+    CHECK(element->IsBackdropPseudoElement())
         << "If this invariant changes, we might need to revisit Container "
            "Queries for top layer elements.";
     wtf_size_t before_position = top_layer_elements_.Find(before);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 3c8ae19..0af8776 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -9003,7 +9003,7 @@
     return nullptr;
   }
 
-  if (pseudo_id == kPseudoIdBackdrop) {
+  if (pseudo_id == kPseudoIdBackdrop && IsInTopLayer()) {
     GetDocument().AddToTopLayer(pseudo_element, this);
   }
 
diff --git a/third_party/blink/renderer/core/dom/range.cc b/third_party/blink/renderer/core/dom/range.cc
index de3597c9..5c41e03e 100644
--- a/third_party/blink/renderer/core/dom/range.cc
+++ b/third_party/blink/renderer/core/dom/range.cc
@@ -388,24 +388,28 @@
 
   Node* this_cont = commonAncestorContainer();
   Node* source_cont = source_range->commonAncestorContainer();
-  if (this_cont->GetDocument() != source_cont->GetDocument()) {
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kWrongDocumentError,
-        "The source range is in a different document than this range.");
-    return 0;
-  }
+  if (this_cont && source_cont) {
+    if (this_cont->GetDocument() != source_cont->GetDocument()) {
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kWrongDocumentError,
+          "The source range is in a different document than this range.");
+      return 0;
+    }
 
-  Node* this_top = this_cont;
-  Node* source_top = source_cont;
-  while (this_top->parentNode())
-    this_top = this_top->parentNode();
-  while (source_top->parentNode())
-    source_top = source_top->parentNode();
-  if (this_top != source_top) {  // in different DocumentFragments
-    exception_state.ThrowDOMException(
-        DOMExceptionCode::kWrongDocumentError,
-        "The source range is in a different document than this range.");
-    return 0;
+    Node* this_top = this_cont;
+    Node* source_top = source_cont;
+    while (this_top->parentNode()) {
+      this_top = this_top->parentNode();
+    }
+    while (source_top->parentNode()) {
+      source_top = source_top->parentNode();
+    }
+    if (this_top != source_top) {  // in different DocumentFragments
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kWrongDocumentError,
+          "The source range is in a different document than this range.");
+      return 0;
+    }
   }
 
   switch (how) {
@@ -1674,7 +1678,7 @@
 
   // Stores the elements selected by the range.
   HeapHashSet<Member<const Node>> selected_elements;
-  for (Node* node = FirstNode(); node != stop_node;
+  for (Node* node = FirstNode(); node && node != stop_node;
        node = NodeTraversal::Next(*node)) {
     if (!node->IsElementNode())
       continue;
@@ -1687,7 +1691,7 @@
     }
   }
 
-  for (const Node* node = FirstNode(); node != stop_node;
+  for (const Node* node = FirstNode(); node && node != stop_node;
        node = NodeTraversal::Next(*node)) {
     auto* element_node = DynamicTo<Element>(node);
     if (element_node) {
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
index 2ddcc1a..4d3b1e4 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.cc
@@ -509,6 +509,9 @@
     possibly_existing_marker_types_ = possibly_existing_marker_types_.Subtract(
         DocumentMarker::MarkerTypes(type));
   }
+  if (type == DocumentMarker::kGlic) {
+    glic_animation_state_ = GlicAnimationState::kNotStarted;
+  }
 }
 
 void DocumentMarkerController::RemoveMarkersInternal(
@@ -1390,13 +1393,15 @@
   InvalidatePaintForNode(*node);
 }
 
-void DocumentMarkerController::StartGlicMarkerAnimation() {
+void DocumentMarkerController::StartGlicMarkerAnimationIfNeeded() {
   CHECK(document_);
-  if (!PossiblyHasMarkers(DocumentMarker::kGlic)) {
+  if (!PossiblyHasMarkers(DocumentMarker::kGlic) ||
+      glic_animation_state_ != GlicAnimationState::kNotStarted) {
     return;
   }
   // Always make sure we start from a clean state.
   glic_marker_animation_start_ = std::nullopt;
+  glic_animation_state_ = GlicAnimationState::kRunning;
   auto* callback = MakeGarbageCollected<RequestAnimationFrameCallback>(this);
   document_->RequestAnimationFrame(callback);
 }
@@ -1408,6 +1413,8 @@
     // The value here can become stale: if before the previous animation
     // finishes, glic removes the highlight.
     glic_marker_animation_start_ = std::nullopt;
+    // Reset when the glic markers are removed.
+    CHECK_EQ(glic_animation_state_, GlicAnimationState::kNotStarted);
     return;
   }
   if (!glic_marker_animation_start_) {
@@ -1422,6 +1429,7 @@
 
   if (is_last_frame) {
     glic_marker_animation_start_ = std::nullopt;
+    glic_animation_state_ = GlicAnimationState::kFinished;
     return;
   }
 
diff --git a/third_party/blink/renderer/core/editing/markers/document_marker_controller.h b/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
index fad618da6..f015d23 100644
--- a/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
+++ b/third_party/blink/renderer/core/editing/markers/document_marker_controller.h
@@ -205,11 +205,15 @@
                               unsigned old_length,
                               unsigned new_length);
 
-  void StartGlicMarkerAnimation();
+  void StartGlicMarkerAnimationIfNeeded();
 
   void ContinueGlicMarkerAnimation(base::TimeTicks tick);
 
  private:
+  // TODO(https://crbug.com/41406914): Remove once we migrate to
+  // scroll-promises.
+  friend class AnnotationAgentImplTest;
+
   void AddMarkerInternal(
       const EphemeralRange&,
       base::FunctionRef<DocumentMarker*(int, int)> create_marker_from_offsets,
@@ -248,6 +252,26 @@
 
   void InvalidatePaintForGlicMarkers();
 
+  // TODO(https://crbug.com/41406914): The state can be removed when we migrate
+  // to the scroll-promises. With scroll-promises we are guaranteed to call
+  // `StartGlicAnimation()` only once and only for the targeted programmatic
+  // scroll.
+  //
+  // Glic animations are highlight animations for `GlicMarker`s. Each
+  // `GlicMarker`s are always removed before they are added to guarantee they
+  // are only animated once.
+  enum class GlicAnimationState {
+    // The default state.
+    kNotStarted = 0,
+    // The animation is running.
+    kRunning,
+    // Finished. Note we don't allow the animation to restart in this case. We
+    // rely on the markers to be removed first, which resets the state back to
+    // `kNotStarted`.
+    kFinished,
+  };
+  GlicAnimationState glic_animation_state_ = GlicAnimationState::kNotStarted;
+
   std::optional<base::TimeTicks> glic_marker_animation_start_;
 
   MarkerMaps markers_;
diff --git a/third_party/blink/renderer/core/layout/flex/flex_item.h b/third_party/blink/renderer/core/layout/flex/flex_item.h
index 5ba0fdf4..a271150 100644
--- a/third_party/blink/renderer/core/layout/flex/flex_item.h
+++ b/third_party/blink/renderer/core/layout/flex/flex_item.h
@@ -25,6 +25,7 @@
            LayoutUnit base_content_size,
            MinMaxSizes main_axis_min_max_sizes,
            LayoutUnit main_axis_border_padding,
+           std::optional<LayoutUnit> max_content_contribution,
            PhysicalBoxStrut initial_margins,
            BoxStrut initial_scrollbars,
            uint8_t main_axis_auto_margin_count,
@@ -34,8 +35,7 @@
            bool is_initial_block_size_indefinite,
            bool is_used_flex_basis_indefinite,
            bool depends_on_min_max_sizes,
-           bool is_horizontal_flow,
-           std::optional<LayoutUnit> max_content_contribution)
+           bool is_horizontal_flow)
       : block_node(block_node),
         item_index(item_index),
         flex_grow(flex_grow),
@@ -45,6 +45,7 @@
             main_axis_min_max_sizes.ClampSizeToMinAndMax(base_content_size)),
         main_axis_min_max_sizes(main_axis_min_max_sizes),
         main_axis_border_padding(main_axis_border_padding),
+        max_content_contribution(max_content_contribution),
         initial_margins(initial_margins),
         initial_scrollbars(initial_scrollbars),
         main_axis_auto_margin_count(main_axis_auto_margin_count),
@@ -55,9 +56,7 @@
         is_initial_block_size_indefinite(is_initial_block_size_indefinite),
         is_used_flex_basis_indefinite(is_used_flex_basis_indefinite),
         depends_on_min_max_sizes(depends_on_min_max_sizes),
-        is_horizontal_flow(is_horizontal_flow),
-        frozen(false),
-        max_content_contribution(max_content_contribution) {}
+        is_horizontal_flow(is_horizontal_flow) {}
 
   LayoutUnit HypotheticalMainAxisMarginBoxSize() const {
     return hypothetical_content_size + main_axis_border_padding +
@@ -107,6 +106,8 @@
   const MinMaxSizes main_axis_min_max_sizes;
   const LayoutUnit main_axis_border_padding;
 
+  const std::optional<LayoutUnit> max_content_contribution;
+
   // `initial_margins` are the margins with auto-margins applied.
   // `initial_scrollbars` is the scrollbar state at the beginning of running
   // flex layout, so it can be compared to the final state.
@@ -123,14 +124,14 @@
   const bool is_used_flex_basis_indefinite;
   const bool depends_on_min_max_sizes;
   const bool is_horizontal_flow;
-  bool frozen;
 
+  // Fields mutated within the line-flexer.
+  bool frozen = false;
   LayoutUnit flexed_content_size;
 
   // The above fields are used by the flex algorithm. The following fields, by
   // contrast, are just convenient storage.
   Member<const LayoutResult> layout_result;
-  std::optional<LayoutUnit> max_content_contribution;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
index 72ceec5..7a0f763 100644
--- a/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flex/flex_layout_algorithm.cc
@@ -1171,14 +1171,17 @@
     child_percentage_size.block_size = kIndefiniteSize;
     space_builder.SetIsInitialBlockSizeIndefinite(true);
   }
-  if (override_inline_size.has_value()) {
-    LogicalSize available_size = ChildAvailableSize();
-    available_size.inline_size = *override_inline_size;
-    space_builder.SetIsFixedInlineSize(true);
-    space_builder.SetAvailableSize(available_size);
+
+  LogicalSize available_size = ChildAvailableSize();
+  if (is_column_) {
+    if (override_inline_size) {
+      available_size.inline_size = *override_inline_size;
+      space_builder.SetIsFixedInlineSize(true);
+    }
   } else {
-    space_builder.SetAvailableSize(ChildAvailableSize());
+    DCHECK(!override_inline_size);
   }
+  space_builder.SetAvailableSize(available_size);
   space_builder.SetPercentageResolutionSize(child_percentage_size);
   return space_builder.ToConstraintSpace();
 }
@@ -1214,19 +1217,17 @@
                                           &space_builder);
   space_builder.SetIsPaintedAtomically(true);
 
-  LogicalSize available_size;
+  LogicalSize available_size = ChildAvailableSize();
   if (is_column_) {
-    available_size.inline_size = line_cross_size_for_stretch
-                                     ? *line_cross_size_for_stretch
-                                     : ChildAvailableSize().inline_size;
-
     if (override_inline_size) {
-      DCHECK(!line_cross_size_for_stretch.has_value())
+      DCHECK(!line_cross_size_for_stretch)
           << "We only override inline size when we are calculating intrinsic "
              "width of multiline column flexboxes, and we don't do any "
              "stretching during the intrinsic width calculation.";
       available_size.inline_size = *override_inline_size;
       space_builder.SetIsFixedInlineSize(true);
+    } else if (line_cross_size_for_stretch) {
+      available_size.inline_size = *line_cross_size_for_stretch;
     }
     available_size.block_size = item_main_axis_final_size;
     space_builder.SetIsFixedBlockSize(true);
@@ -1235,11 +1236,11 @@
       space_builder.SetInlineAutoBehavior(AutoSizeBehavior::kStretchExplicit);
     }
   } else {
-    DCHECK(!override_inline_size.has_value());
+    DCHECK(!override_inline_size);
+    if (line_cross_size_for_stretch) {
+      available_size.block_size = *line_cross_size_for_stretch;
+    }
     available_size.inline_size = item_main_axis_final_size;
-    available_size.block_size = line_cross_size_for_stretch
-                                    ? *line_cross_size_for_stretch
-                                    : ChildAvailableSize().block_size;
     space_builder.SetIsFixedInlineSize(true);
     if (line_cross_size_for_stretch ||
         WillChildCrossSizeBeContainerCrossSize(flex_item_node, alignment)) {
@@ -1616,11 +1617,11 @@
     flex_items_.emplace_back(
         child, item_index++, flex_grow, flex_shrink, base_content_size,
         min_max_sizes_in_main_axis_direction, main_axis_border_padding,
-        physical_child_margins, initial_scrollbars, main_axis_auto_margin_count,
-        alignment, baseline_writing_mode, baseline_group,
-        is_initial_block_size_indefinite, is_used_flex_basis_indefinite,
-        depends_on_min_max_sizes, is_horizontal_flow_,
-        max_content_contribution);
+        max_content_contribution, physical_child_margins, initial_scrollbars,
+        main_axis_auto_margin_count, alignment, baseline_writing_mode,
+        baseline_group, is_initial_block_size_indefinite,
+        is_used_flex_basis_indefinite, depends_on_min_max_sizes,
+        is_horizontal_flow_);
     // Save the layout result so that we can maybe reuse it later.
     if (layout_result && !is_main_axis_inline_axis) {
       flex_items_.back().layout_result = layout_result;
@@ -1811,6 +1812,41 @@
   const LayoutUnit main_axis_inner_size =
       MainAxisContentExtent(result.max_sum_hypothetical_main_size);
 
+  // If we are a single line, and have a definite cross-size, the line
+  // cross-size will be the container cross-size.
+  const std::optional<LayoutUnit> definite_line_cross_size =
+      ([&]() -> std::optional<LayoutUnit> {
+        if (is_multi_line_) {
+          return std::nullopt;
+        }
+        const LayoutUnit cross_available_size =
+            is_column_ ? ChildAvailableSize().inline_size
+                       : ChildAvailableSize().block_size;
+        if (cross_available_size == kIndefiniteSize) {
+          return std::nullopt;
+        }
+        const auto& style = Style();
+        if (!is_column_) {
+          // Treat the block-size as indefinite if we need to apply the
+          // automatic-minimum size for aspect-ratio.
+          // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
+          if (!style.AspectRatio().IsAuto() && !style.IsScrollContainer() &&
+              style.LogicalMinHeight().HasAuto()) {
+            return std::nullopt;
+          }
+          // Similarly if we have a content-based min/max block-size treat it
+          // as indefinite.
+          // NOTE: This behaviour isn't in the specification.
+          // https://github.com/w3c/csswg-drafts/issues/12123
+          if (style.LogicalMinHeight().HasContentOrIntrinsic() ||
+              style.LogicalMaxHeight().HasContentOrIntrinsic()) {
+            return std::nullopt;
+          }
+        }
+
+        return cross_available_size;
+      })();
+
   LayoutUnit sum_line_cross_size;
 
   flex_lines->reserve(result.flex_lines.size());
@@ -1840,6 +1876,10 @@
       main_axis_free_space -= flex_item.FlexedMarginBoxSize();
       main_axis_auto_margin_count += flex_item.main_axis_auto_margin_count;
 
+      const bool has_baseline_alignment =
+          flex_item.alignment == ItemPosition::kBaseline ||
+          flex_item.alignment == ItemPosition::kLastBaseline;
+
       const LayoutUnit cross_axis_size = ([&]() {
         const ConstraintSpace space =
             BuildSpaceForLayout(flex_item.block_node, flex_item.alignment,
@@ -1897,9 +1937,7 @@
       // TODO(crbug.com/1272533): We may not have a layout-result during
       // min/max calculations. This is incorrect, and we should produce a
       // layout-result when baseline aligned.
-      if (flex_item.layout_result &&
-          (flex_item.alignment == ItemPosition::kBaseline ||
-           flex_item.alignment == ItemPosition::kLastBaseline)) {
+      if (flex_item.layout_result && has_baseline_alignment) {
         const LayoutUnit ascent = BaselineAscent(
             flex_item, To<PhysicalBoxFragment>(
                            flex_item.layout_result->GetPhysicalFragment()));
@@ -1917,6 +1955,9 @@
       line_cross_size = std::max(line_cross_size, cross_axis_margin_size);
     }
 
+    // Ensure that we use the definite line cross-line if available.
+    line_cross_size = definite_line_cross_size.value_or(line_cross_size);
+
     flex_lines->emplace_back(std::move(item_indices), main_axis_free_space,
                              line_cross_size, max_major_ascent,
                              max_minor_ascent, main_axis_auto_margin_count);
@@ -2606,29 +2647,27 @@
       }
     }
 
-    std::optional<LayoutUnit> line_cross_size_for_stretch =
-        DoesItemStretch(flex_item->block_node, flex_item->alignment)
-            ? std::optional<LayoutUnit>(flex_line.line_cross_size)
-            : std::nullopt;
+    LayoutUnit line_cross_size = flex_line.line_cross_size;
 
     // If an item broke, its offset may have expanded (as the result of a
     // current or previous break before), in which case, we shouldn't expand by
     // the total line cross size. Otherwise, we would continue to expand the row
     // past the block-size of its items.
-    if (line_cross_size_for_stretch && !is_column_ && item_break_token) {
-      LayoutUnit updated_cross_size_for_stretch =
-          line_cross_size_for_stretch.value();
-      updated_cross_size_for_stretch -=
+    if (!is_column_ && item_break_token) {
+      line_cross_size -=
           offset_in_stitched_container -
           (original_offset.block_offset + flex_line.item_offset_adjustment) -
           item_break_token->ConsumedBlockSize();
-
-      line_cross_size_for_stretch = updated_cross_size_for_stretch;
     }
 
     const bool min_block_size_should_encompass_intrinsic_size =
         MinBlockSizeShouldEncompassIntrinsicSize(*flex_item);
-    ConstraintSpace child_space = BuildSpaceForLayout(
+
+    const std::optional<LayoutUnit> line_cross_size_for_stretch =
+        DoesItemStretch(flex_item->block_node, flex_item->alignment)
+            ? std::optional<LayoutUnit>(line_cross_size)
+            : std::nullopt;
+    const ConstraintSpace child_space = BuildSpaceForLayout(
         flex_item->block_node, flex_item->alignment,
         flex_item->main_axis_final_size,
         flex_item->is_initial_block_size_indefinite,
diff --git a/third_party/blink/renderer/core/layout/grid/grid_line_resolver.cc b/third_party/blink/renderer/core/layout/grid/grid_line_resolver.cc
index f2a88bd38..ce41fd48 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_line_resolver.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_line_resolver.cc
@@ -244,14 +244,6 @@
     const bool has_subgridded_columns =
         subgrid_span.columns.IsTranslatedDefinite();
     const bool has_subgridded_rows = subgrid_span.rows.IsTranslatedDefinite();
-    wtf_size_t subgrid_column_start_line =
-        has_subgridded_columns ? subgrid_span.columns.StartLine() : 0;
-    wtf_size_t subgrid_row_start_line =
-        has_subgridded_rows ? subgrid_span.rows.StartLine() : 0;
-    wtf_size_t subgrid_column_end_line =
-        has_subgridded_columns ? subgrid_span.columns.EndLine() : 1;
-    wtf_size_t subgrid_row_end_line =
-        has_subgridded_rows ? subgrid_span.rows.EndLine() : 1;
     for (const auto& pair : parent_map) {
       auto position = pair.value;
       DCHECK(position.columns.IsTranslatedDefinite());
@@ -281,29 +273,42 @@
       // At this point, the current grid area must be either fully or partially
       // within the subgrid. We can safely clamp this to the subgrid range per
       // the above quote.
-      position.columns.Intersect(subgrid_column_start_line,
-                                 subgrid_column_end_line);
-      position.rows.Intersect(subgrid_row_start_line, subgrid_row_end_line);
+      if (has_subgridded_rows) {
+        position.rows.Intersect(subgrid_span.rows.StartLine(),
+                                subgrid_span.rows.EndLine());
+      }
+      if (has_subgridded_columns) {
+        position.columns.Intersect(subgrid_span.columns.StartLine(),
+                                   subgrid_span.columns.EndLine());
+      }
 
       // Now offset the position by the subgrid's start lines, as subgrids
       // always begin at index 0.
-      position.rows.Translate(-subgrid_row_start_line);
-      position.columns.Translate(-subgrid_column_start_line);
+      if (has_subgridded_rows) {
+        position.rows.Translate(-subgrid_span.rows.StartLine());
+      }
+      if (has_subgridded_columns) {
+        position.columns.Translate(-subgrid_span.columns.StartLine());
+      }
 
       const auto& existing_entry = subgrid_map.find(pair.key);
       if (existing_entry != subgrid_map.end()) {
         // Handle overlapping entries between the subgrid and parent grid by
         // taking the lesser value.
         const auto& existing_position = existing_entry->value;
-        position.rows.SetStart(std::min(position.rows.StartLine(),
-                                        existing_position.rows.StartLine()));
-        position.rows.SetEnd(std::min(position.rows.EndLine(),
-                                      existing_position.rows.EndLine()));
-        position.columns.SetStart(
-            std::min(position.columns.StartLine(),
-                     existing_position.columns.StartLine()));
-        position.columns.SetEnd(std::min(position.columns.EndLine(),
-                                         existing_position.columns.EndLine()));
+        if (has_subgridded_rows) {
+          position.rows.SetStart(std::min(position.rows.StartLine(),
+                                          existing_position.rows.StartLine()));
+          position.rows.SetEnd(std::min(position.rows.EndLine(),
+                                        existing_position.rows.EndLine()));
+        }
+        if (has_subgridded_columns) {
+          position.columns.SetStart(
+              std::min(position.columns.StartLine(),
+                       existing_position.columns.StartLine()));
+          position.columns.SetEnd(std::min(
+              position.columns.EndLine(), existing_position.columns.EndLine()));
+        }
       }
 
       GridArea clamped_area(position.rows, position.columns);
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.cc b/third_party/blink/renderer/core/scroll/scrollable_area.cc
index f2a76e4..ccb6404 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.cc
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/renderer/core/css/properties/longhands.h"
 #include "third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
+#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
 #include "third_party/blink/renderer/core/event_type_names.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
@@ -1158,6 +1159,9 @@
                     node->GetDocument())) {
           viewport_position_tracker->OnScrollEnd();
         }
+        // TODO(https://crbug.com/41406914): This is temporary. Remove once we
+        // start to migrate to scroll-promises.
+        node->GetDocument().Markers().StartGlicMarkerAnimationIfNeeded();
         if (RuntimeEnabledFeatures::ScrollEndEventsEnabled()) {
           node->GetDocument().EnqueueScrollEndEventForNode(node);
         }
diff --git a/third_party/blink/renderer/core/scroll/scrollable_area.h b/third_party/blink/renderer/core/scroll/scrollable_area.h
index a7a692ba..e407145 100644
--- a/third_party/blink/renderer/core/scroll/scrollable_area.h
+++ b/third_party/blink/renderer/core/scroll/scrollable_area.h
@@ -122,6 +122,9 @@
                                   const ScrollOffset&,
                                   ScrollCallback on_finish);
 
+  // See https://crbug.com/413002675: `on_finish` is not always executed at the
+  // end of the scroll (example: it may be executed while the scroll is in
+  // progress for animated programmatic scrolls).
   virtual bool SetScrollOffset(const ScrollOffset&,
                                mojom::blink::ScrollType,
                                mojom::blink::ScrollBehavior,
diff --git a/third_party/blink/renderer/modules/ai/on_device_translation/translator.cc b/third_party/blink/renderer/modules/ai/on_device_translation/translator.cc
index 035be13..33165187 100644
--- a/third_party/blink/renderer/modules/ai/on_device_translation/translator.cc
+++ b/third_party/blink/renderer/modules/ai/on_device_translation/translator.cc
@@ -7,12 +7,14 @@
 #include <limits>
 
 #include "base/functional/callback_helpers.h"
+#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h"
 #include "third_party/blink/public/mojom/ai/model_streaming_responder.mojom-blink.h"
 #include "third_party/blink/public/mojom/on_device_translation/translator.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/modules/ai/ai_interface_proxy.h"
 #include "third_party/blink/renderer/modules/ai/exception_helpers.h"
@@ -71,6 +73,17 @@
       MakeGarbageCollected<ScriptPromiseResolver<V8Availability>>(script_state);
   ScriptPromise<V8Availability> promise = resolver->Promise();
 
+  // Return `unavailable` for cross-origin iframe access with no permission
+  // policy.
+  if (auto* window = DynamicTo<LocalDOMWindow>(context)) {
+    if (window->IsCrossSiteSubframeIncludingScheme() &&
+        !window->IsFeatureEnabled(
+            network::mojom::PermissionsPolicyFeature::kTranslator)) {
+      resolver->Resolve(AvailabilityToV8(Availability::kUnavailable));
+      return promise;
+    }
+  }
+
   AIInterfaceProxy::GetTranslationManagerRemote(context)->TranslationAvailable(
       mojom::blink::TranslatorLanguageCode::New(options->sourceLanguage()),
       mojom::blink::TranslatorLanguageCode::New(options->targetLanguage()),
@@ -104,14 +117,27 @@
     return EmptyPromise();
   }
 
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolver<Translator>>(script_state);
+
+  // Block cross-origin iframe access with no permission policy.
+  if (auto* window = DynamicTo<LocalDOMWindow>(context)) {
+    if (window->GetFrame() &&
+        window->GetFrame()->IsCrossOriginToOutermostMainFrame() &&
+        !window->IsFeatureEnabled(
+            network::mojom::PermissionsPolicyFeature::kTranslator)) {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNotAllowedError,
+          kExceptionMessageCrossOriginAccess));
+      return EmptyPromise();
+    }
+  }
+
   AbortSignal* signal = options->getSignalOr(nullptr);
   if (HandleAbortSignal(signal, script_state, exception_state)) {
     return EmptyPromise();
   }
 
-  auto* resolver =
-      MakeGarbageCollected<ScriptPromiseResolver<Translator>>(script_state);
-
   CreateTranslatorClient* create_translator_client =
       MakeGarbageCollected<CreateTranslatorClient>(script_state, options,
                                                    resolver);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
index 355b683..7551be8 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.cc
@@ -899,8 +899,8 @@
   scrolling_contents_list->Finalize();
 
   gfx::Rect visual_rect = chunk_to_layer_mapper_.MapVisualRectFromState(
-      InfiniteIntRect(),
-      PropertyTreeState(scroll_translation,
+      scroll_translation.ScrollNode()->ContainerRect(),
+      PropertyTreeState(*scroll_container_transform,
                         *scroll_translation.ScrollNode()->OverflowClipNode(),
                         // The effect state doesn't matter.
                         chunk_to_layer_mapper_.LayerState().Effect()));
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
index e875c4c..71173a5 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer_test.cc
@@ -1274,6 +1274,63 @@
   }
 }
 
+// Test for https://crbug.com/413078309.
+TEST_P(PaintChunksToCcLayerTest, VeryTallScrollingContentsIntoDisplayItemList) {
+  // A value larger than InfiniteIntRect can represent.
+  const int kScrollOffset = 1 << 25;
+  auto scroll_state = CreateScrollTranslationState(
+      PropertyTreeState::Root(), 0, -kScrollOffset, gfx::Rect(0, 0, 100, 100),
+      gfx::Size(100, kScrollOffset + 100));
+  TestChunks chunks;
+  chunks.AddChunk(t0(), c0(), e0());
+  chunks.AddChunk(scroll_state);
+  chunks.AddChunk(t0(), c0(), e0());
+
+  auto cc_list = base::MakeRefCounted<cc::DisplayItemList>();
+  PaintChunksToCcLayer::ConvertInto(chunks.Build(), PropertyTreeState::Root(),
+                                    gfx::Vector2dF(), nullptr, *cc_list);
+
+  if (RuntimeEnabledFeatures::RasterInducingScrollEnabled()) {
+    EXPECT_THAT(
+        cc_list->paint_op_buffer(),
+        ElementsAre(PaintOpIs<cc::DrawRecordOp>(),  // chunk 0
+                    PaintOpIs<cc::SaveOp>(),
+                    PaintOpEq<cc::ClipRectOp>(
+                        SkRect::MakeXYWH(0, 0, 100, 100), SkClipOp::kIntersect,
+                        /*antialias=*/true),  // <overflow-clip>
+                    PaintOpIs<cc::DrawScrollingContentsOp>(),
+                    PaintOpIs<cc::RestoreOp>(),       // </overflow-clip>
+                    PaintOpIs<cc::DrawRecordOp>()));  // chunk 2
+    EXPECT_EQ(
+        gfx::Rect(0, 0, 100, 100),
+        cc_list->raster_inducing_scrolls()
+            .at(scroll_state.Transform().ScrollNode()->GetCompositorElementId())
+            .visual_rect);
+    const auto& scrolling_contents_op =
+        static_cast<const cc::DrawScrollingContentsOp&>(
+            cc_list->paint_op_buffer().GetOpAtForTesting(3));
+    ASSERT_EQ(cc::PaintOpType::kDrawScrollingContents,
+              scrolling_contents_op.GetType());
+    EXPECT_THAT(scrolling_contents_op.display_item_list->paint_op_buffer(),
+                ElementsAre(PaintOpIs<cc::DrawRecordOp>()));  // chunk 1
+  } else {
+    EXPECT_THAT(
+        cc_list->paint_op_buffer(),
+        ElementsAre(PaintOpIs<cc::DrawRecordOp>(),  // chunk 0
+                    PaintOpIs<cc::SaveOp>(),
+                    PaintOpEq<cc::ClipRectOp>(
+                        SkRect::MakeXYWH(0, 0, 100, 100), SkClipOp::kIntersect,
+                        /*antialias=*/true),  // <overflow-clip>
+                    PaintOpIs<cc::SaveOp>(),
+                    PaintOpEq<cc::TranslateOp>(
+                        0, -kScrollOffset),           // <scroll-translation>
+                    PaintOpIs<cc::DrawRecordOp>(),    // chunk 1
+                    PaintOpIs<cc::RestoreOp>(),       // </scroll-translation>
+                    PaintOpIs<cc::RestoreOp>(),       // </overflow-clip>
+                    PaintOpIs<cc::DrawRecordOp>()));  // chunk 2
+  }
+}
+
 TEST_P(PaintChunksToCcLayerTest, ScrollingContentsUnderEffect) {
   auto* t1 = Create2DTranslation(t0(), 10, 20);
   auto* e1 = CreateOpacityEffect(e0(), *t1, &c0(), 0.5);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 04a1c1c..0d5aab3 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -5160,7 +5160,7 @@
     },
     {
       name: "WebCodecsOrientation",
-      status: "stable",
+      status: "experimental",
     },
     {
       name: "WebCodecsVideoEncoderBuffers",
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/factory.py b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
index 3d2a2d1..204fb08 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/factory.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/factory.py
@@ -251,10 +251,6 @@
             help=
             ('CI only parameter. Use tests and tools from upstream WPT GitHub repo. '
              'Used to create wpt reports for uploading to wpt.fyi.'))
-        group.add_argument('--stable',
-                           action='store_true',
-                           help=('Run stable release of the target product. '
-                                 'Used together with --use-upstream-wpt'))
 
 
 def add_results_options_group(parser: argparse.ArgumentParser,
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index fa7e7eb..3531a19 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1503,8 +1503,6 @@
 # Popover flaky timeouts:
 crbug.com/40268721 [ Mac ] external/wpt/html/semantics/popovers/light-dismiss-event-ordering.html [ Pass Timeout ]
 crbug.com/332331836 external/wpt/html/semantics/popovers/popover-attribute-basic.html [ Pass Timeout ]
-crbug.com/408782594 [ Mac ] external/wpt/html/semantics/popovers/popover-focus-2.html [ Skip Timeout ]
-crbug.com/408782594 [ Mac ] virtual/html-anchor-attribute-disabled/external/wpt/html/semantics/popovers/popover-focus-2.html [ Skip Timeout ]
 
 # HTML `anchor` attribute disabled virtual suite - these tests should fail:
 crbug.com/335223786 virtual/html-anchor-attribute-disabled/external/wpt/html/semantics/popovers/popover-anchor-inset-rule-display.tentative.html [ Failure ]
@@ -2547,8 +2545,6 @@
 
 # ====== Test expectations added to unblock wpt-importer ======
 crbug.com/413633034 external/wpt/html/webappapis/scripting/event-loops/new-scroll-event-dispatched-at-next-updating-rendering-time.html [ Failure ]
-crbug.com/413595942 external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-requestclose.html [ Failure ]
-crbug.com/413595942 virtual/dialog-close-when-open-removed-disabled/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-requestclose.html [ Failure ]
 crbug.com/406036293 [ Mac ] external/wpt/css/css-overflow/keyboard-scroll.html [ Failure Pass ]
 crbug.com/626703 external/wpt/trusted-types/DOMWindowTimers-setTimeout-setInterval.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/editing/plaintext-only/insertLineBreak.html?white-space=pre [ Failure ]
@@ -2629,8 +2625,9 @@
 crbug.com/413411328 external/wpt/css/css-values/urls/referrer-policy/unsafe-url/url-image-referrerpolicy-same-origin.sub.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+[ Mac13 ] external/wpt/css/css-overflow/scroll-marker-contain-005.tentative.html [ Failure ]
+[ Mac14 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-allow-popups-to-same-origin.https.html?7-8 [ Crash ]
 external/wpt/service-workers/cache-storage/cache-add.https.any.* [ Failure Pass ]
-crbug.com/413085779 external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-requestclose-4-crash.html [ Crash ]
 crbug.com/412326639 [ Mac13 ] virtual/view-transition-wide-gamut/external/wpt/css/css-view-transitions/view-transition-types-mutable-no-document-element-crashtest.html [ Crash ]
 [ Mac12-arm64 ] wpt_internal/fedcm/fedcm-cache-manifest.https.html [ Crash Pass ]
 [ Win ] external/wpt/clear-site-data/clear-cache-partitioning.tentative.https.html [ Crash Failure Pass Timeout ]
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 297ae953..3c4cc0e 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
@@ -143423,6 +143423,19 @@
         {}
        ]
       ],
+      "line-names-014.html": [
+       "fe1d5d46633568287d502c09e0ae1d30c74d1658",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "orthogonal-writing-mode-001.html": [
        "1cf99a73e2ef7b0e3fe42bbf8c188fa4d2733979",
        [
@@ -378950,6 +378963,10 @@
       []
      ]
     },
+    "scoped-registry-initialize-expected.txt": [
+     "03a54dac7fe61d8b7798f7d4122969d3f23dd1ab",
+     []
+    ],
     "state": {
      "META.yml": [
       "df335b339e1324d8c36939df30c8506efa6aae0a",
@@ -390717,10 +390734,6 @@
         "b43598f2cd8f47bcd23373075773ef245c95c21a",
         []
        ],
-       "location_hash_set_empty_string-expected.txt": [
-        "4150dc964681a70da58fdc354359c652e3c66f6e",
-        []
-       ],
        "location_reload-iframe.html": [
         "f08cf5de3e8d6220a5ace3027d789f8ac968d9e6",
         []
@@ -404098,7 +404111,7 @@
         []
        ],
        "popover-utils.js": [
-        "6ab5a08898d53bbfec92be2664e68897600b035f",
+        "7878b125f2f6b1f6d39d1972f7387e8014b6dcf8",
         []
        ]
       }
@@ -405843,7 +405856,7 @@
        ],
        "resources": {
         "invoker-utils.js": [
-         "f731ab7c42c2ce36334d64385cd2d41cf0e8bde9",
+         "bc43e738c34e58f450874d8ccc99e110e07e7800",
          []
         ]
        }
@@ -414211,7 +414224,7 @@
      []
     ],
     "META.yml": [
-     "00a8c7b72a17b91a7438358ff7d311516544d36f",
+     "9e7ffe47ba90b4a60c96648861fb60f2e80e46b7",
      []
     ],
     "resources": {
@@ -432841,7 +432854,7 @@
        []
       ],
       "helpers.py": [
-       "0bd27a39acd5d8c2b6bba106bc24e17f4945a8d2",
+       "f4763deca141b4cf2bb592a740b7fbdd2a4d2869",
        []
       ],
       "html": {
@@ -433828,6 +433841,10 @@
      "5e68ee1fb9be8133501ee3f5338f4335bd4901de",
      []
     ],
+    "script-transform-sendKeyFrameRequest.https-expected.txt": [
+     "8f074d15a45b6450ef764bf44cc184704364d4ee",
+     []
+    ],
     "script-transform-sendKeyFrameRequest.js": [
      "361d7ce0235794b8a39201b5869a38f6720c80ca",
      []
@@ -465452,7 +465469,7 @@
       ]
      ],
      "detector.https.window.js": [
-      "7c86d011032a22c9479d2c746152cf299a69561a",
+      "e85ea6d249b17d3ed1505f206f5ac6659b6dfd94",
       [
        "ai/language_detection/detector.https.window.html",
        {
@@ -465468,6 +465485,10 @@
          [
           "script",
           "../resources/util.js"
+         ],
+         [
+          "script",
+          "../resources/locale-util.js"
          ]
         ]
        }
@@ -647404,7 +647425,7 @@
          ]
         ],
         "select-iterate-before-beginning.tentative.html": [
-         "5ccaa111499d55a1ebe8446e045bc672ff0943d7",
+         "0e8c16f8fdac6412dac0da80a09fe2be93122584",
          [
           null,
           {
@@ -648423,7 +648444,7 @@
         ]
        ],
        "dialog-requestclose.html": [
-        "61fc27183d7710393ac9b36223946e792bbd7d63",
+        "9a9c87167d5eaf93bd31c1f979450415dd9fba0d",
         [
          null,
          {
@@ -649202,9 +649223,23 @@
        ]
       ],
       "popover-focus-2.html": [
-       "cd03c6a8434f821183ebf64fc2ff2136f387f8b0",
+       "9cacd2b8757748c1da7ae3a31a3a55efb72c0245",
        [
-        null,
+        "html/semantics/popovers/popover-focus-2.html?test1",
+        {
+         "testdriver": true,
+         "timeout": "long"
+        }
+       ],
+       [
+        "html/semantics/popovers/popover-focus-2.html?test2",
+        {
+         "testdriver": true,
+         "timeout": "long"
+        }
+       ],
+       [
+        "html/semantics/popovers/popover-focus-2.html?test3",
         {
          "testdriver": true,
          "timeout": "long"
@@ -654345,9 +654380,16 @@
         ]
        ],
        "interesttarget-hide-delay.tentative.html": [
-        "9ec1c229c01aaca68ca6fb379f56b0b17a383c14",
+        "8828d879cac2b55e485b67511e41d64609a635e0",
         [
-         null,
+         "html/semantics/the-button-element/interest-target/interesttarget-hide-delay.tentative.html?method=focus",
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/semantics/the-button-element/interest-target/interesttarget-hide-delay.tentative.html?method=hover",
          {
           "testdriver": true,
           "timeout": "long"
@@ -654393,9 +654435,44 @@
         ]
        ],
        "interesttarget-show-delay.tentative.html": [
-        "ca47c0f237d1fb0e94589fc2a0dc694bf97dc863",
+        "8d7b78de7fc6ae38a25ee70c413939aad7932f8e",
         [
-         null,
+         "html/semantics/the-button-element/interest-target/interesttarget-show-delay.tentative.html?layout=nested&method=focus",
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/semantics/the-button-element/interest-target/interesttarget-show-delay.tentative.html?layout=nested&method=hover",
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/semantics/the-button-element/interest-target/interesttarget-show-delay.tentative.html?layout=nested-offset&method=focus",
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/semantics/the-button-element/interest-target/interesttarget-show-delay.tentative.html?layout=nested-offset&method=hover",
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/semantics/the-button-element/interest-target/interesttarget-show-delay.tentative.html?layout=plain&method=focus",
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ],
+        [
+         "html/semantics/the-button-element/interest-target/interesttarget-show-delay.tentative.html?layout=plain&method=hover",
          {
           "testdriver": true,
           "timeout": "long"
@@ -662058,7 +662135,7 @@
       ]
      ],
      "get_all_cookies.sub.https.html": [
-      "e94167133b3aa852a54f06b49e850e66e2873f65",
+      "70a4ff773333dbd764ce04e99da2f49d5c74f47b",
       [
        null,
        {
@@ -662076,7 +662153,7 @@
       ]
      ],
      "get_named_cookie.sub.https.html": [
-      "8e8f44338128b9f110c22861b067cd6af1ef2e09",
+      "32b3bed459e8a282481a7b3e293155691ee0ab6a",
       [
        null,
        {
@@ -701541,7 +701618,7 @@
       ]
      ],
      "scroll-timeline-invalidation.html": [
-      "a26500989ed0c64ca715f94f120b79ee19f45bd7",
+      "a0d33c28e088b90e74e3528d625892ebc9459c64",
       [
        null,
        {}
@@ -707522,6 +707599,16 @@
      ],
      "reading-flow": {
       "tentative": {
+       "carousel-grid-order.html": [
+        "89b3f8dd928f33fe73fa6b9bd987ad0402e7f255",
+        [
+         null,
+         {
+          "testdriver": true,
+          "timeout": "long"
+         }
+        ]
+       ],
        "flex-flow.html": [
         "a956ae476fae8febccf32054d50f6b550ac4bbd6",
         [
@@ -771337,7 +771424,7 @@
       ]
      ],
      "qdq_subgraph.https.any.js": [
-      "8e2d785c436d0245702b841ebd6f2609abff8e63",
+      "8f6f2246b04f8a6fc9570eced30b31b841da5a06",
       [
        "webnn/conformance_tests/qdq_subgraph.https.any.html?cpu",
        {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/line-names-014.html b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/line-names-014.html
new file mode 100644
index 0000000..fe1d5d4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/subgrid/line-names-014.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Test: Clamping a nested subgrid's grid-template-areas</title>
+<link rel="author" title="Kurt Catti-Schmidt" href="mailto:kschmi@microsoft.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#grid-template-areas-property">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+.grid {
+  background: red;
+  display: grid;
+  height: 100px;
+  width: 100px;
+  grid-template: 50px 50px / 50px 50px;
+}
+.subgrid {
+  display: grid;
+  grid-template-columns: subgrid;
+  grid-template-rows: 50px 50px;
+  grid-column: span 2;
+  grid-row: span 2;
+  grid-template-areas: "item item"
+                       "item item";
+}
+.item {
+  background: green;
+  grid-area: item;
+}
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div class="grid">
+  <div class="subgrid">
+    <div class="subgrid ">
+      <div class="item"></div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/dom/ranges/Range-compareBoundaryPoints-crash.html b/third_party/blink/web_tests/external/wpt/dom/ranges/Range-compareBoundaryPoints-crash.html
new file mode 100644
index 0000000..673eeb6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/dom/ranges/Range-compareBoundaryPoints-crash.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Range.compareBoundaryPoints() crash test</title>
+<link rel=help href="crbug.com/410996563">
+
+<table id="table">
+<thead>
+<tr id="row"></tr>
+
+<script>
+const selection = document.getSelection();
+const range = new Range();
+const cell = row.insertCell();
+range.setEndBefore(cell);
+selection.addRange(range);
+selection.removeRange(range);
+table.tHead = null;
+range.compareBoundaryPoints(Range.START_TO_END, range);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-requestclose.html b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-requestclose.html
index 61fc2718..9a9c8716 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-requestclose.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/interactive-elements/the-dialog-element/dialog-requestclose.html
@@ -10,7 +10,9 @@
 <dialog>Dialog</dialog>
 
 <script>
-const waitForTick = () =>  new Promise(resolve => step_timeout(resolve, 0));
+function waitForRender() {
+  return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+}
 
 const dialog = document.querySelector('dialog');
 function openDialog(openMethod) {
@@ -50,8 +52,8 @@
   if (closedby) {
     dialog.setAttribute('closedby',closedby);
   }
-  // Be sure any pending close events get fired.
-  await waitForTick();
+  // Be sure any pending close events (from prior test cleanups) get fired.
+  await waitForRender();
   return getSignal(t);
 }
 
@@ -77,7 +79,7 @@
       assert_false(dialog.open);
       assert_false(dialog.matches(':open'));
       assert_array_equals(events,['cancel'],'close is scheduled');
-      await waitForTick();
+      await waitForRender();
       assert_array_equals(events,['cancel','close']);
     },`requestClose fires both cancel and close ${testDescription}`);
 
@@ -182,7 +184,7 @@
   dialog.requestClose();
   assert_false(dialog.open);
   assert_array_equals(events,['cancel'],'close is scheduled');
-  await waitForTick();
+  await waitForRender();
   assert_array_equals(events,['cancel','close']);
 },`requestClose fires cancel and close when dialog is open via attribute`);
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2.html b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2.html
index cd03c6a..9cacd2b 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/popover-focus-2.html
@@ -11,6 +11,10 @@
 <script src="/resources/testdriver-vendor.js"></script>
 <script src="resources/popover-utils.js"></script>
 
+<meta name=variant content=?test1>
+<meta name=variant content=?test2>
+<meta name=variant content=?test3>
+
 <div id=fixup>
   <button id=button1 tabindex="0">Button1</button>
   <div popover id=popover0  tabindex="0" style="top:300px">
@@ -96,63 +100,75 @@
   button1.focus();
   await verifyFocusOrder([button1, button2, invoker0, invoker1, inside_popover1, invoker2, inside_popover3, invoker3, inside_popover2, button3, button4],'set 2');
 }
-promise_test(async t => {
-  invoker0.setAttribute('popovertarget', 'popover0');
-  invoker1.setAttribute('popovertarget', 'popover1');
-  invoker2.setAttribute('popovertarget', 'popover2');
-  invoker3.setAttribute('popovertarget', 'popover3');
-  t.add_cleanup(() => {
-    invoker0.removeAttribute('popovertarget');
-    invoker1.removeAttribute('popovertarget');
-    invoker2.removeAttribute('popovertarget');
-    invoker3.removeAttribute('popovertarget');
-  });
-  await testPopoverFocusNavigation();
-}, "Popover focus navigation with popovertarget invocation");
-promise_test(async t => {
-  invoker0.setAttribute('commandfor', 'popover0');
-  invoker1.setAttribute('commandfor', 'popover1');
-  invoker2.setAttribute('commandfor', 'popover2');
-  invoker3.setAttribute('commandfor', 'popover3');
-  invoker0.setAttribute('command', 'toggle-popover');
-  invoker1.setAttribute('command', 'toggle-popover');
-  invoker2.setAttribute('command', 'toggle-popover');
-  invoker3.setAttribute('command', 'toggle-popover');
-  t.add_cleanup(() => {
-    invoker0.removeAttribute('commandfor');
-    invoker1.removeAttribute('commandfor');
-    invoker2.removeAttribute('commandfor');
-    invoker3.removeAttribute('commandfor');
-    invoker0.removeAttribute('command');
-    invoker1.removeAttribute('command');
-    invoker2.removeAttribute('command');
-    invoker3.removeAttribute('command');
-  });
-  await testPopoverFocusNavigation();
-}, "Popover focus navigation with command/commandfor invocation");
-promise_test(async t => {
-  const invoker0Click = () => {
-    popover0.togglePopover({ source: invoker0 });
-  };
-  invoker0.addEventListener('click', invoker0Click);
-  const invoker1Click = () => {
-    popover1.togglePopover({ source: invoker1 });
-  };
-  invoker1.addEventListener('click', invoker1Click);
-  const invoker2Click = () => {
-    popover2.togglePopover({ source: invoker2 });
-  };
-  invoker2.addEventListener('click', invoker2Click);
-  const invoker3Click = () => {
-    popover3.togglePopover({ source: invoker3 });
-  };
-  invoker3.addEventListener('click', invoker3Click);
-  t.add_cleanup(() => {
-    invoker0.removeEventListener('click', invoker0Click);
-    invoker1.removeEventListener('click', invoker1Click);
-    invoker2.removeEventListener('click', invoker2Click);
-    invoker3.removeEventListener('click', invoker3Click);
-  });
-  await testPopoverFocusNavigation()
-}, "Popover focus navigation with imperative invocation");
+
+// This test is very slow. Variants are used to split it into pieces.
+switch (window.location.search.substring(1)) {
+  case 'test1':
+    promise_test(async t => {
+      invoker0.setAttribute('popovertarget', 'popover0');
+      invoker1.setAttribute('popovertarget', 'popover1');
+      invoker2.setAttribute('popovertarget', 'popover2');
+      invoker3.setAttribute('popovertarget', 'popover3');
+      t.add_cleanup(() => {
+        invoker0.removeAttribute('popovertarget');
+        invoker1.removeAttribute('popovertarget');
+        invoker2.removeAttribute('popovertarget');
+        invoker3.removeAttribute('popovertarget');
+      });
+      await testPopoverFocusNavigation();
+    }, "Popover focus navigation with popovertarget invocation");
+    break;
+  case 'test2':
+    promise_test(async t => {
+      invoker0.setAttribute('commandfor', 'popover0');
+      invoker1.setAttribute('commandfor', 'popover1');
+      invoker2.setAttribute('commandfor', 'popover2');
+      invoker3.setAttribute('commandfor', 'popover3');
+      invoker0.setAttribute('command', 'toggle-popover');
+      invoker1.setAttribute('command', 'toggle-popover');
+      invoker2.setAttribute('command', 'toggle-popover');
+      invoker3.setAttribute('command', 'toggle-popover');
+      t.add_cleanup(() => {
+        invoker0.removeAttribute('commandfor');
+        invoker1.removeAttribute('commandfor');
+        invoker2.removeAttribute('commandfor');
+        invoker3.removeAttribute('commandfor');
+        invoker0.removeAttribute('command');
+        invoker1.removeAttribute('command');
+        invoker2.removeAttribute('command');
+        invoker3.removeAttribute('command');
+      });
+      await testPopoverFocusNavigation();
+    }, "Popover focus navigation with command/commandfor invocation");
+    break;
+  case 'test3':
+    promise_test(async t => {
+      const invoker0Click = () => {
+        popover0.togglePopover({ source: invoker0 });
+      };
+      invoker0.addEventListener('click', invoker0Click);
+      const invoker1Click = () => {
+        popover1.togglePopover({ source: invoker1 });
+      };
+      invoker1.addEventListener('click', invoker1Click);
+      const invoker2Click = () => {
+        popover2.togglePopover({ source: invoker2 });
+      };
+      invoker2.addEventListener('click', invoker2Click);
+      const invoker3Click = () => {
+        popover3.togglePopover({ source: invoker3 });
+      };
+      invoker3.addEventListener('click', invoker3Click);
+      t.add_cleanup(() => {
+        invoker0.removeEventListener('click', invoker0Click);
+        invoker1.removeEventListener('click', invoker1Click);
+        invoker2.removeEventListener('click', invoker2Click);
+        invoker3.removeEventListener('click', invoker3Click);
+      });
+      await testPopoverFocusNavigation()
+    }, "Popover focus navigation with imperative invocation");
+    break;
+  default:
+    assert_unreached('Invalid variant');
+}
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js
index 6ab5a0889..7878b12 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/popovers/resources/popover-utils.js
@@ -2,10 +2,6 @@
   return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
 }
 
-function waitForTick() {
-  return new Promise(resolve => step_timeout(resolve, 0));
-}
-
 async function clickOn(element) {
   await waitForRender();
   let rect = element.getBoundingClientRect();
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-timeline-invalidation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-timeline-invalidation.html
index a265009..a0d33c28 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-timeline-invalidation.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/scroll-timeline-invalidation.html
@@ -123,6 +123,15 @@
   // makes the scroll timeline stale.
   // https://github.com/w3c/csswg-drafts/issues/8694
 
+  // runAndWaitForFrameUpdate will yield after the requestAnimationFrame callbacks
+  // have been serviced, which is prior to when stale timelines are updated.
+  // https://github.com/w3c/csswg-drafts/issues/12120
+  assert_approx_equals(timeline.currentTime.value, 60, 0.1);
+
+  // Now wait another beat such that the rest of the HTML Processing Model event loop
+  // has run and we can check whether stale timelines have been updated.
+  await new Promise(setTimeout);
+
   // With a single layout, timeline current time would be at 60%, but the
   // timeline would be stale.
   const expected_progress = 60 * maxScroll / (maxScroll + 1000);
diff --git a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/helpers.py b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/helpers.py
index 0bd27a3..f4763de 100644
--- a/third_party/blink/web_tests/external/wpt/webdriver/tests/support/helpers.py
+++ b/third_party/blink/web_tests/external/wpt/webdriver/tests/support/helpers.py
@@ -80,6 +80,10 @@
 
         session.window_handle = current_window
 
+    # Do not try to clean up already ended session.
+    if session.session_id is None:
+        return
+
     _restore_timeouts(session)
     _ensure_valid_window(session)
     _dismiss_user_prompts(session)
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt
index f418295..b55f86a3 100644
--- a/third_party/blink/web_tests/flag-specific/highdpi/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/highdpi/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt
@@ -3,7 +3,7 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAIAAAAl7d1hAAAAgElEQVR4nOzcsQ0AIQwEQSNROJ3/J1TB7HSwkp3envkGs8eDN581bzv3i7ttQ82Gmg01G2o21Gyo2VCzoWZDzYaaDTUbajbUbKjZULOhZkPNhpoNNRtqNtRsqNlQs6FmQ82Gmg01G2o21Gyo2VCzoWZDzYaaDTUbajasdqUIYvMPAAD//++QO6kAAAAGSURBVAMAzHMFQNflWQwAAAAASUVORK5CYII=
+        data : iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAA2ElEQVR4nOzdsQnAMBAEwTe4cHUuh8aZQRvOlLAcn/49s/dw5B6OiRj4RlzX8NN6r6AlBkQMiBgQMSBiQMSAiAERAyIGRAyIGBAxIGJAxICIAREDIgZEDIgYEDEgYkDEgIgBEQMiBkQMiBgQMSBiQMSAiAERAyIGRAyIGBAxIGJAxICIAREDIgZEDIgYEDEgYkDEgIgBEQMiBkQMiBgQMSBiQMSAiAERAyIGRAyIGBAxIGJAxICIAREDIgZEDIgYEDEgYkDEgIgBEQMiBkQMXL5gnLPEgIiBBwAA////soa8AAAABklEQVQDAOqUBz57mlFyAAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
@@ -11,7 +11,7 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAIAAAAnApehAAAAWElEQVR4nOzYwQnAQAgF0R+wr7WTlJJNZ1tKOjGR9KCwzCB4fHjVpFBH41yW22/5VGUzDzb1hY2NjY2NjY2NjY2NjY2Njb2t/QytS+X9tueUd0T0/Ne+XgAAAP//nKfQRgAAAAZJREFUAwAwAA2a4z2X2AAAAABJRU5ErkJggg==
+        data : iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAYAAACoYAD2AAAAcklEQVR4nOzZywmAUAwF0Su8vrQTS1E7sxQ7eX5LmEWEORDIcsg2Lek9hY3znvZs03bNmnLW934tP2AkxUiKkRQjKUZSjKQYSTGSYiTFSIqRFCMpRlKMpBhJMZJiJOVHkceY7Euq+iKnd4oaeq/9x7mdAAAA//9hhBnLAAAABklEQVQDAOkED5hjUMq5AAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
@@ -19,7 +19,7 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAAKIAAACiCAIAAABNkIFMAAABl0lEQVR4nOzXsQ2AMBAEwUeicHduckQDeGdaWF1w98weTncPATInyJwgc4LMCV+Z1zX82nq/J2tOkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROkDlB5gSZE2ROuGb2cDprTpA5QeYEmRNkTngAAAD//5yzIuMAAAAGSURBVAMA4P4GhGPIlDMAAAAASUVORK5CYII=
+        data : iVBORw0KGgoAAAANSUhEUgAAAKIAAACiCAYAAADC8hYbAAAB1klEQVR4nOzYsQ2AMBAEwUeicHduGoAMyRvM1LC64O6ZvQcOuwcChEiCEEkQIglCJOE7xHUN/G69nzQWkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJECIJQiRBiCQIkQQhkiBEEoRIghBJuGb2HjjMIpIgRBKESIIQSRAiCQ8AAAD//xk/Ji8AAAAGSURBVAMAmeIIghcjqYoAAAAASUVORK5CYII=
     }
     sessionId : <string>
 }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt
index f31d4382..312ad7e 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt
@@ -15,21 +15,21 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAADUAAAA1CAIAAABuhDQnAAAAYklEQVR4nOzZsQ2AMBAEwUdy4e4cREAFmxhppoLVf3hr5p6DrTnb17evOc1+H/uX+51KX6Ov0dfoa/Q1+hp9jb5GX6Ov0dfoa/Q1+hp9jb5GX6Ov0dfoa/Q1+hp9zWXfTx4AAAD///+1WosAAAAGSURBVAMAKBoE0CDq3ckAAAAASUVORK5CYII=
+        data : iVBORw0KGgoAAAANSUhEUgAAADUAAAA1CAYAAADh5qNwAAAAZ0lEQVR4nOzasQ3AMAzEQAXw4N7cKVJkhjN4GxBS+WvmnLnMmgv9UfsZ3v6e7vJLXaQoRVGKohRFKYpSFKUoSlGUoihFUYqiFEUpilIUpShKUZSiKEVRiqIURSmKUhSlKErxNDdFvAAAAP//IIowVgAAAAZJREFUAwCNQgbO8MkUFwAAAABJRU5ErkJggg==
     }
     sessionId : <string>
 }
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAAJ8AAACfCAIAAABoeZy3AAABj0lEQVR4nOzXsQnAMBAEwReocHUu58YNeJlpYbng9swdovbQpW6ZumXqlqlb9lX3rOGPzvv+2G6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumVr5g5Rtlumbpm6ZeqWqVv2AAAA///UlaKIAAAABklEQVQDAOYABnhN8isCAAAAAElFTkSuQmCC
+        data : iVBORw0KGgoAAAANSUhEUgAAAJ8AAACfCAYAAADnGwvgAAABuUlEQVR4nOzYsQnAMBAEwReocHUuN2Bnhk1malguuD1z70BgD0TER0Z8ZMRHRnxkvuM7a+AX5/1QsXxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdmzdw7ELB8ZMRHRnxkxEdGfGQeAAAA///OGXOxAAAABklEQVQDAAg5CHYp08OfAAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAIAAAACtmMCAAAATElEQVR4nOzVMRHAIBQE0WXmC4iDSIEyZRwgJXEGUnACw2DhytvmutdewERXrj32/glJ/YMSqLNo0aJFixYtcn5W0SjcpFwbut7nWgAAAP//yGBfmAAAAAZJREFUAwC+DwlKB1gGPQAAAABJRU5ErkJggg==
+        data : iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAAV0lEQVR4nOzWMRHAIBBE0c8MAuIgUqBMGQdISZyBFJwAwyABrtpf3Fz32vXQGgaFVPDz+x1HK984cWFGCRMmTJgwYcKECduPzV13sBrhBhdSNlnE73PRAQAA//+UEVD7AAAABklEQVQDALJJC0jloUtZAAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt
index bbf630c1..56ea1ac 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/page/page-captureScreenshot-clip-expected.txt
@@ -3,7 +3,7 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAADYAAAA2CAIAAAADJ/2KAAAAZ0lEQVR4nOzasQ2AMBAEwUeicHcOIqCDDWxppoLVf3r3zDN7u2d7f+K6ZkPr+/BBV9yYxILEgsSCxILEgsSCxILEgsSCxILEgsSCxILEgsSCxILEgsSCxILEgsSCxILEwgGJl21E4AUAAP//kUIJagAAAAZJREFUAwCwXQTUKqZL3gAAAABJRU5ErkJggg==
+        data : iVBORw0KGgoAAAANSUhEUgAAADYAAAA2CAYAAACMRWrdAAAAaUlEQVR4nOzasQ2AAAzEwCAxOJtDQcEMDncbWEn558x9z0LnLPWFXcescL0P+IOLLSOsRliNsBphNcJqhNUIqxFWI6xGWI2wGmE1wmqE1QirEVYjrEZYjbAaYTXCaoTVCKtZG3bYBMc8AAAA//8E9uQ1AAAABklEQVQDAEpxBtIpf8UuAAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
@@ -11,7 +11,7 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAIAAAACtmMCAAAAMUlEQVR4nOzWoQ0AQAzDwFTK4N28r9/B0AaBh9PkgtY/O6Haa+gUFRUVFRUV6Qb/FA8AAP///Wd+CQAAAAZJREFUAwADbgRowZsOxAAAAABJRU5ErkJggg==
+        data : iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAAOElEQVR4nOzWsQkAMAwDQRk8uDdXIGSFCAz/hdpr1ZKtUH13St8bPywUGBgYGBgYGBjYWqySj/gAAAD//zUzfJwAAAAGSURBVAMAxFsGZt0vsIUAAAAASUVORK5CYII=
     }
     sessionId : <string>
 }
@@ -19,7 +19,7 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAAGwAAABsCAIAAAAABMCaAAABEElEQVR4nOzVsQnAMBAEwTe4cHUuh8aZQRvOlLAc3D2zhzP3cEzEgIiBb8R1DT+t90ssMSBiQMSAiAERAyIGRAyIGBAxIGJAxICIAREDIgZEDIgYEDEgYkDEgIgBEQMiBkQMiBgQMSBiQMSAiAERAyIGRAyIGBAxIGJAxICIAREDIgZEDIgYEDEgYkDEgIgBEQMiBkQMiBgQMSBiQMSAiAERAyIGRAyIGBAxIGJAxICIAREDIgZEDIgYEDEgYkDEgIgBEQMiBkQMiBgQMSBiQMSAiAERAyIGRAyIGBAxIGJAxICIAREDIgZEDIgYEDEgYkDEgIgBEQMiBkQMiBgQMSBiQMTANbOHM5YYEDEgYuABAAD//+3dugcAAAAGSURBVAMAKEEFrACrEM4AAAAASUVORK5CYII=
+        data : iVBORw0KGgoAAAANSUhEUgAAAGwAAABsCAYAAACPZlfNAAABHUlEQVR4nOzWwQnAMBADwTO4cHfuvEMayMJMC4tAe+beIWMPKYLFCBbzDXbW8CPnfTEsLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFiBIsRLEawGMFi1sy9Q4aFxQgWI1jMAwAA//8RVr84AAAABklEQVQDABBTB6rxw3ApAAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
diff --git a/third_party/blink/web_tests/inspector-protocol/emulation/set-force-dark-mode-expected.txt b/third_party/blink/web_tests/inspector-protocol/emulation/set-force-dark-mode-expected.txt
index d52f218..781b2928 100644
--- a/third_party/blink/web_tests/inspector-protocol/emulation/set-force-dark-mode-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/emulation/set-force-dark-mode-expected.txt
@@ -1,18 +1,18 @@
 Tests that forced dark mode can be emulated.
 <p>Baseline:
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAL0lEQVR4nOzNMQ0AAAgDMA78Wx4ylpDWQDfJNOyUiMVisVgsFovFYrFYLBaLP8YHAAD//ynDvqQAAAAGSURBVAMAnigDnuE5ByEAAAAASUVORK5CYII=">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAQklEQVR4nOzOMQEAIAzAMI75twx46LMjUZC531lsznKClWAlWAlWgpVgJVgJVoKVYCVYCVaClWAlWAlWgpVgJVg9AAAA//9te1frAAAABklEQVQDAF+2BJ0ODdzFAAAAAElFTkSuQmCC">
 <p>Emulating auto dark mode: true
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAMElEQVR4nOzNMQ0AAAgDMA4UzL9YZCwhrYFukmnYKRGLxWKxWCwWi8VisVgsFn+MDwAA///nlV6rAAAABklEQVQDAB5SANdxK/ZrAAAAAElFTkSuQmCC">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAARElEQVR4nOzOMQEAIAzAMA4UzL9XENFnR6Igd2beWeye5QQrwUqwEqwEK8FKsBKsBCvBSrASrAQrwUqwEqwEK8FKsPoAAAD//0z3oiIAAAAGSURBVAMAgxIB1iCHQwUAAAAASUVORK5CYII=">
 <p>Emulating auto dark mode: true
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAMElEQVR4nOzNMQ0AAAgDMA4UzL9YZCwhrYFukmnYKRGLxWKxWCwWi8VisVgsFn+MDwAA///nlV6rAAAABklEQVQDAB5SANdxK/ZrAAAAAElFTkSuQmCC">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAARElEQVR4nOzOMQEAIAzAMA4UzL9XENFnR6Igd2beWeye5QQrwUqwEqwEK8FKsBKsBCvBSrASrAQrwUqwEqwEK8FKsPoAAAD//0z3oiIAAAAGSURBVAMAgxIB1iCHQwUAAAAASUVORK5CYII=">
 <p>Emulating auto dark mode: false
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAL0lEQVR4nOzNMQ0AAAgDMA78Wx4ylpDWQDfJNOyUiMVisVgsFovFYrFYLBaLP8YHAAD//ynDvqQAAAAGSURBVAMAnigDnuE5ByEAAAAASUVORK5CYII=">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAQklEQVR4nOzOMQEAIAzAMI75twx46LMjUZC531lsznKClWAlWAlWgpVgJVgJVoKVYCVYCVaClWAlWAlWgpVgJVg9AAAA//9te1frAAAABklEQVQDAF+2BJ0ODdzFAAAAAElFTkSuQmCC">
 <p>Emulating auto dark mode: true
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAMElEQVR4nOzNMQ0AAAgDMA4UzL9YZCwhrYFukmnYKRGLxWKxWCwWi8VisVgsFn+MDwAA///nlV6rAAAABklEQVQDAB5SANdxK/ZrAAAAAElFTkSuQmCC">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAARElEQVR4nOzOMQEAIAzAMA4UzL9XENFnR6Igd2beWeye5QQrwUqwEqwEK8FKsBKsBCvBSrASrAQrwUqwEqwEK8FKsPoAAAD//0z3oiIAAAAGSURBVAMAgxIB1iCHQwUAAAAASUVORK5CYII=">
 <p>Emulating auto dark mode: undefined
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAL0lEQVR4nOzNMQ0AAAgDMA78Wx4ylpDWQDfJNOyUiMVisVgsFovFYrFYLBaLP8YHAAD//ynDvqQAAAAGSURBVAMAnigDnuE5ByEAAAAASUVORK5CYII=">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAQklEQVR4nOzOMQEAIAzAMI75twx46LMjUZC531lsznKClWAlWAlWgpVgJVgJVoKVYCVYCVaClWAlWAlWgpVgJVg9AAAA//9te1frAAAABklEQVQDAF+2BJ0ODdzFAAAAAElFTkSuQmCC">
 <p>Emulating auto dark mode: true
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAMElEQVR4nOzNMQ0AAAgDMA4UzL9YZCwhrYFukmnYKRGLxWKxWCwWi8VisVgsFn+MDwAA///nlV6rAAAABklEQVQDAB5SANdxK/ZrAAAAAElFTkSuQmCC">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAARElEQVR4nOzOMQEAIAzAMA4UzL9XENFnR6Igd2beWeye5QQrwUqwEqwEK8FKsBKsBCvBSrASrAQrwUqwEqwEK8FKsPoAAAD//0z3oiIAAAAGSURBVAMAgxIB1iCHQwUAAAAASUVORK5CYII=">
 <p>Navigating&mldr;
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAMElEQVR4nOzNMQ0AAAgDMA4UzL9YZCwhrYFukmnYKRGLxWKxWCwWi8VisVgsFn+MDwAA///nlV6rAAAABklEQVQDAB5SANdxK/ZrAAAAAElFTkSuQmCC">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAARElEQVR4nOzOMQEAIAzAMA4UzL9XENFnR6Igd2beWeye5QQrwUqwEqwEK8FKsBKsBCvBSrASrAQrwUqwEqwEK8FKsPoAAAD//0z3oiIAAAAGSURBVAMAgxIB1iCHQwUAAAAASUVORK5CYII=">
 
diff --git a/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency-expected.txt b/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency-expected.txt
index 4c9ee3c..219c977 100644
--- a/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/emulation/set-vision-deficiency-expected.txt
@@ -1,24 +1,24 @@
 Tests that vision deficiencies can be emulated.
 <p>Emulating "none":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAPUlEQVR4nOzWsQ0AIAwDQSMxeDaHHYIEBfcLXOHGM5V+tdJt5lFgMBgMBoPB9xtJ/zedZGMwGAwGg8EfwRsAAP//X6OzJQAAAAZJREFUAwDNzgOce+BqUwAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAU0lEQVR4nOzXsQ0AMAjEwI/E4GxOhjAFhW8AZInqK53Jpt49VznOQMpAykDKQMpAykDKQMpAykDqJbM7Ipb5YspAykDKQMpAykDKQMpAykDqfOAHAAD//31Z3wAAAAAGSURBVAMA1WcFmofdcX8AAAAASUVORK5CYII=">
 <p>Emulating "achromatopsia":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAPUlEQVR4nOzWMREAMAjAQAbsohkr9QB3ZfkY+DVZVTGtu2NaxlFgMBgMBoPBB/Dmm1ZwHAUGg8FgMBj8vwcAAP//GS9z6QAAAAZJREFUAwDANwcieyqhXgAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAU0lEQVR4nOzXMREAMAzDwAyhG8yB0oKQhwx6BLrz5J6ZV0G7W0ldxxlIGUgZSBlIGUgZSBlIGUgZSHX6Q6Q5MWUgZSBlIGUgZSBlIGUgZSB1PvADAAD//9rPqY4AAAAGSURBVAMAHLMIIe8FWVYAAAAASUVORK5CYII=">
 <p>Emulating "blurredVision":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAACDUlEQVR4nOyW3W7aQBCFv/UuNg6QlAg1PEVfus/Vy0q5ihIBBpv1bmfWtE2lYMVuVVWVj0aIH68+ztiaOY5Pn7n9yvyJvMJ4sghSb2pBLOAO/5H2gdOW/YbdHceSZwcZB8O75ZS6/sLykXyPbTBR620tiXPiFn+kMcqzC8INoaCMnBgkR/nE6pGVmH4hE3C47nhFvNVf/Yxmja3wNaeWKiBWB7jtwLOK2Z7ihfmzgrMecEuMxCXnCjGYN7gWF5gxQo7M4xptslBt3QvOibVWkCMeG7AxGY1D7Saw9LarLJW+vwYOxKCms0QyY3ivwBfFS5mepzr+Wrx6Mx78tzWBJ/B/DF7pRJTZRM/kKtMFVhfRnwDfw4bo0xyudTb1jUzZTjNixzaMWA4/wfGOsE3U8oK/CraJutIrQ06whM76GLajla1+pC1o9zr9+4JAlryW+HvahR7p2H4c2D9QG+q1bjrr0+27BjZEqRy/oN7QLGnmNG6sY0kwkiWyinNaxlnv0A9GG+4LzksOH/RgLX894ziY7TQ3SYKRLCFb3fbknk5GeysdFq9CPdxwytlb/X43jO00rXGjCcaFy1bvB4uE3Toqp1R5PQ6mduCSpqBIVPeO5do9StLeRsqmJmcjbrNh4TUjmsunATqa7x0e83CZNKrS4Bqk3Y/jI2VGZ5ff1LQWJ/AEnsATeAL/e+BvAAAA///FYy7QAAAABklEQVQDALlv5bhzINp0AAAAAElFTkSuQmCC">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAACX0lEQVR4nOyX3Y7TMBSEx7H7k+1mYRGCfQpemufiEokrtKg/aVLHZk7sbHcR2obaSHvhkY7Sprb1ZZzmTAy+fPW4+w6sfwLLA6AsUHkAUq9pwyErHt8B9hMwfAaOD8DuI7DlubYGHg1/r4C9wrUyI9z9N+D2BwF3gO4J6UO9qlsCrlmEsi3QqwClCe5uWISvucYRSTKo6VxDuEZc/MULFkCHyw42HHIXxtkFAe8Jxx2wHaEG4MA1xLjrzYuACy66oHMrwq0fA2A1B5AQnmM8nTxxDbFqybmG5w3nL5BFBhXvOdOHrRU43c0EXHJIF8rJGizNedpH13yyewFQtnOqKtb4/RIgx3gXXKwijMoD9RLwSf5cas6/2P+l8MfnrIBvUwUwVQUwVQUwVc8A2VulfUmHwJxOUsexGmNi+U8i4AceGJG8jb21Cx1iVquTNMOm6ydIhSwJ4QWgZ3ZzDxGuPoNeBNQRrgnzHIEdz7nJzTyQBoOETea5gflt2IXGPyuwVtE5wlnuwrAJa0yQNhegZRLuuFjHPCeRSdt4S10C5BwvRecs4TreJj2jV89t7w3yOSgxXZJwxUx3ilmwmtnsnUDQMUvnToTbvw9rdQS0vMo2HdKM7xAS0yUJS9jUc+L+JBW2U7ZVnBO4PeP+ka7udPh9mwZpxhcc3ISYbtw5bM4FFAnkQNcOJsDJsU2Hi4C86p5btIpw5h+z3PRnkC3tpXTc2umxkyaFjfXj25d6OnO92vgM3OZ7FspNFCxrkKbtsyUzSsWn8ptVSTOpKoCpKoCpKoCpKoCpKoCpKoCp+g0AAP//eBKHygAAAAZJREFUAwAaE+m01Bb+jAAAAABJRU5ErkJggg==">
 <p>Emulating "reducedContrast":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAQElEQVR4nOzWMQ0AIAwAwQ5Iw3WtkGAFD21Cl3sDt/7Kk1Hu7qi2YigwGAwGg8HgCbjxTT14KDAYDAaDweD/PQAAAP//ye5FDQAAAAZJREFUAwDy7wU5GsFekgAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAVklEQVR4nOzXMQ0AMAwDwQyBVtalUqlQWhDvIcMfgpc8uffZr5LuqqSu4QykDKQMpAykDKQMpAykDKQMpDr9IdKcmDKQMpAykDKQMpAykDKQMpAaH/gBAAD//4rM2ukAAAAGSURBVAMAGYoGOAmJ8jUAAAAASUVORK5CYII=">
 <p>Emulating "none":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAPUlEQVR4nOzWsQ0AIAwDQSMxeDaHHYIEBfcLXOHGM5V+tdJt5lFgMBgMBoPB9xtJ/zedZGMwGAwGg8EfwRsAAP//X6OzJQAAAAZJREFUAwDNzgOce+BqUwAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAU0lEQVR4nOzXsQ0AMAjEwI/E4GxOhjAFhW8AZInqK53Jpt49VznOQMpAykDKQMpAykDKQMpAykDqJbM7Ipb5YspAykDKQMpAykDKQMpAykDqfOAHAAD//31Z3wAAAAAGSURBVAMA1WcFmofdcX8AAAAASUVORK5CYII=">
 <p>Emulating "deuteranopia":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAR0lEQVR4nOzRMRHAIAAEwc8MImImhmIgGqIPC8igwAMFFOwZ2OLK996Z7a8ts5VsCgwGg8FgMHh9V56eHXkMBoPBYDD4IHgAAAD//yoXYpwAAAAGSURBVAMA9P8FEGE+OlIAAAAASUVORK5CYII=">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAYElEQVR4nOzXQQ0AIRAEwb0EEWcGQxhAA/qwgAwSENH74NGloJN5TentP5FozBWZSjzOQMpAykDKQMpAykDKQMpAykDqi7pTP0k2J6YMpAykDKQMpAykDKQMpAykng+8AAAA//8KbhpPAAAABklEQVQDAE3LBw7qHynvAAAAAElFTkSuQmCC">
 <p>Emulating "none":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAPUlEQVR4nOzWsQ0AIAwDQSMxeDaHHYIEBfcLXOHGM5V+tdJt5lFgMBgMBoPB9xtJ/zedZGMwGAwGg8EfwRsAAP//X6OzJQAAAAZJREFUAwDNzgOce+BqUwAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAU0lEQVR4nOzXsQ0AMAjEwI/E4GxOhjAFhW8AZInqK53Jpt49VznOQMpAykDKQMpAykDKQMpAykDqJbM7Ipb5YspAykDKQMpAykDKQMpAykDqfOAHAAD//31Z3wAAAAAGSURBVAMA1WcFmofdcX8AAAAASUVORK5CYII=">
 <p>Emulating "protanopia":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAARUlEQVR4nOzWMQ0AIRBE0b0Ek2eDngqxyAAPSwIF7xt4xTRTeo10bczIVuJSYDAYDAaDwef74s//pp1sDAaDwWAw+CF4AQAA///VwnwEAAAABklEQVQDAFupBU6Q1B3wAAAAAElFTkSuQmCC">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAW0lEQVR4nOzXsQ0AIAzEwEdiSdagp2JYxoAhnILCN0BkKdX3PXNTaJ3Sc+n5nIGUgZSBlIGUgZSBlIGUgZSBVMu4tSOimC+mDKQMpAykDKQMpAykDKQMpL4PfAAAAP//zu899wAAAAZJREFUAwDUWQdMTBLL4wAAAABJRU5ErkJggg==">
 <p>Emulating "tritanopia":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAQElEQVR4nOzWMREAIAzAwA5VgzYk4BkLeGjv6PIx8Gsyzo5yd0W1jKHAYDAYDAaDJ+DGN/XgocBgMBgMBoP/9wAAAP//dyaAOgAAAAZJREFUAwAezgO76gvJTwAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAVklEQVR4nOzXMREAMAzDwAxBU2yFUM6F0IKQhwx6BLrz5K6zXyXdVUldwxlIGUgZSBlIGUgZSBlIGUgZSHX6Q6Q5MWUgZSBlIGUgZSBlIGUgZSA1PvADAAD//zEpkoQAAAAGSURBVAMAEVsEunH2BVMAAAAASUVORK5CYII=">
 <p>Emulating "tritanopia":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAQElEQVR4nOzWMREAIAzAwA5VgzYk4BkLeGjv6PIx8Gsyzo5yd0W1jKHAYDAYDAaDJ+DGN/XgocBgMBgMBoP/9wAAAP//dyaAOgAAAAZJREFUAwAezgO76gvJTwAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAVklEQVR4nOzXMREAMAzDwAxBU2yFUM6F0IKQhwx6BLrz5K6zXyXdVUldwxlIGUgZSBlIGUgZSBlIGUgZSHX6Q6Q5MWUgZSBlIGUgZSBlIGUgZSA1PvADAAD//zEpkoQAAAAGSURBVAMAEVsEunH2BVMAAAAASUVORK5CYII=">
 <p>Emulating "some-invalid-deficiency":
 {
   "code": -32602,
@@ -50,7 +50,7 @@
   "message": "Unknown vision deficiency type"
 }
 <p>Navigating&mldr;
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAQElEQVR4nOzWMREAIAzAwA5VgzYk4BkLeGjv6PIx8Gsyzo5yd0W1jKHAYDAYDAaDJ+DGN/XgocBgMBgMBoP/9wAAAP//dyaAOgAAAAZJREFUAwAezgO76gvJTwAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAVklEQVR4nOzXMREAMAzDwAxBU2yFUM6F0IKQhwx6BLrz5K6zXyXdVUldwxlIGUgZSBlIGUgZSBlIGUgZSHX6Q6Q5MWUgZSBlIGUgZSBlIGUgZSA1PvADAAD//zEpkoQAAAAGSURBVAMAEVsEunH2BVMAAAAASUVORK5CYII=">
 <p>Emulating "achromatopsia":
-<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAIAAAADnC86AAAAPUlEQVR4nOzWMREAMAjAQAbsohkr9QB3ZfkY+DVZVTGtu2NaxlFgMBgMBoPBB/Dmm1ZwHAUGg8FgMBj8vwcAAP//GS9z6QAAAAZJREFUAwDANwcieyqhXgAAAABJRU5ErkJggg==">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAU0lEQVR4nOzXMREAMAzDwAyhG8yB0oKQhwx6BLrz5J6ZV0G7W0ldxxlIGUgZSBlIGUgZSBlIGUgZSHX6Q6Q5MWUgZSBlIGUgZSBlIGUgZSB1PvADAAD//9rPqY4AAAAGSURBVAMAHLMIIe8FWVYAAAAASUVORK5CYII=">
 
diff --git a/third_party/blink/web_tests/inspector-protocol/overlay/overlay-with-emulation-scale-expected.txt b/third_party/blink/web_tests/inspector-protocol/overlay/overlay-with-emulation-scale-expected.txt
index c905a9e..f57b194 100644
--- a/third_party/blink/web_tests/inspector-protocol/overlay/overlay-with-emulation-scale-expected.txt
+++ b/third_party/blink/web_tests/inspector-protocol/overlay/overlay-with-emulation-scale-expected.txt
@@ -1,4 +1,4 @@
 Verifies that overlay is correctly rendered with emulation scale > 1.
 The test passes if the image URL below is 300x600 image containing a 270x420 brown rectangle, without any green or red.
-data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAJYCAIAAABXRExoAAAGoUlEQVR4nOzaMQ2AUBQEwU+CMZQRlCHtIQFCs82Mh801t8/MAjr7AlIihJgIISZCiIkQYiKEmAghJkKIiRBiIoSYCCH2KcLr2Bbw5rz/PLEtIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJsm5kFdCwhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQkyEEBMhxEQIMRFCTIQQEyHERAgxEUJMhBATIcRECDERQuwBAAD//1vzkscAAAAGSURBVAMAPdISWCdqA1kAAAAASUVORK5CYII=
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAJYCAYAAADYJts/AAAHzUlEQVR4nOzbwQmAMAAEwQg2ZmViZZYW00LAz8JMEct97pzLAAg4B0CEYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWRsB+u5jgGw437/uSxbWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQccxkAARYWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGYIFZAgWkCFYQIZgARmCBWQIFpAhWECGYAEZggVkCBaQIVhAhmABGR8AAAD//0ZrplUAAAAGSURBVAMAxbUUVpjCajUAAAAASUVORK5CYII=
 
diff --git a/third_party/blink/web_tests/platform/linux/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt b/third_party/blink/web_tests/platform/linux/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
index 880517fd..43ddbeb 100644
--- a/third_party/blink/web_tests/platform/linux/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
@@ -1,7 +1,7 @@
 [crbug/1311561] Tests that auto dark mode emulation from DevTools correctly emulates dark mode for select element
 === Before auto dark mode (autoDarkMode and prefers-color-scheme override) is not enabled ===
-data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAO3UlEQVR4nOzdPY4TaQKA4QKRjzqbWQ0SCNK9ABI/FyAgn8k3RQR9BLYTRMZKhDP3AMERVtoIiWwha80Jeq21ZFmuttvV/bpt4Hki11c/7s5effVV+dbZ2dkAAEDn1gAAQEpgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQGxTYB0fHw/fu5OTkwEAILU2sGZ19SPExw/ybwIA18ktQgCAmMACAIgJLACA2OTAunHjxuLz8g9FL48v7x2Pz3fNxtedPh5f+UXq8bl+shoAOBzTAmtz2azsmu9dDG7IoKsE07kBBwCwRze3P3TcPfOJqHMPnlRImy+74Vs2p9irV6++fv26PDLbfP369QAAsEsTAmu/1t1qXHf8mzdvXrx48fDhwy9fvsxHZh9mm8+fP3/79u0AALAzuwqs9s7dJZZYPXv27M6dO58+fXr06NHX/5t9mG3ev3//6dOnAwDAzpRPEa5EVbvwfLwufrOff/7548ePT548mUXVgwcPZiOfP3++d+/ehw8fZrsGAICdKQPrGh7lm9RYv/7667t37x4/fjxLq9nmrK7ev3//yy+/DAAAuzTtFuGOHtnb8rKXCLjbt2/PpqxmpTW7XTirq9mHAQBgxybMYI2f5kteQDX1slMjbxZVs8a6deuWugIArse0W4TjFygMhe0vu3Lk4vPiw7nn3r17dwAAuC6T12Ctq58LY2v8sqttTh+Pr3u1KQDAgfBbhAAAMYEFABATWAAAsU3P6x0fHw/fu5OTkwEAILX2PVg/Ql0NP8y/CQBcp023CP/467fhe/f7T38OAAApa7AAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgNjmw/vuvvy8+/+0f/z53fHnveHy+aza+7vTx+MrIhk0AgL2bFlib42YldOZ7F4MbSujSzXRuvQEA7NfN7Q8dd898Imq4mgsvu+5bzF0BAIfpG16Dpa4AgMM0YQZrkvbmXTJVBgBwPcoZrJUGMsMEAPyYysDaaVGNHzwEADhM3oMFABCbsAZrvBAqmVLa/rJWYgEA34RpM1jjFygMhctddnHK4oMbiADAIbhxdnZ27o7j4+M//vpt+N79/tOfJycnAwBAxxosAICYwAIAiAksAICYwAIAiAksAICYwAIAiG0KrP/88/bwvXv5cgAAaJnBAgCICSwAgJjAAgCICSwAgJjAAgCICSwAgNjkwDo6Olp8Pj09PXd8ee94fL5rNr7u9PH4yshic3HW8gEAAPs1LbDGVbS8uVI5873LMbQugzZfdps/ZstTAACuwc3tDx1HzHwiariaCy+77lsUFQBwmKzBAgCITZjBmuTqM1vLNk+Vtd8FAHBF5QzWSuhczy288Sp4AID9KgNrp5UzfvBwUFcAwEH65tdgqSsA4NBMWIM1XgiVvBxh+8uuHOnVDADAYZo2gzV+gcJQuPRld/HHAABc0eRbhOs65sK+Gb/sapvTNxymqACAw+Q9WAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAscmBdXR0tPh8enp67vjy3vH4fNdsfN3p4/GVkcXm4qzlAwAA9mtaYI2raHlzpXLme5djaF0Gbb7sNn/MlqcAAFyDm9sfOo6Y+UTUcDUXXnbdtygqAOAwWYMFABCbMIM1ydVntpZtmCobL9ICANivcgZrpYGuJ3oWS+k1FgBwIMrA2mnijB88BAA4TLu6RXgN2ruQAACVCYE1XgiVTCltf9nkoUUAgF2bdotw/AKFoXC5yy6f5dYhAHA4Jq/BWpcyFybO+GVX25x+ubMAAPbIe7AAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGKTA+vo6Gjx+fT09Nzx5b3j8fmu2fi608fjKyPLm+eOAADs0bTAGlfR8ua53bMY3JBBmy974Z80AAAckpvbHzrunvlE1HA1F142+RYAgGszIbAOkJuDAMAB2lVgtXNOJrEAgG9I+RThSgPtem7J9BUAcJjKwNpp7qw8eGhCCwA4WN/2e7AWmTV+mwMAwL5MCKxzX1519abZ/rLLK7HyPwMAoDJtBmv8AoWhsKPLAgDsxeRbhOvq58IqGr/sapvTtzlMkAEAB8VvEQIAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBsU2C9fPlyAABgohtnZ2cDAAAdtwgBAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrCAAYCWwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAIDY/wAAAP//AsaImwAAAAZJREFUAwAM7THzv2czMQAAAABJRU5ErkJggg==
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAQAElEQVR4nOzdP44cVQKA8bLlHE0GKyyBIN0LIPHnAgTkkG+KCOYI3kkQGSsRwj1AcISVNkJytnY24gSzbku9tNtdXTVdXd94d34/ydJMvZqulqP36dWrenTzwgAAABB4NAAAAEQECAAAkBEgAABARoAAAAAZAQIAAGQECAAAkBEgAABAZlaAXF5eDsx3dXU1AAAAr5sMkE18mFDfjv8zAAA4zC1YAABARoAAAAAZAQIAAGQWB8iDBw9e+f3m5ubo+P55Y+PbczbjU585Nr5/fDt26PxD5wIAAOe1KEDmTuYPnbM9b3dsTgicOyCOBRAAAHBeD4cTjU36t6sWxyyJhbnXnPM95obLt99+Ozx//vy145tj33333QAAAMxzcoD8r5i6xWvK999/P3zzzTfDxx9/PDx79uy/xzc/b459/fXXww8//DAAAADT7iRAqtuezrGv44svvhjee++94ffffx8++eSTl6sem3+bnzfHPvzww+Hzzz8fAACAaclTsA4FR7Xpe2wj+1xvv/328Ntvvw2fffbZy+D46KOPXh5/+vTp8MEHHwy//vrry3MAAIBpSYC8CU+YWhIh77777vDzzz8Pn3766cvw2NjExy+//DK88847AwAAMM+iW7Du4glSp1zzHAH0+PHjl6sdmxjZ3JK1iY/NzwAAwHwnr4CMPWVqzXdqLL3m0mDaBMcmQh49eiQ+AADgBItuwRp7/O2aTr3mob/b/X3352Of9/777w8AAMBpFu8BmZr83yZIxt7xMefYnPGpFyQCAADrSjahAwAAbAgQAAAgI0AAAIDMg5sZGyEuLy8H5ru6uhoAAIDXTb4HRHzcnv8zAAA4bNYtWD/+8eXAfF+99dMAAAC8zh4QAAAgI0AAAICMAAEAADICBAAAyAgQAAAgI0AAAIDM4gD59z/++srvf/nbP4+O7583Nr49ZzM+9Zlj44eOzzkGAACsY1GAzJ3QH5rgb8/bHZsTA+eMiGPxAwAAnN/km9DHjE36t6sWa7jNNae+h5UPAADo3ds9IOIDAAB6J6+ALFHd+rTmagwAAHB7yQrIoQiwAgEAAPdPEiB3GRtjT9ICAAB63gMCAABkTt4DMra/Ys3VhlOvaS8IAAC8GRatgIw9/nZN57rm7mfs/uxWLQAAWM+DmxeOnXB5eTn8+MeXA/N99dZPw9XV1QAAALzKHhAAACAjQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgMysAPnX3x8PzPfkyQAAABxgBQQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAILM4QC4uLl75/fr6+uj4/nlj49tzNuNTnzk2fuj47rHdz9k/FwAAOL9FATIWB/vHDk3ut+ftB8FUCMy95pRDMSJCAABgXQ+HE41N2LerFmu4zTWnvofYAACAnj0gAABA5uQVkCXWWiHZN3c1pvo+AABw3yUrIIcm+G/KLVBjG9YBAIDzSwLkLif3Y0/S2hAfAADQuvd7QMQHAAB0Tt4DMra/Ys3H2Z56zUN/57G7AADQW7QCMvb42zWd85r1dwcAgPtu8S1YU5P220zqx97xMefYbf9GbAAAQM97QAAAgIwAAQAAMgIEAADICBAAACAjQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgMziALm4uHjl9+vr66Pj++eNjW/P2YxPfebY+KHju8d2P2f/XAAA4PwWBchYHOwfOzS53563HwRTITD3mlMOxYgIAQCAdT0cTjQ2Yd+uWqzhNtec+h5iAwAAevaAAAAAmZNXQJZYa4Vk35zVmLH9IgAAwPklKyCHIuBNmfDvboYXIQAAsK4kQO5yYj/2JC0AAKB3J7dgvQmq28AAAIA/nRwgY/sr1lxtOPWaaz6ZCwAAmG/RLVhjj79d07muuf85btECAID1Ld4DMjVxv83EfuwdH3OOrfE5AADAeXkPCAAAkBEgAABARoAAAAAZAQIAAGQECAAAkBEgAABARoAAAAAZAQIAAGQECAAAkBEgAABARoAAAAAZAQIAAGQECAAAkFkcIBcXF6/8fn19fXR8/7yx8e05m/GpzxwbP3R8/9ix4wAAwHktCpCxONg/dmzSvzs2JwTmXnOuYwEEAACc18PhRGOT/u2qxRpuc801vwcAAHCakwPk/4FbrwAAoHUnAVKtTFgFAQCAN0vyFKxDEXDXKw9WPwAAoJcEyF1O9A89ScuqCAAA3I17/R6Q3RAZe3QvAABwPicHyLF3dKw1iT/1mvt7QcrvDAAA/GnRCsjY42/XdBfXBAAAzmPxLVhTk//bxMHYOz7mHFvyNwIGAAAa93oPCAAA0BIgAABARoAAAAAZAQIAAGQECAAAkBEgAABAZlaAPHnyZAAAAFjqwc0LAwAAQMAtWAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAKuP3vQAAAMVJREFUQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQOY/AAAA//+e0JEQAAAABklEQVQDAJ9gNe+JEmzjAAAAAElFTkSuQmCC
 
 === After auto dark mode (autoDarkMode and prefers-color-scheme override) is enabled ===
-data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAO0ElEQVR4nOzdO44j1QKA4ZoRKzBEiKV0RmYJiQiyuwAGSJyxADJngIjvIpyRILMTEBKPWQPWWLIsl+1xdf/u9vR8X4CqTj3cZL9Onar5YDabDQAAdD4YAABICSwAgJjAAgCICSwAgJjAAgCICSwAgJjAAgCInQusxWIxPHfL5XIAAEi9OPWh0U1drVar4bmbz+caCwBoeUQIABATWAAAMYEFABCbHFjr9Xq3fXd3d3R8/+h4fHtoM37q8vH4/sh28OCcgxMAAJ7QtMA6XzYHh7ZHd4NnMughwXQ04AAAntDLy08dd892IuroyZMK6fxtz/zK+RT78ssvD96R3Ox+8cUXAwDANU0IrKd16lHjqfM///zzb7755qeffvrwww+3I5uNze6333772WefDQAAV3OtwGqf3N1jidWvv/76559/fvLJJz/++OPsjc3GZvf333//7bffBgCAqynfIjyIqnbh+Xhd/HmvX7/+6quvfvjhh01U/fzzz5uRjz/++I8//nj16tXm0AAAcDVlYD3Cq3yTGuvvv//ePCXcNNYmrTa7m7r6+uuv//333wEA4JqmPSK80it7F972HgH3119/baasNv/dPC7c1NUmuQYAgCubEFjjvkk+QDX1tlMjbxNVr95QVwDA45j2iHD8AYWhcPltD87cbe82jl67mb4aAAAey4uDL0XtLBaL1Wo1PHfz+Xy5XA4AAB3/FiEAQExgAQDEBBYAQOzkGqzhzTKs4bmzAAsAyJ38TMP7UFfDe/O/CQA8pnOPCD/69Lvhufvnl+8HAICUNVgAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQmxxY/1/c7bb/t1wfHd8/Oh7fHtqMn7p8PH4wcmYXAODJTQus83FzEDrbo7vBMyV072Y6Wm8AAE/r5eWnjrtnOxE1PMxbb3vqV8xdAQC36R1eg6WuAIDbNGEGa5L24V0yVQYA8DjKGayDBjLDBAC8n8rAumpRjV88BAC4Tb6DBQAQm7AGa7wQKplSuvy2VmIBAO+EaTNY4w8oDIX73XZ3yW7DA0QA4Ba8mM1mRw8sFouPPv1ueO7++eX75XI5AAB0rMECAIgJLACAmMACAIgJLACAmMACAIgJLACA2LnAeh++6jmfzwcAgJQZLACAmMACAIgJLACAmMACAIgJLACAmMACAIhNDqz1er3bvru7Ozq+f3Q8vj20GT91+Xj8YGS3u7tq/wQAgKc1LbDGVbS/e1A526P7MXQqg87f9pI/5sJLAAAewcvLTx1HzHYianiYt9721K8oKgDgNlmDBQAQmzCDNcnDZ7b2nZ8qa38LAOCByhmsg9B5nEd441XwAABPqwysq1bO+MXDQV0BADfpnV+Dpa4AgFszYQ3WeCFU8nGEy297cKZPMwAAt2naDNb4AwpD4d63vcYfAwDwQC9ms9nRA4vFYrVaDc/dfD5fLpcDAEDHd7AAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGKTA2u9Xu+27+7ujo7vHx2Pbw9txk9dPh4/GNnt7q7aPwEA4GlNC6xxFe3vHlTO9uh+DJ3KoPO3veSPufASAIBH8PLyU8cRs52IGh7mrbc99SuKCgC4TdZgAQDEJsxgTfLwma19Z6bKxou0AACeVjmDddBAjxM9u6X0GgsAuBFlYF01ccYvHgIA3KZrPSJ8BO1TSACAyoTAGi+ESqaULr9t8tIiAMC1TXtEOP6AwlC43233r/LoEAC4HS9ms9nRA4vFYrVaDc/dfD5fLpcDAEDHd7AAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGKTA2u9Xu+27+7ujo7vHx2Pbw9txk9dPh4/GNnfPToCAPCEpgXWuIr2d492z27wTAadv+1b/6QBAOCWvLz81HH3bCeihod5622TXwEAeDQTAusGeTgIANygawVWO+dkEgsAeIeUbxEeNNC155ZMXwEAt6kMrKvmzsGLhya0AICb9W5/B2uXWeOvOQAAPJUJgXX041UPb5rLb7u/Eiv/MwAAKtNmsMYfUBgKV7otAMCTeDGbzY4eWCwWq9VqeO7m8/lyuRwAADr+LUIAgJjAAgCICSwAgJjAAgCICSwAgJjAAgCInQus+Xw+AAAw0cnvYAEAcD8eEQIAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEDAC2BBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAALH/AAAA//+oXss/AAAABklEQVQDAA/2wFdhvWdDAAAAAElFTkSuQmCC
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAQAElEQVR4nOzdO44j1QKA4ZoRKzBEiKV0RmYJiQiyuwBeiTMWQOYMEPFdhDMS1OwEhMRrC3NVI/ni9rjsaj/+nnv7+6TWtOucdlkT1a9Tp/zOYrF4NQAAAATeGQAAACICBAAAyAgQAAAgI0AAAICMAAEAADICBAAAyAgQAAAgMytAVqvVwHzr9XoAAADe9OLUFxGO8bHZbAbmWy6XIgQAAA5wCxYAAJARIAAAQEaAAAAAmYsD5P7+/sHru7u7o+P786bGt3PG8VPvOTW+f3w7dmj+obkAAMB1XRQgcy/mD83ZztsdmxMC1w6IYwEEAABc18vhTFMX/dtVi2MuiYW555zzOeaGy6effjosFos3jo/HPvnkkwEAAJjn7AD5X3HqFq9TPv744+HLL78cvv/+++Hdd9/97/Hx9/HYV199NXz00UcDAABw2pMESHXb0zX2dfz000/Db7/9NnzwwQfDd99993rVY/wZfx+P/fLLL8PPP/88AAAApyVPwToUHNWm76mN7HP9/fffw2effTZ8++23r4Pjhx9+eH38/fffH3799dfh888/fz0HAAA4LQmQt+EJU5dEyB9//PH6NqwxQsbwGI3x8cUXXwx//fXXAAAAzHPRLVhP8QSpc855jQD6/fffX692jP+Ot2SN8TGGCQAAMN/ZATJ1UX/L79S49JyXBtMYHGOEjD/iAwAAHu+iW7CmHn97S+ee89Df7b7e/f3Y+42rHwAAwHleLBaLV8cmrFarYbPZDMy3XC6H9Xo9AAAADyWb0AEAAEYCBAAAyAgQAAAgc3IPyGjcB8J89n8AAMBhJx/DKz4ez/8ZAAAcNusWrPc+/Hpgvj9//GYAAADeZA8IAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEDm4gD59+ruwet/re+Pju/PmxrfzhnHT73n1Pih43OOAQAAt3FRgMy9oD90gb+dtzs2JwauGRHH4gcAALi+k9+EPmXqon+7anELjznnqc9h5QMAAHrPdg+I+AAAgN7ZKyCXqG59uuVqDAAA8HjJCsihCLACAQAAz08SIE8ZG1NP0gIAAHq+BwQAAMicvQdkan/FLVcbzj2nvSAAAPB2uGgFZOrxt7d0rXPuvsfu727VAgCA23mxWCxeHZuwWq2G9z78emC+P3/8Zliv1wMAAPCQPSAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAAJlZAeJL/B5nuVwOAADAm6yAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkLg6Q+/v7B6/v7u6Oju/PmxrfzhnHT73n1Pih47vHdt9nfy4AAHB9FwXIVBzsHzt0cb+dtx8Ep0Jg7jlPORQjIgQAAG7r5XCmqQv27arFLTzmnKc+h9gAAICePSAAAEDm7BWQS9xqhWTf3NWY6vMAAMBzl6yAHLrAf1tugZrasA4AAFxfEiBPeXE/9SStkfgAAIDWs98DIj4AAKBz9h6Qqf0Vt3yc7bnnPPR3HrsLAAC9i1ZAph5/e0vXPGf92QEA4Ll7sVgsXh2bsFqths1mMzDfcrkc1uv1AAAAPOR7QAAAgIwAAQAAMgIEAADICBAAACAjQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgMzFAXJ/f//g9d3d3dHx/XlT49s54/ip95waP3R899ju++zPBQAAru+iAJmKg/1jhy7ut/P2g+BUCMw95ymHYkSEAADAbb0czjR1wb5dtbiFx5zz1OcQGwAA0LMHBAAAyJy9AnKJW62Q7JuzGjO1XwQAALi+ZAXkUAS8LRf8u5vhRQgAANxWEiBPeWE/9SQtAACg9yS3YL0NqtvAAACAf5wdIFP7K2652nDuOW/5ZC4AAGC+i27Bmnr87S1d65z77+MWLQAAuL0Xi8Xi1bEJq9Vq2Gw2A/Mtl8thvV4PAADAQ74HBAAAyAgQAAAgI0AAAICMAAEAADICBAAAyAgQAAAgI0AAAICMAAEAADICBAAAyAgQAAAgI0AAAICMAAEAADICBAAAyFwcIPf39w9e393dHR3fnzc1vp0zjp96z6nxQ8f3jx07DgAAXNdFATIVB/vHjl30747NCYG555zrWAABAADX9XI409RF/3bV4hYec85bfg4AAOA8ZwfI/wO3XgEAQOtJAqRambAKAgAAb5fkKViHIuCpVx6sfgAAQC8JkKe80D/0JC2rIgAA8DSe9feA7IbI1KN7AQCA6zk7QI59R8etLuLPPef+XpDyMwMAAP+4aAVk6vG3t/QU5wQAAK7jxWKxeHVswmq1GjabzcB8y+VyWK/XAwAA8NCz3gMCAAC0BAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJCZFSDjY2UBAAAudfJ7QAAAAK7FLVgAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAPglouQAAAMZJREFUAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEDmPwAAAP//YSpLsAAAAAZJREFUAwBwHslOQLlFGAAAAABJRU5ErkJggg==
 
diff --git a/third_party/blink/web_tests/platform/mac/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt b/third_party/blink/web_tests/platform/mac/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
index 410dba88..c07fe6cd8 100644
--- a/third_party/blink/web_tests/platform/mac/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
@@ -1,7 +1,7 @@
 [crbug/1311561] Tests that auto dark mode emulation from DevTools correctly emulates dark mode for select element
 === Before auto dark mode (autoDarkMode and prefers-color-scheme override) is not enabled ===
-data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAMF0lEQVR4nOzdTUojaQCA4XLwEAoKip5C8OcCLjyKuMgZshF3LjyMotcQXOotnGIaGjHdMXHedPeMz7Oq76tKhexevqpKrb++vg4AAHTWBwAAUgILACAmsAAAYgILACAmsAAAYgILACAmsAAAYvMCazKZDF/GdDodAAAKPw2ssa6+VHN8td8LAKyOS4QAADGBBQAQWzqw1tbWvm+/fY/h2/m3e2fn3+569ybEOSefPfLDGQCA32K5wHoXMe+GPyye75MfBtD8k8//+A8zDgDgt/hr8UNnE2ccVmXz4cnnfNeH6XZ5efny8vJ2ZhxeXV0NAAArsERg/bHm19X19fXFxcXh4eHz8/O3mXFjHJ6fn9/c3AwAALX/UmB9bsHs7OxsZ2fn8fHx6Ojo5R/jxjjc398/PT0dAABq5VOE7+rnD7nlfGNj4+Hh4eTkZIyqg4ODcebp6Wlvb+/+/n7cNQAA1MrA+gVF9W0Ra9kv2traur29PT4+HtNqHI51dXd3t7m5OQAArMD/4R6sRWxvb49LVmNpjZcLx7oaNwYAgNVYYgVrdvUo/OupxU/+6UcXx6gaG2t9fV1dAQArtdwlwtm/Thg6i5/80421u7s7AACs2NL3YP2sez6MrdkDFpn5958FAPjFvIsQACAmsAAAYgILACA27zHAyWQyfBnT6XQAACisuTccAKDlEiEAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQMALYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAsb8BAAD//wTQlfMAAAAGSURBVAMAOXPz+OEBwhwAAAAASUVORK5CYII=
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAN7ElEQVR4nOzdTS4taQCA4U/HIkhICKuQ+NmAgaWIgTWYiJmBxRC2ITFkF7rLTf9pxz0O90133+dJJOWrUnVi9L35quosP/9mAAAABJYHAABARIAAAAAZAQIAAGQECAAAkBEgAABARoAAAAAZAQIAAGTmCpDT09PBYs7OzgYAAPDNdwNkig+T6MX5/wEAwJ/cggUAAGQECAAAkPl0gCwtLf3t9+fn53f3vz5u1v7Xx7w+75jz2rP+7iPjAADA1/hUgLw1YX9r7L3J/l/3fSQA5r32vOd8L4QAAICv8ctY0KyJ/TT2oyfzH7n2PJ/nI+Fzfn4+np6e/jE+jV1cXAwAAGC2hQPk/2Te+Li8vBwnJydjd3d3PD4+/jE+bU9jx8fH4+rqagAAAG/7KQLkq1Zljo6OxsbGxri/vx97e3svqx7Tz7Q9jW1vb4/Dw8MBAAC8LXkL1luT///iw94rKyvj7u5uHBwcvATHzs7Oy/jDw8PY2toat7e3L8cAAABvSwLk3xAbv6+CfPazrK2tjevr67G/v/8SHpMpPm5ubsbq6uoAAABm8wzIAtbX119WO6YYmW7JmuJj2gYAAN638ArIrBWF4rs0Fr32V76hawqOKUKWl5fFBwAAzOlTt2DNeu1tYdFrf2WEbG5uDgAAYH6ffgbke5P+jwTJrGM/Ov6jzgcAAHxO8hA6AADARIAAAAAZAQIAAGSWnud48OH09HSwmLOzswEAAHwzV4AAAAB8BbdgAQAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQOZXAAAA//+A1aGDAAAABklEQVQDADnq9vUQBcmbAAAAAElFTkSuQmCC
 
 === After auto dark mode (autoDarkMode and prefers-color-scheme override) is enabled ===
-data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAMBklEQVR4nOzdTUokSQBA4ZyhT6A78SieQBBc6R3823gOdyqexAOI3kQRFD3DBPbQiNqlJa+me9rvW0hGZBm1fWRGZX5bWlqaAADofJsAAEgJLACAmMACAIgJLACAmMACAIgJLACAmMACAIjNCqzDw8Ppyzg6OpoAAAp//exBo6Ouzs/Ppy9jfX1dYwEACbcIAQBiAgsAIDZ3YF1eXv44Xltbe3P++dnX889PPV9h9uKvP/nuDADALzFfYL2ImBfDN4vnx+S7ATR78dn//mbGAQD8En9//KOvE2cMq7J5d/EZ3/Vuum1vb7/Yyz+GW1tbEwDAAswRWL+t2XW1ubm5v79/enq6vLz8fWYcjOHBwcHGxsYEAFD7PwXW5y6YXVxc3N7erq6unpycLD0ZB2N4fX19dXU1AQDUyl8Rvqif32TL+ePj487OzvHx8Yiqs7OzMbOysnJzc7O7uztOTQAAtTKw/oOi+n4Ra94vur+/H3cJR2ONtBrDUVd7e3sPDw8TAMAC/Al7sD7i7u5uXLIaf8ftwlFXI7kmAIDFmCOwXm+BCh899fHFP/3TxRFVu0/UFQCwUPPdInz96ISp8/HFP91Y4/LVBACwYF72/C8vewYAKt5FCAAQE1gAADGBBQAQ++kerOlpG9b0ZdiABQBUZgUWAACf4BYhAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAEAxAQWAEBMYAETAC2BBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAALF/AAAA//8/+42UAAAABklEQVQDAI8L0M7y6QhKAAAAAElFTkSuQmCC
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAN8ElEQVR4nOzdy00jWQBA0dejjsDsEKEQARISK8iB34Y42AEiEgJAkAkICQQp9KiYP4NpY5irmelzJMv2q7LLy7qq98pfZ7PZtwEAABD4OgAAACICBAAAyAgQAAAgI0AAAICMAAEAADICBAAAyAgQAAAgs1CAHB0dDZZzfHw8AACAX3z53h8RTvFxcXExWM7GxoYIAQCAX5mCBQAAZAQIAACQ+XCAXF1d/eX9+vr6m9tf7jdv+8t9Xn7vWPDY8z73nnEAAOBzfChAXjthf23srZP9P297TwAseuxFv/OtEAIAAD7HT2NJ807sp7F/+mT+Pcde5Pe8J3x2dnbGbDb72/g0tr29PQAAgPmWDpD/k0XjY2traxwcHIyzs7OxsrLy+/j0eho7PDwcm5ubAwAAeN0PESCfdVXm8vJy3N3djbW1tXF6evp81WN6TK+nsZubm3F9fT0AAIDXJXfBeu3k/7+42Pvp6Wns7u6Ok5OT5+A4Pz9/Hl9dXR23t7djb2/veR8AAOB1SYD8G2Ljt6sgH/0tDw8Pz9OwpgiZwmMyxcf+/v54fHwcAADAfNaALOH+/v75asf0PE3JmuJjChMAAOBtSwfIvHUVxX9pLHvsz7xD1xQcU4RMD/EBAACL+dAUrHm3vS0se+zPjJDp6gcAALC4L7PZ7NtbOxwdHY2Li4vBcjY2Nsbx8fEAAACiRegAAAATAQIAAGQECAAAkPnuGpDJtA6E5Vj/AQAAf1goQAAAAD6DKVgAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQ+RkAAP//rFqNHQAAAAZJREFUAwDRcdXJY078uwAAAABJRU5ErkJggg==
 
diff --git a/third_party/blink/web_tests/platform/win/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt b/third_party/blink/web_tests/platform/win/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
index 3539f22..7a081f7 100644
--- a/third_party/blink/web_tests/platform/win/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
+++ b/third_party/blink/web_tests/platform/win/inspector-protocol/emulation/select-popup-auto-dark-mode-expected.txt
@@ -1,7 +1,7 @@
 [crbug/1311561] Tests that auto dark mode emulation from DevTools correctly emulates dark mode for select element
 === Before auto dark mode (autoDarkMode and prefers-color-scheme override) is not enabled ===
-data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAOn0lEQVR4nOzdPW4bZwKA4aHhPm2ySIAESbsXCJDYF3DhPum3NVzwDGoMd17AZXIPG/YRtjXgbu02J+ASS4AgNCQ1E72UKPl5CoPzzQ+t7sU33wwfrlarAQCAzsMBAICUwAIAiAksAICYwAIAiAksAICYwAIAiAksAIDYscBaLpfDvXZxcTEAANQOBta6ru59f3wJfyMAcPPcIgQAiAksAICYwAIAiM0OrMVisf28+0PRu+O7e8fjm13r8UOnXxq/9HPU4xP9XjUAcFbmBdbxuNlbQtvBIyV0/LLHTxwAAM7Mg+mHjkNnMxE1XM+Vlz30Lcfnrl68ePH58+fdkfXmy5cvBwCAE5sRWOfmSF29evXq+fPnv/zyy6dPnzYj6w/rzWfPnr1+/XoAADiluxFYc6fKnj59+v3333/48OHXX3/9/H/rD+vNn3766cmTJwMAwCmVTxFeaqBbXHv+9ddfv3///vHjx+uo+vnnn9cjHz9+/PHHH9+9e7feNQAAnFIZWCctqvGDh8d9++23b968efTo0Tqt1pvrunr79u0333wzAACc2B1eg3Wl7777bj1ltS6t9e3CdV2tPwwAAKc3YwZr78urrj9rNf2yf+OhxXVUrRvr4cOH6goAuDHzbhGOX6AwFKZf9m801g8//DAAANyg2WuwDtXPlbE1ftnVlNPH4xNPBAC4LX6LEAAgJrAAAGICCwAgduwxwOVyOdxrFxcXAwBA7eB7sO59XQ1fxt8IANy8Y7cI//jrt+Fe+/2rPwcAgJo1WAAAMYEFABATWAAAMYEFABATWAAAMYEFABCbHVj//fc/t5//8a//7B3f3Tse3+xajx86/dL47uZ4ZHwAAMDtmhdYx+NmbwltB4+U0PHLHj9xAAA4Mw+mHzoOnc1E1HA9V1720LeYuwIAztMdXoOlrgCA8zRjBusWJVNlAAA3o5zButRAZpgAgC9TGVgnLarxg4cAAOfJe7AAAGIz1mCNF0IlU0rTL2slFgBwJ8ybwRq/QGEoTL+sxgIAzt9itVrt3bFcLv/467fhXvv9qz8vLi4GAICUNVgAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQOxZY9/6NU8vlAACQM4MFABATWAAAMYEFABATWAAAMYEFABATWAAAsdmBtVgstp9Xq9Xe8d294/HNrvX4odMvje9u7o4cOgUA4HbNC6xxFe1u7i2h7eA4lSZedu+JU44BALgVD6YfOo6YzUTUcD1XXnbvt8gpAOBsWYMFABCbMYN1i5KpMgCAm1HOYF1qoBu7i2cBFgBwVsrAOmnljB883FBXAMC5udtrsNQVAHCGZqzBGi+ESvpm+mUvHamuAIDzNG8Ga/wChaEw/bLbI3f/vfIsAICbNPsW4aGOubJvxi+7mnL6eHwzIqcAgLPlPVgAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQE1gAALHZgbVYLLafV6vV3vHdvePxza71+KHTL43vbu6OHDoFAOB2zQuscRXtbu4toe3gOJUmXnbviVOOAQC4FQ+mHzqOmM1E1HA9V15277fIKQDgbFmDBQAQuxuBNV6ztbGd2TKhBQCcjzKwLt3Iu4HombLACwDghpWBddLEOTSJBQBwbqzBAgCIzXiKcPw0XzKlNP2yu0de/+lFAIATmTeDNX6BwlCYftntkSf6nwAAXN/sW4SHUubKxBm/7GrK6ePx7YioAgDOkzVYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAACx2YG1WCy2n1er1d7x3b3j8c2u9fih0y+N725OHAEAuEXzAmtcRbube7tnO3gkg45f9ng/7Q04AIBb9GD6oePQ2UxEDddz5WWTbwEAuDEzAusMuTkIAJyhuxFYJrEAgDukfIrwUgOdem7J9BUAcJ7KwDpp7owfPAQAOE939T1Ym9my7ZyZ9gIAzseMwNr78qrrZ830y+6uxMr/GwAAlXkzWOMXKAyF6Ze12h0AOH+zbxEeqp8rY2v8sqspp4/Hp4wAANwiv0UIABATWAAAMYEFABATWAAAMYEFABATWAAAsWOBtVwuBwAAZvIOdACAmFuEAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYAAAxgQUAEBNYwABAS2ABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBATGABAMQEFgBA7H8AAAD//5GpIRgAAAAGSURBVAMAC/hORivxSCkAAAAASUVORK5CYII=
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAQAElEQVR4nOzdva4bZQKA4XGUnhZWIIGg3RtA4ucGKOih3zZK4Ws4TZQuK6WE+0iUXMK2kdJt0nIFXhzJy4nx2OOx/To/zyMhzvm+OTNWqnn1+Zu5u/rTAAAAELg7AAAARAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBmUoAsl8uBaW5ubgYAAGC3gwGyjg831dP59wIAgHG+ggUAAGQECAAAkBEgAABA5uQAWSwWb/2+Wq32zm8fNza/OWY9f+icu+a3x8bGx44FAADO76QAmXpDvy8Gbs9NiYGp15x6LgAAoHNnmGnsBn+zanEJx1zz0OeYuvLx4MGD4fXr138bX489fPhwAAAAppsdIO+7KfHx6NGj4f79+8N33303vHr16v/j65/XY/fu3RseP348AAAA03zQAXLqaszPP/88fPnll8OLFy+G77///s2qx/q/9c/rsW+++Wb46aefBgAAYJrkKVi7IuB92Pj96aefDs+fPx9+/PHHN8Hx7bffvhl/+fLl8PXXXw/Pnj17cwwAADBNEiDXjI2xJ2lN9fnnnw9PnjwZfvjhhzfhsbaOj6dPnw6fffbZAAAATPfR7gE5xhdffPFmtWMdI+uvZK3jY/0zAABwnNkrIPve0XGpFY+51zzHk7nWwbGOkLt374oPAACY6aSvYI09/vaS5l7zHBHy1VdfDQAAwHwn7wE5dPN/TJCMveNjytiU+TnnAgAAzifZhA4AALAmQAAAgIwAAQAAMovVhE0Qy+VyYJqbm5sBAADY7eB7QMTHcfx7AQDAuElfwfrtj18Gpvn1k98HAABgN3tAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyJwfIf//9z7d+/8e//rN3fvu4sfnNMev5Q+fcNb89NjY+diwAAHB+JwXI1Bv6fTFwe25KDEy95tRzAQAAnYNvQh8zdoO/WbW4hGOueehzWPkAAIDeR7sHRHwAAEBv9grI++CSqzEAAMDxkhWQXRFgBQIAAD4+SYBcMzbGnqQFAAD0vAcEAADIzN4DMra/4pKrDXOvaS8IAAC8G05aARl7/O0lzb2mCAEAgOtbrP6074Dlcjn89scvA9P8+snvw83NzQAAAPydPSAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAAJlJAeL9GdMtlwMAADDCCggAAJARIAAAQEaAAAAAGQECAABkBAgAAJARIAAAQObkAFksFm/9vlqt9s5vHzc2vzlmPX/onLvmt8e2xw+dAwAAOL+TAmQsDrbH9sXA7bmxcJhzzX3nOvZ4AADgPO4MM43dsG9WLS7hmGvu+xxCAwAArsMeEAAAIDN7BeR9cMnVGAAA4HjJCsiuCHiXvgZl/wcAADSSALnmzf3Yk7Q2xAcAAHQ+6j0g4gMAAFqz94CM7a+45E393Gvu+jvxAQAAvZNWQMYef3tJc695+++2/3/MeQAAgPlO/grWoZv2Y27qx97xMWVsyvxmXGgAAMB1eA8IAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQOTlAFovFW7+vVqu989vHjc1vjlnPHzrnrvntse3xQ+cAAADO76QAGYuD7bF9MXB7biwc5lxz37mOPR4AADiPO8NMYzfsm1WLSzjmmvs+h9AAAIDrsAcEAADIfNABMraHZOP2ColVEQAAuLwkQHZ9FepduOE/dv8JAABwmiRArnljf2gVBAAA6NgDAgAAZGY/BWvsKVOXXG2Ye83tv7vUU7oAAID9TloBGXv87SXNvebtv7vG5wYAAM7wFaxDN+7H3NiPveNjytiU+dvjggMAAHr2gAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAAJmTA2SxWLz1+2q12ju/fdzY/OaY9fyhc+6a3x6bMw4AAJzXSQEyFgfbY/tu+m/PTQmBqdecGhX7AggAADivO8NMYzf4m1WLSzjmmpf8HAAAwDyzA+RD4KtXAADQ+qADxCoIAAC8W5KnYO2KgGuvPFj9AACAXhIg17zRH3uSFgAA0Pso3wOyWZG5vTIjUgAA4PJmB8i+d3Rc6kZ+7jW394KUnxkAAPjLSSsgY4+/vaS517QhHQAAru/kr2Aduvk/JkjG3vExZWzK/LHjAADAeX2Ue0AAAIDrECAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBmUoAsl8sBAADgVIuVl2AAAAARX8ECAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIcilLRgAAAH5JREFUEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAy/wMAAP//4q2dCQAAAAZJREFUAwDfNVJC+qr6sAAAAABJRU5ErkJggg==
 
 === After auto dark mode (autoDarkMode and prefers-color-scheme override) is enabled ===
-data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAIAAAAVFBUnAAAOmUlEQVR4nOzdPY4j1QJA4ZoRKzBEiKV0RmYJiQiytwAGSJyxADJngIjfIpyRILMTEBJ/a8AaS5blst2u6eNud/N9wch1q+p6Jju6vlXz3mw2GwAA6Lw3AACQElgAADGBBQAQE1gAADGBBQAQE1gAADGBBQAQOxdYi8VieNGWy+UAAFB7depFo5u6Wq1Ww4s2n881FgCQ8xMhAEBMYAEAxAQWAEBscmCt1+vd57u7u6Pj+2fH49tTm/FTtx+M7x+OR8YXAAA8rWmBdT5ujpbQbvBMCZ2f9vyNAwDAjXl9+aXj0NkuRA0Pc++0p77l/NrV559/fvCA5Obws88+GwAArmxCYN2aM3X16aeffvXVVz/88MP777+/Hdl82Bx+/fXXn3zyyQAAcE3PI7CmLpX9/PPPv//++0cfffT999/P3tp82Bz++uuvv/zyywAAcE3lU4QHDfSEe8//+eefL7744rvvvttE1Y8//rgZ+fDDD3/77bc3b95sTg0AANdUBtZVi2r84OF5f/755+ZXwk1jbdJqc7ipqy+//PLvv/8eAACu7BnvwbrXH3/8sVmy2vy5+blwU1eb5BoAAK5vQmCNN0Il76C6fNp3eGhxE1Vv3lJXAMCjmfYT4fgFCkPh8mnfobE2y1cDAMAjenXwsqidxWKxWq2GF20+ny+XywEAIOX/IgQAiAksAICYwAIAiJ3cgzW83YY1vGg2YAEA13DyNQ0vvq6G/8a/EQB4fOd+Ivzg42+GF+2vn74dAABq9mABAMQEFgBATGABAMQEFgBATGABAMQEFgBAbHJg/X9xt/v8v+X66Pj+2fH49tRm/NTtB+P7h+OR8QUAAE9rWmCdj5ujJbQbPFNC56c9f+MAAHBjXl9+6Th0tgtRw8PcO+2pb7F2BQDcpme8B0tdAQC3acIK1hNKlsoAAB5HuYJ10EBWmACA/6YysK5aVOMHDwEAbpP3YAEAxCbswRpvhEqWlC6f1k4sAOBZmLaCNX6BwlC4fFqNBQDcvlez2ezoicVi8cHH3wwv2l8/fbtcLgcAgJQ9WAAAMYEFABATWAAAMYEFABATWAAAMYEFABA7F1gv/o1T8/l8AACoWcECAIgJLACAmMACAIgJLACAmMACAIgJLACA2OTAWq/Xu893d3dHx/fPjse3pzbjp24/GN8/3B85dQsAwNOaFljjKto/PFpCu8FxKl047dEbL7kGAOBJvL780nHEbBeihoe5d9qj3yKnAICbZQ8WAEBswgrWE0qWygAAHke5gnXQQI/2K54NWADATSkD66qVM37wcEtdAQC35nnvwVJXAMANmrAHa7wRKumby6c9uFJdAQC3adoK1vgFCkPh8ml3V+7/ee9dAACP6dVsNjt6YrFYrFar4UWbz+fL5XIAAEh5DxYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQGxyYK3X693nu7u7o+P7Z8fj21Ob8VO3H4zvH+6PnLoFAOBpTQuscRXtHx4tod3gOJUunPbojZdcAwDwJF5ffuk4YrYLUcPD3Dvt0W+RUwDAzbIHCwAg9jwCa7xna2u3smVBCwC4HWVgHfyQ9wjRc8kGLwCAR1YG1lUT59QiFgDArbEHCwAgNuEpwvHTfMmS0uXT7l/58KcXAQCuZNoK1vgFCkPh8ml3V17pbwIA8HCvZrPZ0ROLxWK1Wg0v2nw+Xy6XAwBAyh4sAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAICYwAIAiAksAIDY5MBar9e7z3d3d0fH98+Ox7enNuOnbj8Y3z+8cAQA4AlNC6xxFe0fHu2e3eCZDDo/7fl+OhpwAABP6PXll45DZ7sQNTzMvdMm3wIA8GgmBNYN8uMgAHCDnkdgWcQCAJ6R8inCgwa69tqS5SsA4DaVgXXV3Bk/eAgAcJue63uwtqtluzUz7QUA3I4JgXX05VUPz5rLp93fiZX/NQAAKtNWsMYvUBgKl09rtzsAcPtezWazoycWi8VqtRpetPl8vlwuBwCAlP+LEAAgJrAAAGICCwAgJrAAAGICCwAgJrAAAGLnAms+nw8AAEx08j1YAAC8Gz8RAgDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQDEBBYAQExgAQMALYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAMYEFABATWAAAsX8BAAD//+Kk/F8AAAAGSURBVAMAxUnEPX9EnU4AAAAASUVORK5CYII=
+data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAQAElEQVR4nOzdO44j1QKA4ZoRKzBEiKV0RmYJiQiyuwBeiTMWQOYMEPFdhDMS1OwEhMRjtjBXNVJfejwuu1xu/z2P75NG3a5TXceaqH4dn/J7q9Xq+QAAABB4bwAAAIgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAzKwA2Ww2A/Nst9sBAAA47MmpLyIc42O32w3Ms16vRQgAAEzwESwAACAjQAAAgIwAAQAAMhcHyO3t7Uuvb25ujo7vnzc1fnfOOH7qmofG949NHZ86FwAAeHgXBcjcG/pjMXB/bE4MzJ1z7rUAAIDO02GhqRv8u1WLazhnzlPvY+7Kx+effz6sVqtXjo/HPvvsswEAAJhvcYC86ebEx6effjp8/fXXw48//ji8//77/z8+/j4e++abb4ZPPvlkAAAA5nmrA+TS1Zhffvll+OOPP4aPPvpo+OGHH16seoz/xt/HY7/99tvw66+/DgAAwDzJU7AORcCbsPH72bNnwxdffDF8//33L4Ljp59+enH8ww8/HH7//ffhyy+/fHEOAAAwTxIgjxkbU0/Smuuvv/568TGsMULG8BiN8fHVV18N//zzzwAAAMz3zu4BOceff/75YrVj/Dl+JGuMjzFMAACA8ywOkKn9Fdf8Xo2lcz7Ek7nG4BgjZPwnPgAAYJmLPoI19fjba1o650NEyLj6AQAALPdktVo9P3bCZrMZdrvdwDzr9XrYbrcDAADwqmQTOgAAwEiAAAAAGQECAABkTu4BGY37QJjH/g8AAJh28jG84uM8/r8AAGDarI9gffDxtwPz/P3zdwMAAHCYPSAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAAJmLA+S/m5uXXv9ne3t0fP+8qfG7c8bxU9c8NL5/bOr41LkAAMDDuyhA5t7QH4uB+2NzYmDunHOvBQAAdE5+E/qUqRv8u1WLazhnzlPvw8oHAAD03tk9IOIDAAB6i1dA3gTXXI0BAADOl6yAHIoAKxAAAPDuSQLkMWNj6klaAABAz/eAAAAAmcV7QKb2V1xztWHpnPaCAADA6+GiFZCpx99e09I5RQgAADy+J6vV6vmxEzabzfDBx98OzPP3z98N2+12AAAAXmUPCAAAkBEgAABARoAAAAAZAQIAAGQECAAAkBEgAABAZlaA+P6M+dbr9QAAABxmBQQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAIHNxgNze3r70+ubm5uj4/nlT43fnjOOnrnlofP/Y/vFT1wAAAB7eRQEyFQf7x47FwP2xqXBYMuexa517PgAA8DCeDgtN3bDfrVpcwzlzHnsfQgMAAB6HPSAAAEBm8QrIm+CaqzEAAMD5khWQQxHwOn0Myv4PAABoJAHymDf3U0/SuiM+AACg807vAREfAADQWrwHZGp/xTVv6pfOeejvxAcAAPQuWgGZevztNS2d8/7f7f885zoAAMByT1ar1fNjJ2w2m2G32w3Ms16vh+12OwAAAK/yPSAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEBGgAAAABkBAgAAZAQIAACQESAAAEDm4gC5vb196fXNzc3R8f3zpsbvzhnHT13z0Pj+sf3jp64BAAA8vIsCZCoO9o8di4H7Y1PhsGTOY9c693wAAOBhPB0Wmrphv1u1uIZz5jz2PoQGAAA8DntAAACAzFsdIFN7SO7cXyGxKgIAANeXBMihj0K9Djf85+4/AQAALpMEyGPe2J9aBQEAADr2gAAAAJnFT8GaesrUNVcbls65/3fXekoXAABw3EUrIFOPv72mpXPe/7vHeN8AAMAwPFmtVs+PnbDZbIbdbjcwz3q9Hrbb7QAAALzKHhAAACAjQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgIwAAQAAMgIEAADICBAAACBzcYDc3t6+9Prm5ubo+P55U+N354zjp655aHz/2JLjAADAw7ooQKbiYP/YsZv++2NzQmDunHOj4lgAAQAAD+vpsNDUDf7dqsU1nDPnNd8HAACwzOIAeRv46BUAALTe6gCxCgIAAK+X5ClYhyLgsVcerH4AAEAvCZDHvNGfepIWAADQeye/B+RuReb+yoxIAQCA61scIMe+o+NaN/JL59zfC1K+ZwAA4F8XrYBMPf72mpbOaUM6AAA8vier1er5sRM2m82w2+0G5lmv18N2ux0AAIBXvZN7QAAAgMchQAAAgIwAAQAAMgIEAADICBAAACAjQAAAgMysABkfLQsAAHCpk98DAgAA8FB8BAsAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAykJWIGgAAAIlJREFUAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMgIEAAAICNAAACAjAABAAAyAgQAAMj8DwAA//+8cUNnAAAABklEQVQDAARczTQDRnZbAAAAAElFTkSuQmCC
 
diff --git a/third_party/blink/web_tests/virtual/scalefactor200/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt b/third_party/blink/web_tests/virtual/scalefactor200/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt
index 1c9df16..ce6b3aa 100644
--- a/third_party/blink/web_tests/virtual/scalefactor200/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt
+++ b/third_party/blink/web_tests/virtual/scalefactor200/http/tests/inspector-protocol/page/page-captureScreenshot-clip-emulation-expected.txt
@@ -15,21 +15,21 @@
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAADUAAAA1CAIAAABuhDQnAAAAYklEQVR4nOzZsQ2AMBAEwUdy4e4cREAFmxhppoLVf3hr5p6DrTnb17evOc1+H/uX+51KX6Ov0dfoa/Q1+hp9jb5GX6Ov0dfoa/Q1+hp9jb5GX6Ov0dfoa/Q1+hp9zWXfTx4AAAD///+1WosAAAAGSURBVAMAKBoE0CDq3ckAAAAASUVORK5CYII=
+        data : iVBORw0KGgoAAAANSUhEUgAAADUAAAA1CAYAAADh5qNwAAAAZ0lEQVR4nOzasQ3AMAzEQAXw4N7cKVJkhjN4GxBS+WvmnLnMmgv9UfsZ3v6e7vJLXaQoRVGKohRFKYpSFKUoSlGUoihFUYqiFEUpilIUpShKUZSiKEVRiqIURSmKUhSlKErxNDdFvAAAAP//IIowVgAAAAZJREFUAwCNQgbO8MkUFwAAAABJRU5ErkJggg==
     }
     sessionId : <string>
 }
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAAJ8AAACfCAIAAABoeZy3AAABj0lEQVR4nOzXsQnAMBAEwReocHUu58YNeJlpYbng9swdovbQpW6ZumXqlqlb9lX3rOGPzvv+2G6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumXqlqlbpm6ZumVr5g5Rtlumbpm6ZeqWqVv2AAAA///UlaKIAAAABklEQVQDAOYABnhN8isCAAAAAElFTkSuQmCC
+        data : iVBORw0KGgoAAAANSUhEUgAAAJ8AAACfCAYAAADnGwvgAAABuUlEQVR4nOzYsQnAMBAEwReocHUuN2Bnhk1malguuD1z70BgD0TER0Z8ZMRHRnxkvuM7a+AX5/1QsXxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdGfGTER0Z8ZMRHRnxkxEdmzdw7ELB8ZMRHRnxkxEdGfGQeAAAA///OGXOxAAAABklEQVQDAAg5CHYp08OfAAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
 {
     id : <number>
     result : {
-        data : iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAIAAAACtmMCAAAAPklEQVR4nOzWOxEAMAgE0WMGYXEWpCElTvLTcOVuceVrIaUta/mmQpZ6qivlDhEREREREVH/zlpa407Yf4oDAAD//wbnJvUAAAAGSURBVAMAMvQHxxTMQZwAAAAASUVORK5CYII=
+        data : iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAARUlEQVR4nOzWsQ0AIAgAQUwYzM1kNEdxE1TiCEL1nwDltaiIuxSlsa1JanOcsYcVBQYGBgYGBgYG9h+7f11mq8dplR/xBgAA///IyW8VAAAABklEQVQDAPv4CcVmHlXWAAAAAElFTkSuQmCC
     }
     sessionId : <string>
 }
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
index e1084b90..ad23eeb 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -2228,9 +2228,7 @@
 [Worker]     getter displayHeight
 [Worker]     getter displayWidth
 [Worker]     getter duration
-[Worker]     getter flip
 [Worker]     getter format
-[Worker]     getter rotation
 [Worker]     getter timestamp
 [Worker]     getter visibleRect
 [Worker]     method allocationSize
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 62ae0652..2fa6c563 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -9658,9 +9658,7 @@
     getter displayHeight
     getter displayWidth
     getter duration
-    getter flip
     getter format
-    getter rotation
     getter timestamp
     getter visibleRect
     method allocationSize
diff --git a/third_party/blink/web_tests/fast/css-masonry/auto-placement-001-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-001-ref.html
similarity index 90%
rename from third_party/blink/web_tests/fast/css-masonry/auto-placement-001-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-001-ref.html
index 0801cf66..155c5f7 100644
--- a/third_party/blink/web_tests/fast/css-masonry/auto-placement-001-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-001-ref.html
@@ -1,6 +1,8 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .grid {
     display: grid;
diff --git a/third_party/blink/web_tests/fast/css-masonry/auto-placement-001.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-001.html
similarity index 78%
rename from third_party/blink/web_tests/fast/css-masonry/auto-placement-001.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-001.html
index ab82621..0c5f7d8 100644
--- a/third_party/blink/web_tests/fast/css-masonry/auto-placement-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-001.html
@@ -1,6 +1,9 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="auto-placement-001-ref.html">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .masonry {
     grid-template-columns: auto auto auto;
diff --git a/third_party/blink/web_tests/fast/css-masonry/auto-placement-row-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-row-ref.html
similarity index 92%
rename from third_party/blink/web_tests/fast/css-masonry/auto-placement-row-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-row-ref.html
index bb8200cb..286935e 100644
--- a/third_party/blink/web_tests/fast/css-masonry/auto-placement-row-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-row-ref.html
@@ -2,6 +2,7 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .grid {
     display: grid;
diff --git a/third_party/blink/web_tests/fast/css-masonry/auto-placement-row.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-row.html
similarity index 86%
rename from third_party/blink/web_tests/fast/css-masonry/auto-placement-row.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-row.html
index fe2d772a..87aa1f7 100644
--- a/third_party/blink/web_tests/fast/css-masonry/auto-placement-row.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/auto-placement-row.html
@@ -2,6 +2,8 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="auto-placement-row-ref.html">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .masonry {
     masonry-direction: row;
diff --git a/third_party/blink/web_tests/fast/css-masonry/explicit-placement-001-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/explicit-placement-001-ref.html
similarity index 79%
rename from third_party/blink/web_tests/fast/css-masonry/explicit-placement-001-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/explicit-placement-001-ref.html
index 5481877c..791226f7 100644
--- a/third_party/blink/web_tests/fast/css-masonry/explicit-placement-001-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/explicit-placement-001-ref.html
@@ -1,6 +1,8 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com">
 <style>
 .grid {
     display: grid;
diff --git a/third_party/blink/web_tests/fast/css-masonry/explicit-placement-001.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/explicit-placement-001.html
similarity index 72%
rename from third_party/blink/web_tests/fast/css-masonry/explicit-placement-001.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/explicit-placement-001.html
index 1f0678e..7132802 100644
--- a/third_party/blink/web_tests/fast/css-masonry/explicit-placement-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/explicit-placement-001.html
@@ -1,6 +1,9 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="explicit-placement-001-ref.html">
+<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com">
 <style>
 .masonry {
     width: 200px;
diff --git a/third_party/blink/web_tests/fast/css-masonry/gaps-001-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/gaps-001-ref.html
similarity index 86%
rename from third_party/blink/web_tests/fast/css-masonry/gaps-001-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/gaps-001-ref.html
index c7dbf5c..3bed5b2 100644
--- a/third_party/blink/web_tests/fast/css-masonry/gaps-001-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/gaps-001-ref.html
@@ -1,6 +1,8 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .grid {
     display: grid;
@@ -46,7 +48,7 @@
 }
 </style>
 <body>
-  <p>Test that masonry items with auto placement are correctly positioned within the grid axis.</p>
+  <p>Test that gaps are correctly used within masonry containers.</p>
   <div class="grid">
     <div class="flex">
       <div class="grid-auto-item-1" style="grid-column: 1">This is some text</div>
diff --git a/third_party/blink/web_tests/fast/css-masonry/gaps-001.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/gaps-001.html
similarity index 71%
rename from third_party/blink/web_tests/fast/css-masonry/gaps-001.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/gaps-001.html
index 7fdc7444..f2fb15d0 100644
--- a/third_party/blink/web_tests/fast/css-masonry/gaps-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/gaps-001.html
@@ -1,6 +1,9 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="gaps-001-ref.html">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .masonry {
     grid-template-columns: repeat(3, 100px);
@@ -10,7 +13,7 @@
 }
 </style>
 <body>
-  <p>Test that masonry items with auto placement are correctly positioned within the grid axis.</p>
+  <p>Test that gaps are correctly used within masonry containers.</p>
   <div class="masonry">
     <div class="auto-item">This is some text</div>
     <div class="auto-item">Some larger words in this sentence</div>
diff --git a/third_party/blink/web_tests/fast/css-masonry/intrinsic-inline-container-size-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/intrinsic-inline-container-size-ref.html
similarity index 86%
rename from third_party/blink/web_tests/fast/css-masonry/intrinsic-inline-container-size-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/intrinsic-inline-container-size-ref.html
index af49dad..fb642a0 100644
--- a/third_party/blink/web_tests/fast/css-masonry/intrinsic-inline-container-size-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/intrinsic-inline-container-size-ref.html
@@ -1,6 +1,8 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com">
 <style>
 .grid {
     display: grid;
diff --git a/third_party/blink/web_tests/fast/css-masonry/intrinsic-inline-container-size.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/intrinsic-inline-container-size.html
similarity index 78%
rename from third_party/blink/web_tests/fast/css-masonry/intrinsic-inline-container-size.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/intrinsic-inline-container-size.html
index ff2ace7..197734a 100644
--- a/third_party/blink/web_tests/fast/css-masonry/intrinsic-inline-container-size.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/intrinsic-inline-container-size.html
@@ -1,6 +1,9 @@
 <!DOCTYPE html>
 <html>
+<link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="intrinsic-inline-container-size-ref.html">
+<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com">
 <style>
 .masonry {
     gap: 20px;
diff --git a/third_party/blink/web_tests/fast/css-masonry/justify-items-center-001-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-center-001-ref.html
similarity index 92%
rename from third_party/blink/web_tests/fast/css-masonry/justify-items-center-001-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-center-001-ref.html
index b3dedf4..5fbb72fd 100644
--- a/third_party/blink/web_tests/fast/css-masonry/justify-items-center-001-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-center-001-ref.html
@@ -2,6 +2,7 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .grid {
     display: grid;
diff --git a/third_party/blink/web_tests/fast/css-masonry/justify-items-center-001.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-center-001.html
similarity index 85%
rename from third_party/blink/web_tests/fast/css-masonry/justify-items-center-001.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-center-001.html
index 35c6adfd..4ca38c9 100644
--- a/third_party/blink/web_tests/fast/css-masonry/justify-items-center-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-center-001.html
@@ -2,6 +2,8 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="justify-items-center-001-ref.html">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .masonry {
     grid-template-columns: repeat(3, 100px);
diff --git a/third_party/blink/web_tests/fast/css-masonry/justify-items-end-justify-self-start-001-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-end-justify-self-start-001-ref.html
similarity index 93%
rename from third_party/blink/web_tests/fast/css-masonry/justify-items-end-justify-self-start-001-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-end-justify-self-start-001-ref.html
index 8c58c6b8..f91c4558 100644
--- a/third_party/blink/web_tests/fast/css-masonry/justify-items-end-justify-self-start-001-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-end-justify-self-start-001-ref.html
@@ -2,6 +2,7 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .grid {
     display: grid;
diff --git a/third_party/blink/web_tests/fast/css-masonry/justify-items-end-justify-self-start-001.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-end-justify-self-start-001.html
similarity index 85%
rename from third_party/blink/web_tests/fast/css-masonry/justify-items-end-justify-self-start-001.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-end-justify-self-start-001.html
index 8da788c..74d217d 100644
--- a/third_party/blink/web_tests/fast/css-masonry/justify-items-end-justify-self-start-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/justify-items-end-justify-self-start-001.html
@@ -2,6 +2,8 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="justify-items-end-justify-self-start-001-ref.html">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .masonry {
     grid-template-columns: repeat(3, 100px);
diff --git a/third_party/blink/web_tests/fast/css-masonry/overflow-alignment-001-expected.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/overflow-alignment-001-ref.html
similarity index 92%
rename from third_party/blink/web_tests/fast/css-masonry/overflow-alignment-001-expected.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/overflow-alignment-001-ref.html
index c30e973a..562f5f5 100644
--- a/third_party/blink/web_tests/fast/css-masonry/overflow-alignment-001-expected.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/overflow-alignment-001-ref.html
@@ -2,6 +2,7 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .grid {
     display: grid;
diff --git a/third_party/blink/web_tests/fast/css-masonry/overflow-alignment-001.html b/third_party/blink/web_tests/wpt_internal/css/css-masonry/overflow-alignment-001.html
similarity index 88%
rename from third_party/blink/web_tests/fast/css-masonry/overflow-alignment-001.html
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/overflow-alignment-001.html
index 25c9520..0d57cfb 100644
--- a/third_party/blink/web_tests/fast/css-masonry/overflow-alignment-001.html
+++ b/third_party/blink/web_tests/wpt_internal/css/css-masonry/overflow-alignment-001.html
@@ -2,6 +2,8 @@
 <html>
 <link rel="help" href="https://drafts.csswg.org/css-grid-3">
 <link href="resources/masonry.css" rel="stylesheet">
+<link rel="match" href="overflow-alignment-001-ref.html">
+<link rel="author" title="Celeste Pan" href="mailto:celestepan@microsoft.com">
 <style>
 .masonry {
     grid-template-columns: 50px;
diff --git a/third_party/blink/web_tests/fast/css-masonry/resources/masonry.css b/third_party/blink/web_tests/wpt_internal/css/css-masonry/resources/masonry.css
similarity index 100%
rename from third_party/blink/web_tests/fast/css-masonry/resources/masonry.css
rename to third_party/blink/web_tests/wpt_internal/css/css-masonry/resources/masonry.css
diff --git a/third_party/dawn b/third_party/dawn
index a78978ef..1753adf 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit a78978efe0b237a00be1d5879598cafa5d7bd4b4
+Subproject commit 1753adf84ae4320ae7aa6c921d4825cffe4888ef
diff --git a/third_party/libaom/README.chromium b/third_party/libaom/README.chromium
index ded12f4..f4d81b4e 100644
--- a/third_party/libaom/README.chromium
+++ b/third_party/libaom/README.chromium
@@ -2,7 +2,7 @@
 Short Name: libaom
 URL: https://aomedia.googlesource.com/aom/
 Version: N/A
-Revision: 1983dea2323c761a09555791a81421a24d73cfe1
+Revision: a23a4799ec2d7dd6e436c7b64a34553773014ed7
 CPEPrefix: cpe:/a:aomedia:aomedia:3.12.1
 License: BSD-2-Clause, Patent
 License Android Compatible: yes
diff --git a/third_party/libaom/source/config/config/aom_version.h b/third_party/libaom/source/config/config/aom_version.h
index 6027a1d4..c138ae6 100644
--- a/third_party/libaom/source/config/config/aom_version.h
+++ b/third_party/libaom/source/config/config/aom_version.h
@@ -14,9 +14,9 @@
 #define VERSION_MAJOR 3
 #define VERSION_MINOR 12
 #define VERSION_PATCH 1
-#define VERSION_EXTRA "111-g1983dea232"
+#define VERSION_EXTRA "113-ga23a4799ec"
 #define VERSION_PACKED \
   ((VERSION_MAJOR << 16) | (VERSION_MINOR << 8) | (VERSION_PATCH))
-#define VERSION_STRING_NOSP "3.12.1-111-g1983dea232"
-#define VERSION_STRING " 3.12.1-111-g1983dea232"
+#define VERSION_STRING_NOSP "3.12.1-113-ga23a4799ec"
+#define VERSION_STRING " 3.12.1-113-ga23a4799ec"
 #endif  // AOM_VERSION_H_
diff --git a/third_party/libaom/source/libaom b/third_party/libaom/source/libaom
index 1983dea..a23a479 160000
--- a/third_party/libaom/source/libaom
+++ b/third_party/libaom/source/libaom
@@ -1 +1 @@
-Subproject commit 1983dea2323c761a09555791a81421a24d73cfe1
+Subproject commit a23a4799ec2d7dd6e436c7b64a34553773014ed7
diff --git a/third_party/libc++abi/src b/third_party/libc++abi/src
index 6cc4c9c..f2a7f298 160000
--- a/third_party/libc++abi/src
+++ b/third_party/libc++abi/src
@@ -1 +1 @@
-Subproject commit 6cc4c9c768689b90b0fdc8c6c3c186589e6798d1
+Subproject commit f2a7f2987f9dcdf8b04c2d8cd4dcb186641a7c3e
diff --git a/third_party/pdfium b/third_party/pdfium
index c31ed43..c440145 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit c31ed43bee772633c28f7688eba2d6786f1a6034
+Subproject commit c440145ffc24384f7260c7c8315fbabcf7163746
diff --git a/third_party/perfetto b/third_party/perfetto
index b9acf7f..451625d 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit b9acf7ff3553f8cf7545dc5e2bfb1b1e1a4a7081
+Subproject commit 451625d8a0a35cdcd34a9932d6d0def867cef936
diff --git a/third_party/rust/chromium_crates_io/Cargo.lock b/third_party/rust/chromium_crates_io/Cargo.lock
index fff3449..0f9d24e 100644
--- a/third_party/rust/chromium_crates_io/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/Cargo.lock
@@ -141,7 +141,7 @@
 
 [[package]]
 name = "clap"
-version = "4.5.36"
+version = "4.5.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "clap_builder",
@@ -149,7 +149,7 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.5.36"
+version = "4.5.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "anstyle",
@@ -905,7 +905,7 @@
 
 [[package]]
 name = "llguidance"
-version = "0.7.14"
+version = "0.7.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "anyhow",
@@ -1000,7 +1000,7 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "unicode-ident",
@@ -1287,7 +1287,7 @@
 
 [[package]]
 name = "toktrie"
-version = "0.7.14"
+version = "0.7.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "anyhow",
diff --git a/third_party/rust/chromium_crates_io/supply-chain/config.toml b/third_party/rust/chromium_crates_io/supply-chain/config.toml
index 4490be2..88188a6 100644
--- a/third_party/rust/chromium_crates_io/supply-chain/config.toml
+++ b/third_party/rust/chromium_crates_io/supply-chain/config.toml
@@ -86,10 +86,10 @@
 [policy."cfg-if:1.0.0"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."clap:4.5.36"]
+[policy."clap:4.5.37"]
 criteria = ["crypto-safe", "safe-to-run"]
 
-[policy."clap_builder:4.5.36"]
+[policy."clap_builder:4.5.37"]
 criteria = ["crypto-safe", "safe-to-run"]
 
 [policy."clap_lex:0.7.4"]
@@ -308,7 +308,7 @@
 [policy."litemap:0.7.5"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."llguidance:0.7.14"]
+[policy."llguidance:0.7.18"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."log:0.4.27"]
@@ -341,7 +341,7 @@
 [policy."potential_utf:0.1.2"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."proc-macro2:1.0.94"]
+[policy."proc-macro2:1.0.95"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."prost-derive:0.13.5"]
@@ -452,7 +452,7 @@
 [policy."tinystr:0.8.1"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
-[policy."toktrie:0.7.14"]
+[policy."toktrie:0.7.18"]
 criteria = ["crypto-safe", "safe-to-deploy", "ub-risk-2"]
 
 [policy."unicode-bidi:0.3.18"]
diff --git a/third_party/rust/chromium_crates_io/vendor/clap-v4/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/clap-v4/.cargo_vcs_info.json
index e1ee38d..5c2f1a3 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap-v4/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/clap-v4/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "9ebef155c0c05094f6b25b5884a0bdd4250db9f3"
+    "sha1": "f209bce2203498e743b171b7ac64f0fb9d3ae590"
   },
   "path_in_vcs": ""
 }
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.lock
index c5b8636..e25d08e 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.lock
@@ -134,7 +134,7 @@
 
 [[package]]
 name = "clap"
-version = "4.5.36"
+version = "4.5.37"
 dependencies = [
  "automod",
  "clap-cargo",
@@ -159,9 +159,9 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.5.36"
+version = "4.5.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
+checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
 dependencies = [
  "anstream",
  "anstyle",
diff --git a/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml
index 98bf406..293c0ab 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml
@@ -13,7 +13,7 @@
 edition = "2021"
 rust-version = "1.74"
 name = "clap"
-version = "4.5.36"
+version = "4.5.37"
 build = false
 include = [
     "build.rs",
@@ -510,7 +510,7 @@
 required-features = ["derive"]
 
 [dependencies.clap_builder]
-version = "=4.5.36"
+version = "=4.5.37"
 default-features = false
 
 [dependencies.clap_derive]
diff --git a/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml.orig
index 944a8126..d55d95f9 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/clap-v4/Cargo.toml.orig
@@ -99,7 +99,7 @@
 
 [package]
 name = "clap"
-version = "4.5.36"
+version = "4.5.37"
 description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
 categories = ["command-line-interface"]
 keywords = [
@@ -176,7 +176,7 @@
 bench = false
 
 [dependencies]
-clap_builder = { path = "./clap_builder", version = "=4.5.36", default-features = false }
+clap_builder = { path = "./clap_builder", version = "=4.5.37", default-features = false }
 clap_derive = { path = "./clap_derive", version = "=4.5.32", optional = true }
 
 [dev-dependencies]
diff --git a/third_party/rust/chromium_crates_io/vendor/clap-v4/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/clap-v4/src/lib.rs
index becf5105..fa4603c 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap-v4/src/lib.rs
+++ b/third_party/rust/chromium_crates_io/vendor/clap-v4/src/lib.rs
@@ -11,7 +11,7 @@
 //! - [Cookbook][_cookbook]
 //! - [FAQ][_faq]
 //! - [Discussions](https://github.com/clap-rs/clap/discussions)
-//! - [CHANGELOG](https://github.com/clap-rs/clap/blob/v4.5.36/CHANGELOG.md) (includes major version migration
+//! - [CHANGELOG](https://github.com/clap-rs/clap/blob/v4.5.37/CHANGELOG.md) (includes major version migration
 //!   guides)
 //!
 //! ## Aspirations
diff --git a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/.cargo_vcs_info.json
index 9213bd9..ad9432a 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "9ebef155c0c05094f6b25b5884a0bdd4250db9f3"
+    "sha1": "f209bce2203498e743b171b7ac64f0fb9d3ae590"
   },
   "path_in_vcs": "clap_builder"
 }
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.lock
index db51aaf..a668c8d9 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.lock
@@ -101,7 +101,7 @@
 
 [[package]]
 name = "clap_builder"
-version = "4.5.36"
+version = "4.5.37"
 dependencies = [
  "anstream",
  "anstyle",
diff --git a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml
index ad825f4..b0be359 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml
@@ -13,7 +13,7 @@
 edition = "2021"
 rust-version = "1.74"
 name = "clap_builder"
-version = "4.5.36"
+version = "4.5.37"
 build = false
 include = [
     "build.rs",
diff --git a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml.orig
index e27c2af2..30a10c33 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "clap_builder"
-version = "4.5.36"
+version = "4.5.37"
 description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
 categories = ["command-line-interface"]
 keywords = [
diff --git a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/src/parser/matches/arg_matches.rs b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/src/parser/matches/arg_matches.rs
index 78e6c75..2d53e2b 100644
--- a/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/src/parser/matches/arg_matches.rs
+++ b/third_party/rust/chromium_crates_io/vendor/clap_builder-v4/src/parser/matches/arg_matches.rs
@@ -1226,6 +1226,18 @@
         let presence = self.args.contains_key(id);
         Ok(presence)
     }
+
+    /// Clears the values for the given `id`
+    ///
+    /// Alternative to [`try_remove_*`][ArgMatches::try_remove_one] when the type is not known.
+    ///
+    /// Returns `Err([``MatchesError``])` if the given `id` isn't valid for current `ArgMatches` instance.
+    ///
+    /// Returns `Ok(true)` if there were any matches with the given `id`, `Ok(false)` otherwise.
+    pub fn try_clear_id(&mut self, id: &str) -> Result<bool, MatchesError> {
+        ok!(self.verify_arg(id));
+        Ok(self.args.remove_entry(id).is_some())
+    }
 }
 
 // Private methods
@@ -2052,4 +2064,37 @@
         let b = b_index.into_iter().zip(b_value).rev().collect::<Vec<_>>();
         dbg!(b);
     }
+
+    #[test]
+    fn delete_id_without_returning() {
+        let mut matches = crate::Command::new("myprog")
+            .arg(crate::Arg::new("a").short('a').action(ArgAction::Append))
+            .arg(crate::Arg::new("b").short('b').action(ArgAction::Append))
+            .arg(crate::Arg::new("c").short('c').action(ArgAction::Append))
+            .try_get_matches_from(vec!["myprog", "-b1", "-a1", "-b2"])
+            .unwrap();
+        let matches_ids_count = matches.ids().count();
+        assert_eq!(matches_ids_count, 2);
+
+        let _ = matches
+            .try_clear_id("d")
+            .expect_err("should fail due to there is no arg 'd'");
+
+        let c_was_presented = matches
+            .try_clear_id("c")
+            .expect("doesn't fail because there is no matches for 'c' argument");
+        assert!(!c_was_presented);
+        let matches_ids_count = matches.ids().count();
+        assert_eq!(matches_ids_count, 2);
+
+        let b_was_presented = matches.try_clear_id("b").unwrap();
+        assert!(b_was_presented);
+        let matches_ids_count = matches.ids().count();
+        assert_eq!(matches_ids_count, 1);
+
+        let a_was_presented = matches.try_clear_id("a").unwrap();
+        assert!(a_was_presented);
+        let matches_ids_count = matches.ids().count();
+        assert_eq!(matches_ids_count, 0);
+    }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json
index d0c60cb..cf96482 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "e8f2ad14c019fdd886f1878d54391d578947611f",
+    "sha1": "bdc92c138b650e30bc203357bfe581bbd7ba8fb3",
     "dirty": true
   },
   "path_in_vcs": "parser"
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock
index 8688c089..a48004f1 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.lock
@@ -434,7 +434,7 @@
 
 [[package]]
 name = "llguidance"
-version = "0.7.14"
+version = "0.7.18"
 dependencies = [
  "anyhow",
  "derivre",
@@ -816,9 +816,9 @@
 
 [[package]]
 name = "toktrie"
-version = "0.7.14"
+version = "0.7.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "169b801392f4a9b2d45ff47d15570b0b62f7b27faace0e52b6843432fc0637dc"
+checksum = "08f6977cc4c182d0901a97b7732d56deacd7fb733d8b4a4e523a8b7b040343df"
 dependencies = [
  "anyhow",
  "bytemuck",
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml
index ca7fbf9..21126c86 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "llguidance"
-version = "0.7.14"
+version = "0.7.18"
 build = "build.rs"
 autolib = false
 autobins = false
@@ -95,7 +95,7 @@
 features = ["preserve_order"]
 
 [dependencies.toktrie]
-version = "0.7.14"
+version = "0.7.18"
 
 [dev-dependencies.regex]
 version = "1.11.1"
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig
index f82845b1..8de06eb0 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/Cargo.toml.orig
@@ -1,13 +1,13 @@
 [package]
 name = "llguidance"
-version = "0.7.14"
+version = "0.7.18"
 edition = "2021"
 license = "MIT"
 description = "Super-fast Structured Outputs"
 repository = "https://github.com/guidance-ai/llguidance"
 
 [dependencies]
-toktrie = { version = "0.7.14" }
+toktrie = { version = "0.7.18" }
 derivre = { version = "=0.3.7", default-features = false, features = ["compress"] }
 serde = { version = "1.0.217", features = ["derive"] }
 serde_json = { version = "1.0.138", features = ["preserve_order"] }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h
index 75a7823..06c2d0f 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/llguidance.h
@@ -458,6 +458,20 @@
                                    const char *data);
 
 /**
+ * Check if given grammar is valid.
+ * This about twice as fast as creating a matcher (which also validates).
+ * See llg_new_matcher() for the grammar format.
+ * Returns 0 on success and -1 on error and 1 on warning.
+ * The error message or warning is written to message, which is message_len bytes long.
+ * It's always NUL-terminated.
+ */
+int32_t llg_validate_grammar(const struct LlgConstraintInit *init,
+                             const char *constraint_type,
+                             const char *data,
+                             char *message,
+                             size_t message_len);
+
+/**
  * Compute the set of allowed tokens for the current state.
  * The result is written to mask_dest.
  * mask_byte_len must be equal to llg_matcher_get_mask_byte_size().
@@ -469,7 +483,7 @@
 
 /**
  * Compute the set of allowed tokens for the current state.
- * The pointer to the result is written to mask_dest.
+ * Use llg_matcher_get_mask() to get the result.
  * Returns 0 on success and -1 on error.
  */
 int32_t llg_matcher_compute_mask(struct LlgMatcher *matcher);
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/api.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/api.rs
index f48fb541..9b46c17 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/api.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/api.rs
@@ -10,6 +10,8 @@
     regex_to_lark,
 };
 
+pub use crate::earley::ValidationResult;
+
 /// This represents a collection of grammars, with a designated
 /// "start" grammar at first position.
 /// Grammars can refer to each other via GenGrammar nodes.
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/from_guidance.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/from_guidance.rs
index 6c31e33..4b58247 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/from_guidance.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/from_guidance.rs
@@ -37,14 +37,8 @@
                 let _ = lark;
                 bail!("lark_grammar is not supported in this build")
             }
-        } else if let Some(mut json_schema) = input.json_schema {
-            let mut opts = JsonCompileOptions::default();
-            if let Some(x_guidance) = json_schema.get("x-guidance") {
-                opts = serde_json::from_value(x_guidance.clone())?;
-                // TODO not removing it causes oneOf to be handled as anyOf in Github_medium---o61004.json
-                json_schema.as_object_mut().unwrap().remove("x-guidance");
-            }
-            opts.json_to_llg(builder, json_schema)?
+        } else if let Some(json_schema) = input.json_schema {
+            JsonCompileOptions::default().json_to_llg_with_overrides(builder, json_schema)?
         } else {
             bail!("grammar must have either lark_grammar or json_schema");
         };
@@ -85,15 +79,69 @@
             .collect();
 
         let builder = self.builder.unwrap();
+        let warnings = builder.get_warnings();
         let mut grammar = builder.grammar;
         let mut lexer_spec = builder.regex.spec;
 
         grammar.resolve_grammar_refs(&mut lexer_spec, &grammar_by_idx)?;
 
+        assert!(lexer_spec.grammar_warnings.is_empty());
+        lexer_spec.grammar_warnings = warnings;
+
         Ok((grammar, lexer_spec))
     }
 }
 
+#[derive(Debug, Clone)]
+pub enum ValidationResult {
+    Valid,
+    Warnings(Vec<String>),
+    Error(String),
+}
+
+impl ValidationResult {
+    pub fn from_warning(w: Vec<String>) -> Self {
+        if w.is_empty() {
+            ValidationResult::Valid
+        } else {
+            ValidationResult::Warnings(w)
+        }
+    }
+
+    pub fn into_tuple(self) -> (bool, Vec<String>) {
+        match self {
+            ValidationResult::Valid => (false, vec![]),
+            ValidationResult::Warnings(w) => (false, w),
+            ValidationResult::Error(e) => (true, vec![e]),
+        }
+    }
+
+    pub fn into_error(self) -> Option<String> {
+        match self {
+            ValidationResult::Valid => None,
+            ValidationResult::Warnings(_) => None,
+            ValidationResult::Error(e) => Some(e),
+        }
+    }
+
+    pub fn render(&self, with_warnings: bool) -> String {
+        match self {
+            ValidationResult::Valid => String::new(),
+            ValidationResult::Warnings(w) => {
+                if with_warnings {
+                    w.iter()
+                        .map(|w| format!("WARNING: {}", w))
+                        .collect::<Vec<_>>()
+                        .join("\n")
+                } else {
+                    String::new()
+                }
+            }
+            ValidationResult::Error(e) => format!("ERROR: {}", e),
+        }
+    }
+}
+
 impl GrammarInit {
     pub fn to_internal(
         self,
@@ -119,6 +167,13 @@
         }
     }
 
+    pub fn validate(self, tok_env: Option<TokEnv>, limits: ParserLimits) -> ValidationResult {
+        match self.to_internal(tok_env, limits) {
+            Ok((_, lex_spec)) => ValidationResult::from_warning(lex_spec.render_warnings()),
+            Err(e) => ValidationResult::Error(e.to_string()),
+        }
+    }
+
     pub fn to_cgrammar(
         self,
         tok_env: Option<TokEnv>,
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/lexerspec.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/lexerspec.rs
index a6cf429..8e51f54 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/lexerspec.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/lexerspec.rs
@@ -25,6 +25,7 @@
     pub has_stop: bool,
     pub has_max_tokens: bool,
     pub has_temperature: bool,
+    pub grammar_warnings: Vec<(String, usize)>,
 }
 
 #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
@@ -142,9 +143,28 @@
             has_stop: false,
             has_max_tokens: false,
             has_temperature: false,
+            grammar_warnings: Vec::new(),
         })
     }
 
+    pub fn render_warnings(&self) -> Vec<String> {
+        let mut total_len = 0;
+        let mut r = vec![];
+        for (msg, count) in &self.grammar_warnings {
+            let mut s = msg.clone();
+            if count > &1 {
+                s.push_str(&format!(" ({} times)", count));
+            }
+            total_len += s.len();
+            r.push(s);
+            if total_len > 16 * 1024 {
+                r.push("...".to_string());
+                break;
+            }
+        }
+        r
+    }
+
     pub fn can_rollback(&self) -> bool {
         !self.has_stop && !self.has_max_tokens
     }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/mod.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/mod.rs
index fbace8f..f5ceaa6 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/mod.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/mod.rs
@@ -8,6 +8,7 @@
 pub mod perf;
 pub mod regexvec;
 
+pub use from_guidance::ValidationResult;
 #[allow(unused_imports)]
 pub use grammar::{CGrammar, CSymIdx, Grammar, SymIdx, SymbolProps};
 pub use parser::{
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs
index 89de233..bebf30a 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/parser.rs
@@ -67,6 +67,11 @@
     data: u64,
 }
 
+#[derive(Clone)]
+struct SavedParserState {
+    lexer_stack_length: usize,
+}
+
 #[derive(Debug, Default, Serialize, Deserialize, Clone)]
 pub struct ParserStats {
     pub compute_time_us: u64,
@@ -592,19 +597,25 @@
         let mut lexer = Lexer::from(grammar.lexer_spec(), &mut limits, true)?;
         if limits.precompute_large_lexemes {
             let t0 = crate::Instant::now();
+            lexer.dfa.set_fuel(limits.initial_lexer_fuel);
             for spec in &grammar.lexer_spec().lexemes {
                 let w = lexer.dfa.lexeme_weight(spec.idx);
                 if w > 1000 {
                     // println!(
-                    //     "precomputing lexeme {} (w={w})",
-                    //     lexer.lexer_spec().lexeme_def_to_string(spec.idx)
+                    //     "precomputing lexeme {} (w={w}) f={}",
+                    //     lexer.lexer_spec().lexeme_def_to_string(spec.idx),
+                    //     lexer.dfa.get_fuel()
                     // );
                     let mut allowed = grammar.lexer_spec().alloc_lexeme_set();
                     allowed.add(spec.idx);
                     lexer.precompute_for(tok_env.tok_trie(), &allowed);
+                    // println!("fuel={}", lexer.dfa.get_fuel());
                 }
             }
             perf_counters.precompute.record(t0.elapsed());
+            if lexer.dfa.has_error() {
+                bail!("lexer precomputation failed; either increase limits.initial_lexer_fuel or disable limits.precompute_large_lexemes");
+            }
         }
         let scratch = Scratch::new(Arc::clone(&grammar));
         let lexer_state = lexer.a_dead_state(); // placeholder
@@ -758,10 +769,7 @@
         if start.is_empty() {
             self.run_speculative("token_ranges", |state| {
                 if state.flush_lexer() {
-                    let possible = state
-                        .lexer()
-                        .possible_lexemes(state.lexer_state().lexer_state);
-                    for spec in state.lexer_spec().token_range_lexemes(possible) {
+                    for spec in state.token_range_lexemes() {
                         for range in &spec.token_ranges {
                             set.allow_range(range.clone());
                         }
@@ -996,66 +1004,58 @@
         self.assert_definitive();
         self.run_speculative("validate_tokens", |state| {
             let mut applied_idx = state.byte_to_token_idx.len();
-            let eos = state.tok_env.tok_trie().eos_token();
-            let mut r = ParserRecognizer { state };
-            'token: for (tidx, &tok) in tokens.iter().enumerate() {
+            let tok_env = state.tok_env.clone();
+            let trie = tok_env.tok_trie();
+            let eos = trie.eos_token();
+            let mut recog = ParserRecognizer { state };
+            for (tidx, &tok) in tokens.iter().enumerate() {
+                let state = &mut recog.state;
                 if tok == eos {
-                    if applied_idx == r.state.bytes.len() && r.state.is_accepting_inner() {
+                    if applied_idx == state.bytes.len() && state.is_accepting_inner() {
                         return tidx + 1;
                     } else {
                         return tidx;
                     }
                 }
 
-                let token_bytes = r.state.tok_env.tok_trie().decode_raw(&[tok]);
+                if applied_idx >= state.bytes.len() {
+                    let saved_parser_state = state.save_state();
 
-                let token_bytes = if applied_idx < r.state.bytes.len()
-                    && r.state.bytes[applied_idx] == TokTrie::SPECIAL_TOKEN_MARKER
+                    if let Some(idx) = state.flush_and_check_numeric(tok) {
+                        let numeric_bytes = trie.decode_as_special(tok);
+                        let ok = state.add_numeric_token(idx, &numeric_bytes);
+                        assert!(ok.is_ok());
+                        continue; // next token please
+                    }
+
+                    state.restore_state(saved_parser_state);
+                }
+
+                let token_bytes = trie.decode_raw(&[tok]);
+
+                let token_bytes = if applied_idx < state.bytes.len()
+                    && state.bytes[applied_idx] == TokTrie::SPECIAL_TOKEN_MARKER
                 {
-                    r.state.tok_env.tok_trie().decode_as_special(tok)
+                    trie.decode_as_special(tok)
                 } else {
                     token_bytes
                 };
 
-                'byte: for (bidx, &b) in token_bytes.iter().enumerate() {
-                    if applied_idx < r.state.bytes.len() {
-                        if r.state.bytes[applied_idx] == b {
+                for &b in &token_bytes {
+                    if applied_idx < recog.state.bytes.len() {
+                        if recog.state.bytes[applied_idx] == b {
                             applied_idx += 1;
                         } else {
                             return tidx;
                         }
                     } else {
                         // never push FF
-                        if b != TokTrie::SPECIAL_TOKEN_MARKER && r.try_push_byte(b) {
+                        if b != TokTrie::SPECIAL_TOKEN_MARKER && recog.try_push_byte(b) {
                             // normal path
-                            continue 'byte;
-                        }
-
-                        if bidx != 0 {
-                            // not at the start of the token, bail
+                            continue;
+                        } else {
                             return tidx;
                         }
-
-                        if !r.state.flush_lexer() {
-                            // we need to flush lexer before checking for special/numeric tokens
-                            return tidx;
-                        }
-
-                        for spec in r.state.token_range_lexemes() {
-                            if spec.contains_token(tok) {
-                                let numeric_bytes =
-                                    r.state.tok_env.tok_trie().decode_as_special(tok);
-                                let ok = r.state.add_numeric_token(spec.idx, &numeric_bytes);
-                                if ok.is_ok() {
-                                    continue 'token;
-                                } else {
-                                    unreachable!(); // ???
-                                }
-                            }
-                        }
-
-                        // didn't find numeric token
-                        return tidx;
                     }
                 }
             }
@@ -1093,10 +1093,21 @@
         Ok(())
     }
 
+    fn flush_and_check_numeric(&mut self, tok_id: TokenId) -> Option<LexemeIdx> {
+        if self.flush_lexer() {
+            for spec in self.token_range_lexemes() {
+                if spec.contains_token(tok_id) {
+                    return Some(spec.idx);
+                }
+            }
+        }
+        None
+    }
+
     // apply_tokens() "pushes" the bytes in 'tokens' into the lexer and parser.  It is a top-level
     // method in this file.  It is well below llguidance's top-level methods, but in the llguidance
     // LLInterpreter interface, it is called indirectly via the commit_token() method.
-    pub fn apply_token(&mut self, tok_bytes: &[u8]) -> Result<usize> {
+    pub fn apply_token(&mut self, tok_bytes: &[u8], tok_id: TokenId) -> Result<usize> {
         self.assert_definitive();
 
         item_trace!("apply_token: {:?}", String::from_utf8_lossy(tok_bytes));
@@ -1114,6 +1125,28 @@
             row_to_apply -= 1;
         }
 
+        // tok_id normally matches tok_bytes, except when the caller was handling
+        // some byte prefix
+        if self.tok_env.tok_trie().token(tok_id) == tok_bytes
+            && self.byte_to_token_idx.len() == self.bytes.len()
+        {
+            let applies = self
+                .run_speculative("numeric_apply_token", |state| {
+                    state.flush_and_check_numeric(tok_id)
+                })
+                .is_some();
+            if applies {
+                // non-speculative now
+                let row_idx = self.num_rows() - 1;
+                self.row_infos[row_idx].apply_token_idx(self.token_idx);
+
+                let idx = self.flush_and_check_numeric(tok_id).unwrap();
+                self.add_numeric_token(idx, tok_bytes)?;
+
+                return Ok(0);
+            }
+        }
+
         for (bidx, &b) in tok_bytes.iter().enumerate() {
             check_lexer_max_tokens = false;
             let applied_idx = self.byte_to_token_idx.len();
@@ -1126,17 +1159,6 @@
 
                 let (ok, bt) = self.try_push_byte_definitive(Some(b));
                 if !ok {
-                    if bidx == 0 {
-                        let toks = self.tok_env.tok_trie().greedy_tokenize(tok_bytes);
-                        if self.flush_lexer() && toks.len() == 1 {
-                            for spec in self.token_range_lexemes() {
-                                if spec.contains_token(toks[0]) {
-                                    self.add_numeric_token(spec.idx, tok_bytes)?;
-                                    return Ok(0);
-                                }
-                            }
-                        }
-                    }
                     bail!(
                         "token {:?} doesn't satisfy the grammar; byte {:?} fails parse",
                         String::from_utf8_lossy(tok_bytes),
@@ -1614,6 +1636,16 @@
         slow_res
     }
 
+    fn save_state(&self) -> SavedParserState {
+        SavedParserState {
+            lexer_stack_length: self.lexer_stack.len(),
+        }
+    }
+
+    fn restore_state(&mut self, state: SavedParserState) {
+        self.lexer_stack.truncate(state.lexer_stack_length);
+    }
+
     /// Advance the parser as if the current lexeme (if any)
     /// finished right here.
     /// Returns true if the parser was able to advance (or there were no pending bytes for a lexeme).
@@ -2699,6 +2731,10 @@
         self.with_shared(|state| state.scan_eos())
     }
 
+    pub fn grammar_warnings(&mut self) -> Vec<String> {
+        self.with_shared(|state| state.lexer_spec().render_warnings())
+    }
+
     pub(crate) fn apply_forced(&mut self, byte_idx: usize) {
         self.state.byte_to_token_idx.resize(byte_idx, 0);
     }
@@ -2710,8 +2746,8 @@
         self.state.byte_to_token_idx.truncate(new_len);
     }
 
-    pub fn apply_token(&mut self, tok_bytes: &[u8]) -> Result<usize> {
-        let r = self.with_shared(|state| state.apply_token(tok_bytes));
+    pub fn apply_token(&mut self, tok_bytes: &[u8], tok_id: TokenId) -> Result<usize> {
+        let r = self.with_shared(|state| state.apply_token(tok_bytes, tok_id));
         self.state.token_idx += 1;
         r
     }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/regexvec.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/regexvec.rs
index d33ccef..6874fe31 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/regexvec.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/earley/regexvec.rs
@@ -509,6 +509,10 @@
         }
     }
 
+    pub fn get_fuel(&self) -> u64 {
+        self.fuel
+    }
+
     pub fn has_error(&self) -> bool {
         self.alpha.has_error()
     }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs
index c515cb8..d48c438 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/ffi.rs
@@ -11,7 +11,7 @@
 
 use crate::{
     api::{GrammarInit, ParserLimits, TopLevelGrammar},
-    earley::SlicedBiasComputer,
+    earley::{SlicedBiasComputer, ValidationResult},
     CommitResult, Constraint, Logger, Matcher, ParserFactory, StopController, TokenParser,
 };
 
@@ -170,17 +170,21 @@
         })
     }
 
-    fn to_env(&self) -> TokEnv {
+    pub fn to_env(&self) -> TokEnv {
         self.factory.tok_env().clone()
     }
 
-    fn tok_env(&self) -> &TokEnv {
+    pub fn tok_env(&self) -> &TokEnv {
         self.factory.tok_env()
     }
 
-    fn tok_trie(&self) -> &TokTrie {
+    pub fn tok_trie(&self) -> &TokTrie {
         self.factory.tok_env().tok_trie()
     }
+
+    pub fn factory(&self) -> &ParserFactory {
+        &self.factory
+    }
 }
 
 pub type LlgToken = u32;
@@ -644,8 +648,10 @@
 }
 
 /// Construct a new tokenizer from the given TokenizerInit
+/// # Safety
+/// This function should only be called from C code.
 #[no_mangle]
-pub extern "C" fn llg_new_tokenizer(
+pub unsafe extern "C" fn llg_new_tokenizer(
     tok_init: &LlgTokenizerInit,
     error_string: *mut c_char,
     error_string_len: usize,
@@ -849,7 +855,16 @@
     StopController::new(tokenizer.to_env(), stop_tokens.to_vec(), stop_rx, vec![])
 }
 
-fn save_error_string(e: impl Display, error_string: *mut c_char, error_string_len: usize) {
+/// Save the error string to the given pointer.
+/// The string is NUL-terminated.
+/// The function will write at most error_string_len bytes (including the NUL).
+/// # Safety
+/// This function should only when interacting with pointers passed from C.
+pub unsafe fn save_error_string(
+    e: impl Display,
+    error_string: *mut c_char,
+    error_string_len: usize,
+) {
     if !error_string.is_null() && error_string_len > 0 {
         let e = e.to_string();
         let e = e.as_bytes();
@@ -979,6 +994,55 @@
     }))
 }
 
+fn validate_grammar(
+    init: &LlgConstraintInit,
+    constraint_type: *const c_char,
+    data: *const c_char,
+) -> Result<String> {
+    let tp = unsafe { c_str_to_str(constraint_type, "constraint_type") }?;
+    let data = unsafe { c_str_to_str(data, "data") }?;
+    let grammar = TopLevelGrammar::from_tagged_str(tp, data)?;
+    let tok_env = init.factory()?.tok_env().clone();
+    match GrammarInit::Serialized(grammar).validate(Some(tok_env), init.limits.clone()) {
+        ValidationResult::Valid => Ok(String::new()),
+        ValidationResult::Error(e) => bail!(e),
+        r => Ok(r.render(true)),
+    }
+}
+
+/// Check if given grammar is valid.
+/// This about twice as fast as creating a matcher (which also validates).
+/// See llg_new_matcher() for the grammar format.
+/// Returns 0 on success and -1 on error and 1 on warning.
+/// The error message or warning is written to message, which is message_len bytes long.
+/// It's always NUL-terminated.
+/// # Safety
+/// This function should only be called from C code.
+#[no_mangle]
+pub unsafe extern "C" fn llg_validate_grammar(
+    init: &LlgConstraintInit,
+    constraint_type: *const c_char,
+    data: *const c_char,
+    message: *mut c_char,
+    message_len: usize,
+) -> i32 {
+    match validate_grammar(init, constraint_type, data) {
+        Err(e) => {
+            save_error_string(e, message, message_len);
+            -1
+        }
+        Ok(s) => {
+            if !s.is_empty() {
+                save_error_string(s, message, message_len);
+                1
+            } else {
+                save_error_string("", message, message_len);
+                0
+            }
+        }
+    }
+}
+
 /// Compute the set of allowed tokens for the current state.
 /// The result is written to mask_dest.
 /// mask_byte_len must be equal to llg_matcher_get_mask_byte_size().
@@ -1012,7 +1076,7 @@
 }
 
 /// Compute the set of allowed tokens for the current state.
-/// The pointer to the result is written to mask_dest.
+/// Use llg_matcher_get_mask() to get the result.
 /// Returns 0 on success and -1 on error.
 #[no_mangle]
 pub extern "C" fn llg_matcher_compute_mask(matcher: &mut LlgMatcher) -> i32 {
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs
index 592945f..bdec2279 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/grammar_builder.rs
@@ -38,6 +38,7 @@
     pub regex: RegexBuilder,
     tok_env: Option<TokEnv>,
     limits: ParserLimits,
+    warnings: HashMap<String, usize>,
 
     strings: HashMap<String, NodeRef>,
     at_most_cache: HashMap<(NodeRef, usize), NodeRef>,
@@ -150,6 +151,7 @@
             regex: RegexBuilder::new(),
             at_most_cache: HashMap::default(),
             repeat_exact_cache: HashMap::default(),
+            warnings: HashMap::default(),
             limits,
             tok_env,
         }
@@ -172,6 +174,17 @@
         Ok(())
     }
 
+    pub fn add_warning(&mut self, msg: String) {
+        let count = self.warnings.entry(msg).or_insert(0);
+        *count += 1;
+    }
+
+    pub fn get_warnings(&self) -> Vec<(String, usize)> {
+        let mut warnings: Vec<_> = self.warnings.iter().map(|(k, v)| (k.clone(), *v)).collect();
+        warnings.sort_by(|a, b| a.0.cmp(&b.0));
+        warnings
+    }
+
     pub fn add_grammar(&mut self, options: LLGuidanceOptions, skip: RegexAst) -> Result<SymIdx> {
         self.check_limits()?;
 
@@ -256,6 +269,10 @@
             }
         }
 
+        if trie.is_none() {
+            self.add_warning("no tokenizer - can't validate <[...]>".to_string());
+        }
+
         let id = self.regex.spec.add_special_token(name, token_ranges)?;
         Ok(self.lexeme_to_node(id))
     }
@@ -276,6 +293,7 @@
                 );
             }
         } else {
+            self.add_warning("no tokenizer - can't validate <special_token>".to_string());
             INVALID_TOKEN
         };
 
@@ -370,6 +388,9 @@
 
     pub fn select(&mut self, options: &[NodeRef]) -> NodeRef {
         let ch = self.child_nodes(options);
+        if options.len() == 1 {
+            return options[0];
+        }
         let r = self.new_node("");
         let empty = self.empty().idx;
         for n in &ch {
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/compiler.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/compiler.rs
index 02681b2..42ae03b 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/compiler.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/compiler.rs
@@ -1,14 +1,16 @@
 use crate::api::LLGuidanceOptions;
 use crate::grammar_builder::GrammarResult;
-use crate::HashMap;
-use anyhow::{anyhow, Context, Result};
+use crate::json::schema::{NumberSchema, StringSchema};
+use crate::{regex_to_lark, HashMap};
+use anyhow::{anyhow, bail, Context, Result};
 use derivre::{JsonQuoteOptions, RegexAst};
-use indexmap::IndexMap;
+use indexmap::{IndexMap, IndexSet};
 use serde::{Deserialize, Serialize};
 use serde_json::{json, Value};
 
-use super::numeric::{check_number_bounds, rx_float_range, rx_int_range, Decimal};
-use super::schema::{build_schema, Schema};
+use super::numeric::{check_number_bounds, rx_float_range, rx_int_range};
+use super::schema::{build_schema, ArraySchema, ObjectSchema, OptSchemaExt, Schema};
+use super::shared_context::PatternPropertyCache;
 use super::RetrieveWrapper;
 
 use crate::{GrammarBuilder, NodeRef};
@@ -25,6 +27,7 @@
     pub whitespace_flexible: bool,
     pub whitespace_pattern: Option<String>,
     pub coerce_one_of: bool,
+    pub lenient: bool,
     #[serde(skip)]
     pub retriever: Option<RetrieveWrapper>,
 }
@@ -51,6 +54,7 @@
     options: JsonCompileOptions,
     definitions: HashMap<String, NodeRef>,
     pending_definitions: Vec<(String, NodeRef)>,
+    pattern_cache: PatternPropertyCache,
 
     any_cache: Option<NodeRef>,
     string_cache: Option<NodeRef>,
@@ -73,12 +77,28 @@
             whitespace_pattern: None,
             whitespace_flexible: true,
             coerce_one_of: false,
+            lenient: false,
             retriever: None,
         }
     }
 }
 
 impl JsonCompileOptions {
+    pub fn json_to_llg_with_overrides(
+        &self,
+        builder: GrammarBuilder,
+        mut schema: Value,
+    ) -> Result<GrammarResult> {
+        if let Some(x_guidance) = schema.get("x-guidance") {
+            let opts: Self = serde_json::from_value(x_guidance.clone())?;
+            // TODO: figure out why not removing this still causes problems in maskbench
+            schema.as_object_mut().unwrap().remove("x-guidance");
+            opts.json_to_llg(builder, schema)
+        } else {
+            self.json_to_llg(builder, schema)
+        }
+    }
+
     pub fn json_to_llg(&self, builder: GrammarBuilder, schema: Value) -> Result<GrammarResult> {
         let compiler = Compiler::new(self.clone(), builder);
         #[cfg(feature = "jsonschema_validation")]
@@ -116,6 +136,7 @@
             pending_definitions: vec![],
             any_cache: None,
             string_cache: None,
+            pattern_cache: PatternPropertyCache::default(),
         }
     }
 
@@ -131,16 +152,25 @@
             .builder
             .add_grammar(LLGuidanceOptions::default(), skip)?;
 
-        let (compiled_schema, definitions) = build_schema(schema, self.options.retriever.clone())?;
+        let built = build_schema(schema, &self.options)?;
+        self.pattern_cache = built.pattern_cache;
 
-        let root = self.gen_json(&compiled_schema)?;
+        for w in built.warnings {
+            self.builder.add_warning(w);
+        }
+
+        let root = self.gen_json(&built.schema)?;
         self.builder.set_start_node(root);
 
         while let Some((path, pl)) = self.pending_definitions.pop() {
-            let schema = definitions
+            let schema = built
+                .definitions
                 .get(&path)
                 .ok_or_else(|| anyhow!("Definition not found: {}", path))?;
-            let compiled = self.gen_json(schema)?;
+            let compiled = self.gen_json(schema).map_err(|e| {
+                let top_level = anyhow!("{e}\n  while processing {path}");
+                e.context(top_level)
+            })?;
             self.builder.set_placeholder(pl, compiled);
         }
 
@@ -153,47 +183,26 @@
         }
         match json_schema {
             Schema::Any => Ok(self.gen_json_any()),
-            Schema::Unsatisfiable { reason } => Err(anyhow!(UnsatisfiableSchemaError {
+            Schema::Unsatisfiable(reason) => Err(anyhow!(UnsatisfiableSchemaError {
                 message: reason.to_string(),
             })),
 
-            Schema::Array {
-                min_items,
-                max_items,
-                prefix_items,
-                items,
-            } => self.gen_json_array(
-                prefix_items,
-                items.as_deref().unwrap_or(&Schema::Any),
-                *min_items,
-                *max_items,
-            ),
-            Schema::Object {
-                properties,
-                additional_properties,
-                required,
-            } => self.gen_json_object(
-                properties,
-                additional_properties.as_deref().unwrap_or(&Schema::Any),
-                required.iter().cloned().collect(),
-            ),
+            Schema::Array(arr) => self.gen_json_array(arr),
+            Schema::Object(obj) => self.gen_json_object(obj),
+            Schema::AnyOf(options) => self.process_any_of(options),
+            Schema::OneOf(options) => self.process_one_of(options),
+            Schema::Ref(uri) => self.get_definition(uri),
 
-            Schema::AnyOf { options } => self.process_any_of(options),
-            Schema::OneOf { options } => self.process_one_of(options),
-            Schema::Ref { uri, .. } => self.get_definition(uri),
-
-            Schema::Null
-            | Schema::Boolean
-            | Schema::LiteralBool { .. }
-            | Schema::String { .. }
-            | Schema::Number { .. } => {
+            Schema::Null | Schema::Boolean(_) | Schema::String(_) | Schema::Number(_) => {
                 unreachable!("should be handled in regex_compile()")
             }
         }
     }
 
     fn process_one_of(&mut self, options: &[Schema]) -> Result<NodeRef> {
-        if self.options.coerce_one_of {
+        if self.options.coerce_one_of || self.options.lenient {
+            self.builder
+                .add_warning("oneOf not fully supported, falling back to anyOf. This may cause validation errors in some cases.".to_string());
             self.process_any_of(options)
         } else {
             Err(anyhow!("oneOf constraints are not supported. Enable 'coerce_one_of' option to approximate oneOf with anyOf"))
@@ -249,28 +258,13 @@
         }
     }
 
-    fn json_int(
-        &mut self,
-        minimum: Option<f64>,
-        maximum: Option<f64>,
-        exclusive_minimum: bool,
-        exclusive_maximum: bool,
-        multiple_of: Option<Decimal>,
-    ) -> Result<RegexAst> {
-        check_number_bounds(
-            minimum,
-            maximum,
-            exclusive_minimum,
-            exclusive_maximum,
-            false,
-            multiple_of.clone(),
-        )
-        .map_err(|e| {
+    fn json_int(&mut self, num: &NumberSchema) -> Result<RegexAst> {
+        check_number_bounds(num).map_err(|e| {
             anyhow!(UnsatisfiableSchemaError {
                 message: e.to_string(),
             })
         })?;
-        let minimum = match (minimum, exclusive_minimum) {
+        let minimum = match num.get_minimum() {
             (Some(min_val), true) => {
                 if min_val.fract() != 0.0 {
                     Some(min_val.ceil())
@@ -282,7 +276,7 @@
             _ => None,
         }
         .map(|val| val as i64);
-        let maximum = match (maximum, exclusive_maximum) {
+        let maximum = match num.get_maximum() {
             (Some(max_val), true) => {
                 if max_val.fract() != 0.0 {
                     Some(max_val.floor())
@@ -301,33 +295,20 @@
             )
         })?;
         let mut ast = RegexAst::Regex(rx);
-        if let Some(d) = multiple_of {
+        if let Some(d) = num.multiple_of.as_ref() {
             ast = RegexAst::And(vec![ast, RegexAst::MultipleOf(d.coef, d.exp)]);
         }
         Ok(ast)
     }
 
-    fn json_number(
-        &mut self,
-        minimum: Option<f64>,
-        maximum: Option<f64>,
-        exclusive_minimum: bool,
-        exclusive_maximum: bool,
-        multiple_of: Option<Decimal>,
-    ) -> Result<RegexAst> {
-        check_number_bounds(
-            minimum,
-            maximum,
-            exclusive_minimum,
-            exclusive_maximum,
-            false,
-            multiple_of.clone(),
-        )
-        .map_err(|e| {
+    fn json_number(&mut self, num: &NumberSchema) -> Result<RegexAst> {
+        check_number_bounds(num).map_err(|e| {
             anyhow!(UnsatisfiableSchemaError {
                 message: e.to_string(),
             })
         })?;
+        let (minimum, exclusive_minimum) = num.get_minimum();
+        let (maximum, exclusive_maximum) = num.get_maximum();
         let rx = rx_float_range(minimum, maximum, !exclusive_minimum, !exclusive_maximum)
             .with_context(|| {
                 format!(
@@ -336,7 +317,7 @@
                 )
             })?;
         let mut ast = RegexAst::Regex(rx);
-        if let Some(d) = multiple_of {
+        if let Some(d) = num.multiple_of.as_ref() {
             ast = RegexAst::And(vec![ast, RegexAst::MultipleOf(d.coef, d.exp)]);
         }
         Ok(ast)
@@ -368,16 +349,29 @@
         cache!(self.any_cache, {
             let json_any = self.builder.new_node("json_any");
             self.any_cache = Some(json_any); // avoid infinite recursion
-            let num = self.json_number(None, None, false, false, None).unwrap();
+            let num = self.json_number(&NumberSchema::default()).unwrap();
             let tf = self.builder.regex.regex("true|false").unwrap();
             let options = vec![
                 self.builder.string("null"),
                 self.builder.lexeme(tf),
                 self.ast_lexeme(num).unwrap(),
                 self.json_simple_string(),
-                self.gen_json_array(&[], &Schema::Any, 0, None).unwrap(),
-                self.gen_json_object(&IndexMap::new(), &Schema::Any, vec![])
-                    .unwrap(),
+                self.gen_json_array(&ArraySchema {
+                    min_items: 0,
+                    max_items: None,
+                    prefix_items: vec![],
+                    items: Schema::any_box(),
+                })
+                .unwrap(),
+                self.gen_json_object(&ObjectSchema {
+                    properties: IndexMap::new(),
+                    additional_properties: Schema::any_box(),
+                    required: IndexSet::new(),
+                    pattern_properties: IndexMap::new(),
+                    min_properties: 0,
+                    max_properties: None,
+                })
+                .unwrap(),
             ];
             let inner = self.builder.select(&options);
             self.builder.set_placeholder(json_any, inner);
@@ -385,21 +379,26 @@
         })
     }
 
-    fn gen_json_object(
-        &mut self,
-        properties: &IndexMap<String, Schema>,
-        additional_properties: &Schema,
-        required: Vec<String>,
-    ) -> Result<NodeRef> {
+    fn gen_json_object(&mut self, obj: &ObjectSchema) -> Result<NodeRef> {
         let mut taken_names: Vec<String> = vec![];
+        let mut unquoted_taken_names: Vec<String> = vec![];
         let mut items: Vec<(NodeRef, bool)> = vec![];
-        for name in properties.keys().chain(
-            required
+
+        let colon = self.builder.string(&self.options.key_separator);
+
+        let mut num_required = 0;
+        let mut num_optional = 0;
+
+        for name in obj.properties.keys().chain(
+            obj.required
                 .iter()
-                .filter(|n| !properties.contains_key(n.as_str())),
+                .filter(|n| !obj.properties.contains_key(n.as_str())),
         ) {
-            let property_schema = properties.get(name).unwrap_or(additional_properties);
-            let is_required = required.contains(name);
+            let property_schema = self.pattern_cache.property_schema(obj, name)?;
+            let is_required = obj.required.contains(name);
+            if !obj.pattern_properties.is_empty() {
+                unquoted_taken_names.push(name.to_string());
+            }
             // Quote (and escape) the name
             let quoted_name = json_dumps(&json!(name));
             let property = match self.gen_json(property_schema) {
@@ -422,12 +421,132 @@
             };
             let name = self.builder.string(&quoted_name);
             taken_names.push(quoted_name);
-            let colon = self.builder.string(&self.options.key_separator);
             let item = self.builder.join(&[name, colon, property]);
             items.push((item, is_required));
+            if is_required {
+                num_required += 1;
+            } else {
+                num_optional += 1;
+            }
         }
 
-        match self.gen_json(additional_properties) {
+        let min_properties = obj.min_properties.saturating_sub(num_required);
+        let max_properties = obj.max_properties.map(|v| v.saturating_sub(num_required));
+
+        if num_optional > 0 && (min_properties > 0 || max_properties.is_some()) {
+            // special case for min/maxProperties == 1
+            // this is sometimes used to indicate that at least one property is required
+            if min_properties <= 1
+                && max_properties.unwrap_or(1) == 1
+                && obj.pattern_properties.is_empty()
+                && obj
+                    .additional_properties
+                    .as_ref()
+                    .map(|s| s.is_unsat())
+                    .unwrap_or(false)
+            {
+                let mut options: Vec<Vec<(NodeRef, bool)>> = vec![];
+                if max_properties == Some(1) {
+                    // at most one
+                    for idx in 0..items.len() {
+                        let (_, required) = items[idx];
+                        if !required {
+                            options.push(
+                                items
+                                    .iter()
+                                    .enumerate()
+                                    .filter_map(|(i2, (n, r))| {
+                                        if i2 == idx || *r {
+                                            Some((*n, true))
+                                        } else {
+                                            None
+                                        }
+                                    })
+                                    .collect(),
+                            );
+                        }
+                    }
+                    if min_properties == 1 {
+                        // exactly one - done
+                    } else {
+                        // at most one - add empty option
+                        assert!(min_properties == 0);
+                        options.push(items.into_iter().filter(|(_, r)| *r).collect());
+                    }
+                } else {
+                    assert!(max_properties.is_none());
+                    assert!(min_properties == 1);
+                    // at least one
+                    for idx in 0..items.len() {
+                        let (_, required) = items[idx];
+                        if !required {
+                            options.push(
+                                items
+                                    .iter()
+                                    .enumerate()
+                                    .map(|(i2, (n, r))| (*n, *r || i2 == idx))
+                                    .collect(),
+                            );
+                        }
+                    }
+                }
+                let sel_options = options
+                    .iter()
+                    .map(|v| self.object_fields(v))
+                    .collect::<Vec<_>>();
+                return Ok(self.builder.select(&sel_options));
+            }
+
+            let msg = "min/maxProperties only supported when all keys listed in \"properties\" are required";
+            if self.options.lenient {
+                self.builder.add_warning(msg.to_string());
+            } else {
+                bail!(msg);
+            }
+        }
+
+        let mut taken_name_ids = taken_names
+            .iter()
+            .map(|n| self.builder.regex.literal(n.to_string()))
+            .collect::<Vec<_>>();
+
+        let mut pattern_options = vec![];
+        for (pattern, schema) in obj.pattern_properties.iter() {
+            let regex = self
+                .builder
+                .regex
+                .add_ast(self.json_quote(RegexAst::SearchRegex(regex_to_lark(pattern, "dw"))))?;
+            taken_name_ids.push(regex);
+
+            let schema = match self.gen_json(schema) {
+                Ok(r) => r,
+                Err(e) => match e.downcast_ref::<UnsatisfiableSchemaError>() {
+                    // If it's not an UnsatisfiableSchemaError, just propagate it normally
+                    None => return Err(e),
+                    // Property is optional; don't raise UnsatisfiableSchemaError but mark name as taken
+                    Some(_) => continue,
+                },
+            };
+
+            let exclude_names = unquoted_taken_names
+                .iter()
+                .enumerate()
+                .filter(|(_, name)| self.pattern_cache.is_match(pattern, name).unwrap_or(true))
+                .map(|(idx, _)| taken_name_ids[idx])
+                .collect::<Vec<_>>();
+            let regex = if exclude_names.is_empty() {
+                regex
+            } else {
+                let options = self.builder.regex.select(exclude_names);
+                let not_taken = self.builder.regex.not(options);
+                self.builder.regex.and(vec![regex, not_taken])
+            };
+
+            let name = self.builder.lexeme(regex);
+            pattern_options.push(self.builder.join(&[name, colon, schema]));
+        }
+
+        match self.gen_json(obj.additional_properties.schema_ref()) {
             Err(e) => {
                 if e.downcast_ref::<UnsatisfiableSchemaError>().is_none() {
                     // Propagate errors that aren't UnsatisfiableSchemaError
@@ -436,13 +555,9 @@
                 // Ignore UnsatisfiableSchemaError for additionalProperties
             }
             Ok(property) => {
-                let name = if taken_names.is_empty() {
+                let name = if taken_name_ids.is_empty() {
                     self.json_simple_string()
                 } else {
-                    let taken_name_ids = taken_names
-                        .iter()
-                        .map(|n| self.builder.regex.literal(n.to_string()))
-                        .collect::<Vec<_>>();
                     let taken = self.builder.regex.select(taken_name_ids);
                     let not_taken = self.builder.regex.not(taken);
                     let valid = self
@@ -452,16 +567,33 @@
                     let valid_and_not_taken = self.builder.regex.and(vec![valid, not_taken]);
                     self.builder.lexeme(valid_and_not_taken)
                 };
-                let colon = self.builder.string(&self.options.key_separator);
                 let item = self.builder.join(&[name, colon, property]);
-                let seq = self.sequence(item);
-                items.push((seq, false));
+                pattern_options.push(item);
             }
         }
+
+        if !pattern_options.is_empty() && max_properties != Some(0) {
+            let pattern = self.builder.select(&pattern_options);
+            let required = min_properties > 0;
+            let seq = self.bounded_sequence(pattern, min_properties, max_properties);
+            items.push((seq, required));
+        } else if min_properties > 0 {
+            return Err(anyhow!(UnsatisfiableSchemaError {
+                message: format!(
+                    "minProperties ({}) is greater than number of properties ({})",
+                    min_properties, num_required
+                ),
+            }));
+        }
+
+        Ok(self.object_fields(&items))
+    }
+
+    fn object_fields(&mut self, items: &[(NodeRef, bool)]) -> NodeRef {
         let opener = self.builder.string("{");
-        let inner = self.ordered_sequence(&items, false, &mut HashMap::default());
+        let inner = self.ordered_sequence(items, false, &mut HashMap::default());
         let closer = self.builder.string("}");
-        Ok(self.builder.join(&[opener, inner, closer]))
+        self.builder.join(&[opener, inner, closer])
     }
 
     #[allow(clippy::type_complexity)]
@@ -515,6 +647,20 @@
         node
     }
 
+    fn bounded_sequence(
+        &mut self,
+        item: NodeRef,
+        min_elts: usize,
+        max_elts: Option<usize>,
+    ) -> NodeRef {
+        let min_elts = min_elts.saturating_sub(1);
+        let max_elts = max_elts.map(|v| v.saturating_sub(1));
+        let comma = self.builder.string(&self.options.item_separator);
+        let item_comma = self.builder.join(&[item, comma]);
+        let item_comma_rep = self.builder.repeat(item_comma, min_elts, max_elts);
+        self.builder.join(&[item_comma_rep, item])
+    }
+
     fn sequence(&mut self, item: NodeRef) -> NodeRef {
         let comma = self.builder.string(&self.options.item_separator);
         let item_comma = self.builder.join(&[item, comma]);
@@ -541,87 +687,31 @@
 
         let r = match schema {
             Schema::Null => literal_regex("null"),
-            Schema::Boolean => Some(RegexAst::Regex("true|false".to_string())),
-            Schema::LiteralBool { value } => literal_regex(if *value { "true" } else { "false" }),
+            Schema::Boolean(None) => Some(RegexAst::Regex("true|false".to_string())),
+            Schema::Boolean(Some(value)) => literal_regex(if *value { "true" } else { "false" }),
 
-            Schema::Number {
-                minimum,
-                maximum,
-                exclusive_minimum,
-                exclusive_maximum,
-                integer,
-                multiple_of,
-            } => {
-                let (minimum, exclusive_minimum) = match (minimum, exclusive_minimum) {
-                    (Some(min), Some(xmin)) => {
-                        if xmin >= min {
-                            (Some(*xmin), true)
-                        } else {
-                            (Some(*min), false)
-                        }
-                    }
-                    (Some(min), None) => (Some(*min), false),
-                    (None, Some(xmin)) => (Some(*xmin), true),
-                    (None, None) => (None, false),
-                };
-                let (maximum, exclusive_maximum) = match (maximum, exclusive_maximum) {
-                    (Some(max), Some(xmax)) => {
-                        if xmax <= max {
-                            (Some(*xmax), true)
-                        } else {
-                            (Some(*max), false)
-                        }
-                    }
-                    (Some(max), None) => (Some(*max), false),
-                    (None, Some(xmax)) => (Some(*xmax), true),
-                    (None, None) => (None, false),
-                };
-                Some(if *integer {
-                    self.json_int(
-                        minimum,
-                        maximum,
-                        exclusive_minimum,
-                        exclusive_maximum,
-                        multiple_of.clone(),
-                    )?
-                } else {
-                    self.json_number(
-                        minimum,
-                        maximum,
-                        exclusive_minimum,
-                        exclusive_maximum,
-                        multiple_of.clone(),
-                    )?
-                })
-            }
+            Schema::Number(num) => Some(if num.integer {
+                self.json_int(num)?
+            } else {
+                self.json_number(num)?
+            }),
 
-            Schema::String {
-                min_length,
-                max_length,
-                regex,
-            } => {
-                return self
-                    .gen_json_string(*min_length, *max_length, regex.clone())
-                    .map(Some)
-            }
+            Schema::String(opts) => return self.gen_json_string(opts.clone()).map(Some),
 
             Schema::Any
-            | Schema::Unsatisfiable { .. }
-            | Schema::Array { .. }
-            | Schema::Object { .. }
-            | Schema::AnyOf { .. }
-            | Schema::OneOf { .. }
-            | Schema::Ref { .. } => None,
+            | Schema::Unsatisfiable(_)
+            | Schema::Array(_)
+            | Schema::Object(_)
+            | Schema::AnyOf(_)
+            | Schema::OneOf(_)
+            | Schema::Ref(_) => None,
         };
         Ok(r)
     }
 
-    fn gen_json_string(
-        &self,
-        min_length: u64,
-        max_length: Option<u64>,
-        regex: Option<RegexAst>,
-    ) -> Result<RegexAst> {
+    fn gen_json_string(&self, opts: StringSchema) -> Result<RegexAst> {
+        let min_length = opts.min_length;
+        let max_length = opts.max_length;
         if let Some(max_length) = max_length {
             if min_length > max_length {
                 return Err(anyhow!(UnsatisfiableSchemaError {
@@ -632,10 +722,10 @@
                 }));
             }
         }
-        if min_length == 0 && max_length.is_none() && regex.is_none() {
+        if min_length == 0 && max_length.is_none() && opts.regex.is_none() {
             return Ok(self.json_quote(RegexAst::Regex("(?s:.*)".to_string())));
         }
-        if let Some(mut ast) = regex {
+        if let Some(mut ast) = opts.regex {
             let mut positive = false;
 
             fn mk_rx_repr(ast: &RegexAst) -> String {
@@ -646,9 +736,9 @@
 
             // special-case literals - the length is easy to check
             if let RegexAst::Literal(s) = &ast {
-                let l = s.chars().count() as u64;
+                let l = s.chars().count();
 
-                if l < min_length || l > max_length.unwrap_or(u64::MAX) {
+                if l < min_length || l > max_length.unwrap_or(usize::MAX) {
                     return Err(anyhow!(UnsatisfiableSchemaError {
                         message: format!("Constant {:?} doesn't match length constraints", s)
                     }));
@@ -708,14 +798,9 @@
         }
     }
 
-    fn gen_json_array(
-        &mut self,
-        prefix_items: &[Schema],
-        item_schema: &Schema,
-        min_items: u64,
-        max_items: Option<u64>,
-    ) -> Result<NodeRef> {
-        let mut max_items = max_items;
+    fn gen_json_array(&mut self, arr: &ArraySchema) -> Result<NodeRef> {
+        let mut max_items = arr.max_items;
+        let min_items = arr.min_items;
 
         if let Some(max_items) = max_items {
             if min_items > max_items {
@@ -728,13 +813,13 @@
             }
         }
 
-        let additional_item_grm = match self.gen_json(item_schema) {
+        let additional_item_grm = match self.gen_json(arr.items.schema_ref()) {
             Ok(node) => Some(node),
             Err(e) => match e.downcast_ref::<UnsatisfiableSchemaError>() {
                 // If it's not an UnsatisfiableSchemaError, just propagate it normally
                 None => return Err(e),
                 // Item is optional; don't raise UnsatisfiableSchemaError
-                Some(_) if prefix_items.len() >= min_items as usize => None,
+                Some(_) if arr.prefix_items.len() >= min_items => None,
                 // Item is required; add context and propagate UnsatisfiableSchemaError
                 Some(_) => {
                     return Err(e.context(UnsatisfiableSchemaError {
@@ -748,21 +833,19 @@
         let mut optional_items = vec![];
 
         // If max_items is None, we can add an infinite tail of items later
-        let n_to_add = max_items.map_or(prefix_items.len().max(min_items as usize), |max| {
-            max as usize
-        });
+        let n_to_add = max_items.map_or(arr.prefix_items.len().max(min_items), |max| max);
 
         for i in 0..n_to_add {
-            let item = if i < prefix_items.len() {
-                match self.gen_json(&prefix_items[i]) {
+            let item = if i < arr.prefix_items.len() {
+                match self.gen_json(&arr.prefix_items[i]) {
                     Ok(node) => node,
                     Err(e) => match e.downcast_ref::<UnsatisfiableSchemaError>() {
                         // If it's not an UnsatisfiableSchemaError, just propagate it normally
                         None => return Err(e),
                         // Item is optional; don't raise UnsatisfiableSchemaError.
                         // Set max_items to the current index, as we can't satisfy any more items.
-                        Some(_) if i >= min_items as usize => {
-                            max_items = Some(i as u64);
+                        Some(_) if i >= min_items => {
+                            max_items = Some(i);
                             break;
                         }
                         // Item is required; add context and propagate UnsatisfiableSchemaError
@@ -782,7 +865,7 @@
                 break;
             };
 
-            if i < min_items as usize {
+            if i < min_items {
                 required_items.push(item);
             } else {
                 optional_items.push(item);
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/numeric.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/numeric.rs
index 472e0be..cc1f613 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/numeric.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/numeric.rs
@@ -1,6 +1,8 @@
 use anyhow::{anyhow, Result};
 use regex_syntax::escape;
 
+use super::schema::NumberSchema;
+
 /// coef * 10^-exp
 #[cfg_attr(test, derive(PartialEq))]
 #[derive(Debug, Clone)]
@@ -504,14 +506,9 @@
     }
 }
 
-pub fn check_number_bounds(
-    minimum: Option<f64>,
-    maximum: Option<f64>,
-    exclusive_minimum: bool,
-    exclusive_maximum: bool,
-    integer: bool,
-    multiple_of: Option<Decimal>,
-) -> Result<(), String> {
+pub fn check_number_bounds(num: &NumberSchema) -> Result<(), String> {
+    let (minimum, exclusive_minimum) = num.get_minimum();
+    let (maximum, exclusive_maximum) = num.get_maximum();
     if let (Some(min), Some(max)) = (minimum, maximum) {
         if min > max {
             return Err(format!(
@@ -536,7 +533,7 @@
             ));
         }
     }
-    if let Some(d) = multiple_of {
+    if let Some(d) = num.multiple_of.as_ref() {
         if d.coef == 0 {
             if let Some(min) = minimum {
                 if min > 0.0 || (exclusive_minimum && min >= 0.0) {
@@ -567,7 +564,7 @@
                 } else {
                     first_num_ge_min
                 };
-                if integer {
+                if num.integer {
                     adjusted_min.ceil()
                 } else {
                     adjusted_min
@@ -580,7 +577,7 @@
                 } else {
                     first_num_le_max
                 };
-                if integer {
+                if num.integer {
                     adjusted_max.floor()
                 } else {
                     adjusted_max
@@ -893,6 +890,8 @@
 
 #[cfg(test)]
 mod test_number_bounds {
+    use crate::json::schema::NumberSchema;
+
     use super::{check_number_bounds, Decimal};
 
     #[derive(Debug)]
@@ -906,6 +905,35 @@
         ok: bool,
     }
 
+    impl Case {
+        fn to_number_schema(&self) -> NumberSchema {
+            NumberSchema {
+                minimum: if self.exclusive_minimum {
+                    None
+                } else {
+                    self.minimum
+                },
+                maximum: if self.exclusive_maximum {
+                    None
+                } else {
+                    self.maximum
+                },
+                exclusive_minimum: if self.exclusive_minimum {
+                    self.minimum
+                } else {
+                    None
+                },
+                exclusive_maximum: if self.exclusive_maximum {
+                    self.maximum
+                } else {
+                    None
+                },
+                integer: self.integer,
+                multiple_of: self.multiple_of.clone(),
+            }
+        }
+    }
+
     #[test]
     fn test_check_number_bounds() {
         let cases = vec![
@@ -1142,14 +1170,7 @@
             },
         ];
         for case in cases {
-            let result = check_number_bounds(
-                case.minimum,
-                case.maximum,
-                case.exclusive_minimum,
-                case.exclusive_maximum,
-                case.integer,
-                case.multiple_of.clone(),
-            );
+            let result = check_number_bounds(&case.to_number_schema());
             assert_eq!(
                 result.is_ok(),
                 case.ok,
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/schema.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/schema.rs
index 9555c36e..b3842e12 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/schema.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/schema.rs
@@ -1,5 +1,5 @@
-use crate::{regex_to_lark, HashMap};
-use anyhow::{anyhow, bail, Result};
+use crate::{regex_to_lark, HashMap, JsonCompileOptions};
+use anyhow::{anyhow, bail, ensure, Result};
 use derivre::RegexAst;
 use indexmap::{IndexMap, IndexSet};
 use serde_json::Value;
@@ -8,12 +8,12 @@
 use super::context::{Context, Draft, PreContext, ResourceRef};
 use super::formats::lookup_format;
 use super::numeric::Decimal;
-use super::RetrieveWrapper;
+use super::shared_context::BuiltSchema;
 
 const TYPES: [&str; 6] = ["null", "boolean", "number", "string", "array", "object"];
 
 // Keywords that are implemented in this module
-pub(crate) const IMPLEMENTED: [&str; 24] = [
+pub(crate) const IMPLEMENTED: [&str; 27] = [
     // Core
     "anyOf",
     "oneOf",
@@ -31,7 +31,10 @@
     // Object
     "properties",
     "additionalProperties",
+    "patternProperties",
     "required",
+    "minProperties",
+    "maxProperties",
     // String
     "minLength",
     "maxLength",
@@ -78,60 +81,127 @@
 #[derive(Debug, Clone)]
 pub enum Schema {
     Any,
-    Unsatisfiable {
-        reason: String,
-    },
+    Unsatisfiable(String),
     Null,
-    Boolean,
-    Number {
-        minimum: Option<f64>,
-        maximum: Option<f64>,
-        exclusive_minimum: Option<f64>,
-        exclusive_maximum: Option<f64>,
-        multiple_of: Option<Decimal>,
-        integer: bool,
-    },
-    String {
-        min_length: u64,
-        max_length: Option<u64>,
-        regex: Option<RegexAst>,
-    },
-    Array {
-        min_items: u64,
-        max_items: Option<u64>,
-        prefix_items: Vec<Schema>,
-        items: Option<Box<Schema>>,
-    },
-    Object {
-        properties: IndexMap<String, Schema>,
-        additional_properties: Option<Box<Schema>>,
-        required: IndexSet<String>,
-    },
-    LiteralBool {
-        value: bool,
-    },
-    AnyOf {
-        options: Vec<Schema>,
-    },
-    OneOf {
-        options: Vec<Schema>,
-    },
-    Ref {
-        uri: String,
-    },
+    Number(NumberSchema),
+    String(StringSchema),
+    Array(ArraySchema),
+    Object(ObjectSchema),
+    Boolean(Option<bool>),
+    AnyOf(Vec<Schema>),
+    OneOf(Vec<Schema>),
+    Ref(String),
 }
 
-impl Schema {
-    pub fn false_schema() -> Schema {
-        Schema::Unsatisfiable {
-            reason: "schema is false".to_string(),
+#[derive(Debug, Clone, Default)]
+pub struct NumberSchema {
+    pub minimum: Option<f64>,
+    pub maximum: Option<f64>,
+    pub exclusive_minimum: Option<f64>,
+    pub exclusive_maximum: Option<f64>,
+    pub integer: bool,
+    pub multiple_of: Option<Decimal>,
+}
+
+impl NumberSchema {
+    pub fn get_minimum(&self) -> (Option<f64>, bool) {
+        match (self.minimum, self.exclusive_minimum) {
+            (Some(min), Some(xmin)) => {
+                if xmin >= min {
+                    (Some(xmin), true)
+                } else {
+                    (Some(min), false)
+                }
+            }
+            (Some(min), None) => (Some(min), false),
+            (None, Some(xmin)) => (Some(xmin), true),
+            (None, None) => (None, false),
         }
     }
 
-    /// Shallowly normalize the schema, removing any unnecessary nesting or empty options.
-    fn normalize(self) -> Schema {
+    pub fn get_maximum(&self) -> (Option<f64>, bool) {
+        match (self.maximum, self.exclusive_maximum) {
+            (Some(max), Some(xmax)) => {
+                if xmax <= max {
+                    (Some(xmax), true)
+                } else {
+                    (Some(max), false)
+                }
+            }
+            (Some(max), None) => (Some(max), false),
+            (None, Some(xmax)) => (Some(xmax), true),
+            (None, None) => (None, false),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct StringSchema {
+    pub min_length: usize,
+    pub max_length: Option<usize>,
+    pub regex: Option<RegexAst>,
+}
+
+#[derive(Debug, Clone)]
+pub struct ArraySchema {
+    pub min_items: usize,
+    pub max_items: Option<usize>,
+    pub prefix_items: Vec<Schema>,
+    pub items: Option<Box<Schema>>,
+}
+
+#[derive(Debug, Clone)]
+pub struct ObjectSchema {
+    pub properties: IndexMap<String, Schema>,
+    pub pattern_properties: IndexMap<String, Schema>,
+    pub additional_properties: Option<Box<Schema>>,
+    pub required: IndexSet<String>,
+    pub min_properties: usize,
+    pub max_properties: Option<usize>,
+}
+
+pub trait OptSchemaExt {
+    fn schema(&self) -> Schema;
+    fn schema_ref(&self) -> &Schema;
+}
+
+impl OptSchemaExt for Option<Box<Schema>> {
+    fn schema(&self) -> Schema {
         match self {
-            Schema::AnyOf { options } => {
+            Some(schema) => schema.as_ref().clone(),
+            None => Schema::Any,
+        }
+    }
+
+    fn schema_ref(&self) -> &Schema {
+        match self {
+            Some(schema) => schema.as_ref(),
+            None => &Schema::Any,
+        }
+    }
+}
+
+impl Schema {
+    pub fn unsat(reason: &str) -> Schema {
+        Schema::Unsatisfiable(reason.to_string())
+    }
+
+    pub fn false_schema() -> Schema {
+        Self::unsat("schema is false")
+    }
+
+    pub fn any_box() -> Option<Box<Schema>> {
+        Some(Box::new(Schema::Any))
+    }
+
+    pub fn is_unsat(&self) -> bool {
+        matches!(self, Schema::Unsatisfiable(_))
+    }
+
+    /// Shallowly normalize the schema, removing any unnecessary nesting or empty options.
+    fn normalize(self, ctx: &Context) -> Schema {
+        match self {
+            Schema::AnyOf(options) => {
                 let mut unsats = Vec::new();
                 let mut valid = Vec::new();
                 for option in options.into_iter() {
@@ -139,10 +209,8 @@
                         Schema::Any => {
                             return Schema::Any;
                         }
-                        Schema::Unsatisfiable { reason } => {
-                            unsats.push(Schema::Unsatisfiable { reason })
-                        }
-                        Schema::AnyOf { options: nested } => valid.extend(nested),
+                        Schema::Unsatisfiable(reason) => unsats.push(Schema::Unsatisfiable(reason)),
+                        Schema::AnyOf(nested) => valid.extend(nested),
                         other => valid.push(other),
                     }
                 }
@@ -152,26 +220,22 @@
                         return unsat;
                     }
                     // We must not have had any schemas to begin with
-                    return Schema::Unsatisfiable {
-                        reason: "anyOf is empty".to_string(),
-                    };
+                    return Schema::unsat("anyOf is empty");
                 }
                 if valid.len() == 1 {
                     // Unwrap singleton
                     return valid.swap_remove(0);
                 }
-                Schema::AnyOf { options: valid }
+                Schema::AnyOf(valid)
             }
-            Schema::OneOf { options } => {
+            Schema::OneOf(options) => {
                 let mut unsats = Vec::new();
                 let mut valid = Vec::new();
                 for option in options.into_iter() {
                     match option {
-                        Schema::Unsatisfiable { reason } => {
-                            unsats.push(Schema::Unsatisfiable { reason })
-                        }
+                        Schema::Unsatisfiable(reason) => unsats.push(Schema::Unsatisfiable(reason)),
                         // Flatten nested oneOfs: (A⊕B)⊕(C⊕D) = A⊕B⊕C⊕D
-                        Schema::OneOf { options: nested } => valid.extend(nested),
+                        Schema::OneOf(nested) => valid.extend(nested),
                         other => valid.push(other),
                     }
                 }
@@ -181,9 +245,7 @@
                         return unsat;
                     }
                     // We must not have had any schemas to begin with
-                    return Schema::Unsatisfiable {
-                        reason: "oneOf is empty".to_string(),
-                    };
+                    return Schema::unsat("oneOf is empty");
                 }
                 if valid.len() == 1 {
                     // Unwrap singleton
@@ -193,11 +255,11 @@
                     valid
                         .iter()
                         .skip(i + 1) // "upper diagonal"
-                        .all(|y| x.is_verifiably_disjoint_from(y))
+                        .all(|y| x.is_verifiably_disjoint_from(y, ctx))
                 }) {
-                    Schema::AnyOf { options: valid }
+                    Schema::AnyOf(valid)
                 } else {
-                    Schema::OneOf { options: valid }
+                    Schema::OneOf(valid)
                 }
             }
             other_schema => other_schema,
@@ -214,242 +276,204 @@
         let merged = match (self, other) {
             (Schema::Any, schema1) => schema1,
             (schema0, Schema::Any) => schema0,
-            (Schema::Unsatisfiable { reason }, _) => Schema::Unsatisfiable { reason },
-            (_, Schema::Unsatisfiable { reason }) => Schema::Unsatisfiable { reason },
-            (Schema::Ref { uri }, schema1) => {
+            (Schema::Unsatisfiable(reason), _) => Schema::Unsatisfiable(reason),
+            (_, Schema::Unsatisfiable(reason)) => Schema::Unsatisfiable(reason),
+            (Schema::Ref(uri), schema1) => {
                 intersect_ref(ctx, &uri, schema1, true, stack_level + 1)?
             }
-            (schema0, Schema::Ref { uri }) => {
+            (schema0, Schema::Ref(uri)) => {
                 intersect_ref(ctx, &uri, schema0, false, stack_level + 1)?
             }
-            (Schema::OneOf { options }, schema1) => Schema::OneOf {
-                options: options
+            (Schema::OneOf(options), schema1) => Schema::OneOf(
+                options
                     .into_iter()
                     .map(|opt| opt.intersect(schema1.clone(), ctx, stack_level + 1))
                     .collect::<Result<Vec<_>>>()?,
-            },
-            (schema0, Schema::OneOf { options }) => Schema::OneOf {
-                options: options
+            ),
+
+            (schema0, Schema::OneOf(options)) => Schema::OneOf(
+                options
                     .into_iter()
                     .map(|opt| schema0.clone().intersect(opt, ctx, stack_level + 1))
                     .collect::<Result<Vec<_>>>()?,
-            },
-            (Schema::AnyOf { options }, schema1) => Schema::AnyOf {
-                options: options
+            ),
+            (Schema::AnyOf(options), schema1) => Schema::AnyOf(
+                options
                     .into_iter()
                     .map(|opt| opt.intersect(schema1.clone(), ctx, stack_level + 1))
                     .collect::<Result<Vec<_>>>()?,
-            },
-            (schema0, Schema::AnyOf { options }) => Schema::AnyOf {
-                options: options
+            ),
+            (schema0, Schema::AnyOf(options)) => Schema::AnyOf(
+                options
                     .into_iter()
                     .map(|opt| schema0.clone().intersect(opt, ctx, stack_level + 1))
                     .collect::<Result<Vec<_>>>()?,
-            },
+            ),
             (Schema::Null, Schema::Null) => Schema::Null,
-            (Schema::Boolean, Schema::Boolean) => Schema::Boolean,
-            (Schema::Boolean, Schema::LiteralBool { value }) => Schema::LiteralBool { value },
-            (Schema::LiteralBool { value }, Schema::Boolean) => Schema::LiteralBool { value },
-            (Schema::LiteralBool { value: value1 }, Schema::LiteralBool { value: value2 }) => {
-                if value1 == value2 {
-                    Schema::LiteralBool { value: value1 }
+            (Schema::Boolean(value1), Schema::Boolean(value2)) => {
+                if value1 == value2 || value2.is_none() {
+                    Schema::Boolean(value1)
+                } else if value1.is_none() {
+                    Schema::Boolean(value2)
                 } else {
-                    Schema::Unsatisfiable {
-                        reason: "incompatible boolean values".to_string(),
-                    }
+                    Schema::unsat("incompatible boolean values")
                 }
             }
-            (
-                Schema::Number {
-                    minimum: min1,
-                    maximum: max1,
-                    exclusive_minimum: emin1,
-                    exclusive_maximum: emax1,
-                    integer: int1,
-                    multiple_of: mult1,
-                },
-                Schema::Number {
-                    minimum: min2,
-                    maximum: max2,
-                    exclusive_minimum: emin2,
-                    exclusive_maximum: emax2,
-                    integer: int2,
-                    multiple_of: mult2,
-                },
-            ) => Schema::Number {
-                minimum: opt_max(min1, min2),
-                maximum: opt_min(max1, max2),
-                exclusive_minimum: opt_max(emin1, emin2),
-                exclusive_maximum: opt_min(emax1, emax2),
-                integer: int1 || int2,
-                multiple_of: match (mult1, mult2) {
+
+            (Schema::Number(n1), Schema::Number(n2)) => Schema::Number(NumberSchema {
+                minimum: opt_max(n1.minimum, n2.minimum),
+                maximum: opt_min(n1.maximum, n2.maximum),
+                exclusive_minimum: opt_max(n1.exclusive_minimum, n2.exclusive_minimum),
+                exclusive_maximum: opt_min(n1.exclusive_maximum, n2.exclusive_maximum),
+                integer: n1.integer || n2.integer,
+                multiple_of: match (n1.multiple_of, n2.multiple_of) {
                     (None, None) => None,
-                    (None, Some(mult)) => Some(mult),
-                    (Some(mult), None) => Some(mult),
-                    (Some(mult1), Some(mult2)) => Some(mult1.lcm(&mult2)),
+                    (None, Some(m)) | (Some(m), None) => Some(m),
+                    (Some(m1), Some(m2)) => Some(m1.lcm(&m2)),
                 },
-            },
-            (
-                Schema::String {
-                    min_length: min1,
-                    max_length: max1,
-                    regex: r1,
-                },
-                Schema::String {
-                    min_length: min2,
-                    max_length: max2,
-                    regex: r2,
-                },
-            ) => Schema::String {
-                min_length: min1.max(min2),
-                max_length: opt_min(max1, max2),
-                regex: match (r1, r2) {
+            }),
+
+            (Schema::String(s1), Schema::String(s2)) => Schema::String(StringSchema {
+                min_length: s1.min_length.max(s2.min_length),
+                max_length: opt_min(s1.max_length, s2.max_length),
+                regex: match (s1.regex, s2.regex) {
                     (None, None) => None,
-                    (None, Some(r)) => Some(r),
-                    (Some(r), None) => Some(r),
+                    (None, Some(r)) | (Some(r), None) => Some(r),
                     (Some(r1), Some(r2)) => Some(RegexAst::And(vec![r1, r2])),
                 },
-            },
-            (
-                Schema::Array {
-                    min_items: min1,
-                    max_items: max1,
-                    prefix_items: mut prefix1,
-                    items: items1,
-                },
-                Schema::Array {
-                    min_items: min2,
-                    max_items: max2,
-                    prefix_items: mut prefix2,
-                    items: items2,
-                },
-            ) => Schema::Array {
-                min_items: min1.max(min2),
-                max_items: opt_min(max1, max2),
+            }),
+
+            (Schema::Array(mut a1), Schema::Array(mut a2)) => Schema::Array(ArraySchema {
+                min_items: a1.min_items.max(a2.min_items),
+                max_items: opt_min(a1.max_items, a2.max_items),
                 prefix_items: {
-                    let len = prefix1.len().max(prefix2.len());
-                    prefix1.resize_with(len, || items1.as_deref().cloned().unwrap_or(Schema::Any));
-                    prefix2.resize_with(len, || items2.as_deref().cloned().unwrap_or(Schema::Any));
-                    prefix1
+                    let len = a1.prefix_items.len().max(a2.prefix_items.len());
+                    a1.prefix_items.resize_with(len, || a1.items.schema());
+                    a2.prefix_items.resize_with(len, || a2.items.schema());
+                    a1.prefix_items
                         .into_iter()
-                        .zip(prefix2.into_iter())
+                        .zip(a2.prefix_items.into_iter())
                         .map(|(item1, item2)| item1.intersect(item2, ctx, stack_level + 1))
                         .collect::<Result<Vec<_>>>()?
                 },
-                items: match (items1, items2) {
+                items: match (a1.items, a2.items) {
                     (None, None) => None,
-                    (None, Some(item)) => Some(item),
-                    (Some(item), None) => Some(item),
-                    (Some(item1), Some(item2)) => Some(Box::new((*item1).intersect(
-                        *item2,
-                        ctx,
-                        stack_level + 1,
-                    )?)),
+                    (None, Some(item)) | (Some(item), None) => Some(item),
+                    (Some(item1), Some(item2)) => {
+                        Some(Box::new(item1.intersect(*item2, ctx, stack_level + 1)?))
+                    }
                 },
-            },
-            (
-                Schema::Object {
-                    properties: props1,
-                    additional_properties: add1,
-                    required: req1,
-                },
-                Schema::Object {
-                    properties: mut props2,
-                    additional_properties: add2,
-                    required: req2,
-                },
-            ) => {
-                let mut new_props = IndexMap::new();
-                for (key, prop1) in props1.into_iter() {
-                    let prop2 = props2
-                        .shift_remove(&key)
-                        .or_else(|| add2.as_deref().cloned())
-                        .unwrap_or(Schema::Any);
-                    new_props.insert(key, prop1.intersect(prop2, ctx, stack_level + 1)?);
+            }),
+
+            (Schema::Object(mut o1), Schema::Object(mut o2)) => {
+                let mut properties = IndexMap::new();
+                for (key, prop1) in std::mem::take(&mut o1.properties).into_iter() {
+                    let prop2 = ctx.property_schema(&o2, &key)?;
+                    properties.insert(key, prop1.intersect(prop2.clone(), ctx, stack_level + 1)?);
                 }
-                for (key, prop2) in props2.into_iter() {
-                    let prop1 = add1.as_deref().cloned().unwrap_or(Schema::Any);
-                    new_props.insert(key, prop1.intersect(prop2, ctx, stack_level + 1)?);
+                for (key, prop2) in o2.properties.into_iter() {
+                    if properties.contains_key(&key) {
+                        continue;
+                    }
+                    let prop1 = ctx.property_schema(&o1, &key)?;
+                    properties.insert(key, prop1.clone().intersect(prop2, ctx, stack_level + 1)?);
                 }
-                let mut required = req1;
-                required.extend(req2);
-                Schema::Object {
-                    properties: new_props,
-                    additional_properties: match (add1, add2) {
+                let mut required = o1.required;
+                required.extend(o2.required);
+
+                let mut pattern_properties = IndexMap::new();
+                for (key, prop1) in o1.pattern_properties.into_iter() {
+                    if let Some(prop2) = o2.pattern_properties.get_mut(&key) {
+                        let prop2 = std::mem::replace(prop2, Schema::Null);
+                        pattern_properties.insert(
+                            key.clone(),
+                            prop1.intersect(prop2.clone(), ctx, stack_level + 1)?,
+                        );
+                    } else {
+                        pattern_properties.insert(key.clone(), prop1);
+                    }
+                }
+                for (key, prop2) in o2.pattern_properties.into_iter() {
+                    if pattern_properties.contains_key(&key) {
+                        continue;
+                    }
+                    pattern_properties.insert(key.clone(), prop2);
+                }
+
+                let keys = pattern_properties.keys().collect::<Vec<_>>();
+                if !keys.is_empty() {
+                    ctx.check_disjoint_pattern_properties(&keys)?;
+                }
+
+                let additional_properties =
+                    match (o1.additional_properties, o2.additional_properties) {
                         (None, None) => None,
-                        (None, Some(add2)) => Some(add2),
-                        (Some(add1), None) => Some(add1),
-                        (Some(add1), Some(add2)) => {
-                            Some(Box::new((*add1).intersect(*add2, ctx, stack_level + 1)?))
+                        (None, Some(p)) | (Some(p), None) => Some(p),
+                        (Some(p1), Some(p2)) => {
+                            Some(Box::new((*p1).intersect(*p2, ctx, stack_level + 1)?))
                         }
-                    },
+                    };
+
+                let min_properties = o1.min_properties.max(o2.min_properties);
+                let max_properties = opt_min(o1.max_properties, o2.max_properties);
+
+                mk_object_schema(ObjectSchema {
+                    properties,
+                    pattern_properties,
+                    additional_properties,
                     required,
-                }
+                    min_properties,
+                    max_properties,
+                })
             }
+
             //TODO: get types for error message
-            _ => Schema::Unsatisfiable {
-                reason: "incompatible types".to_string(),
-            },
+            _ => Schema::unsat("incompatible types"),
         };
-        Ok(merged.normalize())
+        Ok(merged.normalize(ctx))
     }
 
-    fn is_verifiably_disjoint_from(&self, other: &Schema) -> bool {
+    fn is_verifiably_disjoint_from(&self, other: &Schema, ctx: &Context) -> bool {
         match (self, other) {
-            (Schema::Unsatisfiable { .. }, _) => true,
-            (_, Schema::Unsatisfiable { .. }) => true,
+            (Schema::Unsatisfiable(_), _) => true,
+            (_, Schema::Unsatisfiable(_)) => true,
             (Schema::Any, _) => false,
             (_, Schema::Any) => false,
-            (Schema::Boolean, Schema::LiteralBool { .. }) => false,
-            (Schema::LiteralBool { .. }, Schema::Boolean) => false,
-            (Schema::Ref { .. }, _) => false, // TODO: could resolve
-            (_, Schema::Ref { .. }) => false, // TODO: could resolve
-            (Schema::LiteralBool { value: value1 }, Schema::LiteralBool { value: value2 }) => {
-                value1 != value2
+            (Schema::Ref(_), _) => false, // TODO: could resolve
+            (_, Schema::Ref(_)) => false, // TODO: could resolve
+            (Schema::Boolean(value1), Schema::Boolean(value2)) => {
+                value1.is_some() && value2.is_some() && value1 != value2
             }
-            (Schema::AnyOf { options }, _) => options
+            (Schema::AnyOf(options), _) => options
                 .iter()
-                .all(|opt| opt.is_verifiably_disjoint_from(other)),
-            (_, Schema::AnyOf { options }) => options
+                .all(|opt| opt.is_verifiably_disjoint_from(other, ctx)),
+            (_, Schema::AnyOf(options)) => options
                 .iter()
-                .all(|opt| self.is_verifiably_disjoint_from(opt)),
-            (Schema::OneOf { options }, _) => options
+                .all(|opt| self.is_verifiably_disjoint_from(opt, ctx)),
+            (Schema::OneOf(options), _) => options
                 .iter()
-                .all(|opt| opt.is_verifiably_disjoint_from(other)),
-            (_, Schema::OneOf { options }) => options
+                .all(|opt| opt.is_verifiably_disjoint_from(other, ctx)),
+            (_, Schema::OneOf(options)) => options
                 .iter()
-                .all(|opt| self.is_verifiably_disjoint_from(opt)),
+                .all(|opt| self.is_verifiably_disjoint_from(opt, ctx)),
             // TODO: could actually compile the regexes and check for overlap
             (
-                Schema::String {
+                Schema::String(StringSchema {
                     regex: Some(RegexAst::Literal(lit1)),
                     ..
-                },
-                Schema::String {
+                }),
+                Schema::String(StringSchema {
                     regex: Some(RegexAst::Literal(lit2)),
                     ..
-                },
+                }),
             ) => lit1 != lit2,
-            (
-                Schema::Object {
-                    properties: props1,
-                    required: req1,
-                    additional_properties: add1,
-                },
-                Schema::Object {
-                    properties: props2,
-                    required: req2,
-                    additional_properties: add2,
-                },
-            ) => req1.union(req2).any(|key| {
-                let prop1 = props1
-                    .get(key)
-                    .unwrap_or(add1.as_deref().unwrap_or(&Schema::Any));
-                let prop2 = props2
-                    .get(key)
-                    .unwrap_or(add2.as_deref().unwrap_or(&Schema::Any));
-                prop1.is_verifiably_disjoint_from(prop2)
-            }),
+            (Schema::Object(o1), Schema::Object(o2)) => {
+                o1.required.union(&o2.required).any(|key| {
+                    let prop1 = ctx.property_schema(o1, key).unwrap_or(&Schema::Any);
+                    let prop2 = ctx.property_schema(o2, key).unwrap_or(&Schema::Any);
+                    prop1.is_verifiably_disjoint_from(prop2, ctx)
+                })
+            }
             _ => {
                 // Except for in the cases above, it should suffice to check that the types are different
                 mem::discriminant(self) != mem::discriminant(other)
@@ -474,7 +498,7 @@
                     .iter()
                     .map(compile_const)
                     .collect::<Result<Vec<_>>>()?;
-                result = result.intersect(Schema::AnyOf { options }, ctx, 0)?;
+                result = result.intersect(Schema::AnyOf(options), ctx, 0)?;
             }
             "allOf" => {
                 let all_of = v
@@ -493,7 +517,7 @@
                     .iter()
                     .map(|value| compile_resource(ctx, ctx.as_resource_ref(value)))
                     .collect::<Result<Vec<_>>>()?;
-                result = result.intersect(Schema::AnyOf { options }, ctx, 0)?;
+                result = result.intersect(Schema::AnyOf(options), ctx, 0)?;
             }
             "oneOf" => {
                 let one_of = v
@@ -503,7 +527,7 @@
                     .iter()
                     .map(|value| compile_resource(ctx, ctx.as_resource_ref(value)))
                     .collect::<Result<Vec<_>>>()?;
-                result = result.intersect(Schema::OneOf { options }, ctx, 0)?;
+                result = result.intersect(Schema::OneOf(options), ctx, 0)?;
             }
             "$ref" => {
                 let reference = v
@@ -513,7 +537,7 @@
                 let uri: String = ctx.normalize_ref(&reference)?;
                 if matches!(result, Schema::Any) {
                     define_ref(ctx, &uri)?;
-                    result = Schema::Ref { uri };
+                    result = Schema::Ref(uri);
                 } else {
                     result = intersect_ref(ctx, &uri, result, false, 0)?;
                 }
@@ -528,6 +552,7 @@
 pub struct SchemaBuilderOptions {
     pub max_size: usize,
     pub max_stack_level: usize,
+    pub lenient: bool,
 }
 
 impl Default for SchemaBuilderOptions {
@@ -535,28 +560,29 @@
         SchemaBuilderOptions {
             max_size: 50_000,
             max_stack_level: 128, // consumes ~2.5k of stack per level
+            lenient: false,
         }
     }
 }
 
-pub fn build_schema(
-    contents: Value,
-    retriever: Option<RetrieveWrapper>,
-) -> Result<(Schema, HashMap<String, Schema>)> {
+pub fn build_schema(contents: Value, options: &JsonCompileOptions) -> Result<BuiltSchema> {
     if let Some(b) = contents.as_bool() {
-        if b {
-            return Ok((Schema::Any, HashMap::default()));
+        let s = if b {
+            Schema::Any
         } else {
-            return Ok((Schema::false_schema(), HashMap::default()));
-        }
+            Schema::false_schema()
+        };
+        return Ok(BuiltSchema::simple(s));
     }
 
-    let pre_ctx = PreContext::new(contents, retriever)?;
-    let ctx = Context::new(&pre_ctx)?;
+    let pre_ctx = PreContext::new(contents, options.retriever.clone())?;
+    let mut ctx = Context::new(&pre_ctx)?;
+
+    ctx.options.lenient = options.lenient;
 
     let root_resource = ctx.lookup_resource(&pre_ctx.base_uri)?;
     let schema = compile_resource(&ctx, root_resource)?;
-    Ok((schema, ctx.take_defs()))
+    Ok(ctx.into_result(schema))
 }
 
 fn compile_resource(ctx: &Context, resource: ResourceRef) -> Result<Schema> {
@@ -565,7 +591,7 @@
 }
 
 fn compile_contents(ctx: &Context, contents: &Value) -> Result<Schema> {
-    compile_contents_inner(ctx, contents).map(|schema| schema.normalize())
+    compile_contents_inner(ctx, contents).map(|schema| schema.normalize(ctx))
 }
 
 fn compile_contents_inner(ctx: &Context, contents: &Value) -> Result<Schema> {
@@ -611,7 +637,12 @@
     if !unimplemented_keys.is_empty() {
         // ensure consistent order for tests
         unimplemented_keys.sort();
-        bail!("Unimplemented keys: {:?}", unimplemented_keys);
+        let msg = format!("Unimplemented keys: {:?}", unimplemented_keys);
+        if ctx.options.lenient {
+            ctx.record_warning(msg);
+        } else {
+            bail!(msg);
+        }
     }
 
     // Some dummy values to use for properties and prefixItems if we need to apply additionalProperties or items
@@ -754,7 +785,7 @@
 fn compile_const(instance: &Value) -> Result<Schema> {
     match instance {
         Value::Null => Ok(Schema::Null),
-        Value::Bool(b) => Ok(Schema::LiteralBool { value: *b }),
+        Value::Bool(b) => Ok(Schema::Boolean(Some(*b))),
         Value::Number(n) => {
             let value = n.as_f64().ok_or_else(|| {
                 anyhow!(
@@ -762,31 +793,31 @@
                     limited_str(instance)
                 )
             })?;
-            Ok(Schema::Number {
+            Ok(Schema::Number(NumberSchema {
                 minimum: Some(value),
                 maximum: Some(value),
                 exclusive_minimum: None,
                 exclusive_maximum: None,
                 integer: n.is_i64(),
                 multiple_of: None,
-            })
+            }))
         }
-        Value::String(s) => Ok(Schema::String {
+        Value::String(s) => Ok(Schema::String(StringSchema {
             min_length: 0,
             max_length: None,
             regex: Some(RegexAst::Literal(s.to_string())),
-        }),
+        })),
         Value::Array(items) => {
             let prefix_items = items
                 .iter()
                 .map(compile_const)
                 .collect::<Result<Vec<Schema>>>()?;
-            Ok(Schema::Array {
-                min_items: prefix_items.len() as u64,
-                max_items: Some(prefix_items.len() as u64),
+            Ok(Schema::Array(ArraySchema {
+                min_items: prefix_items.len(),
+                max_items: Some(prefix_items.len()),
                 prefix_items,
                 items: Some(Box::new(Schema::false_schema())),
-            })
+            }))
         }
         Value::Object(mapping) => {
             let properties = mapping
@@ -794,11 +825,14 @@
                 .map(|(k, v)| Ok((k.clone(), compile_const(v)?)))
                 .collect::<Result<IndexMap<String, Schema>>>()?;
             let required = properties.keys().cloned().collect();
-            Ok(Schema::Object {
+            Ok(Schema::Object(ObjectSchema {
                 properties,
+                pattern_properties: IndexMap::default(),
                 additional_properties: Some(Box::new(Schema::false_schema())),
                 required,
-            })
+                min_properties: 0,
+                max_properties: None,
+            }))
         }
     }
 }
@@ -816,58 +850,31 @@
     if options.len() == 1 {
         Ok(options.swap_remove(0))
     } else {
-        Ok(Schema::AnyOf { options })
+        Ok(Schema::AnyOf(options))
     }
 }
 
 fn compile_type(ctx: &Context, tp: &str, schema: &HashMap<&str, &Value>) -> Result<Schema> {
     ctx.increment()?;
 
-    let get = |key: &str| schema.get(key).copied();
-
     match tp {
         "null" => Ok(Schema::Null),
-        "boolean" => Ok(Schema::Boolean),
-        "number" | "integer" => compile_numeric(
-            get("minimum"),
-            get("maximum"),
-            get("exclusiveMinimum"),
-            get("exclusiveMaximum"),
-            tp == "integer",
-            get("multipleOf"),
-        ),
-        "string" => compile_string(
-            get("minLength"),
-            get("maxLength"),
-            get("pattern"),
-            get("format"),
-        ),
-        "array" => compile_array(
-            ctx,
-            get("minItems"),
-            get("maxItems"),
-            get("prefixItems"),
-            get("items"),
-            get("additionalItems"),
-        ),
-        "object" => compile_object(
-            ctx,
-            get("properties"),
-            get("additionalProperties"),
-            get("required"),
-        ),
+        "boolean" => Ok(Schema::Boolean(None)),
+        "number" | "integer" => compile_numeric(schema, tp == "integer"),
+        "string" => compile_string(ctx, schema),
+        "array" => compile_array(ctx, schema),
+        "object" => compile_object(ctx, schema),
         _ => bail!("Invalid type: {}", tp),
     }
 }
 
-fn compile_numeric(
-    minimum: Option<&Value>,
-    maximum: Option<&Value>,
-    exclusive_minimum: Option<&Value>,
-    exclusive_maximum: Option<&Value>,
-    integer: bool,
-    multiple_of: Option<&Value>,
-) -> Result<Schema> {
+fn compile_numeric(schema: &HashMap<&str, &Value>, integer: bool) -> Result<Schema> {
+    let minimum = schema.get("minimum").copied();
+    let maximum = schema.get("maximum").copied();
+    let exclusive_minimum = schema.get("exclusiveMinimum").copied();
+    let exclusive_maximum = schema.get("exclusiveMaximum").copied();
+    let multiple_of = schema.get("multipleOf").copied();
+
     let minimum = match minimum {
         None => None,
         Some(val) => Some(
@@ -917,35 +924,23 @@
             Some(Decimal::try_from(f.abs())?)
         }
     };
-    Ok(Schema::Number {
+    Ok(Schema::Number(NumberSchema {
         minimum,
         maximum,
         exclusive_minimum,
         exclusive_maximum,
         integer,
         multiple_of,
-    })
+    }))
 }
 
-fn compile_string(
-    min_length: Option<&Value>,
-    max_length: Option<&Value>,
-    pattern: Option<&Value>,
-    format: Option<&Value>,
-) -> Result<Schema> {
-    let min_length = match min_length {
-        None => 0,
-        Some(val) => val
-            .as_u64()
-            .ok_or_else(|| anyhow!("Expected u64 for 'minLength', got {}", limited_str(val)))?,
-    };
-    let max_length = match max_length {
-        None => None,
-        Some(val) => Some(
-            val.as_u64()
-                .ok_or_else(|| anyhow!("Expected u64 for 'maxLength', got {}", limited_str(val)))?,
-        ),
-    };
+fn compile_string(ctx: &Context, schema: &HashMap<&str, &Value>) -> Result<Schema> {
+    let pattern = schema.get("pattern").copied();
+    let format = schema.get("format").copied();
+
+    let min_length = get_usize(schema, "minLength")?.unwrap_or(0);
+    let max_length = get_usize(schema, "maxLength")?;
+
     let pattern_rx = match pattern {
         None => None,
         Some(val) => Some({
@@ -958,14 +953,24 @@
     };
     let format_rx = match format {
         None => None,
-        Some(val) => Some({
+        Some(val) => {
             let key = val
                 .as_str()
                 .ok_or_else(|| anyhow!("Expected string for 'format', got {}", limited_str(val)))?
                 .to_string();
-            let fmt = lookup_format(&key).ok_or_else(|| anyhow!("Unknown format: {}", key))?;
-            RegexAst::Regex(fmt.to_string())
-        }),
+
+            if let Some(fmt) = lookup_format(&key) {
+                Some(RegexAst::Regex(fmt.to_string()))
+            } else {
+                let msg = format!("Unknown format: {}", key);
+                if ctx.options.lenient {
+                    ctx.record_warning(msg);
+                    None
+                } else {
+                    bail!(msg);
+                }
+            }
+        }
     };
     let regex = match (pattern_rx, format_rx) {
         (None, None) => None,
@@ -973,21 +978,20 @@
         (Some(pat), None) => Some(pat),
         (Some(pat), Some(fmt)) => Some(RegexAst::And(vec![pat, fmt])),
     };
-    Ok(Schema::String {
+    Ok(Schema::String(StringSchema {
         min_length,
         max_length,
         regex,
-    })
+    }))
 }
 
-fn compile_array(
-    ctx: &Context,
-    min_items: Option<&Value>,
-    max_items: Option<&Value>,
-    prefix_items: Option<&Value>,
-    items: Option<&Value>,
-    additional_items: Option<&Value>,
-) -> Result<Schema> {
+fn compile_array(ctx: &Context, schema: &HashMap<&str, &Value>) -> Result<Schema> {
+    let min_items = get_usize(schema, "minItems")?.unwrap_or(0);
+    let max_items = get_usize(schema, "maxItems")?;
+    let prefix_items = schema.get("prefixItems").copied();
+    let items = schema.get("items").copied();
+    let additional_items = schema.get("additionalItems").copied();
+
     let (prefix_items, items) = {
         // Note that draft detection falls back to Draft202012 if the draft is unknown, so let's relax the draft constraint a bit
         // and assume we're in an old draft if additionalItems is present or items is an array
@@ -1005,19 +1009,6 @@
             (prefix_items, items)
         }
     };
-    let min_items = match min_items {
-        None => 0,
-        Some(val) => val
-            .as_u64()
-            .ok_or_else(|| anyhow!("Expected u64 for 'minItems', got {}", limited_str(val)))?,
-    };
-    let max_items = match max_items {
-        None => None,
-        Some(val) => Some(
-            val.as_u64()
-                .ok_or_else(|| anyhow!("Expected u64 for 'maxItems', got {}", limited_str(val)))?,
-        ),
-    };
     let prefix_items = match prefix_items {
         None => vec![],
         Some(val) => val
@@ -1031,29 +1022,60 @@
         None => None,
         Some(val) => Some(Box::new(compile_resource(ctx, ctx.as_resource_ref(val))?)),
     };
-    Ok(Schema::Array {
+    Ok(Schema::Array(ArraySchema {
         min_items,
         max_items,
         prefix_items,
         items,
-    })
+    }))
 }
 
-fn compile_object(
+fn compile_prop_map(
     ctx: &Context,
-    properties: Option<&Value>,
-    additional_properties: Option<&Value>,
-    required: Option<&Value>,
-) -> Result<Schema> {
-    let properties = match properties {
-        None => IndexMap::new(),
+    lbl: &str,
+    prop_map: Option<&Value>,
+) -> Result<IndexMap<String, Schema>> {
+    match prop_map {
+        None => Ok(IndexMap::new()),
         Some(val) => val
             .as_object()
-            .ok_or_else(|| anyhow!("Expected object for 'properties', got {}", limited_str(val)))?
+            .ok_or_else(|| anyhow!("Expected object for '{lbl}', got {}", limited_str(val)))?
             .iter()
             .map(|(k, v)| compile_resource(ctx, ctx.as_resource_ref(v)).map(|v| (k.clone(), v)))
-            .collect::<Result<IndexMap<String, Schema>>>()?,
-    };
+            .collect(),
+    }
+}
+
+fn get_usize(schema: &HashMap<&str, &Value>, name: &str) -> Result<Option<usize>> {
+    if let Some(val) = schema.get(name) {
+        if let Some(val) = val.as_u64() {
+            ensure!(
+                val <= usize::MAX as u64,
+                "Value {val} for '{name}' is too large"
+            );
+            Ok(Some(val as usize))
+        } else {
+            bail!(
+                "Expected positive integer for '{name}', got {}",
+                limited_str(val)
+            )
+        }
+    } else {
+        Ok(None)
+    }
+}
+
+fn compile_object(ctx: &Context, schema: &HashMap<&str, &Value>) -> Result<Schema> {
+    let properties = schema.get("properties").copied();
+    let pattern_properties = schema.get("patternProperties").copied();
+    let additional_properties = schema.get("additionalProperties").copied();
+    let required = schema.get("required").copied();
+    let min_properties = get_usize(schema, "minProperties")?.unwrap_or(0);
+    let max_properties = get_usize(schema, "maxProperties")?;
+
+    let properties = compile_prop_map(ctx, "properties", properties)?;
+    let pattern_properties = compile_prop_map(ctx, "patternProperties", pattern_properties)?;
+    ctx.check_disjoint_pattern_properties(&pattern_properties.keys().collect::<Vec<_>>())?;
     let additional_properties = match additional_properties {
         None => None,
         Some(val) => Some(Box::new(compile_resource(ctx, ctx.as_resource_ref(val))?)),
@@ -1076,11 +1098,28 @@
             })
             .collect::<Result<IndexSet<String>>>()?,
     };
-    Ok(Schema::Object {
+
+    Ok(mk_object_schema(ObjectSchema {
         properties,
+        pattern_properties,
         additional_properties,
         required,
-    })
+        min_properties,
+        max_properties,
+    }))
+}
+
+fn mk_object_schema(obj: ObjectSchema) -> Schema {
+    if let Some(max) = obj.max_properties {
+        if obj.min_properties > max {
+            return Schema::unsat("minProperties > maxProperties");
+        }
+    }
+    if obj.required.len() > obj.max_properties.unwrap_or(usize::MAX) {
+        return Schema::unsat("required > maxProperties");
+    }
+
+    Schema::Object(obj)
 }
 
 fn opt_max<T: PartialOrd>(a: Option<T>, b: Option<T>) -> Option<T> {
@@ -1116,8 +1155,9 @@
 #[cfg(all(test, feature = "referencing"))]
 mod test_retriever {
     use crate::json::{Retrieve, RetrieveWrapper};
+    use crate::JsonCompileOptions;
 
-    use super::{build_schema, Schema};
+    use super::{build_schema, Schema, StringSchema};
     use serde_json::{json, Value};
     use std::{fmt, sync::Arc};
 
@@ -1161,9 +1201,15 @@
             .collect(),
         };
         let wrapper = RetrieveWrapper::new(Arc::new(retriever));
-        let (schema, defs) = build_schema(schema, Some(wrapper)).unwrap();
+        let options = JsonCompileOptions {
+            retriever: Some(wrapper.clone()),
+            ..Default::default()
+        };
+        let r = build_schema(schema, &options).unwrap();
+        let schema = r.schema;
+        let defs = r.definitions;
         match schema {
-            Schema::Ref { uri } => {
+            Schema::Ref(uri) => {
                 assert_eq!(uri, key);
             }
             _ => panic!("Unexpected schema: {:?}", schema),
@@ -1172,11 +1218,11 @@
         let val = defs.get(key).unwrap();
         // poor-man's partial_eq
         match val {
-            Schema::String {
+            Schema::String(StringSchema {
                 min_length: 0,
                 max_length: None,
                 regex: None,
-            } => {}
+            }) => {}
             _ => panic!("Unexpected schema: {:?}", val),
         }
     }
@@ -1214,6 +1260,7 @@
             }
         });
         // Test failure amounts to this resulting in a stack overflow
-        let _ = build_schema(schema, None);
+        let options = JsonCompileOptions::default();
+        let _ = build_schema(schema, &options);
     }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/shared_context.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/shared_context.rs
index 15d31c7..6826bcc 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/shared_context.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/json/shared_context.rs
@@ -1,15 +1,96 @@
-use crate::{HashMap, HashSet};
-use anyhow::{bail, Result};
+use crate::{json::schema::OptSchemaExt, regex_to_lark, HashMap, HashSet};
+use anyhow::{anyhow, bail, Result};
+use derivre::{Regex, RegexAst, RegexBuilder};
 
 use super::{
     context::Context,
-    schema::{Schema, IMPLEMENTED, META_AND_ANNOTATIONS},
+    schema::{ObjectSchema, Schema, IMPLEMENTED, META_AND_ANNOTATIONS},
 };
 
 pub struct SharedContext {
     defs: HashMap<String, Schema>,
     seen: HashSet<String>,
     n_compiled: usize,
+    pending_warnings: Vec<String>,
+    pattern_cache: PatternPropertyCache,
+}
+
+#[derive(Default)]
+pub struct PatternPropertyCache {
+    inner: HashMap<String, Regex>,
+}
+
+const CHECK_LIMIT: u64 = 10_000;
+
+impl PatternPropertyCache {
+    pub fn is_match(&mut self, regex: &str, value: &str) -> Result<bool> {
+        if let Some(cached_regex) = self.inner.get_mut(regex) {
+            return Ok(cached_regex.is_match(value));
+        }
+
+        let regex = regex_to_lark(regex, "dw");
+        let mut builder = RegexBuilder::new();
+        let eref = builder.mk_regex_for_serach(regex.as_str())?;
+        let mut rx = builder.to_regex_limited(eref, CHECK_LIMIT)?;
+        let res = rx.is_match(value);
+        self.inner.insert(regex.to_string(), rx);
+        Ok(res)
+    }
+
+    pub fn check_disjoint(&mut self, regexes: &[&String]) -> Result<()> {
+        // TODO cache something?
+        let mut builder = RegexBuilder::new();
+        let erefs = regexes
+            .iter()
+            .map(|regex| {
+                let regex = regex_to_lark(regex, "dw");
+                builder.mk_regex_for_serach(regex.as_str())
+            })
+            .collect::<Result<Vec<_>>>()?;
+        for (ai, a) in erefs.iter().enumerate() {
+            for (bi, b) in erefs.iter().enumerate() {
+                if ai >= bi {
+                    continue;
+                }
+                let intersect = builder.mk(&RegexAst::And(vec![
+                    RegexAst::ExprRef(*a),
+                    RegexAst::ExprRef(*b),
+                ]))?;
+                let mut rx = builder
+                    .to_regex_limited(intersect, CHECK_LIMIT)
+                    .map_err(|_| {
+                        anyhow!(
+                            "can't determine if patternProperty regexes /{}/ and /{}/ are disjoint",
+                            regex_to_lark(regexes[ai], ""),
+                            regex_to_lark(regexes[bi], "")
+                        )
+                    })?;
+                if !rx.always_empty() {
+                    return Err(anyhow!(
+                        "patternProperty regexes /{}/ and /{}/ are not disjoint",
+                        regex_to_lark(regexes[ai], ""),
+                        regex_to_lark(regexes[bi], "")
+                    ));
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    pub fn property_schema<'a>(&mut self, obj: &'a ObjectSchema, prop: &str) -> Result<&'a Schema> {
+        if let Some(schema) = obj.properties.get(prop) {
+            return Ok(schema);
+        }
+
+        for (key, schema) in obj.pattern_properties.iter() {
+            if self.is_match(key, prop)? {
+                return Ok(schema);
+            }
+        }
+
+        Ok(obj.additional_properties.schema_ref())
+    }
 }
 
 impl SharedContext {
@@ -18,6 +99,8 @@
             defs: HashMap::default(),
             seen: HashSet::default(),
             n_compiled: 0,
+            pending_warnings: Vec::new(),
+            pattern_cache: PatternPropertyCache::default(),
         }
     }
 }
@@ -61,7 +144,49 @@
         Ok(())
     }
 
-    pub fn take_defs(&self) -> HashMap<String, Schema> {
-        std::mem::take(&mut self.shared.borrow_mut().defs)
+    pub fn record_warning(&self, msg: String) {
+        self.shared.borrow_mut().pending_warnings.push(msg);
+    }
+
+    pub fn property_schema<'a>(&self, obj: &'a ObjectSchema, prop: &str) -> Result<&'a Schema> {
+        self.shared
+            .borrow_mut()
+            .pattern_cache
+            .property_schema(obj, prop)
+    }
+
+    pub fn check_disjoint_pattern_properties(&self, regexes: &[&String]) -> Result<()> {
+        self.shared
+            .borrow_mut()
+            .pattern_cache
+            .check_disjoint(regexes)
+    }
+
+    pub fn into_result(self, schema: Schema) -> BuiltSchema {
+        let mut shared = self.shared.borrow_mut();
+        BuiltSchema {
+            schema,
+            definitions: std::mem::take(&mut shared.defs),
+            warnings: std::mem::take(&mut shared.pending_warnings),
+            pattern_cache: std::mem::take(&mut shared.pattern_cache),
+        }
+    }
+}
+
+pub struct BuiltSchema {
+    pub schema: Schema,
+    pub definitions: HashMap<String, Schema>,
+    pub warnings: Vec<String>,
+    pub pattern_cache: PatternPropertyCache,
+}
+
+impl BuiltSchema {
+    pub fn simple(schema: Schema) -> Self {
+        BuiltSchema {
+            schema,
+            definitions: HashMap::default(),
+            warnings: Vec::new(),
+            pattern_cache: PatternPropertyCache::default(),
+        }
     }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs
index d4330bd..a75e1a8 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/lark/compiler.rs
@@ -552,11 +552,9 @@
         let mut builder = self.builder;
         for (gg, loc, grm) in self.pending_grammars {
             let res = match grm {
-                PendingGrammar::Json(json_schema) => {
-                    let opts = JsonCompileOptions::default();
-                    opts.json_to_llg_no_validate(builder, json_schema)
-                        .map_err(|e| loc.augment(anyhow!("failed to compile JSON schema: {}", e)))?
-                }
+                PendingGrammar::Json(json_schema) => JsonCompileOptions::default()
+                    .json_to_llg_with_overrides(builder, json_schema)
+                    .map_err(|e| loc.augment(anyhow!("failed to compile JSON schema: {}", e)))?,
                 PendingGrammar::Lark(items) => compile_lark(builder, ParsedLark { items })?,
             };
             builder = res.builder;
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs
index 91d6343..d5439d5 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/matcher.rs
@@ -186,6 +186,13 @@
         }
     }
 
+    pub fn grammar_warnings(&mut self) -> Vec<String> {
+        match &mut self.0 {
+            MatcherState::Normal(inner) => inner.parser.grammar_warnings(),
+            MatcherState::Error(_) => vec![],
+        }
+    }
+
     pub fn tok_env(&self) -> Result<TokEnv> {
         match &self.0 {
             MatcherState::Normal(inner) => Ok(inner.parser.token_env.clone()),
diff --git a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs
index c5f72a7..1e97e74a 100644
--- a/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs
+++ b/third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/src/tokenparser.rs
@@ -113,6 +113,10 @@
         })
     }
 
+    pub fn grammar_warnings(&mut self) -> Vec<String> {
+        self.parser.grammar_warnings()
+    }
+
     pub fn get_capture(&self, name: &str) -> Option<&[u8]> {
         self.parser.get_capture(name)
     }
@@ -507,7 +511,7 @@
         }
 
         // now apply normally
-        match self.parser.apply_token(tok_bytes) {
+        match self.parser.apply_token(tok_bytes, tok_id) {
             Err(e) => {
                 return Err(self.stop(
                     &format!("Parser Error: {}", e),
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/.cargo_vcs_info.json
index 70e4aab..4635f95 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "7cb0f3cdee5888a581c0041ba9acae5727372277"
+    "sha1": "24bbf16d9df01d5f7d9ac39bdfbaea85f4c194fb"
   },
   "path_in_vcs": ""
 }
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.lock
index ec39714..466b6841 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.lock
@@ -56,15 +56,15 @@
 
 [[package]]
 name = "either"
-version = "1.14.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 
 [[package]]
 name = "errno"
-version = "0.3.10"
+version = "0.3.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
 dependencies = [
  "libc",
  "windows-sys",
@@ -84,9 +84,9 @@
 
 [[package]]
 name = "flate2"
-version = "1.1.0"
+version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
+checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -94,9 +94,9 @@
 
 [[package]]
 name = "libc"
-version = "0.2.170"
+version = "0.2.172"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
 [[package]]
 name = "libredox"
@@ -111,31 +111,31 @@
 
 [[package]]
 name = "linux-raw-sys"
-version = "0.4.15"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.5"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
 dependencies = [
  "adler2",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.93"
+version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 dependencies = [
  "flate2",
  "quote",
@@ -147,11 +147,11 @@
 
 [[package]]
 name = "quote"
-version = "1.0.38"
+version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
 dependencies = [
- "proc-macro2 1.0.93",
+ "proc-macro2 1.0.94",
 ]
 
 [[package]]
@@ -176,18 +176,18 @@
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.9"
+version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
+checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "rustix"
-version = "0.38.44"
+version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
 dependencies = [
  "bitflags",
  "errno",
@@ -198,9 +198,9 @@
 
 [[package]]
 name = "rustversion"
-version = "1.0.19"
+version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
 
 [[package]]
 name = "tar"
@@ -215,9 +215,9 @@
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.17"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
 
 [[package]]
 name = "windows-sys"
@@ -294,11 +294,10 @@
 
 [[package]]
 name = "xattr"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
+checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e"
 dependencies = [
  "libc",
- "linux-raw-sys",
  "rustix",
 ]
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml
index 8450546..45b0f43 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml
@@ -13,7 +13,7 @@
 edition = "2021"
 rust-version = "1.56"
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 authors = [
     "David Tolnay <dtolnay@gmail.com>",
     "Alex Crichton <alex@alexcrichton.com>",
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml.orig
index f6844ef..e35be018 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 authors = ["David Tolnay <dtolnay@gmail.com>", "Alex Crichton <alex@alexcrichton.com>"]
 autobenches = false
 categories = ["development-tools::procedural-macro-helpers"]
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/fallback.rs b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/fallback.rs
index 1acdf06..fbce9c4 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/fallback.rs
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/fallback.rs
@@ -311,34 +311,6 @@
     }
 }
 
-#[cfg(procmacro2_semver_exempt)]
-#[derive(Clone, PartialEq, Eq)]
-pub(crate) struct SourceFile {
-    path: PathBuf,
-}
-
-#[cfg(procmacro2_semver_exempt)]
-impl SourceFile {
-    /// Get the path to this source file as a string.
-    pub(crate) fn path(&self) -> PathBuf {
-        self.path.clone()
-    }
-
-    pub(crate) fn is_real(&self) -> bool {
-        false
-    }
-}
-
-#[cfg(procmacro2_semver_exempt)]
-impl Debug for SourceFile {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        f.debug_struct("SourceFile")
-            .field("path", &self.path())
-            .field("is_real", &self.is_real())
-            .finish()
-    }
-}
-
 #[cfg(all(span_locations, not(fuzzing)))]
 thread_local! {
     static SOURCE_MAP: RefCell<SourceMap> = RefCell::new(SourceMap {
@@ -484,14 +456,14 @@
     }
 
     #[cfg(procmacro2_semver_exempt)]
-    fn filepath(&self, span: Span) -> PathBuf {
+    fn filepath(&self, span: Span) -> String {
         for (i, file) in self.files.iter().enumerate() {
             if file.span_within(span) {
-                return PathBuf::from(if i == 0 {
+                return if i == 0 {
                     "<unspecified>".to_owned()
                 } else {
                     format!("<parsed string {}>", i)
-                });
+                };
             }
         }
         unreachable!("Invalid span with no related FileInfo!");
@@ -555,21 +527,6 @@
         other
     }
 
-    #[cfg(procmacro2_semver_exempt)]
-    pub(crate) fn source_file(&self) -> SourceFile {
-        #[cfg(fuzzing)]
-        return SourceFile {
-            path: PathBuf::from("<unspecified>"),
-        };
-
-        #[cfg(not(fuzzing))]
-        SOURCE_MAP.with(|sm| {
-            let sm = sm.borrow();
-            let path = sm.filepath(*self);
-            SourceFile { path }
-        })
-    }
-
     #[cfg(span_locations)]
     pub(crate) fn byte_range(&self) -> Range<usize> {
         #[cfg(fuzzing)]
@@ -611,6 +568,23 @@
         })
     }
 
+    #[cfg(procmacro2_semver_exempt)]
+    pub(crate) fn file(&self) -> String {
+        #[cfg(fuzzing)]
+        return "<unspecified>".to_owned();
+
+        #[cfg(not(fuzzing))]
+        SOURCE_MAP.with(|sm| {
+            let sm = sm.borrow();
+            sm.filepath(*self)
+        })
+    }
+
+    #[cfg(procmacro2_semver_exempt)]
+    pub(crate) fn local_file(&self) -> Option<PathBuf> {
+        None
+    }
+
     #[cfg(not(span_locations))]
     pub(crate) fn join(&self, _other: Span) -> Option<Span> {
         Some(Span {})
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/lib.rs b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/lib.rs
index 2ed9b72..6f83037 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/lib.rs
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/lib.rs
@@ -84,7 +84,7 @@
 //! a different thread.
 
 // Proc-macro2 types in rustdoc of other crates get linked to here.
-#![doc(html_root_url = "https://docs.rs/proc-macro2/1.0.94")]
+#![doc(html_root_url = "https://docs.rs/proc-macro2/1.0.95")]
 #![cfg_attr(any(proc_macro_span, super_unstable), feature(proc_macro_span))]
 #![cfg_attr(super_unstable, feature(proc_macro_def_site))]
 #![cfg_attr(docsrs, feature(doc_cfg))]
@@ -338,57 +338,6 @@
 
 impl Error for LexError {}
 
-/// The source file of a given `Span`.
-///
-/// This type is semver exempt and not exposed by default.
-#[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
-#[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))]
-#[derive(Clone, PartialEq, Eq)]
-pub struct SourceFile {
-    inner: imp::SourceFile,
-    _marker: ProcMacroAutoTraits,
-}
-
-#[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
-impl SourceFile {
-    fn _new(inner: imp::SourceFile) -> Self {
-        SourceFile {
-            inner,
-            _marker: MARKER,
-        }
-    }
-
-    /// Get the path to this source file.
-    ///
-    /// ### Note
-    ///
-    /// If the code span associated with this `SourceFile` was generated by an
-    /// external macro, this may not be an actual path on the filesystem. Use
-    /// [`is_real`] to check.
-    ///
-    /// Also note that even if `is_real` returns `true`, if
-    /// `--remap-path-prefix` was passed on the command line, the path as given
-    /// may not actually be valid.
-    ///
-    /// [`is_real`]: #method.is_real
-    pub fn path(&self) -> PathBuf {
-        self.inner.path()
-    }
-
-    /// Returns `true` if this source file is a real source file, and not
-    /// generated by an external macro's expansion.
-    pub fn is_real(&self) -> bool {
-        self.inner.is_real()
-    }
-}
-
-#[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
-impl Debug for SourceFile {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        Debug::fmt(&self.inner, f)
-    }
-}
-
 /// A region of source code, along with macro expansion information.
 #[derive(Copy, Clone)]
 pub struct Span {
@@ -470,15 +419,6 @@
         self.unwrap()
     }
 
-    /// The original source file into which this span points.
-    ///
-    /// This method is semver exempt and not exposed by default.
-    #[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
-    #[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))]
-    pub fn source_file(&self) -> SourceFile {
-        SourceFile::_new(self.inner.source_file())
-    }
-
     /// Returns the span's byte position range in the source file.
     ///
     /// This method requires the `"span-locations"` feature to be enabled.
@@ -524,6 +464,33 @@
         self.inner.end()
     }
 
+    /// The path to the source file in which this span occurs, for display
+    /// purposes.
+    ///
+    /// This might not correspond to a valid file system path. It might be
+    /// remapped, or might be an artificial path such as `"<macro expansion>"`.
+    ///
+    /// This method is semver exempt and not exposed by default.
+    #[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
+    #[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))]
+    pub fn file(&self) -> String {
+        self.inner.file()
+    }
+
+    /// The path to the source file in which this span occurs on disk.
+    ///
+    /// This is the actual path on disk. It is unaffected by path remapping.
+    ///
+    /// This path should not be embedded in the output of the macro; prefer
+    /// `file()` instead.
+    ///
+    /// This method is semver exempt and not exposed by default.
+    #[cfg(all(procmacro2_semver_exempt, any(not(wrap_proc_macro), super_unstable)))]
+    #[cfg_attr(docsrs, doc(cfg(procmacro2_semver_exempt)))]
+    pub fn local_file(&self) -> Option<PathBuf> {
+        self.inner.local_file()
+    }
+
     /// Create a new span encompassing `self` and `other`.
     ///
     /// Returns `None` if `self` and `other` are from different files.
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/wrapper.rs b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/wrapper.rs
index 17d7b41..ee31fa6 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/wrapper.rs
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/src/wrapper.rs
@@ -360,45 +360,6 @@
     }
 }
 
-#[derive(Clone, PartialEq, Eq)]
-#[cfg(super_unstable)]
-pub(crate) enum SourceFile {
-    Compiler(proc_macro::SourceFile),
-    Fallback(fallback::SourceFile),
-}
-
-#[cfg(super_unstable)]
-impl SourceFile {
-    fn nightly(sf: proc_macro::SourceFile) -> Self {
-        SourceFile::Compiler(sf)
-    }
-
-    /// Get the path to this source file as a string.
-    pub(crate) fn path(&self) -> PathBuf {
-        match self {
-            SourceFile::Compiler(a) => a.path(),
-            SourceFile::Fallback(a) => a.path(),
-        }
-    }
-
-    pub(crate) fn is_real(&self) -> bool {
-        match self {
-            SourceFile::Compiler(a) => a.is_real(),
-            SourceFile::Fallback(a) => a.is_real(),
-        }
-    }
-}
-
-#[cfg(super_unstable)]
-impl Debug for SourceFile {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            SourceFile::Compiler(a) => Debug::fmt(a, f),
-            SourceFile::Fallback(a) => Debug::fmt(a, f),
-        }
-    }
-}
-
 #[derive(Copy, Clone)]
 pub(crate) enum Span {
     Compiler(proc_macro::Span),
@@ -456,14 +417,6 @@
         }
     }
 
-    #[cfg(super_unstable)]
-    pub(crate) fn source_file(&self) -> SourceFile {
-        match self {
-            Span::Compiler(s) => SourceFile::nightly(s.source_file()),
-            Span::Fallback(s) => SourceFile::Fallback(s.source_file()),
-        }
-    }
-
     #[cfg(span_locations)]
     pub(crate) fn byte_range(&self) -> Range<usize> {
         match self {
@@ -506,6 +459,22 @@
         }
     }
 
+    #[cfg(super_unstable)]
+    pub(crate) fn file(&self) -> String {
+        match self {
+            Span::Compiler(s) => s.file(),
+            Span::Fallback(s) => s.file(),
+        }
+    }
+
+    #[cfg(super_unstable)]
+    pub(crate) fn local_file(&self) -> Option<PathBuf> {
+        match self {
+            Span::Compiler(s) => s.local_file(),
+            Span::Fallback(s) => s.local_file(),
+        }
+    }
+
     pub(crate) fn join(&self, other: Span) -> Option<Span> {
         let ret = match (self, other) {
             #[cfg(proc_macro_span)]
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/marker.rs b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/marker.rs
index 99f64c06..af8932a 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/marker.rs
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/marker.rs
@@ -56,19 +56,17 @@
 
 #[cfg(procmacro2_semver_exempt)]
 mod semver_exempt {
-    use proc_macro2::{LineColumn, SourceFile};
+    use proc_macro2::LineColumn;
 
     assert_impl!(LineColumn is Send and Sync);
-
-    assert_impl!(SourceFile is not Send or Sync);
 }
 
 mod unwind_safe {
+    #[cfg(procmacro2_semver_exempt)]
+    use proc_macro2::LineColumn;
     use proc_macro2::{
         Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree,
     };
-    #[cfg(procmacro2_semver_exempt)]
-    use proc_macro2::{LineColumn, SourceFile};
     use std::panic::{RefUnwindSafe, UnwindSafe};
 
     macro_rules! assert_unwind_safe {
@@ -95,6 +93,5 @@
     #[cfg(procmacro2_semver_exempt)]
     assert_unwind_safe! {
         LineColumn
-        SourceFile
     }
 }
diff --git a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/test.rs b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/test.rs
index 0d7c88d3..aa7397b6 100644
--- a/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/test.rs
+++ b/third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/tests/test.rs
@@ -549,9 +549,8 @@
     let end = Span::call_site().end();
     assert_eq!(end.line, 1);
     assert_eq!(end.column, 0);
-    let source_file = Span::call_site().source_file();
-    assert_eq!(source_file.path().to_string_lossy(), "<unspecified>");
-    assert!(!source_file.is_real());
+    assert_eq!(Span::call_site().file(), "<unspecified>");
+    assert!(Span::call_site().local_file().is_none());
 }
 
 #[cfg(procmacro2_semver_exempt)]
@@ -568,11 +567,8 @@
         .into_iter()
         .collect::<Vec<_>>();
 
-    assert!(source1[0].span().source_file() != source2[0].span().source_file());
-    assert_eq!(
-        source1[0].span().source_file(),
-        source1[1].span().source_file()
-    );
+    assert!(source1[0].span().file() != source2[0].span().file());
+    assert_eq!(source1[0].span().file(), source1[1].span().file());
 
     let joined1 = source1[0].span().join(source1[1].span());
     let joined2 = source1[0].span().join(source2[0].span());
@@ -586,10 +582,7 @@
     assert_eq!(end.line, 2);
     assert_eq!(end.column, 3);
 
-    assert_eq!(
-        joined1.unwrap().source_file(),
-        source1[0].span().source_file()
-    );
+    assert_eq!(joined1.unwrap().file(), source1[0].span().file());
 }
 
 #[test]
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json
index c8ed0fe..d7bf6bff 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "e8f2ad14c019fdd886f1878d54391d578947611f"
+    "sha1": "bdc92c138b650e30bc203357bfe581bbd7ba8fb3"
   },
   "path_in_vcs": "toktrie"
 }
\ No newline at end of file
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock
index 4a7704cea..079d3000 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.lock
@@ -106,7 +106,7 @@
 
 [[package]]
 name = "toktrie"
-version = "0.7.14"
+version = "0.7.18"
 dependencies = [
  "anyhow",
  "bytemuck",
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml
index 30830f2..c683e03 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2021"
 name = "toktrie"
-version = "0.7.14"
+version = "0.7.18"
 build = false
 autolib = false
 autobins = false
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig
index 9707ca9..4546bfda 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "toktrie"
-version = "0.7.14"
+version = "0.7.18"
 edition = "2021"
 license = "MIT"
 description = "LLM Token Trie library"
diff --git a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs
index 6c08c33..39857213 100644
--- a/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs
+++ b/third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/src/tokenv.rs
@@ -26,7 +26,13 @@
                 .position(|&x| x == ff)
                 .unwrap_or(s.len() - idx);
             if normal_len != 0 {
-                result.extend_from_slice(&self.tokenize_bytes(&s[idx..idx + normal_len]));
+                let new_tokens = self.tokenize_bytes(&s[idx..idx + normal_len]);
+                for (idx, t) in new_tokens.iter().enumerate() {
+                    if trie.is_special_token(*t) {
+                        num_fixed_tokens = result.len() + idx + 1;
+                    }
+                }
+                result.extend_from_slice(&new_tokens);
                 idx += normal_len;
             }
             idx += 1; // skip ff
diff --git a/third_party/rust/clap/v4/BUILD.gn b/third_party/rust/clap/v4/BUILD.gn
index af6e4fc..9e1ea6b 100644
--- a/third_party/rust/clap/v4/BUILD.gn
+++ b/third_party/rust/clap/v4/BUILD.gn
@@ -151,7 +151,7 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "4.5.36"
+  cargo_pkg_version = "4.5.37"
   cargo_pkg_name = "clap"
   cargo_pkg_description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
   library_configs -= [ "//build/config/coverage:default_coverage" ]
diff --git a/third_party/rust/clap/v4/README.chromium b/third_party/rust/clap/v4/README.chromium
index c65566bd..a46fa31 100644
--- a/third_party/rust/clap/v4/README.chromium
+++ b/third_party/rust/clap/v4/README.chromium
@@ -1,7 +1,7 @@
 Name: clap
 URL: https://crates.io/crates/clap
-Version: 4.5.36
-Revision: 9ebef155c0c05094f6b25b5884a0bdd4250db9f3
+Version: 4.5.37
+Revision: f209bce2203498e743b171b7ac64f0fb9d3ae590
 License: Apache-2.0
 License File: //third_party/rust/chromium_crates_io/vendor/clap-v4/LICENSE-APACHE
 Shipped: no
diff --git a/third_party/rust/clap_builder/v4/BUILD.gn b/third_party/rust/clap_builder/v4/BUILD.gn
index 766de3b..7245e285 100644
--- a/third_party/rust/clap_builder/v4/BUILD.gn
+++ b/third_party/rust/clap_builder/v4/BUILD.gn
@@ -76,7 +76,7 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "4.5.36"
+  cargo_pkg_version = "4.5.37"
   cargo_pkg_name = "clap_builder"
   cargo_pkg_description = "A simple to use, efficient, and full-featured Command Line Argument Parser"
   library_configs -= [ "//build/config/coverage:default_coverage" ]
diff --git a/third_party/rust/clap_builder/v4/README.chromium b/third_party/rust/clap_builder/v4/README.chromium
index f94d0ad..4dce490 100644
--- a/third_party/rust/clap_builder/v4/README.chromium
+++ b/third_party/rust/clap_builder/v4/README.chromium
@@ -1,7 +1,7 @@
 Name: clap_builder
 URL: https://crates.io/crates/clap_builder
-Version: 4.5.36
-Revision: 9ebef155c0c05094f6b25b5884a0bdd4250db9f3
+Version: 4.5.37
+Revision: f209bce2203498e743b171b7ac64f0fb9d3ae590
 License: Apache-2.0
 License File: //third_party/rust/chromium_crates_io/vendor/clap_builder-v4/LICENSE-APACHE
 Shipped: no
diff --git a/third_party/rust/llguidance/v0_7/BUILD.gn b/third_party/rust/llguidance/v0_7/BUILD.gn
index d40240c..8dd7f3a 100644
--- a/third_party/rust/llguidance/v0_7/BUILD.gn
+++ b/third_party/rust/llguidance/v0_7/BUILD.gn
@@ -65,7 +65,7 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.7.14"
+  cargo_pkg_version = "0.7.18"
   cargo_pkg_name = "llguidance"
   cargo_pkg_description = "Super-fast Structured Outputs"
   library_configs -= [ "//build/config/coverage:default_coverage" ]
diff --git a/third_party/rust/llguidance/v0_7/README.chromium b/third_party/rust/llguidance/v0_7/README.chromium
index 38fbeae..10e3a9cc 100644
--- a/third_party/rust/llguidance/v0_7/README.chromium
+++ b/third_party/rust/llguidance/v0_7/README.chromium
@@ -1,7 +1,7 @@
 Name: llguidance
 URL: https://crates.io/crates/llguidance
-Version: 0.7.14
-Revision: e8f2ad14c019fdd886f1878d54391d578947611f
+Version: 0.7.18
+Revision: bdc92c138b650e30bc203357bfe581bbd7ba8fb3
 License: MIT
 License File: //third_party/rust/chromium_crates_io/vendor/llguidance-v0_7/LICENSE
 Shipped: yes
diff --git a/third_party/rust/proc_macro2/v1/BUILD.gn b/third_party/rust/proc_macro2/v1/BUILD.gn
index a318210..7cba827 100644
--- a/third_party/rust/proc_macro2/v1/BUILD.gn
+++ b/third_party/rust/proc_macro2/v1/BUILD.gn
@@ -29,7 +29,7 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "1.0.94"
+  cargo_pkg_version = "1.0.95"
   cargo_pkg_authors =
       "David Tolnay <dtolnay@gmail.com>, Alex Crichton <alex@alexcrichton.com>"
   cargo_pkg_name = "proc-macro2"
diff --git a/third_party/rust/proc_macro2/v1/README.chromium b/third_party/rust/proc_macro2/v1/README.chromium
index 38dc292..3393dca 100644
--- a/third_party/rust/proc_macro2/v1/README.chromium
+++ b/third_party/rust/proc_macro2/v1/README.chromium
@@ -1,7 +1,7 @@
 Name: proc-macro2
 URL: https://crates.io/crates/proc-macro2
-Version: 1.0.94
-Revision: 7cb0f3cdee5888a581c0041ba9acae5727372277
+Version: 1.0.95
+Revision: 24bbf16d9df01d5f7d9ac39bdfbaea85f4c194fb
 License: Apache-2.0
 License File: //third_party/rust/chromium_crates_io/vendor/proc-macro2-v1/LICENSE-APACHE
 Shipped: yes
diff --git a/third_party/rust/toktrie/v0_7/BUILD.gn b/third_party/rust/toktrie/v0_7/BUILD.gn
index dac82d3..5331260 100644
--- a/third_party/rust/toktrie/v0_7/BUILD.gn
+++ b/third_party/rust/toktrie/v0_7/BUILD.gn
@@ -27,7 +27,7 @@
 
   build_native_rust_unit_tests = false
   edition = "2021"
-  cargo_pkg_version = "0.7.14"
+  cargo_pkg_version = "0.7.18"
   cargo_pkg_name = "toktrie"
   cargo_pkg_description = "LLM Token Trie library"
   library_configs -= [ "//build/config/coverage:default_coverage" ]
diff --git a/third_party/rust/toktrie/v0_7/README.chromium b/third_party/rust/toktrie/v0_7/README.chromium
index 317e9fb..d9da355 100644
--- a/third_party/rust/toktrie/v0_7/README.chromium
+++ b/third_party/rust/toktrie/v0_7/README.chromium
@@ -1,7 +1,7 @@
 Name: toktrie
 URL: https://crates.io/crates/toktrie
-Version: 0.7.14
-Revision: e8f2ad14c019fdd886f1878d54391d578947611f
+Version: 0.7.18
+Revision: bdc92c138b650e30bc203357bfe581bbd7ba8fb3
 License: MIT
 License File: //third_party/rust/chromium_crates_io/vendor/toktrie-v0_7/LICENSE
 Shipped: yes
diff --git a/third_party/skia b/third_party/skia
index 1161451..50ed51d 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 1161451d903b47647c31aa0b463623ff58071b56
+Subproject commit 50ed51d9e5294acc90db06d904bb5f3a89ca1a75
diff --git a/tools/android/checkstyle/chromium-style-5.0.xml b/tools/android/checkstyle/chromium-style-5.0.xml
index eef69acb..763a27a3 100644
--- a/tools/android/checkstyle/chromium-style-5.0.xml
+++ b/tools/android/checkstyle/chromium-style-5.0.xml
@@ -236,10 +236,16 @@
       <property name="message" value="Parameter comments should use the ErrorProne-aware syntax: /* paramName= */."/>
     </module>
     <module name="RegexpSinglelineJava">
+      <property name="id" value="ClosingJavadocs"/>
+      <property name="severity" value="warning"/>
+      <property name="format" value="\* ?\*\/"/>
+      <property name="message" value="Closing a javadoc only takes a single '*'."/>
+    </module>
+    <module name="RegexpSinglelineJava">
       <property name="id" value="RemoveObserverMethodReference"/>
       <property name="severity" value="warning"/>
       <property name="format" value="remove[A-Z][a-zA-Z]*\([a-zA-Z]+::"/>
-      <property name="message"  value="Each method reference creates a new instance. The specific object given to the addObserver-like-method needs to be remembered and given to the removeObserver-like-method."/>
+      <property name="message" value="Each method reference creates a new instance. The specific object given to the addObserver-like-method needs to be remembered and given to the removeObserver-like-method."/>
     </module>
     <module name="RegexpSinglelineJava">
       <property name="id" value="MockitoInitMocks"/>
diff --git a/tools/binary_size/supersize.pydeps b/tools/binary_size/supersize.pydeps
index 1d0b3c14c..219dc2bb 100644
--- a/tools/binary_size/supersize.pydeps
+++ b/tools/binary_size/supersize.pydeps
@@ -24,7 +24,6 @@
 ../grit/grit/gather/skeleton_gatherer.py
 ../grit/grit/gather/tr_html.py
 ../grit/grit/gather/txt.py
-../grit/grit/gender.py
 ../grit/grit/lazy_re.py
 ../grit/grit/node/__init__.py
 ../grit/grit/node/base.py
diff --git a/tools/grit/grit/clique.py b/tools/grit/grit/clique.py
index 57c3032..9451d360 100644
--- a/tools/grit/grit/clique.py
+++ b/tools/grit/grit/clique.py
@@ -15,7 +15,6 @@
 from grit import pseudo
 from grit import pseudolocales
 from grit import tclib
-from grit import gender
 
 
 class UberClique:
@@ -41,15 +40,15 @@
     # A map of clique IDs to list of languages to indicate missing translations.
     self.missing_translations_ = {}
 
-  def _AddMissingTranslation(self, lang, clique, is_error):
+  def _AddMissingTranslation(self, lang, gender, clique, is_error):
     tl = self.fallback_translations_
     if is_error:
       tl = self.missing_translations_
     id = clique.GetId()
     if id not in tl:
       tl[id] = {}
-    if lang not in tl[id]:
-      tl[id][lang] = 1
+    if (lang, gender) not in tl[id]:
+      tl[id][(lang, gender)] = 1
 
   def HasMissingTranslations(self):
     return len(self.missing_translations_) > 0
@@ -58,7 +57,8 @@
     '''Returns a string suitable for printing to report missing
     and fallback translations to the user.
     '''
-    def ReportTranslation(clique, langs):
+
+    def ReportTranslation(clique, langs_and_genders):
       text = clique.GetMessage().GetPresentableContent()
       # The text 'error' (usually 'Error:' but we are conservative)
       # can trigger some build environments (Visual Studio, we're
@@ -68,24 +68,27 @@
       ellipsis = ''
       if len(text) > 40:
         ellipsis = '...'
-      langs_extract = langs[0:6]
-      describe_langs = ','.join(langs_extract)
-      if len(langs) > 6:
-        describe_langs += " and %d more" % (len(langs) - 6)
+      langs_and_genders_extract = langs_and_genders[0:6]
+      describe_langs = ','.join(
+          str(lang_and_gender) for lang_and_gender in langs_and_genders_extract)
+      if len(langs_and_genders) > 6:
+        describe_langs += " and %d more" % (len(langs_and_genders) - 6)
       return "  %s \"%s%s\" %s" % (clique.GetId(), extract, ellipsis,
                                    describe_langs)
     lines = []
     if len(self.fallback_translations_):
       lines.append(
         "WARNING: Fell back to English for the following translations:")
-      for (id, langs) in self.fallback_translations_.items():
+      for (id, langs_and_genders) in self.fallback_translations_.items():
         lines.append(
-            ReportTranslation(self.cliques_[id][0], list(langs.keys())))
+            ReportTranslation(self.cliques_[id][0],
+                              list(langs_and_genders.keys())))
     if len(self.missing_translations_):
       lines.append("ERROR: The following translations are MISSING:")
-      for (id, langs) in self.missing_translations_.items():
+      for (id, langs_and_genders) in self.missing_translations_.items():
         lines.append(
-            ReportTranslation(self.cliques_[id][0], list(langs.keys())))
+            ReportTranslation(self.cliques_[id][0],
+                              list(langs_and_genders.keys())))
     return '\n'.join(lines)
 
   def MakeClique(self, message, translateable=True):
@@ -113,20 +116,21 @@
 
     return clique
 
-  def FindCliqueAndAddTranslation(self, translation, language):
+  def FindCliqueAndAddTranslation(self, translation, language, gender):
     '''Adds the specified translation to the clique with the source message
     it is a translation of.
 
     Args:
       translation: tclib.Translation()
       language: 'en' | 'fr' ...
+      gender: 'OTHER' | 'FEMININE' | 'MASCULINE' | 'NEUTER'
 
     Return:
       True if the source message was found, otherwise false.
     '''
     if translation.GetId() in self.cliques_:
       for clique in self.cliques_[translation.GetId()]:
-        clique.AddTranslation(translation, language)
+        clique.AddTranslation(translation, language, gender)
       return True
     else:
       return False
@@ -206,7 +210,8 @@
       lang: 'fr'
       debug: True | False
     '''
-    def Callback(id, structure):
+
+    def Callback(id, structures_by_gender):
       if id not in self.cliques_:
         if debug:
           print("Ignoring translation #%s" % id)
@@ -219,24 +224,25 @@
       # only contains placeholder names).
       original_msg = self.BestClique(id).GetMessage()
 
-      translation = tclib.Translation(id=id)
-      # TODO(crbug.com/409770185): Support gendered translations.
-      for is_ph, text in structure[gender.DEFAULT_GENDER]:
-        if not is_ph:
-          translation.AppendText(text)
-        else:
-          found_placeholder = False
-          for ph in original_msg.GetPlaceholders():
-            if ph.GetPresentation() == text:
-              translation.AppendPlaceholder(tclib.Placeholder(
-                ph.GetPresentation(), ph.GetOriginal(), ph.GetExample()))
-              found_placeholder = True
-              break
-          if not found_placeholder:
-            raise exception.MismatchingPlaceholders(
-              'Translation for message ID %s had <ph name="%s"/>, no match\n'
-              'in original message' % (id, text))
-      self.FindCliqueAndAddTranslation(translation, lang)
+      for (gender, structure) in structures_by_gender.items():
+        translation = tclib.Translation(id=id)
+        for is_ph, text in structure:
+          if not is_ph:
+            translation.AppendText(text)
+          else:
+            found_placeholder = False
+            for ph in original_msg.GetPlaceholders():
+              if ph.GetPresentation() == text:
+                translation.AppendPlaceholder(
+                    tclib.Placeholder(ph.GetPresentation(), ph.GetOriginal(),
+                                      ph.GetExample()))
+                found_placeholder = True
+                break
+            if not found_placeholder:
+              raise exception.MismatchingPlaceholders(
+                  'Translation for message ID %s had <ph name="%s"/>, no '
+                  'match\nin original message' % (id, text))
+        self.FindCliqueAndAddTranslation(translation, lang, gender)
     return Callback
 
 
@@ -304,6 +310,7 @@
   # change this to the language code of Messages you add to cliques_.
   # TODO(joi) Actually change this based on the <grit> node's source language
   source_language = 'en'
+  source_gender = constants.DEFAULT_GENDER
 
   # A constant translation we use when asked for a translation into the
   # special language constants.CONSTANT_LANGUAGE.
@@ -337,7 +344,9 @@
 
     # A mapping of language identifiers to tclib.BaseMessage and its
     # subclasses (i.e. tclib.Message and tclib.Translation).
-    self.clique = { MessageClique.source_language : message }
+    self.clique = {
+        (MessageClique.source_language, MessageClique.source_gender): message
+    }
     # A list of the "shortcut groups" this clique is
     # part of.  Within any given shortcut group, no shortcut key (e.g. &J)
     # must appear more than once in each language for all cliques that
@@ -350,7 +359,8 @@
 
   def GetMessage(self):
     '''Retrieves the tclib.Message that is the source for this clique.'''
-    return self.clique[MessageClique.source_language]
+    return self.clique[(MessageClique.source_language,
+                        MessageClique.source_gender)]
 
   def GetId(self):
     '''Retrieves the message ID of the messages in this clique.'''
@@ -370,8 +380,11 @@
     if custom_type and not custom_type.Validate(self.GetMessage()):
       raise exception.InvalidMessage(self.GetMessage().GetRealContent())
 
-  def MessageForLanguage(self, lang, pseudo_if_no_match=True,
-                         fallback_to_english=False):
+  def MessageForLanguageAndGender(self,
+                                  lang,
+                                  gender,
+                                  pseudo_if_no_match=True,
+                                  fallback_to_english=False):
     '''Returns the message/translation for the specified language, providing
     a pseudotranslation if there is no available translation and a pseudo-
     translation is requested.
@@ -381,6 +394,7 @@
 
     Args:
       lang: 'en'
+      gender: 'OTHER' | 'FEMININE' | 'MASCULINE' | 'NEUTER'
       pseudo_if_no_match: True
       fallback_to_english: False
 
@@ -393,9 +407,15 @@
     if lang == constants.CONSTANT_LANGUAGE:
       return self.CONSTANT_TRANSLATION
 
-    for msglang in self.clique:
-      if lang == msglang:
-        return self.clique[msglang]
+    for (msglang, msggender) in self.clique:
+      if lang == msglang and gender == msggender:
+        return self.clique[(msglang, msggender)]
+
+    # gender translations are expected to be sparse, so if we didn't find a
+    # match, fall back to the source gender
+    for (msglang, msggender) in self.clique:
+      if lang == msglang and MessageClique.source_gender == msggender:
+        return self.clique[(msglang, msggender)]
 
     if pseudo_if_no_match:
       if lang == constants.PSEUDOLOCALE_LONG_STRINGS:
@@ -404,14 +424,17 @@
         return pseudolocales.PseudoRTLMessage(self.GetMessage())
 
     if fallback_to_english:
-      self.uber_clique._AddMissingTranslation(lang, self, is_error=False)
+      self.uber_clique._AddMissingTranslation(lang,
+                                              gender,
+                                              self,
+                                              is_error=False)
       return self.GetMessage()
 
     # If we're not supposed to generate pseudotranslations, we add an error
     # report to a list of errors, then fail at a higher level, so that we
     # get a list of all messages that are missing translations.
     if not pseudo_if_no_match:
-      self.uber_clique._AddMissingTranslation(lang, self, is_error=True)
+      self.uber_clique._AddMissingTranslation(lang, gender, self, is_error=True)
 
     return pseudo.PseudoMessage(self.GetMessage())
 
@@ -424,24 +447,26 @@
       include_pseudo: True
 
     Return:
-      { 'en' : tclib.Message,
-        'fr' : tclib.Translation,
-        pseudo.PSEUDO_LANG : tclib.Translation }
+      { ('en', 'OTHER') : tclib.Message,
+        ('fr', 'OTHER') : tclib.Translation,
+        (pseudo.PSEUDO_LANG, 'OTHER') : tclib.Translation }
     '''
     if not self.translateable:
       return [self.GetMessage()]
 
     matches = {}
-    for msglang in self.clique:
+    for (msglang, msggender) in self.clique:
       if lang_re.match(msglang):
-        matches[msglang] = self.clique[msglang]
+        matches[(msglang, msggender)] = self.clique[(msglang, msggender)]
 
     if include_pseudo:
-      matches[pseudo.PSEUDO_LANG] = pseudo.PseudoMessage(self.GetMessage())
+      for gender in constants.ALL_GENDERS:
+        matches[(pseudo.PSEUDO_LANG,
+                 gender)] = pseudo.PseudoMessage(self.GetMessage())
 
     return matches
 
-  def AddTranslation(self, translation, language):
+  def AddTranslation(self, translation, language, gender):
     '''Add a translation to this clique.  The translation must have the same
     ID as the message that is the source for this clique.
 
@@ -450,6 +475,7 @@
     Args:
       translation: tclib.Translation()
       language: 'en'
+      gender: 'OTHER' | 'FEMININE' | 'MASCULINE' | 'NEUTER'
 
     Throws:
       grit.exception.InvalidTranslation if the translation you're trying to add
@@ -461,7 +487,7 @@
       raise exception.InvalidTranslation(
         'Msg ID %s, transl ID %s' % (self.GetId(), translation.GetId()))
 
-    assert not language in self.clique
+    assert not (language, gender) in self.clique
 
     # Because two messages can differ in the original content of their
     # placeholders yet share the same ID (because they are otherwise the
@@ -474,10 +500,11 @@
     # See grit.clique_unittest.MessageCliqueUnittest.testSemiIdenticalCliques
     # for a concrete explanation of why this is necessary.
 
-    original = self.MessageForLanguage(self.source_language, False)
+    original = self.MessageForLanguageAndGender(self.source_language,
+                                                self.source_gender, False)
     if len(original.GetPlaceholders()) != len(translation.GetPlaceholders()):
-      print("ERROR: '%s' translation of message id %s does not match" %
-            (language, translation.GetId()))
+      print("ERROR: '%s/%s' translation of message id %s does not match" %
+            (language, gender, translation.GetId()))
       assert False
 
     transl_msg = tclib.Translation(id=self.GetId(),
@@ -486,7 +513,10 @@
 
     if (self.custom_type and
         not self.custom_type.ValidateAndModify(language, transl_msg)):
-      print("WARNING: %s translation failed validation: %s" %
-            (language, transl_msg.GetId()))
+      print("WARNING: %s/%s translation failed validation: %s" %
+            (language, gender, transl_msg.GetId()))
 
-    self.clique[language] = transl_msg
+    self.clique[(language, gender)] = transl_msg
+
+  def HasTranslation(self, lang, gender):
+    return self.clique.get((lang, gender)) is not None
diff --git a/tools/grit/grit/clique_unittest.py b/tools/grit/grit/clique_unittest.py
index 27cde7b..e694d50c 100755
--- a/tools/grit/grit/clique_unittest.py
+++ b/tools/grit/grit/clique_unittest.py
@@ -17,10 +17,11 @@
 from io import StringIO
 
 from grit import clique
+from grit import constants
 from grit import exception
+from grit import grd_reader
 from grit import pseudo
 from grit import tclib
-from grit import grd_reader
 from grit import util
 
 class MessageCliqueUnittest(unittest.TestCase):
@@ -34,35 +35,78 @@
     self.assertTrue(c.GetMessage() == msg)
     self.assertTrue(c.GetId() == msg.GetId())
 
+    msg_en_f = tclib.Translation(
+        text='Hello USERNAME, how are youe?',
+        id=msg.GetId(),
+        placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')])
     msg_fr = tclib.Translation(text='Bonjour USERNAME, comment ca va?',
                                id=msg.GetId(), placeholders=[
                                 tclib.Placeholder('USERNAME', '%s', 'Joi')])
+    msg_fr_f = tclib.Translation(
+        text='Bonjour USERNAME, comment ca vae?',
+        id=msg.GetId(),
+        placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')])
     msg_de = tclib.Translation(text='Guten tag USERNAME, wie geht es dir?',
                                id=msg.GetId(), placeholders=[
                                 tclib.Placeholder('USERNAME', '%s', 'Joi')])
+    msg_de_f = tclib.Translation(
+        text='Guten tag USERNAME, wie gehten es dir?',
+        id=msg.GetId(),
+        placeholders=[tclib.Placeholder('USERNAME', '%s', 'Joi')])
 
-    c.AddTranslation(msg_fr, 'fr')
-    factory.FindCliqueAndAddTranslation(msg_de, 'de')
+    c.AddTranslation(msg_en_f, 'en', constants.GENDER_FEMININE)
+    c.AddTranslation(msg_fr, 'fr', constants.DEFAULT_GENDER)
+    c.AddTranslation(msg_fr_f, 'fr', constants.GENDER_FEMININE)
+    factory.FindCliqueAndAddTranslation(msg_de, 'de', constants.DEFAULT_GENDER)
+    factory.FindCliqueAndAddTranslation(msg_de_f, 'de',
+                                        constants.GENDER_FEMININE)
 
     # sort() sorts lists in-place and does not return them
     for lang in ('en', 'fr', 'de'):
-      self.assertTrue(lang in c.clique)
+      for gender in (constants.DEFAULT_GENDER, constants.GENDER_FEMININE):
+        self.assertTrue((lang, gender) in c.clique,
+                        msg=f'({lang}, {gender}) is not in c.clique')
 
-    self.assertTrue(c.MessageForLanguage('fr').GetRealContent() ==
-                    msg_fr.GetRealContent())
+    self.assertEqual(
+        c.MessageForLanguageAndGender(
+            'fr', constants.DEFAULT_GENDER).GetRealContent(),
+        msg_fr.GetRealContent())
+    self.assertEqual(
+        c.MessageForLanguageAndGender(
+            'fr', constants.GENDER_FEMININE).GetRealContent(),
+        msg_fr_f.GetRealContent())
 
     try:
-      c.MessageForLanguage('zh-CN', False)
+      c.MessageForLanguageAndGender('zh-CN', constants.DEFAULT_GENDER, False)
       self.fail('Should have gotten exception')
     except:
       pass
 
-    self.assertTrue(c.MessageForLanguage('zh-CN', True) != None)
+    self.assertIsNotNone(
+        c.MessageForLanguageAndGender('zh-CN', constants.DEFAULT_GENDER, True))
+
+    # Missing gender translation falls back to the default gender for the same
+    # language.
+    self._assertTranslationPartsEqual(
+        c.MessageForLanguageAndGender('de', constants.GENDER_MASCULINE, False),
+        msg_de)
 
     rex = re.compile('fr|de|bingo')
-    self.assertTrue(len(c.AllMessagesThatMatch(rex, False)) == 2)
-    self.assertTrue(
-        c.AllMessagesThatMatch(rex, True)[pseudo.PSEUDO_LANG] is not None)
+    self.assertEqual(len(c.AllMessagesThatMatch(rex, False)), 4)
+    self.assertIsNotNone(
+        c.AllMessagesThatMatch(rex, True)[(pseudo.PSEUDO_LANG,
+                                           constants.DEFAULT_GENDER)])
+
+  def _assertTranslationPartsEqual(self, msg1, msg2):
+    for part in zip(msg1.parts, msg2.parts):
+      if isinstance(part[0], str):
+        self.assertEqual(part[0], part[1])
+      elif isinstance(part[0], tclib.Placeholder):
+        self.assertEqual(part[0].GetPresentation(), part[1].GetPresentation())
+        self.assertEqual(part[0].GetOriginal(), part[1].GetOriginal())
+        self.assertEqual(part[0].GetExample(), part[1].GetExample())
+      else:
+        self.fail(f'unsupported type in translation parts: {part[0]}')
 
   def testBestClique(self):
     factory = clique.UberClique()
@@ -148,31 +192,69 @@
     cliques = [factory.MakeClique(msg) for msg in messages]
 
     for clq in cliques:
-      clq.AddTranslation(translation, 'fr')
+      clq.AddTranslation(translation, 'fr', constants.DEFAULT_GENDER)
 
-    self.assertTrue(cliques[0].MessageForLanguage('fr').GetRealContent() ==
-                    'Bonjour $1')
-    self.assertTrue(cliques[1].MessageForLanguage('fr').GetRealContent() ==
-                    'Bonjour %s')
+    self.assertTrue(cliques[0].MessageForLanguageAndGender(
+        'fr', constants.DEFAULT_GENDER).GetRealContent() == 'Bonjour $1')
+    self.assertTrue(cliques[1].MessageForLanguageAndGender(
+        'fr', constants.DEFAULT_GENDER).GetRealContent() == 'Bonjour %s')
 
   def testMissingTranslations(self):
-    messages = [ tclib.Message(text='Hello'), tclib.Message(text='Goodbye') ]
+    messages = [
+        tclib.Message(text='Hello'),
+        tclib.Message(text='Goodbye'),
+        tclib.Message(text='gender fallback to english example'),
+        tclib.Message(text='gender fallback to default gender example'),
+    ]
     factory = clique.UberClique()
     cliques = [factory.MakeClique(msg) for msg in messages]
 
-    cliques[1].MessageForLanguage('fr', False, True)
+    cliques[3].AddTranslation(
+        tclib.Translation(text='Buongiorno',
+                          id=messages[3].GetId(),
+                          placeholders=[]), 'it', constants.DEFAULT_GENDER)
 
-    self.assertTrue(not factory.HasMissingTranslations())
+    cliques[1].MessageForLanguageAndGender('fr', constants.DEFAULT_GENDER,
+                                           False, True)
+    cliques[2].MessageForLanguageAndGender('es', constants.DEFAULT_GENDER,
+                                           False, True)
 
-    cliques[0].MessageForLanguage('de', False, False)
+    # Non-default genders can still fall back to English
+    cliques[2].MessageForLanguageAndGender('es', constants.GENDER_MASCULINE,
+                                           False, True)
+
+    cliques[3].MessageForLanguageAndGender('it', constants.DEFAULT_GENDER,
+                                           False, False)
+
+    # Non-default genders can fall back to the default constants without an error
+    # or warning.
+    cliques[3].MessageForLanguageAndGender('it', constants.GENDER_MASCULINE,
+                                           False, False)
+
+    self.assertFalse(factory.HasMissingTranslations())
+
+    cliques[0].MessageForLanguageAndGender('de', constants.DEFAULT_GENDER,
+                                           False, False)
+    cliques[0].MessageForLanguageAndGender('de', constants.GENDER_FEMININE,
+                                           False, False)
 
     self.assertTrue(factory.HasMissingTranslations())
 
     report = factory.MissingTranslationsReport()
-    self.assertTrue(report.count('WARNING') == 1)
-    self.assertTrue(report.count('8053599568341804890 "Goodbye" fr') == 1)
-    self.assertTrue(report.count('ERROR') == 1)
-    self.assertTrue(report.count('800120468867715734 "Hello" de') == 1)
+    self.assertEqual(report.count('WARNING'), 1)
+    self.assertEqual(
+        report.count('''8053599568341804890 "Goodbye" ('fr', 'OTHER')'''), 1)
+    self.assertEqual(
+        report.count(
+            '''362457071231374324 "gender fallback to english example" ('es', 'OTHER'),('es', 'MASCULINE')'''
+        ), 1)
+    self.assertEqual(report.count('ERROR'), 1)
+    self.assertEqual(
+        report.count(
+            '''800120468867715734 "Hello" ('de', 'OTHER'),('de', 'FEMININE')'''
+        ), 1)
+    self.assertEqual(report.count('gender fallback to default gender example'),
+                     0)
 
   def testCustomTypes(self):
     factory = clique.UberClique()
@@ -189,8 +271,10 @@
     c.SetCustomType(util.NewClassInstance(
       'grit.clique_unittest.DummyCustomType', clique.CustomType))
     translation = tclib.Translation(id=message.GetId(), text='Bilingo bolongo')
-    c.AddTranslation(translation, 'fr')
-    self.assertTrue(c.MessageForLanguage('fr').GetRealContent().startswith('jjj'))
+    c.AddTranslation(translation, 'fr', constants.DEFAULT_GENDER)
+    self.assertTrue(
+        c.MessageForLanguageAndGender(
+            'fr', constants.DEFAULT_GENDER).GetRealContent().startswith('jjj'))
 
   def testWhitespaceMessagesAreNontranslateable(self):
     factory = clique.UberClique()
diff --git a/tools/grit/grit/constants.py b/tools/grit/grit/constants.py
index 3e02457..192d616 100644
--- a/tools/grit/grit/constants.py
+++ b/tools/grit/grit/constants.py
@@ -22,3 +22,14 @@
 # to easily identify resources as being brotli compressed. See
 # ui/base/resource/resource_bundle.h for decompression usage.
 BROTLI_CONST = b'\x1e\x9b'
+
+
+# Gender-related constants.
+GENDER_OTHER = 'OTHER'
+GENDER_MASCULINE = 'MASCULINE'
+GENDER_FEMININE = 'FEMININE'
+GENDER_NEUTER = 'NEUTER'
+
+DEFAULT_GENDER = GENDER_OTHER
+TRANSLATED_GENDERS = (GENDER_MASCULINE, GENDER_FEMININE, GENDER_NEUTER)
+ALL_GENDERS = (DEFAULT_GENDER, ) + TRANSLATED_GENDERS
diff --git a/tools/grit/grit/format/android_xml.py b/tools/grit/grit/format/android_xml.py
index 3d7f75b..6869e34 100644
--- a/tools/grit/grit/format/android_xml.py
+++ b/tools/grit/grit/format/android_xml.py
@@ -64,6 +64,7 @@
 import re
 import xml.sax.saxutils
 
+from grit import constants
 from grit import lazy_re
 from grit.node import message
 
@@ -204,7 +205,8 @@
     raise Exception('Unexpected resource name: %s' % mangled_name)
   name = match.group('name').lower()
 
-  value = item.ws_at_start + item.Translate(lang) + item.ws_at_end
+  value = item.ws_at_start + item.Translate(
+      lang, constants.DEFAULT_GENDER) + item.ws_at_end
   # Replace < > & with &lt; &gt; &amp; to ensure we generate valid XML and
   # replace ' " with \' \" to conform to Android's string formatting rules.
   value = xml.sax.saxutils.escape(value, {"'": "\\'", '"': '\\"'})
diff --git a/tools/grit/grit/format/c_format.py b/tools/grit/grit/format/c_format.py
index 10601f1d..8073b20 100644
--- a/tools/grit/grit/format/c_format.py
+++ b/tools/grit/grit/format/c_format.py
@@ -9,6 +9,7 @@
 import os
 import re
 
+from grit import constants
 from grit import util
 
 
@@ -61,7 +62,8 @@
 def _FormatMessage(item, lang):
   """Format a single <message> element."""
 
-  message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
+  message = item.ws_at_start + item.Translate(
+      lang, constants.DEFAULT_GENDER) + item.ws_at_end
   # Output message with non-ascii chars escaped as octal numbers C's grammar
   # allows escaped hexadecimal numbers to be infinite, but octal is always of
   # the form \OOO.  Python 3 doesn't support string-escape, so we have to jump
diff --git a/tools/grit/grit/format/chrome_messages_json.py b/tools/grit/grit/format/chrome_messages_json.py
index 1459b21..50b718f8 100644
--- a/tools/grit/grit/format/chrome_messages_json.py
+++ b/tools/grit/grit/format/chrome_messages_json.py
@@ -25,15 +25,17 @@
       if id.startswith('IDR_') or id.startswith('IDS_'):
         id = id[4:]
 
-      translation_missing = child.GetCliques()[0].clique.get(lang) is None;
+      translation_missing = not child.GetCliques()[0].HasTranslation(
+          lang, constants.DEFAULT_GENDER)
       if (child.ShouldFallbackToEnglish() and translation_missing
           and lang not in constants.PSEUDOLOCALES):
         # Skip the string if it's not translated. Chrome will fallback
         # to English automatically.
         continue
 
-      loc_message = encoder.encode(child.ws_at_start + child.Translate(lang) +
-                                   child.ws_at_end)
+      loc_message = encoder.encode(
+          child.ws_at_start + child.Translate(lang, constants.DEFAULT_GENDER) +
+          child.ws_at_end)
 
       # Replace $n place-holders with $n$ and add an appropriate "placeholders"
       # entry. Note that chrome.i18n.getMessage only supports 9 placeholders:
diff --git a/tools/grit/grit/format/chrome_messages_json_unittest.py b/tools/grit/grit/format/chrome_messages_json_unittest.py
index fac8e9f..5b4aa7f 100755
--- a/tools/grit/grit/format/chrome_messages_json_unittest.py
+++ b/tools/grit/grit/format/chrome_messages_json_unittest.py
@@ -128,6 +128,40 @@
 """
     self.assertEqual(json.loads(test), json.loads(output))
 
+  # This test makes sure that we don't always get False back from:
+  #
+  #   translation_missing = not child.GetCliques()[0].HasTranslation(lang,
+  #     constants.DEFAULT_GENDER)
+  #
+  # If we got False back all the time (say, due to calling it with an argument
+  # of the wrong type), then we would assume it was completely normal to just
+  # skip every translation and let Chrome fall back to English all the time with
+  # no errors or warnings.
+  def testTranslationsWithFallbackToEnglish(self):
+    root = util.ParseGrdForUnittest("""
+    <messages fallback_to_english="true">
+        <message name="ID_HELLO">Hello!</message>
+        <message name="ID_HELLO_USER">Hello <ph name="USERNAME">%s<ex>
+          Joi</ex></ph></message>
+      </messages>
+    """)
+
+    buf = io.StringIO()
+    build.RcBuilder.ProcessNode(root, DummyOutput('chrome_messages_json', 'en'),
+                                buf)
+    output = buf.getvalue()
+    test = """
+{
+  "ID_HELLO": {
+    "message": "Hello!"
+  },
+  "ID_HELLO_USER": {
+    "message": "Hello %s"
+  }
+}
+"""
+    self.assertEqual(json.loads(test), json.loads(output))
+
   def testSkipMissingTranslations(self):
     grd = """<?xml version="1.0" encoding="UTF-8"?>
 <grit latest_public_release="2" current_release="3" source_lang_id="en"
diff --git a/tools/grit/grit/format/rc.py b/tools/grit/grit/format/rc.py
index 477c609..e5994f76 100644
--- a/tools/grit/grit/format/rc.py
+++ b/tools/grit/grit/format/rc.py
@@ -9,6 +9,7 @@
 import re
 from functools import partial
 
+from grit import constants
 from grit import util
 from grit.node import misc
 
@@ -360,7 +361,8 @@
 
 def FormatMessage(item, lang):
   '''Returns a single message of a string table.'''
-  message = item.ws_at_start + item.Translate(lang) + item.ws_at_end
+  message = item.ws_at_start + item.Translate(
+      lang, constants.DEFAULT_GENDER) + item.ws_at_end
   # Escape quotation marks (RC format uses doubling-up
   message = message.replace('"', '""')
   # Replace linebreaks with a \n escape
diff --git a/tools/grit/grit/gather/skeleton_gatherer.py b/tools/grit/grit/gather/skeleton_gatherer.py
index e155440..e8ca888 100644
--- a/tools/grit/grit/gather/skeleton_gatherer.py
+++ b/tools/grit/grit/gather/skeleton_gatherer.py
@@ -8,6 +8,7 @@
 
 from grit.gather import interface
 from grit import clique
+from grit import constants
 from grit import exception
 from grit import tclib
 
@@ -84,9 +85,9 @@
       else:
         if skeleton_gatherer:  # Make sure the skeleton is like the original
           assert (not isinstance(skeleton_gatherer.skeleton_[ix], str))
-        msg = self.skeleton_[ix].MessageForLanguage(lang,
-                                                    pseudo_if_not_available,
-                                                    fallback_to_english)
+        msg = self.skeleton_[ix].MessageForLanguageAndGender(
+            lang, constants.DEFAULT_GENDER, pseudo_if_not_available,
+            fallback_to_english)
 
         def MyEscape(text):
           return self.Escape(text)
diff --git a/tools/grit/grit/gather/tr_html.py b/tools/grit/grit/gather/tr_html.py
index c6f73ef..5ce7850 100644
--- a/tools/grit/grit/gather/tr_html.py
+++ b/tools/grit/grit/gather/tr_html.py
@@ -52,10 +52,11 @@
 import re
 
 from grit import clique
+from grit import constants
 from grit import exception
 from grit import lazy_re
-from grit import util
 from grit import tclib
+from grit import util
 
 from grit.gather import interface
 
@@ -655,9 +656,9 @@
       if isinstance(item, str):
         out.append(item)
       else:
-        msg = item.MessageForLanguage(lang,
-                                      pseudo_if_not_available,
-                                      fallback_to_english)
+        msg = item.MessageForLanguageAndGender(lang, constants.DEFAULT_GENDER,
+                                               pseudo_if_not_available,
+                                               fallback_to_english)
         for content in msg.GetContent():
           if isinstance(content, tclib.Placeholder):
             out.append(content.GetOriginal())
diff --git a/tools/grit/grit/gather/txt.py b/tools/grit/grit/gather/txt.py
index 94a77dd..7e7cf9a3 100644
--- a/tools/grit/grit/gather/txt.py
+++ b/tools/grit/grit/gather/txt.py
@@ -7,6 +7,7 @@
 
 
 from grit.gather import interface
+from grit import constants
 from grit import tclib
 
 
@@ -32,6 +33,6 @@
 
   def Translate(self, lang, pseudo_if_not_available=True,
                 skeleton_gatherer=None, fallback_to_english=False):
-    return self.clique_.MessageForLanguage(lang,
-                                           pseudo_if_not_available,
-                                           fallback_to_english).GetRealContent()
+    return self.clique_.MessageForLanguageAndGender(
+        lang, constants.DEFAULT_GENDER, pseudo_if_not_available,
+        fallback_to_english).GetRealContent()
diff --git a/tools/grit/grit/gender.py b/tools/grit/grit/gender.py
deleted file mode 100644
index b0db627..0000000
--- a/tools/grit/grit/gender.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2025 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-'''Gender constants.
-'''
-
-DEFAULT_GENDER = 'OTHER'
-TRANSLATED_GENDERS = ['MASCULINE', 'FEMININE', 'NEUTER']
-ALL_GENDERS = [DEFAULT_GENDER] + TRANSLATED_GENDERS
diff --git a/tools/grit/grit/node/custom/filename_unittest.py b/tools/grit/grit/node/custom/filename_unittest.py
index c12bcff..421b9db 100755
--- a/tools/grit/grit/node/custom/filename_unittest.py
+++ b/tools/grit/grit/node/custom/filename_unittest.py
@@ -14,6 +14,7 @@
 import unittest
 from grit.node.custom import filename
 from grit import clique
+from grit import constants
 from grit import tclib
 
 
@@ -25,8 +26,10 @@
     c = factory.MakeClique(msg)
     c.SetCustomType(filename.WindowsFilename())
     translation = tclib.Translation(id=msg.GetId(), text='Bilingo bolongo:')
-    c.AddTranslation(translation, 'fr')
-    self.assertTrue(c.MessageForLanguage('fr').GetRealContent() == 'Bilingo bolongo ')
+    c.AddTranslation(translation, 'fr', constants.DEFAULT_GENDER)
+    self.assertTrue(
+        c.MessageForLanguageAndGender('fr', constants.DEFAULT_GENDER).
+        GetRealContent() == 'Bilingo bolongo ')
 
 
 if __name__ == '__main__':
diff --git a/tools/grit/grit/node/message.py b/tools/grit/grit/node/message.py
index 399c86f..0f10499 100644
--- a/tools/grit/grit/node/message.py
+++ b/tools/grit/grit/node/message.py
@@ -10,6 +10,7 @@
 from grit.node import base
 
 from grit import clique
+from grit import constants
 from grit import exception
 from grit import lazy_re
 from grit import tclib
@@ -257,14 +258,13 @@
   def GetCliques(self):
     return [self.clique] if self.clique else []
 
-  def Translate(self, lang):
+  def Translate(self, lang, gender):
     '''Returns a translated version of this message.
     '''
     assert self.clique
-    msg = self.clique.MessageForLanguage(lang,
-                                         self.PseudoIsAllowed(),
-                                         self.ShouldFallbackToEnglish()
-                                         ).GetRealContent()
+    msg = self.clique.MessageForLanguageAndGender(
+        lang, gender, self.PseudoIsAllowed(),
+        self.ShouldFallbackToEnglish()).GetRealContent()
     if self._replace_ellipsis:
       msg = _ELLIPSIS_PATTERN.sub(_ELLIPSIS_SYMBOL, msg)
     # Always remove all byte order marks (\uFEFF) https://crbug.com/1033305
@@ -281,7 +281,8 @@
 
   def GetDataPackValue(self, lang, encoding):
     '''Returns a str represenation for a data_pack entry.'''
-    message = self.ws_at_start + self.Translate(lang) + self.ws_at_end
+    message = self.ws_at_start + self.Translate(
+        lang, constants.DEFAULT_GENDER) + self.ws_at_end
     return util.Encode(message, encoding)
 
   def IsResourceMapSource(self):
diff --git a/tools/grit/grit/node/message_unittest.py b/tools/grit/grit/node/message_unittest.py
index 8ee03c0..69737c9 100755
--- a/tools/grit/grit/node/message_unittest.py
+++ b/tools/grit/grit/node/message_unittest.py
@@ -13,6 +13,7 @@
 if __name__ == '__main__':
   sys.path.append(os.path.join(os.path.dirname(__file__), '../..'))
 
+from grit import constants
 from grit import exception
 from grit import tclib
 from grit import util
@@ -94,7 +95,7 @@
         </messages>''')
     msg, = root.GetChildrenOfType(message.MessageNode)
     msg.SetReplaceEllipsis(True)
-    content = msg.Translate('en')
+    content = msg.Translate('en', constants.DEFAULT_GENDER)
     self.assertEqual('A...B.... %s\u2026 B\u2026 C\u2026', content)
 
   def testRemoveByteOrderMark(self):
@@ -105,7 +106,7 @@
         </message>
         </messages>''')
     msg, = root.GetChildrenOfType(message.MessageNode)
-    content = msg.Translate('en')
+    content = msg.Translate('en', constants.DEFAULT_GENDER)
     self.assertEqual('This is OK', content)
 
   def testPlaceholderHasTooManyExamples(self):
diff --git a/tools/grit/grit/node/node_io.py b/tools/grit/grit/node/node_io.py
index 65b54f1d..bbdc1de 100644
--- a/tools/grit/grit/node/node_io.py
+++ b/tools/grit/grit/node/node_io.py
@@ -10,9 +10,9 @@
 import os
 import re
 
+from grit import constants
 from grit import xtb_reader
 from grit.node import base
-from grit import gender
 
 DATA_PACKAGE_FILENAME_RE = re.compile(r'((\w|-)+).pak')
 ANDROID_FILENAME_RE = re.compile(r'(values(-((\w|-|\+)+))?)/')
@@ -82,12 +82,12 @@
     # Create 3 extra copies of each data_package and android output node so that
     # we have one per gender per language. Adjust file names accordingly.
     if self.GetType() == 'data_package' or self.GetType() == 'android':
-      for gndr in gender.TRANSLATED_GENDERS:
+      for gender in constants.TRANSLATED_GENDERS:
         cloned_node = self._Clone()
-        cloned_node.gender = gndr
+        cloned_node.gender = gender
         cloned_node._AddGenderToFilenames()
 
-      self.gender = gender.DEFAULT_GENDER
+      self.gender = constants.DEFAULT_GENDER
       self._AddGenderToFilenames()
 
   def _AddGenderToFilenames(self):
diff --git a/tools/grit/grit/node/node_io_unittest.py b/tools/grit/grit/node/node_io_unittest.py
index 87aa249..df4814b 100755
--- a/tools/grit/grit/node/node_io_unittest.py
+++ b/tools/grit/grit/node/node_io_unittest.py
@@ -16,6 +16,7 @@
 from grit.node import misc
 from grit.node import node_io
 from grit.node import empty
+from grit import constants
 from grit import grd_reader
 from grit import util
 
@@ -51,7 +52,9 @@
   def VerifyCliquesContainEnglishAndFrenchAndNothingElse(self, cliques):
     self.assertEqual(2, len(cliques))
     for clique in cliques:
-      self.assertEqual({'en', 'fr'}, set(clique.clique.keys()))
+      self.assertEqual(
+          {('en', constants.DEFAULT_GENDER), ('fr', constants.DEFAULT_GENDER)},
+          set(clique.clique.keys()))
 
   def testLoadTranslations(self):
     xml = '''<?xml version="1.0" encoding="UTF-8"?>
@@ -92,7 +95,8 @@
     cliques = _GetAllCliques(grd)
     self.assertEqual(2, len(cliques))
     for clique in cliques:
-      self.assertEqual({'en'}, set(clique.clique.keys()))
+      self.assertEqual({('en', constants.DEFAULT_GENDER)},
+                       set(clique.clique.keys()))
 
     grd.SetOutputLanguage('fr')
     grd.RunGatherers()
diff --git a/tools/grit/grit/util.py b/tools/grit/grit/util.py
index bb722c2..0ab0ef7 100644
--- a/tools/grit/grit/util.py
+++ b/tools/grit/grit/util.py
@@ -16,6 +16,7 @@
 
 from xml.sax import saxutils
 
+from grit import constants
 from grit import lazy_re
 
 _root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
@@ -557,7 +558,8 @@
       messages: a list of node.Message objects.
       lang: The translation language to use in substitutions.
     '''
-    subs = [(str(msg.attrs['name']), msg.Translate(lang)) for msg in messages]
+    subs = [(str(msg.attrs['name']),
+             msg.Translate(lang, constants.DEFAULT_GENDER)) for msg in messages]
     self.AddSubstitutions(dict(subs))
     self.dirty_ = True
 
diff --git a/tools/grit/grit/xtb_reader.py b/tools/grit/grit/xtb_reader.py
index abd6bc68..2247158 100644
--- a/tools/grit/grit/xtb_reader.py
+++ b/tools/grit/grit/xtb_reader.py
@@ -12,7 +12,7 @@
 import xml.sax.handler
 
 import grit.node.base
-from grit import gender
+from grit import constants
 
 
 GRAMMATICAL_GENDER_RE = re.compile(
@@ -117,7 +117,7 @@
 
   # Gets the current gender, or DEFAULT_GENDER if the current gender is None.
   def get_effective_gender(self):
-    return self.current_gender or gender.DEFAULT_GENDER
+    return self.current_gender or constants.DEFAULT_GENDER
 
   # Gets the self.current_structure entry for the current gender, including
   # proper initialization.
diff --git a/tools/grit/grit/xtb_reader_unittest.py b/tools/grit/grit/xtb_reader_unittest.py
index 28fba09..48cc750b 100755
--- a/tools/grit/grit/xtb_reader_unittest.py
+++ b/tools/grit/grit/xtb_reader_unittest.py
@@ -14,9 +14,9 @@
 
 import unittest
 
+from grit import constants
 from grit import util
 from grit import xtb_reader
-from grit import gender
 from grit.node import empty
 
 
@@ -52,11 +52,11 @@
     def Callback(id, structure):
       messages.append((id, structure))
     xtb_reader.Parse(xtb_file, Callback)
-    self.assertTrue(len(messages[0][1][gender.DEFAULT_GENDER]) == 1)
-    self.assertTrue(
-        messages[3][1][gender.DEFAULT_GENDER][0])  # PROBLEM_REPORT placeholder
+    self.assertTrue(len(messages[0][1][constants.DEFAULT_GENDER]) == 1)
+    self.assertTrue(messages[3][1][constants.DEFAULT_GENDER]
+                    [0])  # PROBLEM_REPORT placeholder
     self.assertTrue(messages[4][0] == '7729135689895381486')
-    self.assertTrue(messages[4][1][gender.DEFAULT_GENDER][7][1] ==
+    self.assertTrue(messages[4][1][constants.DEFAULT_GENDER][7][1] ==
                     'and another after a blank line.')
     self.assertEqual(messages[5][1]['FEMININE'][0][1], 'Je suis allee')
     self.assertEqual(messages[6][1]['NEUTER'][0][1], 'Aller est moi a ')
@@ -87,10 +87,14 @@
 
     xtb_reader.Parse(xtb_file,
                      msgs.UberClique().GenerateXtbParserCallback('is'))
-    self.assertEqual('Meirihattar!',
-                     clique_mega.MessageForLanguage('is').GetRealContent())
-    self.assertTrue('Saelir %s',
-                    clique_hello_user.MessageForLanguage('is').GetRealContent())
+    self.assertEqual(
+        'Meirihattar!',
+        clique_mega.MessageForLanguageAndGender(
+            'is', constants.DEFAULT_GENDER).GetRealContent())
+    self.assertTrue(
+        'Saelir %s',
+        clique_hello_user.MessageForLanguageAndGender(
+            'is', constants.DEFAULT_GENDER).GetRealContent())
 
   def testIfNodesWithUseNameForId(self):
     root = util.ParseGrdForUnittest('''
@@ -114,7 +118,10 @@
     xtb_reader.Parse(xtb_file,
                      msgs.UberClique().GenerateXtbParserCallback('is'),
                      target_platform='darwin')
-    self.assertEqual('Congo!', clique.MessageForLanguage('is').GetRealContent())
+    self.assertEqual(
+        'Congo!',
+        clique.MessageForLanguageAndGender(
+            'is', constants.DEFAULT_GENDER).GetRealContent())
 
   def testParseLargeFile(self):
     def Callback(id, structure):
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index bba8a67c..f05c716 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -49338,6 +49338,8 @@
       label="Google One AI Premium Plan details page button"/>
   <suffix name="AutoUpdateButton" label="Auto Update details page button"/>
   <suffix name="BatteryButton" label="Battery details page button"/>
+  <suffix name="BetterTogetherButton"
+      label="Better Together details page button"/>
   <suffix name="BuiltInSecurityButton"
       label="Built in security details page button"/>
   <suffix name="ComparisonButton" label="Comparison details page button"/>
@@ -49345,15 +49347,21 @@
       label="Display (Entertainment) details page button"/>
   <suffix name="DisplayPerformanceButton"
       label="Display (Performance) details page button"/>
+  <suffix name="DriveIntegrationButton"
+      label="Drive Integration details page button"/>
   <suffix name="EasySetupButton" label="Easy setup details page button"/>
   <suffix name="EntertainmentAppsButton"
       label="Entertainment Apps details page button"/>
   <suffix name="FastBootButton" label="Fast boot details page button"/>
+  <suffix name="FileSyncButton" label="File Sync details page button"/>
   <suffix name="GameDashboardButton"
       label="Game Dashboard details page button"/>
+  <suffix name="GeminiAdvanceButton"
+      label="Gemini Advance details page button"/>
   <suffix name="GeminiForAllButton" label="Gemini for All details page button"/>
   <suffix name="GeminiForWorkSpaceButton"
       label="Gemini for Work space details page button"/>
+  <suffix name="GeminiPWAButton" label="Gemini PWA details page button"/>
   <suffix name="GoogleAppsButton" label="Google Apps details page button"/>
   <suffix name="GoogleToolsBuiltInButton"
       label="Google tools built in details page button"/>
@@ -49364,6 +49372,7 @@
   <suffix name="LiveTranslateButton"
       label="Live translate details page button"/>
   <suffix name="LumaFusionButton" label="LumaFusion details page button"/>
+  <suffix name="MagicEditorButton" label="Magic Editor details page button"/>
   <suffix name="MessagingButton" label="Messaging details page button"/>
   <suffix name="MobileGamingButton" label="Mobile Gaming details page button"/>
   <suffix name="MS365AppsButton" label="MS365 Apps details page button"/>
@@ -49373,15 +49382,20 @@
   <suffix name="OfflineModeButton" label="Offline Mode details page button"/>
   <suffix name="PCConsoleGamingButton"
       label="PC/Console Gaming details page button"/>
+  <suffix name="PhoneHubButton" label="Phone Hub details page button"/>
   <suffix name="PhotosButton" label="Photos details page button"/>
   <suffix name="PlayStoreButton" label="Play Store details page button"/>
   <suffix name="ProcessorButton" label="Processor details page button"/>
   <suffix name="ProductivityButton" label="Productivity details page button"/>
+  <suffix name="QuickInsertButton" label="Quick Insert details page button"/>
+  <suffix name="SelectToSearchButton"
+      label="Select To Search details page button"/>
   <suffix name="StorageButton" label="Storage details page button"/>
   <suffix name="SwitchingButton" label="Switching details page button"/>
   <suffix name="TitanC2Button" label="Titan C2 details page button"/>
   <suffix name="VideoCallButton" label="Video Call details page button"/>
   <suffix name="WebcamButton" label="Webcam details page button"/>
+  <suffix name="WelcomeRecapButton" label="Welcome Recap details page button"/>
   <affected-action name="DemoMode_Highlights_DetailsPage_Clicked"/>
 </action-suffix>
 
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index 24e2f273..35663ea 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1343,6 +1343,33 @@
   </summary>
 </histogram>
 
+<histogram name="Ash.Boca.Spotlight.{Request}.ErrorCode" enum="ApiErrorCode"
+    expires_after="2026-04-25">
+  <owner>caott@google.com</owner>
+  <owner>cros-edu-eng@google.com</owner>
+  <summary>
+    Records HTTP response code of the {Request} request needed to register host
+    for view screen in a Class Tools session.
+  </summary>
+  <token key="Request">
+    <variant name="RegisterScreen"/>
+  </token>
+</histogram>
+
+<histogram name="Ash.Boca.{Request}.ErrorCode" enum="ApiErrorCode"
+    expires_after="2025-10-23">
+  <owner>zifanzhang@google.com</owner>
+  <owner>cros-edu-eng@google.com</owner>
+  <summary>
+    Records HTTP response code of the {Request} request needed to send/receive
+    captions in a Class Tools.
+  </summary>
+  <token key="Request">
+    <variant name="StudentHeartbeat"/>
+    <variant name="UpdateStudentActivities"/>
+  </token>
+</histogram>
+
 <histogram name="Ash.BrowserContext.UnexpectedGetPrimaryUserProfile"
     enum="Boolean" expires_after="2024-11-03">
   <owner>hidehiko@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 65d572eb..3d3fa9f 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -476,34 +476,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="CustomTabs.IntentToFirstCommitNavigationTime3{IntentToFirstCommitZoom}"
-    units="ms" expires_after="2024-09-01">
-  <owner>lizeb@chromium.org</owner>
-  <summary>
-    Time between the intent arrival to a Custom Tab and the first navigation
-    commit. Recorded for every custom tabs intent when the page has finished
-    loading. {IntentToFirstCommitZoom}
-  </summary>
-  <token key="IntentToFirstCommitZoom" variants="IntentToFirstCommitZoom">
-    <variant name=""/>
-  </token>
-</histogram>
-
-<histogram
-    name="CustomTabs.IntentToFirstNavigationStartTime{IntentToFirstCommitZoom}"
-    units="ms" expires_after="2024-09-01">
-  <owner>lizeb@chromium.org</owner>
-  <summary>
-    Time between the intent arrival to a Custom Tab and the navigation start.
-    Recorded when the page has finished loading. Non-&quot;Herb&quot; mode.
-    {IntentToFirstCommitZoom}
-  </summary>
-  <token key="IntentToFirstCommitZoom" variants="IntentToFirstCommitZoom">
-    <variant name=""/>
-  </token>
-</histogram>
-
 <histogram name="CustomTabs.MayLaunchUrlType" enum="MayLaunchUrlType"
     expires_after="2025-08-24">
   <owner>lizeb@chromium.org</owner>
@@ -915,11 +887,14 @@
 </histogram>
 
 <histogram name="CustomTabs.WebContentsStateOnLaunch" enum="WebContentsState"
-    expires_after="2025-04-13">
+    expires_after="2026-04-13">
   <owner>lizeb@chromium.org</owner>
+  <owner>mthiesse@chromium.org</owner>
   <summary>
     Android: When a CustomTabActivity creates its Tab, whether we use a
     prerendered WebContents, a spare renderer or create a new WebContents.
+
+    Note: Expired from April 13-25, 2025.
   </summary>
 </histogram>
 
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 5063305..3f3ab5b 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -319,6 +319,15 @@
   </token>
 </histogram>
 
+<histogram name="Enterprise.Boca.Spotlight.Crd.Result"
+    enum="EnterpriseCrdSessionResultCode" expires_after="2026-04-25">
+  <owner>caott@google.com</owner>
+  <owner>cros-edu-eng@google.com</owner>
+  <summary>
+    Reports success/failure when a spotlight session in Boca app is attempted.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.BrowserSigninIOS.SignedOutByPolicy"
     enum="BooleanHit" expires_after="2023-06-30">
   <owner>gujen@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 2e46abd19..33e68f42a 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -65,6 +65,8 @@
       summary="Highlights app Google One AI Premium Plan detail page"/>
   <variant name="AutoUpdatePage" summary="Auto Update detail page"/>
   <variant name="BatteryPage" summary="Highlights app Battery detail page"/>
+  <variant name="BetterTogetherPage"
+      summary="Highlights app Better Together detail page"/>
   <variant name="BuiltInSecurityPage"
       summary="Highlights app Built In Security detail page"/>
   <variant name="ComparisonPage"
@@ -73,16 +75,23 @@
       summary="Highlights app Display (Entertainment) detail page"/>
   <variant name="DisplayPerformancePage"
       summary="Highlights app Display (Performance) detail page"/>
+  <variant name="DriveIntegrationPage"
+      summary="Highlights app Drive Integration detail page"/>
   <variant name="EasySetupPage" summary="Easy setup detail page"/>
   <variant name="EntertainmentAppsPage"
       summary="Highlights app Entertainment Apps detail page"/>
   <variant name="FastBootPage" summary="Fast boot detail page"/>
+  <variant name="FileSyncPage" summary="Highlights app File Sync detail page"/>
   <variant name="GameDashboardPage"
       summary="Highlights app Game Dashboard detail page"/>
+  <variant name="GeminiAdvancePage"
+      summary="Highlights app Gemini Advance detail page"/>
   <variant name="GeminiForAllPage"
       summary="Highlights app Gemini for all detail page"/>
   <variant name="GeminiForWorkSpacePage"
       summary="Highlights app Gemini for Work space detail page"/>
+  <variant name="GeminiPWAPage"
+      summary="Highlights app Gemini PWA detail page"/>
   <variant name="GoogleAppsPage"
       summary="Highlights app Google Apps detail page"/>
   <variant name="GoogleToolsBuiltInPage"
@@ -97,6 +106,8 @@
       summary="Highlights app Live translate detail page"/>
   <variant name="LumaFusionPage"
       summary="Highlights app LumaFusion detail page"/>
+  <variant name="MagicEditorPage"
+      summary="Highlights app Magic Editor detail page"/>
   <variant name="MessagingPage" summary="Highlights app Messaging detail page"/>
   <variant name="MobileGamingPage"
       summary="Highlights app Mobile Gaming detail page"/>
@@ -109,10 +120,15 @@
       summary="Highlights app Offline Mode detail page"/>
   <variant name="PCConsoleGamingPage"
       summary="Highlights app PC/Console Gaming detail page"/>
+  <variant name="PhoneHubPage" summary="Highlights app Phone Hub detail page"/>
   <variant name="PhotosPage" summary="Highlights app Photos detail page"/>
   <variant name="PlayStorePage" summary="Play Store detail page"/>
   <variant name="ProcessorPage" summary="Highlights app Processor detail page"/>
   <variant name="ProductivityPage" summary="Productivity detail page"/>
+  <variant name="QuickInsertPage"
+      summary="Highlights app Quick Insert detail page"/>
+  <variant name="SelectToSearchPage"
+      summary="Highlights app Select To Search detail page"/>
   <variant name="StoragePage" summary="Highlights app Storage detail page"/>
   <variant name="SwitchingPage" summary="Highlights app Switching detail page"/>
   <variant name="SwitchToChromeBookPage"
@@ -121,6 +137,8 @@
   <variant name="VideoCallPage"
       summary="Highlights app Video Call detail page"/>
   <variant name="WebcamPage" summary="Highlights app Webcam detail page"/>
+  <variant name="WelcomeRecapPage"
+      summary="Highlights app Welcome Recap detail page"/>
 </variants>
 
 <variants name="HighlightsPageName">
diff --git a/tools/metrics/histograms/metadata/prefetch/histograms.xml b/tools/metrics/histograms/metadata/prefetch/histograms.xml
index 812ad90..66f8127 100644
--- a/tools/metrics/histograms/metadata/prefetch/histograms.xml
+++ b/tools/metrics/histograms/metadata/prefetch/histograms.xml
@@ -27,14 +27,14 @@
   <variant name="TLS" summary="TLS canary check"/>
 </variants>
 
-<variants name="SpeculationEagerness">
-  <variant name="Conservative" summary="Conservative"/>
-  <variant name="Eager" summary="Eager"/>
-  <variant name="Moderate" summary="Moderate"/>
-</variants>
+<!-- LINT.IfChange(TriggerTypeAndEagerness) -->
 
 <variants name="TriggerTypeAndEagerness">
-  <variant name="Embedder" summary="Embedder trigger"/>
+  <variant name="Embedder_ChromeCustomTabs"
+      summary="Embedder trigger, ChromeCustomTabs"/>
+  <variant name="Embedder_DefaultSearchEngine"
+      summary="Embedder trigger, DefaultSearchEngine"/>
+  <variant name="Embedder_WebView" summary="Embedder trigger, WebView"/>
   <variant name="SpeculationRule_Conservative"
       summary="SpeculationRules trigger, Conservative"/>
   <variant name="SpeculationRule_Eager"
@@ -55,13 +55,44 @@
       summary="SpeculationRulesFromIsolatedWorld trigger, Moderate"/>
 </variants>
 
+<!-- LINT.ThenChange(//content/browser/preloading/prefetch/prefetch_params.cc:GetMetricsSuffixTriggerTypeAndEagerness) -->
+
 <variants name="WasServed">
   <variant name="NotServed" summary="not served"/>
   <variant name="Served" summary="served"/>
 </variants>
 
 <histogram
-    name="Prefetch.PrefetchContainer.AddedToHeaderDeterminedSuccessfully.{TriggerTypeAndEagerness}.NoEmbedderSuffix"
+    name="Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.{WasServed}.{TriggerTypeAndEagerness}"
+    units="ms" expires_after="2025-10-18">
+  <owner>kenoss@chromium.org</owner>
+  <owner>taiyo@chromium.org</owner>
+  <owner>chrome-loading@chromium.org</owner>
+  <summary>
+    When a navigation started, `PrefetchMatchResolver` collects potentially
+    matching prefetches for it. Judging a prefetch actually matches or not needs
+    non-redirect header. If a prefetch has non-redirect header and actually
+    matched, then this process ends synchronously. If not, it waits non-redirect
+    headers until it finds a matched and servable one or there is no more
+    candidates.
+
+    This histogram records that duration navigation was blocked by the prefetch
+    matching, when the matching is settled. Zero is recorded when the navigation
+    is not blocked.
+
+    This histogram is recorded per potentially matching candidate, with its
+    TriggerType and Eagerness as {TriggerTypeAndEagerness}, and whether its
+    candidate was eventually served or not as {WasServed}.
+
+    Before 2025-04-25, this histogram was recorded as
+    `PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.*`.
+  </summary>
+  <token key="WasServed" variants="WasServed"/>
+  <token key="TriggerTypeAndEagerness" variants="TriggerTypeAndEagerness"/>
+</histogram>
+
+<histogram
+    name="Prefetch.PrefetchContainer.AddedToHeaderDeterminedSuccessfully.{TriggerTypeAndEagerness}"
     units="ms" expires_after="2025-10-01">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
@@ -78,7 +109,7 @@
 </histogram>
 
 <histogram
-    name="Prefetch.PrefetchContainer.AddedToInitialEligibility.{TriggerTypeAndEagerness}.NoEmbedderSuffix"
+    name="Prefetch.PrefetchContainer.AddedToInitialEligibility.{TriggerTypeAndEagerness}"
     units="ms" expires_after="2025-10-01">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
@@ -95,7 +126,7 @@
 </histogram>
 
 <histogram
-    name="Prefetch.PrefetchContainer.AddedToPrefetchCompletedSuccessfully.{TriggerTypeAndEagerness}.NoEmbedderSuffix"
+    name="Prefetch.PrefetchContainer.AddedToPrefetchCompletedSuccessfully.{TriggerTypeAndEagerness}"
     units="ms" expires_after="2025-10-01">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
@@ -112,7 +143,7 @@
 </histogram>
 
 <histogram
-    name="Prefetch.PrefetchContainer.AddedToPrefetchStarted.{TriggerTypeAndEagerness}.NoEmbedderSuffix"
+    name="Prefetch.PrefetchContainer.AddedToPrefetchStarted.{TriggerTypeAndEagerness}"
     units="ms" expires_after="2025-10-01">
   <owner>kenoss@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
@@ -129,57 +160,31 @@
 </histogram>
 
 <histogram
-    name="PrefetchProxy.AfterClick.BlockUntilHeadDuration2.{WasServed}.{SpeculationEagerness}"
-    units="ms" expires_after="2025-10-12">
+    name="Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.{TriggerTypeAndEagerness}"
+    enum="Boolean" expires_after="2025-10-12">
   <owner>kouhei@chromium.org</owner>
   <owner>kenoss@chromium.org</owner>
+  <owner>taiyo@chromium.org</owner>
   <owner>chrome-loading@chromium.org</owner>
   <summary>
     When a navigation started, `PrefetchMatchResolver` collects potentially
-    matching prefetches for it. Judging a prefetch accutally matches or not
-    needs non-redirect header. If a prefetch has non-redirect header and
-    actually matched, then this process ends synchronously. If not, it waits
-    non-redirect headers until it finds a matched and servable one or there is
-    no more candidates.
+    matching prefetches for it. Judging a prefetch actually matches or not needs
+    non-redirect header. If a prefetch has non-redirect header and actually
+    matched, then this process ends synchronously. If not, it waits non-redirect
+    headers until it finds a matched and servable one or there is no more
+    candidates.
 
-    This histogram is recorded iff the matching process didn't end synchronously
-    and when the judge for a prefetch is determined. The value is the blocked
-    duration (for each tuple (navigation, candidate prefetch)).
+    This histogram records whether this process ends synchronously (i.e. whether
+    prefetch matching blocked the navigation), when the above prefetch matching
+    is settled.
 
-    This histogram is only recorded for prefetches that are {WasServed} and with
-    an Eagerness of {SpeculationEagerness}.
+    This histogram is recorded per potentially matching candidates, with its
+    TriggerType and Eagerness as {TriggerTypeAndEagerness}.
+
+    Before 2025-04-25, this histogram was recorded as
+    `PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch.*`
   </summary>
-  <token key="WasServed" variants="WasServed"/>
-  <token key="SpeculationEagerness" variants="SpeculationEagerness"/>
-</histogram>
-
-<histogram
-    name="PrefetchProxy.AfterClick.BlockUntilHeadDuration2NoBias.{WasServed}.{SpeculationEagerness}"
-    units="ms" expires_after="2025-03-23">
-  <owner>kenoss@chromium.org</owner>
-  <owner>chrome-loading@chromium.org</owner>
-  <summary>
-    When a navigation started, `PrefetchMatchResolver` collects potentially
-    matching prefetches for it. Judging a prefetch accutally matches or not
-    needs non-redirect header. If a prefetch has non-redirect header and
-    actually matched, then this process ends synchronously. If not, it waits
-    non-redirect headers until it finds a matched and servable one or there is
-    no more candidates.
-
-    This histogram is recorded iff a judge for a prefetch is determined. The
-    value is the blocked duration (for each tuple (navigation, candidate
-    prefetch)).
-
-    This histogram is only recorded for prefetches that are {WasServed} and with
-    an Eagerness of {SpeculationEagerness}.
-
-    The difference from
-    PrefetchProxy.AfterClick.BlockUntilHeadDuration2.{WasServed}.{SpeculationEagerness}
-    is that this is also recorded for non-blocked case (i.e. the blocked
-    duration is zero).
-  </summary>
-  <token key="WasServed" variants="WasServed"/>
-  <token key="SpeculationEagerness" variants="SpeculationEagerness"/>
+  <token key="TriggerTypeAndEagerness" variants="TriggerTypeAndEagerness"/>
 </histogram>
 
 <histogram
@@ -272,29 +277,6 @@
   </summary>
 </histogram>
 
-<histogram
-    name="PrefetchProxy.AfterClick.PrefetchMatchingBlockedNavigationWithPrefetch.{SpeculationEagerness}"
-    enum="Boolean" expires_after="2025-10-12">
-  <owner>kouhei@chromium.org</owner>
-  <owner>kenoss@chromium.org</owner>
-  <owner>chrome-loading@chromium.org</owner>
-  <summary>
-    When a navigation started, `PrefetchMatchResolver` collects potentially
-    matching prefetches for it. Judging a prefetch accutally matches or not
-    needs non-redirect header. If a prefetch has non-redirect header and
-    actually matched, then this process ends synchronously. If not, it waits
-    non-redirect headers until it finds a matched and servable one or there is
-    no more candidates.
-
-    This histogram is recorded when this process ends, for each tuple
-    (navigation, candidate prefetch), true iff didn't end synchronously.
-
-    This histogram is only for prefetches with an eagerness of
-    {SpeculationEagerness}.
-  </summary>
-  <token key="SpeculationEagerness" variants="SpeculationEagerness"/>
-</histogram>
-
 <histogram name="PrefetchProxy.AfterClick.RedirectChainSize" units="count"
     expires_after="2025-09-07">
   <owner>kouhei@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 37f3e0d8..d228a76 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "8a1ee5642da9d94ef5bd5d754ed999d046c3c93a",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/b9acf7ff3553f8cf7545dc5e2bfb1b1e1a4a7081/trace_processor_shell.exe"
+            "hash": "2949eecb1176c7ac0d071c8ee16ba5843ba8766a",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/451625d8a0a35cdcd34a9932d6d0def867cef936/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "99f971ca131f6d11c73f4b918099d434bdd8093c",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v50.1/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "10f385bffae6657b01d20dca71335daf1769dccf",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/b9acf7ff3553f8cf7545dc5e2bfb1b1e1a4a7081/trace_processor_shell"
+            "hash": "1c11ad215f13a07d82d9f82b2be81b02df914c4c",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/451625d8a0a35cdcd34a9932d6d0def867cef936/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts
index 0198658..9530c1a1e 100644
--- a/tools/typescript/definitions/autofill_private.d.ts
+++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -247,6 +247,7 @@
         instrumentId?: string;
         displayName?: string;
         imageSrc?: string;
+        imageSrcDark?: string;
       }
 
       export function getAccountInfo(): Promise<AccountInfo|undefined>;
diff --git a/ui/android/java/src/org/chromium/ui/modelutil/LayoutViewBuilder.java b/ui/android/java/src/org/chromium/ui/modelutil/LayoutViewBuilder.java
index dd6e4a0..dc10b39 100644
--- a/ui/android/java/src/org/chromium/ui/modelutil/LayoutViewBuilder.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/LayoutViewBuilder.java
@@ -38,6 +38,16 @@
         }
 
         T view = (T) mInflater.inflate(mLayoutResId, parent, false);
+        return postInflationInit(view);
+    }
+
+    /**
+     * Allows for additional post processing of the view following inflation.
+     *
+     * @param view The view to be initialized.
+     * @return The view with post inflation processing completed.
+     */
+    protected T postInflationInit(T view) {
         return view;
     }
 }
diff --git a/ui/events/x/events_x_utils.cc b/ui/events/x/events_x_utils.cc
index 86ee1475..0fcb43d 100644
--- a/ui/events/x/events_x_utils.cc
+++ b/ui/events/x/events_x_utils.cc
@@ -166,7 +166,10 @@
   DCHECK(xievent);
   DCHECK(xievent->opcode == x11::Input::DeviceEvent::KeyPress ||
          xievent->opcode == x11::Input::DeviceEvent::KeyRelease);
+  bool is_repeat =
+      static_cast<bool>(xievent->flags & x11::Input::KeyEventFlags::KeyRepeat);
   return GetEventFlagsFromXState(xievent->mods.effective) |
+         (is_repeat ? ui::EF_IS_REPEAT : 0) |
          (x11_event.send_event() ? ui::EF_FINAL : 0);
 }
 
diff --git a/ui/gl/startup_trace.h b/ui/gl/startup_trace.h
index d3bae4e7..3b5b5544 100644
--- a/ui/gl/startup_trace.h
+++ b/ui/gl/startup_trace.h
@@ -77,6 +77,20 @@
 
 }  // namespace gl
 
-#define GPU_STARTUP_TRACE_EVENT(name) TRACE_EVENT("gpu,startup", name);
+// Generate a unique variable name with a given prefix.
+#define GPU_STARTUP_TRACE_INTERNAL_CONCAT2(a, b) a##b
+#define GPU_STARTUP_TRACE_INTERNAL_CONCAT(a, b) \
+  GPU_STARTUP_TRACE_INTERNAL_CONCAT2(a, b)
+#define GPU_STARTUP_TRACE_UID(prefix) \
+  GPU_STARTUP_TRACE_INTERNAL_CONCAT(prefix, __LINE__)
+
+#define GPU_STARTUP_TRACE_EVENT(name)                                       \
+  gl::StartupTrace::ScopedStage GPU_STARTUP_TRACE_UID(scoped_gpu_trace){0}; \
+  if (gl::StartupTrace::IsEnabled()) {                                      \
+    GPU_STARTUP_TRACE_UID(scoped_gpu_trace) =                               \
+        gl::StartupTrace::GetInstance()->AddStage(name);                    \
+  } else {                                                                  \
+    TRACE_EVENT0("gpu,startup", name);                                      \
+  }
 
 #endif  // UI_GL_STARTUP_TRACE_H_
diff --git a/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc b/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc
index 205dffac..2525890 100644
--- a/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc
+++ b/ui/ozone/platform/x11/test/x11_event_translation_unittest.cc
@@ -64,6 +64,30 @@
   EXPECT_EQ(ui::DomKey::ENTER, copy.GetDomKey());
 }
 
+// Ensure KeyEvent repeat flag is set when XI2 key event repeat flag is set.
+TEST(XEventTranslationTest, KeyEventXI2EventRepeat) {
+  ScopedXI2Event scoped_xev;
+  // deviceid of XI2 key event must match that of virtual core keyboard in
+  // x11::TouchFactory to be processed.
+  scoped_xev.InitGenericKeyEvent(/*deviceid=*/3, /*sourceid=*/0,
+                                 EventType::kKeyPressed, KeyboardCode::VKEY_A,
+                                 EF_NONE);
+  x11::Event* xev = scoped_xev;
+  auto* xievent = xev->As<x11::Input::DeviceEvent>();
+
+  // Repeat flag is not set at first.
+  auto non_repeat_key_event = ui::BuildKeyEventFromXEvent(*xev);
+  ASSERT_TRUE(non_repeat_key_event);
+  EXPECT_FALSE(non_repeat_key_event->is_repeat());
+
+  // Now set repeat flag on XI2 key event.
+  xievent->flags = x11::Input::KeyEventFlags::KeyRepeat;
+
+  auto repeat_key_event = ui::BuildKeyEventFromXEvent(*xev);
+  ASSERT_TRUE(repeat_key_event);
+  EXPECT_TRUE(repeat_key_event->is_repeat());
+}
+
 // Ensure KeyEvent::Properties is properly set when XI2 key events are used.
 TEST(XEventTranslationTest, KeyEventXI2EventPropertiesSet) {
   ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
diff --git a/v8 b/v8
index 1610539..f379828 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 1610539c8018d743b4aeb2a24a12aa5a0190159f
+Subproject commit f37982889df3b07bfea01fd35d891b9452cb791b