diff --git a/DEPS b/DEPS
index 1b24dff..27944f7 100644
--- a/DEPS
+++ b/DEPS
@@ -294,7 +294,7 @@
   # 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': 'e84f5ed7d152063a4efd1399eb379305ebe5d3d6',
+  'skia_revision': 'd37ac42bd8d6ff5a91f6bef83a09260524cae274',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -406,7 +406,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.
-  'quiche_revision': '203951a0d6e8a190839bf7a59e00d4df1b45835f',
+  'quiche_revision': '1852058ed84fc551bd4cb9dba8330446d7d02411',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ink
   # and whatever else without interference from each other.
@@ -474,7 +474,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.
-  'cros_components_revision': '97dc8c7a1df880206cc54d9913a7e9d73677072a',
+  'cros_components_revision': 'e7f1a1f42262790f48a9b69761c6c3e45ef225ca',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1563,7 +1563,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '0942c904bb5a624a6b2a10f0550ff9d35b419504',
+    '0b19c12de7a158deb75f357e88759d445a9d7721',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1717,7 +1717,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '-vqiQr1TxuVkiMwnwlZW9xZr22EcZKAIQqxEHKRZHjQC',
+          'version': 'eC0hJWeKwG4zHsm2yGfpxc-rvzQHBmJYdNrA3KQH2ScC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -4673,7 +4673,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        '37e9f4b495cdbb05bd7ec76311668998b6053bf9',
+        'a8b6dc620e36af6428e9953791c2b66ebd6b3641',
       'condition': 'checkout_src_internal',
   },
 
@@ -4739,7 +4739,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '97f5aa28bc4471c48f2f4488c5671bee87b9922a',
+        '91544a50032a3960c09f26b99b4aab8efbaee788',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_contents.cc b/android_webview/browser/aw_contents.cc
index 4b32a64..2b7e4629 100644
--- a/android_webview/browser/aw_contents.cc
+++ b/android_webview/browser/aw_contents.cc
@@ -1585,7 +1585,8 @@
               /*planned_max_preloading_type=*/content::PreloadingType::
                   kPrerender),
           /*preloading_attempt=*/nullptr, /*url_match_predicate=*/{},
-          /*prerender_navigation_handle_callback=*/{});
+          /*prerender_navigation_handle_callback=*/{},
+          /*allow_reuse=*/false);
 
   int32_t handle_id = -1;
   if (prerender_handle) {
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 1427e93..c7590c4 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -832,6 +832,7 @@
         Flag.baseFeature("InProcessGpuUseIOThread"),
         Flag.baseFeature("EnableCustomInputStreamBufferSize"),
         Flag.baseFeature("NetworkServiceDedicatedThread"),
+        Flag.baseFeature("NetworkServiceTaskScheduler"),
         Flag.baseFeature("BrowserThreadPoolAdjustment"),
         Flag.baseFeature(
                 AwFeatures.WEBVIEW_DISABLE_CHIPS,
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index b412794..fe6630f0 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -1806,7 +1806,7 @@
 // Enables lobster restriction based on the current active IME.
 BASE_FEATURE(kLobsterDisabledByInvalidIME,
              "LobsterDisabledByInvalidIME",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Controls lobster availability on managed accounts.
 BASE_FEATURE(kLobsterForManagedUsers,
@@ -1814,7 +1814,7 @@
              base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables lobster i18n response.
-BASE_FEATURE(kLobsterI18n, "LobsterI18n", base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kLobsterI18n, "LobsterI18n", base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Enables lobster entry point in quick insert zero state.
 BASE_FEATURE(kLobsterQuickInsertZeroState,
diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc
index 61c020573..88ee0954 100644
--- a/cc/layers/picture_layer_impl_unittest.cc
+++ b/cc/layers/picture_layer_impl_unittest.cc
@@ -663,15 +663,16 @@
   ASSERT_EQ(0u, active_layer()->tilings()->num_tilings());
 }
 
-TEST_F(LegacySWPictureLayerImplTest, ZoomOutCrash) {
+TEST_F(LegacySWPictureLayerImplTest, ZoomOut) {
   gfx::Size layer_bounds(1300, 1900);
 
-  // Set up the high and low res tilings before pinch zoom.
+  // Set up the high res tilings before pinch zoom.
   SetupDefaultTrees(layer_bounds);
   ResetTilingsAndRasterScales();
   EXPECT_EQ(0u, active_layer()->tilings()->num_tilings());
   SetContentsScaleOnBothLayers(32.0f, 1.0f, 32.0f);
-  EXPECT_EQ(32.f, active_layer()->HighResTiling()->contents_scale_key());
+  EXPECT_BOTH_EQ(num_tilings(), 1u);
+  EXPECT_BOTH_EQ(tilings()->tiling_at(0)->contents_scale_key(), 32.0f);
 
   // Since this test simulates a pinch it needs an input handler.
   // TODO(bokan): This is a raster unit test, it shouldn't be using a real
@@ -681,8 +682,9 @@
   host_impl()->GetInputHandler().PinchGestureBegin(
       gfx::Point(), ui::ScrollInputType::kTouchscreen);
   SetContentsScaleOnBothLayers(1.0f, 1.0f, 1.0f);
-  SetContentsScaleOnBothLayers(1.0f, 1.0f, 1.0f);
   EXPECT_EQ(active_layer()->tilings()->NumHighResTilings(), 1);
+  EXPECT_EQ(1.0f,
+            pending_layer()->tilings()->tiling_at(0)->contents_scale_key());
 }
 
 TEST_F(LegacySWPictureLayerImplTest, ScaledBoundsOverflowInt) {
@@ -692,7 +694,7 @@
 
   gfx::Size layer_bounds(600000, 60);
 
-  // Set up the high and low res tilings before pinch zoom.
+  // Set up the high res tilings before pinch zoom.
   SetupDefaultTrees(layer_bounds);
   ResetTilingsAndRasterScales();
   EXPECT_EQ(0u, active_layer()->tilings()->num_tilings());
@@ -812,8 +814,7 @@
   // Ensure UpdateTiles won't remove any tilings.
   active_layer()->MarkAllTilingsUsed();
 
-  // Zoom out further, close to our low-res scale factor. We should
-  // create a new tiling.
+  // Zoom out further. We should create a new tiling.
   SetContentsScaleOnBothLayers(0.1f, 1.0f, 0.1f);
   ASSERT_EQ(3u, active_layer()->tilings()->num_tilings());
 
@@ -978,7 +979,7 @@
   host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
   UpdateDrawProperties(host_impl()->pending_tree());
 
-  // Masks are scaled, and do not have a low res tiling.
+  // Masks are scaled and have only a high res tiling.
   EXPECT_EQ(1.3f, pending_mask->HighResTiling()->contents_scale_key());
   EXPECT_EQ(1u, pending_mask->num_tilings());
 
@@ -1025,7 +1026,7 @@
   host_impl()->AdvanceToNextFrame(base::Milliseconds(1));
   UpdateDrawProperties(host_impl()->pending_tree());
 
-  // Masks are scaled, and do not have a low res tiling.
+  // Masks are scaled and have only a high res tiling.
   EXPECT_EQ(1.3f, pending_mask->HighResTiling()->contents_scale_key());
   EXPECT_EQ(1u, pending_mask->num_tilings());
 
@@ -3106,7 +3107,6 @@
 
 TEST_F(LegacySWPictureLayerImplTest, TilingSetEvictionQueue) {
   gfx::Size layer_bounds(1000, 1000);
-  float low_res_factor = 0.25f;
 
   host_impl()->active_tree()->SetDeviceViewportRect(gfx::Rect(500, 500));
 
@@ -3161,8 +3161,7 @@
       all_tiles);
 
   std::set<Tile*> unique_tiles;
-  auto expected_scales = std::to_array<float>({low_res_factor, 1.f});
-  size_t scale_index = 0;
+  auto expected_scale = 1.f;
   bool reached_visible = false;
   PrioritizedTile last_tile;
   size_t distance_decreasing = 0;
@@ -3188,13 +3187,7 @@
 
     EXPECT_FALSE(tile->required_for_activation());
 
-    while (std::abs(tile->contents_scale_key() - expected_scales[scale_index]) >
-           std::numeric_limits<float>::epsilon()) {
-      ++scale_index;
-      ASSERT_LT(scale_index, std::size(expected_scales));
-    }
-
-    EXPECT_FLOAT_EQ(tile->contents_scale_key(), expected_scales[scale_index]);
+    EXPECT_FLOAT_EQ(tile->contents_scale_key(), expected_scale);
     unique_tiles.insert(tile);
 
     if (tile->required_for_activation() ==
@@ -3219,7 +3212,6 @@
   EXPECT_EQ(1u, distance_increasing);
   EXPECT_EQ(11u, distance_decreasing);
 
-  scale_index = 0;
   bool reached_required = false;
   while (!queue->IsEmpty()) {
     PrioritizedTile prioritized_tile = queue->Top();
@@ -3233,16 +3225,9 @@
       EXPECT_TRUE(tile->required_for_activation());
     } else if (tile->required_for_activation()) {
       reached_required = true;
-      scale_index = 0;
     }
 
-    while (std::abs(tile->contents_scale_key() - expected_scales[scale_index]) >
-           std::numeric_limits<float>::epsilon()) {
-      ++scale_index;
-      ASSERT_LT(scale_index, std::size(expected_scales));
-    }
-
-    EXPECT_FLOAT_EQ(tile->contents_scale_key(), expected_scales[scale_index]);
+    EXPECT_FLOAT_EQ(tile->contents_scale_key(), expected_scale);
     unique_tiles.insert(tile);
     queue->Pop();
   }
@@ -3693,7 +3678,7 @@
   SetContentsScaleOnBothLayers(0.5f, device_scale, page_scale);
 
   // The high resolution tiling is between target and ideal, so is not
-  // removed.  The low res tiling for the old ideal=1.0 scale is removed.
+  // removed.
   used_tilings.clear();
   active_layer()->CleanUpTilingsOnActiveLayer(used_tilings);
   ASSERT_EQ(2u, active_layer()->tilings()->num_tilings());
@@ -5160,35 +5145,6 @@
   EXPECT_EQ(result.height(), 256);
 }
 
-TEST_F(LegacySWPictureLayerImplTest, LowResWasHighResCollision) {
-  gfx::Size layer_bounds(1300, 1900);
-
-  float low_res_factor = 0.25f;
-  SetupDefaultTrees(layer_bounds);
-  ResetTilingsAndRasterScales();
-
-  float page_scale = 2.f;
-  SetContentsScaleOnBothLayers(page_scale, 1.0f, page_scale);
-  EXPECT_BOTH_EQ(num_tilings(), 1u);
-  EXPECT_BOTH_EQ(tilings()->tiling_at(0)->contents_scale_key(), page_scale);
-
-  // Since this test simulates a pinch it needs an input handler.
-  // TODO(bokan): This is a raster unit test, it shouldn't be using a real
-  // input handler.
-  InputHandler::Create(static_cast<CompositorDelegateForInput&>(*host_impl()));
-
-  host_impl()->GetInputHandler().PinchGestureBegin(
-      gfx::Point(), ui::ScrollInputType::kTouchscreen);
-
-  // Zoom out to exactly the low res factor so that the previous high res
-  // would be equal to the current low res (if it were possible to have one).
-  float zoomed = page_scale / low_res_factor;
-  SetContentsScaleOnBothLayers(zoomed, 1.0f, zoomed);
-  EXPECT_EQ(1u, pending_layer()->num_tilings());
-  EXPECT_EQ(zoomed,
-            pending_layer()->tilings()->tiling_at(0)->contents_scale_key());
-}
-
 TEST_F(LegacySWPictureLayerImplTest, CompositedImageCalculateContentsScale) {
   gfx::Size layer_bounds(400, 400);
   gfx::Rect layer_rect(layer_bounds);
diff --git a/cc/paint/paint_flags.cc b/cc/paint/paint_flags.cc
index f165770..118f7f7 100644
--- a/cc/paint/paint_flags.cc
+++ b/cc/paint/paint_flags.cc
@@ -244,21 +244,14 @@
   return has_discardable_images;
 }
 
-float PaintFlags::DynamicRangeLimitMixture::ComputeHdrHeadroom(
+float PaintFlags::DynamicRangeLimitMixture::ComputeEffectiveHdrHeadroom(
     float target_hdr_headroom) const {
-  if (constrained_high_mix == 0.f && standard_mix == 0.f) {
-    return target_hdr_headroom;
-  }
+  // It would make more sense to store only `high_mix` and `constrained_mix`,
+  // since `standard_mix` is multiplied by zero.
   const float high_mix = 1.f - constrained_high_mix - standard_mix;
-
-  // Average the headrooms in log-space.
-  const float log2_high_headroom = std::log2(target_hdr_headroom);
-  const float log2_constrained_high_headroom =
-      std::min(1.f, log2_high_headroom);
-  const float log2_standard_headroom = 0.f;
-  return std::exp2(standard_mix * log2_standard_headroom +
-                   constrained_high_mix * log2_constrained_high_headroom +
-                   high_mix * log2_high_headroom);
+  constexpr float kConstrainedMax = 1.f;  // Constrained allows at most 1 stop
+  return constrained_high_mix * std::min(kConstrainedMax, target_hdr_headroom) +
+         high_mix * target_hdr_headroom;
 }
 
 }  // namespace cc
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index 32dcf07..5b3b2671 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -120,7 +120,10 @@
           constrained_high_mix(constrained_high_mix) {}
     friend bool operator==(const DynamicRangeLimitMixture&,
                            const DynamicRangeLimitMixture&) = default;
-    float ComputeHdrHeadroom(float target_hdr_headroom) const;
+    // Compute the effective HDR headroom when this limit is applied to
+    // `target_hdr_headroom`.
+    float ComputeEffectiveHdrHeadroom(float target_hdr_headroom) const;
+
     float standard_mix = 0.f;
     float constrained_high_mix = 0.f;
     // The weight for "high" is implicit and calculated as "one minus the
diff --git a/cc/paint/paint_flags_unittest.cc b/cc/paint/paint_flags_unittest.cc
index c910b5c..e0abdff 100644
--- a/cc/paint/paint_flags_unittest.cc
+++ b/cc/paint/paint_flags_unittest.cc
@@ -11,6 +11,11 @@
 
 constexpr float kEpsilon = 1e-5f;
 
+float ComputeHdrHeadroom(const PaintFlags::DynamicRangeLimitMixture& d,
+                         float h) {
+  return std::exp2(d.ComputeEffectiveHdrHeadroom(std::log2(h)));
+}
+
 TEST(PaintFlags, DynamicRangeLimitSimple) {
   const PaintFlags::DynamicRangeLimitMixture kStandard(
       PaintFlags::DynamicRangeLimit::kStandard);
@@ -20,54 +25,54 @@
       PaintFlags::DynamicRangeLimit::kHigh);
 
   // Target HDR headroom is 1.
-  EXPECT_NEAR(kStandard.ComputeHdrHeadroom(1.f), 1.f, kEpsilon);
-  EXPECT_NEAR(kConstrainedHigh.ComputeHdrHeadroom(1.f), 1.f, kEpsilon);
-  EXPECT_NEAR(kHigh.ComputeHdrHeadroom(1.f), 1.f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kStandard, 1.f), 1.f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kConstrainedHigh, 1.f), 1.f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kHigh, 1.f), 1.f, kEpsilon);
 
   // Target HDR headroom is 2^(1/2). The "high" and "constrained-high" are the
   // same here.
   constexpr float kSqrt2 = 1.4142135623730951f;
-  EXPECT_NEAR(kStandard.ComputeHdrHeadroom(kSqrt2), 1.f, kEpsilon);
-  EXPECT_NEAR(kConstrainedHigh.ComputeHdrHeadroom(kSqrt2), kSqrt2, kEpsilon);
-  EXPECT_NEAR(kHigh.ComputeHdrHeadroom(kSqrt2), kSqrt2, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kStandard, kSqrt2), 1.f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kConstrainedHigh, kSqrt2), kSqrt2, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kHigh, kSqrt2), kSqrt2, kEpsilon);
 
   // Target HDR headroom is 4.
-  EXPECT_NEAR(kStandard.ComputeHdrHeadroom(4.f), 1.f, kEpsilon);
-  EXPECT_NEAR(kConstrainedHigh.ComputeHdrHeadroom(4.f), 2.f, kEpsilon);
-  EXPECT_NEAR(kHigh.ComputeHdrHeadroom(4.f), 4.f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kStandard, 4.f), 1.f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kConstrainedHigh, 4.f), 2.f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(kHigh, 4.f), 4.f, kEpsilon);
 }
 
 TEST(PaintFlags, DynamicRangeLimitMix) {
   EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(1.f, 0.0f).ComputeHdrHeadroom(4.f),
+      ComputeHdrHeadroom(PaintFlags::DynamicRangeLimitMixture(1.f, 0.0f), 4.f),
       1.f, kEpsilon);
 
+  EXPECT_NEAR(ComputeHdrHeadroom(
+                  PaintFlags::DynamicRangeLimitMixture(0.75f, 0.0f), 4.f),
+              1.4142135623730951f, kEpsilon);
+
   EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(0.75f, 0.0f).ComputeHdrHeadroom(4.f),
+      ComputeHdrHeadroom(PaintFlags::DynamicRangeLimitMixture(0.5f, 0.5f), 4.f),
       1.4142135623730951f, kEpsilon);
 
-  EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(0.5f, 0.5f).ComputeHdrHeadroom(4.f),
-      1.4142135623730951f, kEpsilon);
+  EXPECT_NEAR(ComputeHdrHeadroom(
+                  PaintFlags::DynamicRangeLimitMixture(0.25f, 0.5f), 4.f),
+              2.f, kEpsilon);
+
+  EXPECT_NEAR(ComputeHdrHeadroom(
+                  PaintFlags::DynamicRangeLimitMixture(0.25f, 0.0f), 4.f),
+              2.8284271247461903f, kEpsilon);
 
   EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(0.25f, 0.5f).ComputeHdrHeadroom(4.f),
-      2.f, kEpsilon);
-
-  EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(0.25f, 0.0f).ComputeHdrHeadroom(4.f),
+      ComputeHdrHeadroom(PaintFlags::DynamicRangeLimitMixture(0.f, 0.5f), 4.f),
       2.8284271247461903f, kEpsilon);
 
   EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(0.f, 0.5f).ComputeHdrHeadroom(4.f),
-      2.8284271247461903f, kEpsilon);
-
-  EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(0.f, 0.25f).ComputeHdrHeadroom(4.f),
+      ComputeHdrHeadroom(PaintFlags::DynamicRangeLimitMixture(0.f, 0.25f), 4.f),
       3.363585661014858f, kEpsilon);
 
   EXPECT_NEAR(
-      PaintFlags::DynamicRangeLimitMixture(0.f, 0.f).ComputeHdrHeadroom(4.f),
+      ComputeHdrHeadroom(PaintFlags::DynamicRangeLimitMixture(0.f, 0.f), 4.f),
       4.f, kEpsilon);
 }
 
diff --git a/cc/paint/paint_op.cc b/cc/paint/paint_op.cc
index 3ad507f..68acfdb8 100644
--- a/cc/paint/paint_op.cc
+++ b/cc/paint/paint_op.cc
@@ -6,6 +6,7 @@
 #include "cc/paint/paint_op.h"
 
 #include <algorithm>
+#include <cmath>
 #include <limits>
 #include <memory>
 #include <type_traits>
@@ -1267,13 +1268,15 @@
   });
 }
 
-static float ComputeEffectiveHdrHeadroom(const PaintFlags* flags,
-                                         const PlaybackParams& params) {
-  if (!flags || params.destination_hdr_headroom == 1.f) {
+static float ComputeLinearEffectiveHdrHeadroom(const PaintFlags* flags,
+                                               const PlaybackParams& params) {
+  if (!flags) {
     return 1.f;
   }
-  return flags->getDynamicRangeLimit().ComputeHdrHeadroom(
-      params.destination_hdr_headroom);
+  // TODO(https://crbug.com/428575083): Change the callers of this to use log2
+  // based headroom.
+  return std::exp2(flags->getDynamicRangeLimit().ComputeEffectiveHdrHeadroom(
+      std::log2(params.destination_hdr_headroom)));
 }
 
 void DrawImageOp::RasterWithFlags(const DrawImageOp* op,
@@ -1306,7 +1309,7 @@
       skia::DrawGainmapImage(canvas, op->image.cached_sk_image_,
                              op->image.gainmap_sk_image_,
                              op->image.gainmap_info_.value(),
-                             ComputeEffectiveHdrHeadroom(flags, params),
+                             ComputeLinearEffectiveHdrHeadroom(flags, params),
                              op->left, op->top, op->sampling, paint);
       return;
     }
@@ -1316,7 +1319,7 @@
                                             canvas->imageInfo().colorSpace())) {
       ToneMapUtil::AddGlobalToneMapFilterToPaint(
           paint, op->image.cached_sk_image_.get(), op->image.hdr_metadata_,
-          ComputeEffectiveHdrHeadroom(flags, params));
+          ComputeLinearEffectiveHdrHeadroom(flags, params));
     }
 
     SkTiledImageUtils::DrawImage(canvas, sk_image.get(), op->left, op->top,
@@ -1445,11 +1448,11 @@
       // If the PaintImage uses a gainmap shader, then replace DrawImage with a
       // shader.
       if (ToneMapUtil::UseGainmapShader(op->image)) {
-        skia::DrawGainmapImageRect(c, op->image.cached_sk_image_,
-                                   op->image.gainmap_sk_image_,
-                                   op->image.gainmap_info_.value(),
-                                   ComputeEffectiveHdrHeadroom(flags, params),
-                                   adjusted_src, op->dst, sampling, p);
+        skia::DrawGainmapImageRect(
+            c, op->image.cached_sk_image_, op->image.gainmap_sk_image_,
+            op->image.gainmap_info_.value(),
+            ComputeLinearEffectiveHdrHeadroom(flags, params), adjusted_src,
+            op->dst, sampling, p);
         return;
       }
 
@@ -1461,7 +1464,7 @@
         ToneMapUtil::AddGlobalToneMapFilterToPaint(
             tonemap_paint, op->image.cached_sk_image_.get(),
             op->image.hdr_metadata_,
-            ComputeEffectiveHdrHeadroom(flags, params));
+            ComputeLinearEffectiveHdrHeadroom(flags, params));
         DrawImageRect(c, sk_image.get(), adjusted_src, op->dst, sampling,
                       &tonemap_paint, op->constraint);
         return;
diff --git a/chrome/VERSION b/chrome/VERSION
index 1a67ca8..d764e76 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=140
 MINOR=0
-BUILD=7281
+BUILD=7282
 PATCH=0
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 94c6b31..c2a0251 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5489,9 +5489,6 @@
      flag_descriptions::kPermissiveUsbPassthroughName,
      flag_descriptions::kPermissiveUsbPassthroughDescription, kOsCrOS,
      PLATFORM_FEATURE_NAME_TYPE("CrOSLateBootPermissiveUsbPassthrough")},
-    {"camera-angle-backend", flag_descriptions::kCameraAngleBackendName,
-     flag_descriptions::kCameraAngleBackendDescription, kOsCrOS,
-     PLATFORM_FEATURE_NAME_TYPE("CrOSLateBootCameraAngleBackend")},
     {"crostini-containerless", flag_descriptions::kCrostiniContainerlessName,
      flag_descriptions::kCrostiniContainerlessDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kCrostiniContainerless)},
diff --git a/chrome/browser/ash/lobster/lobster_candidate_resizer_unittest.cc b/chrome/browser/ash/lobster/lobster_candidate_resizer_unittest.cc
index 11909e1..5a35e674 100644
--- a/chrome/browser/ash/lobster/lobster_candidate_resizer_unittest.cc
+++ b/chrome/browser/ash/lobster/lobster_candidate_resizer_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/functional/callback.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/test/protobuf_matchers.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ash/lobster/lobster_candidate_id_generator.h"
@@ -36,14 +35,7 @@
 
   ~LobsterCandidateResizerTest() override = default;
 
-  void SetUp() override {
-    feature_list_.InitWithFeatures(
-        /*enabled_features=*/{ash::features::kLobsterUseRewrittenQuery},
-        /*disabled_features=*/{ash::features::kLobsterI18n});
-  }
-
  private:
-  base::test::ScopedFeatureList feature_list_;
   base::test::TaskEnvironment task_environment_;
   data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
 };
@@ -63,7 +55,7 @@
                /*seed=*/kFakeBaseGenerationSeed, /*size=*/
                gfx::Size(kFullImageDimensionLength, kFullImageDimensionLength),
                /*num_outputs=*/1, /*use_query_rewriter=*/true,
-               /*use_i18n=*/false)),
+               /*use_i18n=*/true)),
            testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const manta::proto::Request& request,
@@ -111,7 +103,7 @@
                /*seed=*/kFakeBaseGenerationSeed, /*size=*/
                gfx::Size(kFullImageDimensionLength, kFullImageDimensionLength),
                /*num_outputs=*/1, /*use_query_rewriter=*/true,
-               /*use_i18n=*/false)),
+               /*use_i18n=*/true)),
            testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const manta::proto::Request& request,
@@ -156,7 +148,7 @@
                /*seed=*/kFakeBaseGenerationSeed, /*size=*/
                gfx::Size(kFullImageDimensionLength, kFullImageDimensionLength),
                /*num_outputs=*/1, /*use_query_rewriter=*/true,
-               /*use_i18n=*/false)),
+               /*use_i18n=*/true)),
            testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const manta::proto::Request& request,
diff --git a/chrome/browser/ash/lobster/lobster_image_provider_from_snapper_unittest.cc b/chrome/browser/ash/lobster/lobster_image_provider_from_snapper_unittest.cc
index 7d6b024..f0eb4c7 100644
--- a/chrome/browser/ash/lobster/lobster_image_provider_from_snapper_unittest.cc
+++ b/chrome/browser/ash/lobster/lobster_image_provider_from_snapper_unittest.cc
@@ -9,7 +9,6 @@
 #include "base/functional/callback.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/test/protobuf_matchers.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/ash/lobster/lobster_test_utils.h"
@@ -36,14 +35,7 @@
 
   ~LobsterImageProviderFromSnapperTest() override = default;
 
-  void SetUp() override {
-    feature_list_.InitWithFeatures(
-        /*enabled_features=*/{ash::features::kLobsterUseRewrittenQuery},
-        /*disabled_features=*/{ash::features::kLobsterI18n});
-  }
-
  private:
-  base::test::ScopedFeatureList feature_list_;
   base::test::TaskEnvironment task_environment_;
   data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
 };
@@ -62,7 +54,7 @@
               /*query=*/"a lovely cake", /*seed=*/std::nullopt, /*size=*/
               gfx::Size(kPreviewImageDimensionSize, kPreviewImageDimensionSize),
               /*num_outputs=*/2, /*use_query_rewriter=*/true,
-              /*use_i18n=*/false)),
+              /*use_i18n=*/true)),
           testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const manta::proto::Request& request,
@@ -120,7 +112,7 @@
                /*seed=*/kFakeBaseGenerationSeed, /*size=*/
                gfx::Size(kFullImageDimensionSize, kFullImageDimensionSize),
                /*num_outputs=*/1, /*use_query_rewriter=*/true,
-               /*use_i18n=*/false)),
+               /*use_i18n=*/true)),
            testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const manta::proto::Request& request,
@@ -168,7 +160,7 @@
               /*query=*/"a sweet candy", /*seed=*/std::nullopt, /*size=*/
               gfx::Size(kPreviewImageDimensionSize, kPreviewImageDimensionSize),
               /*num_outputs=*/2, /*use_query_rewriter=*/true,
-              /*use_i18n=*/false)),
+              /*use_i18n=*/true)),
           testing::_, testing::_))
       .WillOnce(testing::Invoke(
           [](const manta::proto::Request& request,
diff --git a/chrome/browser/ash/lobster/lobster_system_state_provider_impl_unittest.cc b/chrome/browser/ash/lobster/lobster_system_state_provider_impl_unittest.cc
index 17c3b2e0..e9a1c38 100644
--- a/chrome/browser/ash/lobster/lobster_system_state_provider_impl_unittest.cc
+++ b/chrome/browser/ash/lobster/lobster_system_state_provider_impl_unittest.cc
@@ -202,6 +202,8 @@
 
   TestingPrefServiceSimple* local_state_pref() { return &local_state_pref_; }
 
+  base::test::ScopedFeatureList& scoped_feature_list() { return feature_list_; }
+
  protected:
   content::BrowserTaskEnvironment task_environment_;
 
@@ -375,6 +377,8 @@
             std::get<1>(GetParam()));
 }
 
+// This test only applies when we enforce IME restriction that only allows
+// Lobster to show when eligibile IMEs are active.
 class LobsterSystemStateProviderImplImeTest
     : public LobsterSystemStateProviderImplBaseTest,
       public ::testing::WithParamInterface<std::tuple<
@@ -382,7 +386,10 @@
           /*expected_lobster_status=*/ash::LobsterStatus>> {
  public:
   void SetUp() override {
-    SetUpEligibleHardware();
+    scoped_feature_list().InitWithFeatures(
+        /*enabled_features=*/{ash::features::kFeatureManagementLobster,
+                              ash::features::kLobsterDisabledByInvalidIME},
+        /*disabled_features=*/{});
     SetConsentStatus(chromeos::editor_menu::EditorConsentStatus::kApproved);
     SetSettingsToggle(/*enabled=*/true);
     SetOnlineStatus(/*is_online=*/true);
diff --git a/chrome/browser/chrome_back_forward_cache_browsertest.cc b/chrome/browser/chrome_back_forward_cache_browsertest.cc
index f60187f..f92582c 100644
--- a/chrome/browser/chrome_back_forward_cache_browsertest.cc
+++ b/chrome/browser/chrome_back_forward_cache_browsertest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/task_manager/task_manager_tester.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/grit/generated_resources.h"
@@ -380,7 +381,7 @@
       browser()->tab_strip_model()->GetActiveWebContents());
   std::unique_ptr<ContentSettingBubbleModel> model(
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
-          browser()->content_setting_bubble_model_delegate(),
+          browser()->GetFeatures().content_setting_bubble_model_delegate(),
           browser()->tab_strip_model()->GetActiveWebContents(),
           ContentSettingsType::MIXEDSCRIPT));
   model->OnCustomLinkClicked();
diff --git a/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.cc b/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.cc
index df0793a8..66686e2 100644
--- a/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.cc
+++ b/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.cc
@@ -54,7 +54,7 @@
   return GetWindowAppIcon();
 }
 
-GURL WebKioskBrowserControllerBase::GetAppStartUrl() const {
+const GURL& WebKioskBrowserControllerBase::GetAppStartUrl() const {
   return registrar().GetAppStartUrl(app_id());
 }
 
diff --git a/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.h b/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.h
index 0ccfb85..bb545fb 100644
--- a/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.h
+++ b/chrome/browser/chromeos/app_mode/web_kiosk_browser_controller_base.h
@@ -44,7 +44,7 @@
   ui::ImageModel GetWindowIcon() const override;
   std::u16string GetAppShortName() const override;
   std::u16string GetFormattedUrlOrigin() const override;
-  GURL GetAppStartUrl() const override;
+  const GURL& GetAppStartUrl() const override;
   bool IsUrlInAppScope(const GURL& url) const override;
   bool CanUserUninstall() const override;
   bool IsInstalled() const override;
diff --git a/chrome/browser/content_settings/BUILD.gn b/chrome/browser/content_settings/BUILD.gn
index 5430614..bc8123d22 100644
--- a/chrome/browser/content_settings/BUILD.gn
+++ b/chrome/browser/content_settings/BUILD.gn
@@ -203,6 +203,7 @@
       "//base/test:test_support",
       "//chrome/browser/profiles:profile",
       "//chrome/browser/ui",
+      "//chrome/browser/ui/browser_window",
       "//chrome/browser/ui/content_settings",
       "//chrome/test:test_support_ui",
       "//components/content_settings/browser",
diff --git a/chrome/browser/content_settings/mixed_content_settings_tab_helper_browsertest.cc b/chrome/browser/content_settings/mixed_content_settings_tab_helper_browsertest.cc
index 151cc3b..a515ee6 100644
--- a/chrome/browser/content_settings/mixed_content_settings_tab_helper_browsertest.cc
+++ b/chrome/browser/content_settings/mixed_content_settings_tab_helper_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/command_line.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -82,8 +83,8 @@
   content::TestNavigationObserver observer(web_contents());
   std::unique_ptr<ContentSettingBubbleModel> model(
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
-          browser()->content_setting_bubble_model_delegate(), web_contents(),
-          ContentSettingsType::MIXEDSCRIPT));
+          browser()->GetFeatures().content_setting_bubble_model_delegate(),
+          web_contents(), ContentSettingsType::MIXEDSCRIPT));
   model->OnCustomLinkClicked();
 
   // Waits for reload.
@@ -156,7 +157,7 @@
       browser()->tab_strip_model()->GetActiveWebContents());
   std::unique_ptr<ContentSettingBubbleModel> model(
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
-          browser()->content_setting_bubble_model_delegate(),
+          browser()->GetFeatures().content_setting_bubble_model_delegate(),
           browser()->tab_strip_model()->GetActiveWebContents(),
           ContentSettingsType::MIXEDSCRIPT));
   model->OnCustomLinkClicked();
@@ -324,7 +325,7 @@
       browser()->tab_strip_model()->GetActiveWebContents());
   std::unique_ptr<ContentSettingBubbleModel> model(
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
-          browser()->content_setting_bubble_model_delegate(),
+          browser()->GetFeatures().content_setting_bubble_model_delegate(),
           browser()->tab_strip_model()->GetActiveWebContents(),
           ContentSettingsType::MIXEDSCRIPT));
   model->OnCustomLinkClicked();
diff --git a/chrome/browser/extensions/api/tabs/tabs_event_router_android.cc b/chrome/browser/extensions/api/tabs/tabs_event_router_android.cc
index ccebb2b..d72223f 100644
--- a/chrome/browser/extensions/api/tabs/tabs_event_router_android.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_event_router_android.cc
@@ -15,7 +15,7 @@
     : profile_(profile) {
   CHECK(profile_);
   TabModelList::AddObserver(this);
-  for (TabModel* model : TabModelList::models()) {
+  for (TabModel* const model : TabModelList::models()) {
     OnTabModelAdded(model);
   }
 }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 13a6167..4eb1b69a3e 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1335,14 +1335,6 @@
     "expiry_milestone": 130
   },
   {
-    "name": "camera-angle-backend",
-    "owners": [
-      "rjodin@chromium.org",
-      "chromeos-gfx-gpu@google.com"
-    ],
-    "expiry_milestone": 140
-  },
-  {
     "name": "camera-mic-effects",
     "owners": [
       "bryantchandler@chromium.org",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index fe000d1..b9e36b5 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -6806,10 +6806,6 @@
 const char kPermissiveUsbPassthroughDescription[] =
     "When enabled, applies more permissive rules passthrough of USB devices.";
 
-const char kCameraAngleBackendName[] = "Camera service ANGLE backend";
-const char kCameraAngleBackendDescription[] =
-    "When enabled, uses ANGLE as the GL driver in the camera service.";
-
 const char kChromeboxUsbPassthroughRestrictionsName[] =
     "Limit primary mice/keyboards from USB passthrough on chromeboxes";
 const char kChromeboxUsbPassthroughRestrictionsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index c7c9cda0..0c1d53c 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -3982,9 +3982,6 @@
 extern const char kPermissiveUsbPassthroughName[];
 extern const char kPermissiveUsbPassthroughDescription[];
 
-extern const char kCameraAngleBackendName[];
-extern const char kCameraAngleBackendDescription[];
-
 extern const char kDisableBruschettaInstallChecksName[];
 extern const char kDisableBruschettaInstallChecksDescription[];
 
diff --git a/chrome/browser/password_manager/password_change_delegate.h b/chrome/browser/password_manager/password_change_delegate.h
index 7c31b0d..bf0f4e4 100644
--- a/chrome/browser/password_manager/password_change_delegate.h
+++ b/chrome/browser/password_manager/password_change_delegate.h
@@ -17,6 +17,7 @@
   // Internal state of a password change flow. Corresponds to
   // `PasswordChangeFlowState` in enums.xml. These values are persisted to logs.
   // Entries should not be renumbered and numeric values should never be reused.
+  // LINT.IfChange(State)
   enum class State {
     // Password change is being offered to the user, waiting from the to accept
     // or reject it.
@@ -50,6 +51,7 @@
 
     kMaxValue = kCanceled,
   };
+  // LINT.ThenChange(/tools/metrics/histograms/metadata/password/enums.xml:PasswordChangeFlowState)
 
   // An interface used to notify clients (observers) of delegate state. Register
   // the observer via `PasswordChangeDelegate::AddObserver`.
diff --git a/chrome/browser/password_manager/password_change_delegate_impl_unittest.cc b/chrome/browser/password_manager/password_change_delegate_impl_unittest.cc
index eeaab09..9f263c8 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl_unittest.cc
+++ b/chrome/browser/password_manager/password_change_delegate_impl_unittest.cc
@@ -159,7 +159,8 @@
   ResetDelegate();
   histogram_tester.ExpectUniqueSample(
       PasswordChangeDelegateImpl::kFinalPasswordChangeStatusHistogram,
-      PasswordChangeDelegate::State::kChangePasswordFormNotFound, 1);
+      PasswordChangeDelegate::State::kChangePasswordFormNotFound,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(PasswordChangeDelegateImplTest, MetricsReportedFlowOffered) {
@@ -170,7 +171,8 @@
   ResetDelegate();
   histogram_tester.ExpectUniqueSample(
       PasswordChangeDelegateImpl::kFinalPasswordChangeStatusHistogram,
-      PasswordChangeDelegate::State::kOfferingPasswordChange, 1);
+      PasswordChangeDelegate::State::kOfferingPasswordChange,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(PasswordChangeDelegateImplTest,
@@ -182,7 +184,8 @@
   ResetDelegate();
   histogram_tester.ExpectUniqueSample(
       PasswordChangeDelegateImpl::kFinalPasswordChangeStatusHistogram,
-      PasswordChangeDelegate::State::kWaitingForAgreement, 1);
+      PasswordChangeDelegate::State::kWaitingForAgreement,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(PasswordChangeDelegateImplTest,
@@ -195,7 +198,8 @@
   ResetDelegate();
   histogram_tester.ExpectUniqueSample(
       PasswordChangeDelegateImpl::kFinalPasswordChangeStatusHistogram,
-      PasswordChangeDelegate::State::kWaitingForChangePasswordForm, 1);
+      PasswordChangeDelegate::State::kWaitingForChangePasswordForm,
+      /*expected_bucket_count=*/1);
 }
 
 TEST_F(PasswordChangeDelegateImplTest,
@@ -237,6 +241,8 @@
 TEST_F(PasswordChangeDelegateImplTest, OtpDetectionProcessed) {
   SetOptimizationFeatureEnabled(true);
   CreateDelegate();
+  base::HistogramTester histogram_tester;
+
   delegate()->StartPasswordChangeFlow();
   EXPECT_EQ(delegate()->GetCurrentState(),
             PasswordChangeDelegate::State::kWaitingForChangePasswordForm);
@@ -245,4 +251,28 @@
       static_cast<PasswordChangeDelegateImpl*>(delegate())->executor());
   EXPECT_EQ(delegate()->GetCurrentState(),
             PasswordChangeDelegate::State::kOtpDetected);
+
+  ResetDelegate();
+  histogram_tester.ExpectUniqueSample(
+      PasswordChangeDelegateImpl::kFinalPasswordChangeStatusHistogram,
+      PasswordChangeDelegate::State::kOtpDetected, /*expected_bucket_count=*/1);
+}
+
+TEST_F(PasswordChangeDelegateImplTest, PasswordChangeFlowCanceled) {
+  SetOptimizationFeatureEnabled(true);
+  CreateDelegate();
+  base::HistogramTester histogram_tester;
+
+  delegate()->StartPasswordChangeFlow();
+  EXPECT_EQ(delegate()->GetCurrentState(),
+            PasswordChangeDelegate::State::kWaitingForChangePasswordForm);
+
+  delegate()->CancelPasswordChangeFlow();
+  EXPECT_EQ(delegate()->GetCurrentState(),
+            PasswordChangeDelegate::State::kCanceled);
+
+  ResetDelegate();
+  histogram_tester.ExpectUniqueSample(
+      PasswordChangeDelegateImpl::kFinalPasswordChangeStatusHistogram,
+      PasswordChangeDelegate::State::kCanceled, /*expected_bucket_count=*/1);
 }
diff --git a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
index f4acd6c..d6848c1 100644
--- a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
+++ b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
@@ -101,6 +101,7 @@
       content::PreloadingHoldbackStatus::kUnspecified, pipeline_info_,
       preloading_attempt,
       /*url_match_predicate=*/{},
-      std::move(prerender_navigation_handle_callback));
+      std::move(prerender_navigation_handle_callback),
+      /*allow_reuse=*/false);
   return prerender_handle_ != nullptr;
 }
diff --git a/chrome/browser/preloading/new_tab_page_preload/new_tab_page_preload_pipeline.cc b/chrome/browser/preloading/new_tab_page_preload/new_tab_page_preload_pipeline.cc
index 4600ea6a..4817ca1 100644
--- a/chrome/browser/preloading/new_tab_page_preload/new_tab_page_preload_pipeline.cc
+++ b/chrome/browser/preloading/new_tab_page_preload/new_tab_page_preload_pipeline.cc
@@ -89,7 +89,8 @@
       preloading_attempt,
       /*url_match_predicate=*/{},
       base::BindRepeating(&page_load_metrics::NavigationHandleUserData::
-                              AttachNewTabPageNavigationHandleUserData));
+                              AttachNewTabPageNavigationHandleUserData),
+      /*allow_reuse=*/false);
 
   return prerender_handle_ != nullptr;
 }
diff --git a/chrome/browser/preloading/prerender/prerender_manager.cc b/chrome/browser/preloading/prerender/prerender_manager.cc
index b4e091c8..ff2e3170b 100644
--- a/chrome/browser/preloading/prerender/prerender_manager.cc
+++ b/chrome/browser/preloading/prerender/prerender_manager.cc
@@ -204,7 +204,8 @@
       content::PreloadPipelineInfo::Create(
           /*planned_max_preloading_type=*/content::PreloadingType::kPrerender),
       &preloading_attempt,
-      /*url_match_predicate=*/{}, /*prerender_navigation_handle_callback=*/{});
+      /*url_match_predicate=*/{}, /*prerender_navigation_handle_callback=*/{},
+      /*allow_reuse=*/false);
 
   if (direct_url_input_prerender_handle_) {
     return direct_url_input_prerender_handle_->GetWeakPtr();
@@ -255,7 +256,8 @@
           [](const GURL& url, const std::optional<content::UrlMatchType>&) {
             return false;
           }),
-      /*prerender_navigation_handle_callback=*/{});
+      /*prerender_navigation_handle_callback=*/{},
+      /*allow_reuse=*/true);
 
   return search_prewarm_handle_ != nullptr;
 }
@@ -304,7 +306,8 @@
               /*planned_max_preloading_type=*/content::PreloadingType::
                   kPrerender),
           preloading_attempt.get(), std::move(url_match_predicate),
-          /*prerender_navigation_handle_callback=*/{});
+          /*prerender_navigation_handle_callback=*/{},
+          /*allow_reuse=*/true);
 
   if (prerender_handle) {
     CHECK(!search_prerender_task_)
diff --git a/chrome/browser/preloading/search_preload/search_preload_pipeline.cc b/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
index 7805057..83a1eab9 100644
--- a/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
@@ -153,7 +153,8 @@
       /*should_prepare_paint_tree=*/true,
       content::PreloadingHoldbackStatus::kUnspecified, pipeline_info_, attempt,
       std::move(url_match_predicate),
-      /*prerender_navigation_handle_callback=*/{});
+      /*prerender_navigation_handle_callback=*/{},
+      /*allow_reuse=*/false);
   return SearchPreloadSignalResult::kPrerenderTriggered;
 }
 
diff --git a/chrome/browser/profile_resetter/profile_resetter_browsertest.cc b/chrome/browser/profile_resetter/profile_resetter_browsertest.cc
index 85a28d9..44de74e9 100644
--- a/chrome/browser/profile_resetter/profile_resetter_browsertest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test.h"
@@ -23,6 +24,17 @@
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 
+#if BUILDFLAG(IS_CHROMEOS)
+#include "base/containers/to_vector.h"
+#include "chromeos/ash/components/dbus/hermes/hermes_euicc_client.h"
+#include "chromeos/ash/components/dbus/hermes/hermes_manager_client.h"
+#include "chromeos/ash/components/dbus/hermes/hermes_profile_client.h"
+#include "chromeos/ash/components/dbus/shill/shill_clients.h"
+#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
+#include "chromeos/ash/components/network/managed_network_configuration_handler_impl.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 namespace {
 
 const char kCookieName[] = "A";
@@ -165,4 +177,141 @@
   EXPECT_FALSE(tester.GetCookie(kCookieHostname, &cookie));
 }
 
+// PinnedTabsResetTest --------------------------------------------------------
+
+class PinnedTabsResetTest : public InProcessBrowserTest,
+                            public ProfileResetterTestBase {
+ protected:
+  // InProcessBrowserTest:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    resetter_ = std::make_unique<ProfileResetter>(browser()->profile());
+  }
+
+  content::WebContents* AddTab(const GURL& url) {
+    ui_test_utils::NavigateToURLWithDisposition(
+        browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
+        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(PinnedTabsResetTest, ResetPinnedTabs) {
+  TabStripModel* tab_strip_model = browser()->tab_strip_model();
+
+  // Start with one tab (about:blank). Navigating it.
+  content::WebContents* initial_contents = tab_strip_model->GetWebContentsAt(0);
+  ASSERT_TRUE(
+      ui_test_utils::NavigateToURL(browser(), GURL("http://example.com/0")));
+  initial_contents = tab_strip_model->GetWebContentsAt(0);
+
+  // Add 4 tabs
+  content::WebContents* tab_contents1 = AddTab(GURL("http://example.com/1"));
+  content::WebContents* tab_contents2 = AddTab(GURL("http://example.com/2"));
+  content::WebContents* tab_contents3 = AddTab(GURL("http://example.com/3"));
+  content::WebContents* tab_contents4 = AddTab(GURL("http://example.com/4"));
+
+  // Current order: initial, tab_contents1, tab_contents2, tab_contents3,
+  // tab_contents4
+
+  // Pin tab_contents2 and tab_contents1
+  tab_strip_model->SetTabPinned(
+      tab_strip_model->GetIndexOfWebContents(tab_contents2), true);
+  tab_strip_model->SetTabPinned(
+      tab_strip_model->GetIndexOfWebContents(tab_contents1), true);
+
+  // Expected order after pinning: tab_contents2, tab_contents1, initial,
+  // tab_contents3, tab_contents4
+  EXPECT_EQ(5, tab_strip_model->count());
+  EXPECT_EQ(tab_contents2, tab_strip_model->GetWebContentsAt(0));
+  EXPECT_EQ(tab_contents1, tab_strip_model->GetWebContentsAt(1));
+  EXPECT_EQ(initial_contents, tab_strip_model->GetWebContentsAt(2));
+  EXPECT_EQ(tab_contents3, tab_strip_model->GetWebContentsAt(3));
+  EXPECT_EQ(tab_contents4, tab_strip_model->GetWebContentsAt(4));
+  EXPECT_EQ(2, tab_strip_model->IndexOfFirstNonPinnedTab());
+
+  // Note: unpinning in the function below occurs in reverse order, because
+  // if we unpin the tab, it could be moved to the right, and traversing
+  // in left-to-right order would skip some pinned tabs.
+  ResetAndWait(ProfileResetter::PINNED_TABS);
+
+  // The order should be preserved, just all unpinned.
+  EXPECT_EQ(tab_contents2, tab_strip_model->GetWebContentsAt(0));
+  EXPECT_EQ(tab_contents1, tab_strip_model->GetWebContentsAt(1));
+  EXPECT_EQ(initial_contents, tab_strip_model->GetWebContentsAt(2));
+  EXPECT_EQ(tab_contents3, tab_strip_model->GetWebContentsAt(3));
+  EXPECT_EQ(tab_contents4, tab_strip_model->GetWebContentsAt(4));
+  EXPECT_EQ(0, tab_strip_model->IndexOfFirstNonPinnedTab());
+}
+
+#if BUILDFLAG(IS_CHROMEOS)
+// Returns the configured static name servers from `shill_properties`, or an
+// empty vector if no static name servers are configured.
+std::vector<std::string> GetStaticNameServersFromShillProperties(
+    const base::Value::Dict& shill_properties) {
+  const base::Value::Dict* static_ip_config =
+      shill_properties.FindDict(shill::kStaticIPConfigProperty);
+  if (!static_ip_config) {
+    return {};
+  }
+  const base::Value::List* nameservers =
+      static_ip_config->FindList(shill::kNameServersProperty);
+  if (!nameservers) {
+    return {};
+  }
+  return base::ToVector(*nameservers, [](const base::Value& nameserver) {
+    return nameserver.GetString();
+  });
+}
+
+// DnsConfigResetTest --------------------------------------------------------
+
+class DnsConfigResetTest : public InProcessBrowserTest,
+                           public ProfileResetterTestBase {
+ protected:
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    resetter_ = std::make_unique<ProfileResetter>(browser()->profile());
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(DnsConfigResetTest, ResetDnsConfigurations) {
+  ash::ShillServiceClient::TestInterface* shill_service_client =
+      ash::ShillServiceClient::Get()->GetTestInterface();
+
+  // DNS settings.
+  // Set the profile so this shows up as a configured network.
+  const std::string kWifi1Path = "/service/wifi1";
+  ash::NetworkHandler::Get()
+      ->managed_network_configuration_handler()
+      ->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+                  base::Value::List(), base::Value::Dict());
+  // Set a static NameServers config.
+  base::Value::Dict static_ip_config;
+  base::Value::List name_servers;
+  name_servers.Append("8.8.3.1");
+  name_servers.Append("8.8.2.1");
+  name_servers.Append("0.0.0.0");
+  name_servers.Append("0.0.0.0");
+  static_ip_config.Set(shill::kNameServersProperty, std::move(name_servers));
+  shill_service_client->SetServiceProperty(
+      kWifi1Path, shill::kStaticIPConfigProperty,
+      base::Value(std::move(static_ip_config)));
+
+  // Verify that network exists and the custom name server has been applied.
+  const base::Value::Dict* shill_properties =
+      shill_service_client->GetServiceProperties(kWifi1Path);
+  ASSERT_TRUE(shill_properties);
+  EXPECT_THAT(GetStaticNameServersFromShillProperties(*shill_properties),
+              testing::ElementsAre("8.8.3.1", "8.8.2.1", "0.0.0.0", "0.0.0.0"));
+
+  ResetAndWait(ProfileResetter::DNS_CONFIGURATIONS);
+
+  // Check DNS settings have changed to expected defaults.
+  // Verify that the given network has it's NameServers field cleared.
+  EXPECT_THAT(GetStaticNameServersFromShillProperties(*shill_properties),
+              testing::IsEmpty());
+}
+#endif  // BUILDFLAG(IS_CHROMEOS)
+
 }  // namespace
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index 4d345f17..63e10f6c 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -40,7 +40,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/webdata_services/web_data_service_factory.h"
 #include "chrome/common/pref_names.h"
-#include "chrome/test/base/browser_with_test_window_test.h"
 #include "components/content_settings/core/browser/content_settings_info.h"
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -74,17 +73,6 @@
 #include "base/win/shortcut.h"
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS)
-#include "base/containers/to_vector.h"
-#include "chromeos/ash/components/dbus/hermes/hermes_euicc_client.h"
-#include "chromeos/ash/components/dbus/hermes/hermes_manager_client.h"
-#include "chromeos/ash/components/dbus/hermes/hermes_profile_client.h"
-#include "chromeos/ash/components/dbus/shill/shill_clients.h"
-#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
-#include "chromeos/ash/components/network/managed_network_configuration_handler_impl.h"
-#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 using extensions::mojom::ManifestLocation;
 
 namespace {
@@ -198,61 +186,6 @@
   resetter_ = std::make_unique<ProfileResetter>(profile());
 }
 
-// PinnedTabsResetTest --------------------------------------------------------
-
-class PinnedTabsResetTest : public BrowserWithTestWindowTest,
-                            public ProfileResetterTestBase {
- protected:
-  void SetUp() override;
-
-  std::unique_ptr<content::WebContents> CreateWebContents();
-};
-
-void PinnedTabsResetTest::SetUp() {
-  BrowserWithTestWindowTest::SetUp();
-  resetter_ = std::make_unique<ProfileResetter>(profile());
-}
-
-std::unique_ptr<content::WebContents> PinnedTabsResetTest::CreateWebContents() {
-  return content::WebContents::Create(
-      content::WebContents::CreateParams(profile()));
-}
-
-#if BUILDFLAG(IS_CHROMEOS)
-// DnsConfigResetTest --------------------------------------------------------
-
-class DnsConfigResetTest : public BrowserWithTestWindowTest,
-                           public ProfileResetterTestBase {
- protected:
-  void SetUp() override {
-    BrowserWithTestWindowTest::SetUp();
-
-    // Required for initializing NetworkHandler.
-    ash::HermesProfileClient::InitializeFake();
-    ash::HermesManagerClient::InitializeFake();
-    ash::HermesEuiccClient::InitializeFake();
-
-    ash::shill_clients::InitializeFakes();
-    ash::NetworkHandler::InitializeFake();
-
-    // Run the message loop to run the signal connection result callback.
-    base::RunLoop().RunUntilIdle();
-
-    resetter_ = std::make_unique<ProfileResetter>(profile());
-  }
-  void TearDown() override {
-    ash::NetworkHandler::Shutdown();
-    ash::shill_clients::Shutdown();
-
-    ash::HermesEuiccClient::Shutdown();
-    ash::HermesManagerClient::Shutdown();
-    ash::HermesProfileClient::Shutdown();
-
-    BrowserWithTestWindowTest::TearDown();
-  }
-};
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 // ConfigParserTest -----------------------------------------------------------
 
 class ConfigParserTest : public testing::Test {
@@ -460,27 +393,6 @@
   str->replace(placeholder_pos, placeholder.size(), substitution);
 }
 
-#if BUILDFLAG(IS_CHROMEOS)
-// Returns the configured static name servers from `shill_properties`, or an
-// empty vector if no static name servers are configured.
-std::vector<std::string> GetStaticNameServersFromShillProperties(
-    const base::Value::Dict& shill_properties) {
-  const base::Value::Dict* static_ip_config =
-      shill_properties.FindDict(shill::kStaticIPConfigProperty);
-  if (!static_ip_config) {
-    return {};
-  }
-  const base::Value::List* nameservers =
-      static_ip_config->FindList(shill::kNameServersProperty);
-  if (!nameservers) {
-    return {};
-  }
-  return base::ToVector(*nameservers, [](const base::Value& nameserver) {
-    return nameserver.GetString();
-  });
-}
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 /********************* Tests *********************/
 
 TEST_F(ProfileResetterTest, ResetNothing) {
@@ -776,39 +688,6 @@
             startup_pref.urls);
 }
 
-TEST_F(PinnedTabsResetTest, ResetPinnedTabs) {
-  std::unique_ptr<content::WebContents> contents1(CreateWebContents());
-  std::unique_ptr<content::WebContents> contents2(CreateWebContents());
-  std::unique_ptr<content::WebContents> contents3(CreateWebContents());
-  std::unique_ptr<content::WebContents> contents4(CreateWebContents());
-  content::WebContents* raw_contents1 = contents1.get();
-  content::WebContents* raw_contents2 = contents2.get();
-  content::WebContents* raw_contents3 = contents3.get();
-  content::WebContents* raw_contents4 = contents4.get();
-  TabStripModel* tab_strip_model = browser()->tab_strip_model();
-
-  tab_strip_model->AppendWebContents(std::move(contents4), true);
-  tab_strip_model->AppendWebContents(std::move(contents3), true);
-  tab_strip_model->AppendWebContents(std::move(contents2), true);
-  tab_strip_model->SetTabPinned(2, true);
-  tab_strip_model->AppendWebContents(std::move(contents1), true);
-  tab_strip_model->SetTabPinned(3, true);
-
-  EXPECT_EQ(raw_contents2, tab_strip_model->GetWebContentsAt(0));
-  EXPECT_EQ(raw_contents1, tab_strip_model->GetWebContentsAt(1));
-  EXPECT_EQ(raw_contents4, tab_strip_model->GetWebContentsAt(2));
-  EXPECT_EQ(raw_contents3, tab_strip_model->GetWebContentsAt(3));
-  EXPECT_EQ(2, tab_strip_model->IndexOfFirstNonPinnedTab());
-
-  ResetAndWait(ProfileResetter::PINNED_TABS);
-
-  EXPECT_EQ(raw_contents2, tab_strip_model->GetWebContentsAt(0));
-  EXPECT_EQ(raw_contents1, tab_strip_model->GetWebContentsAt(1));
-  EXPECT_EQ(raw_contents4, tab_strip_model->GetWebContentsAt(2));
-  EXPECT_EQ(raw_contents3, tab_strip_model->GetWebContentsAt(3));
-  EXPECT_EQ(0, tab_strip_model->IndexOfFirstNonPinnedTab());
-}
-
 TEST_F(ProfileResetterTest, ResetShortcuts) {
   ShortcutHandler shortcut;
   ShortcutCommand command_line = shortcut.CreateWithArguments(
@@ -1149,44 +1028,4 @@
       ntp_custom_background_service->GetCustomBackground().has_value());
 }
 
-#if BUILDFLAG(IS_CHROMEOS)
-TEST_F(DnsConfigResetTest, ResetDnsConfigurations) {
-  ash::ShillServiceClient::TestInterface* shill_service_client =
-      ash::ShillServiceClient::Get()->GetTestInterface();
-
-  // DNS settings.
-  // Set the profile so this shows up as a configured network.
-  const std::string kWifi1Path = "/service/wifi1";
-  ash::NetworkHandler::Get()
-      ->managed_network_configuration_handler()
-      ->SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
-                  base::Value::List(), base::Value::Dict());
-  // Set a static NameServers config.
-  base::Value::Dict static_ip_config;
-  base::Value::List name_servers;
-  name_servers.Append("8.8.3.1");
-  name_servers.Append("8.8.2.1");
-  name_servers.Append("0.0.0.0");
-  name_servers.Append("0.0.0.0");
-  static_ip_config.Set(shill::kNameServersProperty, std::move(name_servers));
-  shill_service_client->SetServiceProperty(
-      kWifi1Path, shill::kStaticIPConfigProperty,
-      base::Value(std::move(static_ip_config)));
-
-  // Verify that network exists and the custom name server has been applied.
-  const base::Value::Dict* shill_properties =
-      shill_service_client->GetServiceProperties(kWifi1Path);
-  ASSERT_TRUE(shill_properties);
-  EXPECT_THAT(GetStaticNameServersFromShillProperties(*shill_properties),
-              testing::ElementsAre("8.8.3.1", "8.8.2.1", "0.0.0.0", "0.0.0.0"));
-
-  ResetAndWait(ProfileResetter::DNS_CONFIGURATIONS);
-
-  // Check DNS settings have changed to expected defaults.
-  // Verify that the given network has it's NameServers field cleared.
-  EXPECT_THAT(GetStaticNameServersFromShillProperties(*shill_properties),
-              testing::IsEmpty());
-}
-#endif  // BUILDFLAG(IS_CHROMEOS)
-
 }  // namespace
diff --git a/chrome/browser/signin/dice_browsertest.cc b/chrome/browser/signin/dice_browsertest.cc
index bc92fb1..f052cae 100644
--- a/chrome/browser/signin/dice_browsertest.cc
+++ b/chrome/browser/signin/dice_browsertest.cc
@@ -1903,10 +1903,21 @@
 
 IN_PROC_BROWSER_TEST_F(DiceManageAccountBrowserTest,
                        ClearManagedProfileOnStartup) {
-  // Initial profile should have been deleted as sign-in and sign out were no
-  // longer allowed.
   PrefService* local_state = g_browser_process->local_state();
   DCHECK(local_state);
+
+  // Initial profile should have been deleted as sign-in and sign out were no
+  // longer allowed. If the profile has not yet been deleted, wait for the pref
+  // to be updated.
+  if (local_state->GetList(prefs::kProfilesDeleted).empty()) {
+    base::RunLoop run_loop;
+    PrefChangeRegistrar pref_registrar;
+    pref_registrar.Init(local_state);
+    // Quit the run loop when the 'kProfilesDeleted' pref changes.
+    pref_registrar.Add(prefs::kProfilesDeleted, run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
   const base::Value::List& deleted_profiles =
       local_state->GetList(prefs::kProfilesDeleted);
   EXPECT_EQ(1U, deleted_profiles.size());
diff --git a/chrome/browser/ui/breadcrumb_manager_browser_agent.cc b/chrome/browser/ui/breadcrumb_manager_browser_agent.cc
index 6c110326..df9af42 100644
--- a/chrome/browser/ui/breadcrumb_manager_browser_agent.cc
+++ b/chrome/browser/ui/breadcrumb_manager_browser_agent.cc
@@ -6,10 +6,10 @@
 
 #include <optional>
 
+#include "base/check_deref.h"
 #include "chrome/browser/breadcrumbs/breadcrumb_manager_keyed_service_factory.h"
 #include "chrome/browser/breadcrumbs/breadcrumb_manager_tab_helper.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "components/breadcrumbs/core/breadcrumb_manager_keyed_service.h"
 
 namespace {
@@ -24,19 +24,19 @@
 
 }  // namespace
 
-BreadcrumbManagerBrowserAgent::BreadcrumbManagerBrowserAgent(Browser* browser)
-    : browser_(browser) {
-  browser_->tab_strip_model()->AddObserver(this);
+BreadcrumbManagerBrowserAgent::BreadcrumbManagerBrowserAgent(
+    TabStripModel* tab_strip_model,
+    content::BrowserContext* browser_context)
+    : breadcrumb_manager_(CHECK_DEREF(
+          BreadcrumbManagerKeyedServiceFactory::GetForBrowserContext(
+              browser_context))) {
+  tab_strip_model->AddObserver(this);
 }
 
-BreadcrumbManagerBrowserAgent::~BreadcrumbManagerBrowserAgent() {
-  browser_->tab_strip_model()->RemoveObserver(this);
-}
+BreadcrumbManagerBrowserAgent::~BreadcrumbManagerBrowserAgent() = default;
 
 void BreadcrumbManagerBrowserAgent::PlatformLogEvent(const std::string& event) {
-  BreadcrumbManagerKeyedServiceFactory::GetForBrowserContext(
-      browser_->profile())
-      ->AddEvent(event);
+  breadcrumb_manager_->AddEvent(event);
 }
 
 void BreadcrumbManagerBrowserAgent::OnTabStripModelChanged(
diff --git a/chrome/browser/ui/breadcrumb_manager_browser_agent.h b/chrome/browser/ui/breadcrumb_manager_browser_agent.h
index aed7288..d2e9d707 100644
--- a/chrome/browser/ui/breadcrumb_manager_browser_agent.h
+++ b/chrome/browser/ui/breadcrumb_manager_browser_agent.h
@@ -5,22 +5,28 @@
 #ifndef CHROME_BROWSER_UI_BREADCRUMB_MANAGER_BROWSER_AGENT_H_
 #define CHROME_BROWSER_UI_BREADCRUMB_MANAGER_BROWSER_AGENT_H_
 
-#include <string>
-
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "components/breadcrumbs/core/breadcrumb_manager_browser_agent.h"
 
-class Browser;
 class TabStripModel;
 class TabStripModelChange;
 struct TabStripSelectionChange;
 
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace breadcrumbs {
+class BreadcrumbManagerKeyedService;
+}  // namespace breadcrumbs
+
 class BreadcrumbManagerBrowserAgent
     : public breadcrumbs::BreadcrumbManagerBrowserAgent,
       public TabStripModelObserver {
  public:
-  explicit BreadcrumbManagerBrowserAgent(Browser* browser);
+  BreadcrumbManagerBrowserAgent(TabStripModel* tab_strip_model,
+                                content::BrowserContext* browser_context);
   BreadcrumbManagerBrowserAgent(const BreadcrumbManagerBrowserAgent&) = delete;
   BreadcrumbManagerBrowserAgent& operator=(
       const BreadcrumbManagerBrowserAgent&) = delete;
@@ -36,9 +42,7 @@
       const TabStripModelChange& change,
       const TabStripSelectionChange& selection) override;
 
-  // The browser whose tab strip this agent observes. Can't be nullptr because
-  // |browser_| owns this object.
-  raw_ptr<Browser> browser_;
+  const raw_ref<breadcrumbs::BreadcrumbManagerKeyedService> breadcrumb_manager_;
 };
 
 #endif  // CHROME_BROWSER_UI_BREADCRUMB_MANAGER_BROWSER_AGENT_H_
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 32516a8..d6125bb9 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -92,11 +92,9 @@
 #include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
-#include "chrome/browser/ui/breadcrumb_manager_browser_agent.h"
 #include "chrome/browser/ui/browser_actions.h"
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -166,7 +164,6 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
-#include "components/breadcrumbs/core/breadcrumbs_status.h"
 #include "components/captive_portal/core/buildflags.h"
 #include "components/content_settings/browser/page_specific_content_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
@@ -663,19 +660,13 @@
           params.initial_visible_on_all_workspaces_state),
       creation_source_(params.creation_source),
       unload_controller_(this),
-      content_setting_bubble_model_delegate_(
-          new BrowserContentSettingBubbleModelDelegate(this)),
       live_tab_context_(new BrowserLiveTabContext(this)),
       app_controller_(web_app::MaybeCreateAppBrowserController(this)),
       bookmark_bar_state_(BookmarkBar::HIDDEN),
       browser_actions_(new BrowserActions(*this)),
       command_controller_(new chrome::BrowserCommandController(this)),
       window_has_shown_(false),
-      user_title_(params.user_title),
-      breadcrumb_manager_browser_agent_(
-          breadcrumbs::IsEnabled(g_browser_process->local_state())
-              ? std::make_unique<BreadcrumbManagerBrowserAgent>(this)
-              : nullptr) {
+      user_title_(params.user_title) {
   browser_actions_->InitializeBrowserActions();
 
   if (!profile_->IsOffTheRecord()) {
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index a795d65..fc24449 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -63,9 +63,7 @@
 #endif
 
 class BackgroundContents;
-class BreadcrumbManagerBrowserAgent;
 class BrowserActions;
-class BrowserContentSettingBubbleModelDelegate;
 class BrowserLiveTabContext;
 class BrowserView;
 class BrowserWindow;
@@ -483,10 +481,6 @@
   bool should_trigger_session_restore() const {
     return should_trigger_session_restore_;
   }
-  BrowserContentSettingBubbleModelDelegate*
-  content_setting_bubble_model_delegate() {
-    return content_setting_bubble_model_delegate_.get();
-  }
   BrowserLiveTabContext* live_tab_context() { return live_tab_context_.get(); }
   const web_app::AppBrowserController* app_controller() const {
     return app_controller_.get();
@@ -1402,10 +1396,6 @@
   // Dialog box used for opening and saving files.
   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
 
-  // Helper which implements the ContentSettingBubbleModel interface.
-  std::unique_ptr<BrowserContentSettingBubbleModelDelegate>
-      content_setting_bubble_model_delegate_;
-
   // Helper which implements the LiveTabContext interface.
   std::unique_ptr<BrowserLiveTabContext> live_tab_context_;
 
@@ -1425,10 +1415,6 @@
 
   std::string user_title_;
 
-  // Listens for browser-related breadcrumb events to be added to crash reports.
-  std::unique_ptr<BreadcrumbManagerBrowserAgent>
-      breadcrumb_manager_browser_agent_;
-
   std::unique_ptr<ScopedKeepAlive> keep_alive_;
 
   WarnBeforeClosingCallback warn_before_closing_callback_;
diff --git a/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc b/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc
index 112c9a6..8911da80 100644
--- a/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc
+++ b/chrome/browser/ui/browser_content_setting_bubble_model_delegate.cc
@@ -4,15 +4,17 @@
 
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
 
+#include "base/check_deref.h"
 #include "chrome/browser/content_settings/chrome_content_settings_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/tab_dialogs.h"
-#include "chrome/common/url_constants.h"
-#include "components/google/core/common/google_util.h"
+#include "chrome/common/webui_url_constants.h"
 #include "components/subresource_filter/core/browser/subresource_filter_constants.h"
 
+namespace {
 // The URL for when the user clicks "Learn more" on the mixed scripting page
 // icon bubble.
 constexpr char kInsecureScriptHelpUrl[] =
@@ -22,10 +24,11 @@
 // permission prompt.
 constexpr char kNotificationsHelpUrl[] =
     "https://support.google.com/chrome/answer/3220216";
+}  // namespace
 
 BrowserContentSettingBubbleModelDelegate::
-    BrowserContentSettingBubbleModelDelegate(Browser* browser)
-    : browser_(browser) {}
+    BrowserContentSettingBubbleModelDelegate(BrowserWindowInterface* browser)
+    : browser_(CHECK_DEREF(browser)) {}
 
 BrowserContentSettingBubbleModelDelegate::
     ~BrowserContentSettingBubbleModelDelegate() = default;
@@ -38,17 +41,19 @@
 void BrowserContentSettingBubbleModelDelegate::ShowMediaSettingsPage() {
   // Microphone and camera settings appear in the content settings menu right
   // next to each other, the microphone section is first.
-  chrome::ShowContentSettings(browser_, ContentSettingsType::MEDIASTREAM_MIC);
+  chrome::ShowContentSettings(browser_->GetBrowserForMigrationOnly(),
+                              ContentSettingsType::MEDIASTREAM_MIC);
 }
 
 void BrowserContentSettingBubbleModelDelegate::ShowContentSettingsPage(
     ContentSettingsType type) {
+  Browser* const browser = browser_->GetBrowserForMigrationOnly();
   if (type == ContentSettingsType::PROTOCOL_HANDLERS) {
-    chrome::ShowSettingsSubPage(browser_, chrome::kHandlerSettingsSubPage);
+    chrome::ShowSettingsSubPage(browser, chrome::kHandlerSettingsSubPage);
   } else if (type == ContentSettingsType::COOKIES) {
-    chrome::ShowSettingsSubPage(browser_, chrome::kCookieSettingsSubPage);
+    chrome::ShowSettingsSubPage(browser, chrome::kCookieSettingsSubPage);
   } else {
-    chrome::ShowContentSettingsExceptions(browser_, type);
+    chrome::ShowContentSettingsExceptions(browser, type);
   }
 }
 
@@ -68,7 +73,6 @@
     default:
       return;
   }
-  DCHECK(!learn_more_url.is_empty());
-  chrome::AddSelectedTabWithURL(browser_, learn_more_url,
-                                ui::PAGE_TRANSITION_LINK);
+  chrome::AddSelectedTabWithURL(browser_->GetBrowserForMigrationOnly(),
+                                learn_more_url, ui::PAGE_TRANSITION_LINK);
 }
diff --git a/chrome/browser/ui/browser_content_setting_bubble_model_delegate.h b/chrome/browser/ui/browser_content_setting_bubble_model_delegate.h
index aba61aa..beca5e1 100644
--- a/chrome/browser/ui/browser_content_setting_bubble_model_delegate.h
+++ b/chrome/browser/ui/browser_content_setting_bubble_model_delegate.h
@@ -5,17 +5,18 @@
 #ifndef CHROME_BROWSER_UI_BROWSER_CONTENT_SETTING_BUBBLE_MODEL_DELEGATE_H_
 #define CHROME_BROWSER_UI_BROWSER_CONTENT_SETTING_BUBBLE_MODEL_DELEGATE_H_
 
-#include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model_delegate.h"
 
-class Browser;
+class BrowserWindowInterface;
 
 // Implementation of ContentSettingBubbleModelDelegate which uses an instance of
 // Browser in order to fulfil its duties.
 class BrowserContentSettingBubbleModelDelegate
     : public ContentSettingBubbleModelDelegate {
  public:
-  explicit BrowserContentSettingBubbleModelDelegate(Browser* browser);
+  explicit BrowserContentSettingBubbleModelDelegate(
+      BrowserWindowInterface* browser);
 
   BrowserContentSettingBubbleModelDelegate(
       const BrowserContentSettingBubbleModelDelegate&) = delete;
@@ -31,7 +32,7 @@
   void ShowLearnMorePage(ContentSettingsType type) override;
 
  private:
-  const raw_ptr<Browser> browser_;
+  const raw_ref<BrowserWindowInterface> browser_;
 };
 
 #endif  // CHROME_BROWSER_UI_BROWSER_CONTENT_SETTING_BUBBLE_MODEL_DELEGATE_H_
diff --git a/chrome/browser/ui/browser_window/internal/BUILD.gn b/chrome/browser/ui/browser_window/internal/BUILD.gn
index 3fd2e1a..7b95402 100644
--- a/chrome/browser/ui/browser_window/internal/BUILD.gn
+++ b/chrome/browser/ui/browser_window/internal/BUILD.gn
@@ -41,6 +41,7 @@
       "//chrome/browser/ui/views/side_panel",
       "//chrome/browser/ui/views/toolbar",
       "//chrome/browser/ui/web_applications",
+      "//components/breadcrumbs/core:status",
       "//components/collaboration/public:public",
       "//components/commerce/core:feature_list",
       "//components/data_sharing/public:public",
diff --git a/chrome/browser/ui/browser_window/internal/browser_window_features.cc b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
index 3132655..465021c 100644
--- a/chrome/browser/ui/browser_window/internal/browser_window_features.cc
+++ b/chrome/browser/ui/browser_window/internal/browser_window_features.cc
@@ -10,6 +10,7 @@
 #include "base/feature_list.h"
 #include "base/memory/ptr_util.h"
 #include "base/no_destructor.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/collaboration/collaboration_service_factory.h"
 #include "chrome/browser/commerce/shopping_service_factory.h"
 #include "chrome/browser/download/bubble/download_bubble_prefs.h"
@@ -21,9 +22,11 @@
 #include "chrome/browser/lens/region_search/lens_region_search_controller.h"
 #include "chrome/browser/media/router/media_router_feature.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/breadcrumb_manager_browser_agent.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_actions.h"
 #include "chrome/browser/ui/browser_command_controller.h"
+#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
 #include "chrome/browser/ui/browser_instant_controller.h"
 #include "chrome/browser/ui/browser_location_bar_model_delegate.h"
 #include "chrome/browser/ui/browser_tab_menu_model_delegate.h"
@@ -82,6 +85,7 @@
 #include "chrome/browser/ui/views/user_education/impl/browser_user_education_interface_impl.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
 #include "chrome/common/chrome_features.h"
+#include "components/breadcrumbs/core/breadcrumbs_status.h"
 #include "components/collaboration/public/collaboration_service.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/feature_utils.h"
@@ -89,6 +93,7 @@
 #include "components/lens/lens_features.h"
 #include "components/omnibox/browser/location_bar_model.h"
 #include "components/omnibox/browser/location_bar_model_impl.h"
+#include "components/prefs/pref_service.h"
 #include "components/profile_metrics/browser_profile_type.h"
 #include "components/saved_tab_groups/public/features.h"
 #include "components/search/ntp_features.h"
@@ -241,6 +246,9 @@
   data_sharing_bubble_controller_ =
       std::make_unique<DataSharingBubbleController>(browser);
 
+  content_setting_bubble_model_delegate_ =
+      std::make_unique<BrowserContentSettingBubbleModelDelegate>(browser);
+
   tab_list_bridge_ = std::make_unique<TabListBridge>(
       *tab_strip_model_, browser->GetUnownedUserDataHost());
 
@@ -251,6 +259,12 @@
           browser->GetTabStripModel(), browser->GetProfile());
 #endif
 
+  if (breadcrumbs::IsEnabled(g_browser_process->local_state())) {
+    breadcrumb_manager_browser_agent_ =
+        std::make_unique<BreadcrumbManagerBrowserAgent>(
+            browser->GetTabStripModel(), browser->GetProfile());
+  }
+
 #if defined(USE_AURA)
   overscroll_pref_manager_ = std::make_unique<OverscrollPrefManager>(
       tab_strip_model_,
diff --git a/chrome/browser/ui/browser_window/public/browser_window_features.h b/chrome/browser/ui/browser_window/public/browser_window_features.h
index e2800c3..1b634ed 100644
--- a/chrome/browser/ui/browser_window/public/browser_window_features.h
+++ b/chrome/browser/ui/browser_window/public/browser_window_features.h
@@ -20,7 +20,9 @@
 #endif
 
 class BookmarksSidePanelCoordinator;
+class BreadcrumbManagerBrowserAgent;
 class Browser;
+class BrowserContentSettingBubbleModelDelegate;
 class BrowserInstantController;
 class BrowserLocationBarModelDelegate;
 class BrowserSyncedWindowDelegate;
@@ -352,6 +354,11 @@
     return upgrade_notification_controller_.get();
   }
 
+  BrowserContentSettingBubbleModelDelegate*
+  content_setting_bubble_model_delegate() {
+    return content_setting_bubble_model_delegate_.get();
+  }
+
   static UserDataFactoryWithOwner<BrowserWindowInterface>&
   GetUserDataFactoryForTesting();
 
@@ -493,11 +500,19 @@
   std::unique_ptr<UpgradeNotificationController>
       upgrade_notification_controller_;
 
+  // Helper which implements the ContentSettingBubbleModel interface.
+  std::unique_ptr<BrowserContentSettingBubbleModelDelegate>
+      content_setting_bubble_model_delegate_;
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
   std::unique_ptr<extensions::ExtensionBrowserWindowHelper>
       extension_browser_window_helper_;
 #endif
 
+  // Listens for browser-related breadcrumb events to be added to crash reports.
+  std::unique_ptr<BreadcrumbManagerBrowserAgent>
+      breadcrumb_manager_browser_agent_;
+
   // TODO(crbug.com/423956131): Remove this.
   raw_ptr<BrowserWindowInterface> browser_ = nullptr;
 
diff --git a/chrome/browser/ui/content_settings/BUILD.gn b/chrome/browser/ui/content_settings/BUILD.gn
index 2f6a188b..07a9e24d 100644
--- a/chrome/browser/ui/content_settings/BUILD.gn
+++ b/chrome/browser/ui/content_settings/BUILD.gn
@@ -88,6 +88,7 @@
     "//chrome/browser/content_settings:content_settings_factory",
     "//chrome/browser/content_settings:content_settings_util",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/browser_window",
     "//chrome/test:test_support",
     "//components/content_settings/core/test:test_support",
     "//components/subresource_filter/core/browser:browser",
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
index 3808e7a..78d8fbd 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/content_settings/fake_owner.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/webui_url_constants.h"
@@ -72,7 +73,8 @@
         ->OnMediaStreamPermissionSet(web_contents->GetLastCommittedURL(),
                                      state);
     return std::make_unique<ContentSettingMediaStreamBubbleModel>(
-        browser()->content_setting_bubble_model_delegate(), web_contents);
+        browser()->GetFeatures().content_setting_bubble_model_delegate(),
+        web_contents);
   }
 
   content::WebContents* GetActiveTab() {
@@ -181,7 +183,8 @@
           {PageSpecificContentSettings::kMicrophoneAccessed});
   std::unique_ptr<ContentSettingBubbleModel> mic_bubble =
       std::make_unique<ContentSettingMediaStreamBubbleModel>(
-          browser()->content_setting_bubble_model_delegate(), web_contents);
+          browser()->GetFeatures().content_setting_bubble_model_delegate(),
+          web_contents);
 
   EXPECT_TRUE(mic_bubble->bubble_content().is_user_modifiable);
 }
@@ -339,7 +342,7 @@
   // Creates the ContentSettingPopupBubbleModel in order to emulate clicks.
   std::unique_ptr<ContentSettingBubbleModel> model(
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
-          browser()->content_setting_bubble_model_delegate(),
+          browser()->GetFeatures().content_setting_bubble_model_delegate(),
           browser()->tab_strip_model()->GetActiveWebContents(),
           ContentSettingsType::POPUPS));
   std::unique_ptr<FakeOwner> owner =
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc
index bc3be16b..7d5f9730 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -119,7 +120,8 @@
 
   auto model = ContentSettingImageModel::CreateForContentType(ImageType::ADS);
   std::unique_ptr<ContentSettingBubbleModel> bubble(model->CreateBubbleModel(
-      browser()->content_setting_bubble_model_delegate(), web_contents));
+      browser()->GetFeatures().content_setting_bubble_model_delegate(),
+      web_contents));
 
   content::TestNavigationObserver observer(nullptr);
   observer.StartWatchingNewWebContents();
diff --git a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
index dd30768..f9bc5ad 100644
--- a/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
+++ b/chrome/browser/ui/content_settings/framebust_block_browsertest.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/blocked_content/framebust_block_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
 #include "chrome/browser/ui/content_settings/fake_owner.h"
@@ -179,7 +180,8 @@
 
   // Simulate clicking on the second blocked URL.
   ContentSettingFramebustBlockBubbleModel framebust_block_bubble_model(
-      browser()->content_setting_bubble_model_delegate(), GetWebContents());
+      browser()->GetFeatures().content_setting_bubble_model_delegate(),
+      GetWebContents());
 
   EXPECT_FALSE(clicked_index_.has_value());
   EXPECT_FALSE(clicked_url_.has_value());
@@ -220,7 +222,8 @@
   // Create a content bubble and simulate clicking on the first radio button
   // before closing it.
   ContentSettingFramebustBlockBubbleModel framebust_block_bubble_model(
-      browser()->content_setting_bubble_model_delegate(), GetWebContents());
+      browser()->GetFeatures().content_setting_bubble_model_delegate(),
+      GetWebContents());
   std::unique_ptr<FakeOwner> owner = FakeOwner::Create(
       framebust_block_bubble_model, kDisallowRadioButtonIndex);
 
@@ -250,7 +253,8 @@
   // Create a content bubble and simulate clicking on the second radio button
   // before closing it.
   ContentSettingFramebustBlockBubbleModel framebust_block_bubble_model(
-      browser()->content_setting_bubble_model_delegate(), GetWebContents());
+      browser()->GetFeatures().content_setting_bubble_model_delegate(),
+      GetWebContents());
 
   std::unique_ptr<FakeOwner> owner =
       FakeOwner::Create(framebust_block_bubble_model, kAllowRadioButtonIndex);
@@ -285,7 +289,8 @@
   // Create a content bubble and simulate clicking on the second radio button
   // before closing it.
   ContentSettingFramebustBlockBubbleModel framebust_block_bubble_model(
-      browser()->content_setting_bubble_model_delegate(), GetWebContents());
+      browser()->GetFeatures().content_setting_bubble_model_delegate(),
+      GetWebContents());
 
   content::TestNavigationObserver navigation_observer(nullptr);
   navigation_observer.StartWatchingNewWebContents();
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
index 3c80819..2074917 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.cc
@@ -126,10 +126,10 @@
   return AppBrowserController::GetTitle();
 }
 
-GURL HostedAppBrowserController::GetAppStartUrl() const {
+const GURL& HostedAppBrowserController::GetAppStartUrl() const {
   const Extension* extension = GetExtension();
   if (!extension) {
-    return GURL();
+    return GURL::EmptyGURL();
   }
 
   return AppLaunchInfo::GetLaunchWebURL(extension);
diff --git a/chrome/browser/ui/extensions/hosted_app_browser_controller.h b/chrome/browser/ui/extensions/hosted_app_browser_controller.h
index 76f94bd..481466e6 100644
--- a/chrome/browser/ui/extensions/hosted_app_browser_controller.h
+++ b/chrome/browser/ui/extensions/hosted_app_browser_controller.h
@@ -43,7 +43,7 @@
   std::u16string GetTitle() const override;
   std::u16string GetAppShortName() const override;
   std::u16string GetFormattedUrlOrigin() const override;
-  GURL GetAppStartUrl() const override;
+  const GURL& GetAppStartUrl() const override;
   bool IsUrlInAppScope(const GURL& url) const override;
   bool CanUserUninstall() const override;
   void Uninstall(
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
index e18e16e..c944d559 100644
--- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/picture_in_picture/picture_in_picture_window_manager.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/content_settings/content_setting_image_model_states.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -1121,7 +1122,7 @@
 PictureInPictureBrowserFrameView::GetContentSettingBubbleModelDelegate() {
   // Use the opener browser delegate to open any new tab.
   Browser* browser = chrome::FindBrowserWithTab(GetWebContents());
-  return browser->content_setting_bubble_model_delegate();
+  return browser->GetFeatures().content_setting_bubble_model_delegate();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 625b68e..dcdc1d5c9 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -715,7 +715,7 @@
 
 ContentSettingBubbleModelDelegate*
 ToolbarView::GetContentSettingBubbleModelDelegate() {
-  return browser_->content_setting_bubble_model_delegate();
+  return browser_->GetFeatures().content_setting_bubble_model_delegate();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
index 605476d..f37f288 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/browser_actions.h"
 #include "chrome/browser/ui/browser_command_controller.h"
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
@@ -418,7 +419,9 @@
 
 ContentSettingBubbleModelDelegate*
 WebAppToolbarButtonContainer::GetContentSettingBubbleModelDelegate() {
-  return browser_view_->browser()->content_setting_bubble_model_delegate();
+  return browser_view_->browser()
+      ->GetFeatures()
+      .content_setting_bubble_model_delegate();
 }
 
 // ImmersiveModeController::Observer:
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.cc b/chrome/browser/ui/web_applications/app_browser_controller.cc
index 6f3ded2..7efe2a7b 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/app_browser_controller.cc
@@ -582,7 +582,7 @@
   return std::string();
 }
 
-GURL AppBrowserController::GetAppNewTabUrl() const {
+const GURL& AppBrowserController::GetAppNewTabUrl() const {
   return GetAppStartUrl();
 }
 
diff --git a/chrome/browser/ui/web_applications/app_browser_controller.h b/chrome/browser/ui/web_applications/app_browser_controller.h
index 6cdee5aa..2959c5a 100644
--- a/chrome/browser/ui/web_applications/app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/app_browser_controller.h
@@ -178,10 +178,10 @@
   virtual std::u16string GetFormattedUrlOrigin() const = 0;
 
   // Gets the start_url for the app.
-  virtual GURL GetAppStartUrl() const = 0;
+  virtual const GURL& GetAppStartUrl() const = 0;
 
   // Gets the new tab URL for tabbed apps.
-  virtual GURL GetAppNewTabUrl() const;
+  virtual const GURL& GetAppNewTabUrl() const;
 
   // Returns the pinned home tab if there is one, otherwise nullptr.
   virtual content::WebContents* GetPinnedHomeTab() const;
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.cc b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
index da90bc2..c6b63657 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.cc
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.cc
@@ -446,11 +446,11 @@
   return result;
 }
 
-GURL WebAppBrowserController::GetAppStartUrl() const {
+const GURL& WebAppBrowserController::GetAppStartUrl() const {
   return registrar().GetAppStartUrl(app_id());
 }
 
-GURL WebAppBrowserController::GetAppNewTabUrl() const {
+const GURL& WebAppBrowserController::GetAppNewTabUrl() const {
   return registrar().GetAppNewTabUrl(app_id());
 }
 
diff --git a/chrome/browser/ui/web_applications/web_app_browser_controller.h b/chrome/browser/ui/web_applications/web_app_browser_controller.h
index 484ad20..26c3312 100644
--- a/chrome/browser/ui/web_applications/web_app_browser_controller.h
+++ b/chrome/browser/ui/web_applications/web_app_browser_controller.h
@@ -82,8 +82,8 @@
   std::u16string GetTitle() const override;
   std::u16string GetAppShortName() const override;
   std::u16string GetFormattedUrlOrigin() const override;
-  GURL GetAppStartUrl() const override;
-  GURL GetAppNewTabUrl() const override;
+  const GURL& GetAppStartUrl() const override;
+  const GURL& GetAppNewTabUrl() const override;
   content::WebContents* GetPinnedHomeTab() const override;
   bool ShouldHideNewTabButton() const override;
   bool IsUrlInHomeTabScope(const GURL& url) const override;
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index 49ddce72..07cf68a9 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -338,7 +338,7 @@
     return management_to_external_config_map_;
   }
 
-  const std::optional<blink::Manifest::TabStrip> tab_strip() const {
+  const std::optional<blink::Manifest::TabStrip>& tab_strip() const {
     return tab_strip_;
   }
 
diff --git a/chrome/browser/web_applications/web_app_registrar.cc b/chrome/browser/web_applications/web_app_registrar.cc
index ece6be0..83952a8f 100644
--- a/chrome/browser/web_applications/web_app_registrar.cc
+++ b/chrome/browser/web_applications/web_app_registrar.cc
@@ -669,15 +669,17 @@
   return GetAppEffectiveDisplayMode(app_id) == DisplayMode::kTabbed;
 }
 
-GURL WebAppRegistrar::GetAppNewTabUrl(const webapps::AppId& app_id) const {
+const GURL& WebAppRegistrar::GetAppNewTabUrl(
+    const webapps::AppId& app_id) const {
   if (IsTabbedWindowModeEnabled(app_id)) {
     auto* web_app = GetAppById(app_id);
     if (!web_app) {
-      return GURL();
+      return GURL::EmptyGURL();
     }
 
     if (web_app->tab_strip()) {
-      std::optional<GURL> url = web_app->tab_strip().value().new_tab_button.url;
+      const std::optional<GURL>& url =
+          web_app->tab_strip().value().new_tab_button.url;
       if (url.has_value()) {
         return url.value();
       }
diff --git a/chrome/browser/web_applications/web_app_registrar.h b/chrome/browser/web_applications/web_app_registrar.h
index 428d391..dd2a4aa7 100644
--- a/chrome/browser/web_applications/web_app_registrar.h
+++ b/chrome/browser/web_applications/web_app_registrar.h
@@ -431,7 +431,7 @@
   // Returns whether the app should be opened in tabbed window mode.
   bool IsTabbedWindowModeEnabled(const webapps::AppId& app_id) const;
 
-  GURL GetAppNewTabUrl(const webapps::AppId& app_id) const;
+  const GURL& GetAppNewTabUrl(const webapps::AppId& app_id) const;
 
   // Returns the URL of the pinned home tab for tabbed apps which have this
   // enabled, otherwise returns nullopt.
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 81ad39d7..9d443b6 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1751781337-0be4dfa4b257432a9e23105192fa199111d5e88d-f18179f52616f5556495f5aa39908219927061ce.profdata
+chrome-android32-main-1751867715-f2d20a7ae26cc3b90d19a19a08ac554d57e0f44e-d363bce624961fb87bb7bfb4cefa2009cc8cdc4d.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 10d057f..e7076f5d 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1751815143-c9aa9a9fd64fa4b66afd731323f8a93cf4bf4680-0c60d4b9bc565a2ea1ff92793d15571fdfc81f2f.profdata
+chrome-android64-main-1751863636-cd1421396c136d17f9ef24af2a2e67ef41c2cb34-6eaf85807a9a0749fd99b2351c7d097d447b195d.profdata
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index 521fffeb..c59c99d 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1751750903-688e1f54140647f8d121ac0017eb390a42e54e09-78d16a76c61263d49053b89f8f43c36f785cfaa8.profdata
+chrome-android-desktop-x64-main-1751855501-b1870e4303e855160a73e5f64ef9b062beb699cd-9ad267ad0f148357206f2eb1f7acd357c4d2a416.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index a58013b..d7282d24 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1751799075-a47cd1b32cbcc1f40006aff66da531b2f8d1aec0-50c2093adcc75bfb8dd9bcdfe7e7422caa86c933.profdata
+chrome-linux-main-1751867715-3d91b7eefeee3021e661ea454247ecbaada557d1-d363bce624961fb87bb7bfb4cefa2009cc8cdc4d.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index b2d5c6dc..4a8b0e19 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1751815143-b658a69ade49fa035fdd9fcfc07d5c5dcc2674f2-0c60d4b9bc565a2ea1ff92793d15571fdfc81f2f.profdata
+chrome-mac-arm-main-1751867715-63505c034783fed05e670031f6d9e3562d61f8e3-d363bce624961fb87bb7bfb4cefa2009cc8cdc4d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index b7723db4..bfab790 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1751799075-21c4055ab8697f032544fc2126bfc5da68365030-50c2093adcc75bfb8dd9bcdfe7e7422caa86c933.profdata
+chrome-mac-main-1751846036-0bd4039fee6b263cfa761fe8b429daa1e50a0af9-5037e5d1e0f66103399f58e8d8d4b42068d2018d.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 5e06edc..e3fd61ae 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1751799075-c1dd1ce97ad1f3cf239025d8180a122d63f1aaef-50c2093adcc75bfb8dd9bcdfe7e7422caa86c933.profdata
+chrome-win-arm64-main-1751846036-14521a118307fefcbd7c0de7c10fd7786984f164-5037e5d1e0f66103399f58e8d8d4b42068d2018d.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 3471da7..fd2cb122 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1751781337-0d161790993f6dff85e9737cd44440530d33a199-f18179f52616f5556495f5aa39908219927061ce.profdata
+chrome-win32-main-1751846036-244bbaeb75b0346b780763671e87e0515a78c0fe-5037e5d1e0f66103399f58e8d8d4b42068d2018d.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 7b7021d..9bf94ea4 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1751799075-178e9356995fc0cc442bca85c2f050a81945912c-50c2093adcc75bfb8dd9bcdfe7e7422caa86c933.profdata
+chrome-win64-main-1751846036-63af2534cd868953766271060869920fe92152b9-5037e5d1e0f66103399f58e8d8d4b42068d2018d.profdata
diff --git a/clank b/clank
index 0942c90..0b19c12 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 0942c904bb5a624a6b2a10f0550ff9d35b419504
+Subproject commit 0b19c12de7a158deb75f357e88759d445a9d7721
diff --git a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
index 820c4d41..a57437d 100644
--- a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
+++ b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.cc
@@ -68,36 +68,34 @@
   return dafs;
 }
 
-// Finds the first field in |form_structure| with |field.value|=|value|.
-AutofillField* FindFirstFieldWithValue(const FormStructure& form_structure,
-                                       std::u16string_view value) {
-  for (const auto& field : form_structure) {
-    std::u16string trimmed_value;
-    base::TrimWhitespace(field->value_for_import(), base::TRIM_ALL,
-                         &trimmed_value);
-    if (trimmed_value == value) {
-      return field.get();
+// Adds `CREDIT_CARD_VERIFICATION_CODE` to the possible types of fields whose
+// value is `last_unlocked_credit_card_cvc` or looks like a CVC.
+//
+// In the former case, `kKnownValue` is added to the property mask.
+// TODO(crbug.com/429655113): Do we need this? If not, remove.
+void FindAndSetPossibleCvcFieldTypes(
+    std::u16string_view last_unlocked_credit_card_cvc,
+    const FormStructure& form_structure,
+    base::span<PossibleTypes> possible_types) {
+  if (!last_unlocked_credit_card_cvc.empty()) {
+    for (auto [field, pt] :
+         base::zip(form_structure.fields(), possible_types)) {
+      if (last_unlocked_credit_card_cvc ==
+          base::TrimWhitespace(field->value_for_import(), base::TRIM_ALL)) {
+        pt.types.insert(CREDIT_CARD_VERIFICATION_CODE);
+        pt.known_value = true;
+        return;
+      }
     }
   }
-  return nullptr;
-}
-
-// Heuristically identifies all possible credit card verification fields.
-AutofillField* HeuristicallyFindCVCFieldForUpload(
-    const FormStructure& form_structure) {
-  // Stores a pointer to the explicitly found expiration year.
-  bool found_explicit_expiration_year_field = false;
 
   // The first pass checks the existence of an explicitly marked field for the
   // credit card expiration year.
-  for (const auto& field : form_structure) {
-    const FieldTypeSet& type_set = field->possible_types();
-    if (type_set.find(CREDIT_CARD_EXP_2_DIGIT_YEAR) != type_set.end() ||
-        type_set.find(CREDIT_CARD_EXP_4_DIGIT_YEAR) != type_set.end()) {
-      found_explicit_expiration_year_field = true;
-      break;
-    }
-  }
+  const bool found_explicit_expiration_year_field =
+      std::ranges::any_of(possible_types, [](const PossibleTypes& pt) {
+        return pt.types.contains_any(
+            {CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_EXP_4_DIGIT_YEAR});
+      });
 
   // Keeps track if a credit card number field was found.
   bool credit_card_number_found = false;
@@ -109,59 +107,33 @@
   // * it does not look like an expiration year or an expiration year was
   //   already found;
   // * it is filled with a 3-4 digit number;
-  for (const auto& field : form_structure) {
-    const FieldTypeSet& type_set = field->possible_types();
-
-    // Checks if the field is of |CREDIT_CARD_NUMBER| type.
-    if (type_set.find(CREDIT_CARD_NUMBER) != type_set.end()) {
+  for (auto [field, pt] : base::zip(form_structure.fields(), possible_types)) {
+    if (pt.types.contains(CREDIT_CARD_NUMBER)) {
       credit_card_number_found = true;
       continue;
     }
-    // Skip the field if no credit card number was found yet.
     if (!credit_card_number_found) {
       continue;
     }
-
-    // Don't consider fields that already have any prediction.
-    if (!type_set.empty()) {
+    if (!pt.types.empty()) {
       continue;
     }
 
-    std::u16string trimmed_value;
-    base::TrimWhitespace(field->value_for_import(), base::TRIM_ALL,
-                         &trimmed_value);
+    const std::u16string& value = field->value_for_import();
+    const std::u16string_view trimmed_value =
+        base::TrimWhitespace(value, base::TRIM_ALL);
 
     // Skip the field if it can be confused with a expiration year.
     if (!found_explicit_expiration_year_field &&
         IsPlausible4DigitExpirationYear(trimmed_value)) {
       continue;
     }
-
-    // Skip the field if its value does not like a CVC value.
     if (!IsPlausibleCreditCardCVCNumber(trimmed_value)) {
       continue;
     }
 
-    return field.get();
+    pt.types.insert(CREDIT_CARD_VERIFICATION_CODE);
   }
-  return nullptr;
-}
-
-// Iff the CVC of the credit card is known, find the first field with this
-// value (also set |properties_mask| to |kKnownValue|). Otherwise, heuristically
-// search for the CVC field if any.
-AutofillField* GetBestPossibleCVCFieldForUpload(
-    const FormStructure& form_structure,
-    std::u16string_view last_unlocked_credit_card_cvc) {
-  if (!last_unlocked_credit_card_cvc.empty()) {
-    AutofillField* result =
-        FindFirstFieldWithValue(form_structure, last_unlocked_credit_card_cvc);
-    if (result) {
-      result->set_properties_mask(FieldPropertiesFlags::kKnownValue);
-    }
-    return result;
-  }
-  return HeuristicallyFindCVCFieldForUpload(form_structure);
 }
 
 // Returns the FieldTypes for some given EntityInstance defines a non-empty
@@ -243,12 +215,14 @@
     base::span<const EntityInstance> entities,
     const std::map<FieldGlobalId, DatesAndFormats>& dates_and_formats,
     const std::string& app_locale,
-    FormStructure& form) {
-  std::map<data_util::Date, std::vector<AutofillField*>> date_to_field;
-  for (const auto& [field_id, dafs] : dates_and_formats) {
-    for (const data_util::Date& date : dafs.dates) {
-      if (AutofillField* field = form.GetFieldById(field_id)) {
-        date_to_field[date].push_back(field);
+    const FormStructure& form,
+    base::span<PossibleTypes> possible_types) {
+  std::map<data_util::Date, std::vector<size_t>> date_to_field_indices;
+  for (size_t i = 0; i < form.field_count(); ++i) {
+    if (auto it = dates_and_formats.find(form.field(i)->global_id());
+        it != dates_and_formats.end()) {
+      for (const data_util::Date& date : it->second.dates) {
+        date_to_field_indices[date].push_back(i);
       }
     }
   }
@@ -262,11 +236,10 @@
         data_util::Date date;
         if (data_util::ParseDate(attribute.GetCompleteInfo(app_locale),
                                  u"YYYY-MM-DD", date)) {
-          if (auto it = date_to_field.find(date); it != date_to_field.end()) {
-            for (AutofillField* field : it->second) {
-              FieldTypeSet field_types = field->possible_types();
-              field_types.insert(field_type);
-              field->set_possible_types(field_types);
+          if (auto it = date_to_field_indices.find(date);
+              it != date_to_field_indices.end()) {
+            for (size_t field_index : it->second) {
+              possible_types[field_index].types.insert(field_type);
             }
           }
         }
@@ -321,6 +294,11 @@
 
 }  // namespace
 
+PossibleTypes::PossibleTypes() = default;
+PossibleTypes::PossibleTypes(PossibleTypes&&) = default;
+PossibleTypes& PossibleTypes::operator=(PossibleTypes&&) = default;
+PossibleTypes::~PossibleTypes() = default;
+
 DatesAndFormats::DatesAndFormats() = default;
 DatesAndFormats::DatesAndFormats(base::flat_set<data_util::Date> dates,
                                  base::flat_set<std::u16string> formats)
@@ -366,7 +344,7 @@
   return fields_that_match_state;
 }
 
-void DeterminePossibleFieldTypesForUpload(
+std::vector<PossibleTypes> DeterminePossibleFieldTypesForUpload(
     base::span<const AutofillProfile> profiles,
     base::span<const CreditCard> credit_cards,
     base::span<const EntityInstance> entities,
@@ -375,34 +353,33 @@
     std::u16string_view last_unlocked_credit_card_cvc,
     const std::map<FieldGlobalId, DatesAndFormats>& dates_and_formats,
     const std::string& app_locale,
-    FormStructure& form) {
+    const FormStructure& form) {
+  std::vector<PossibleTypes> possible_types;
+  possible_types.resize(form.fields().size());
+
   // Most type detection happens in this loop.
-  for (const std::unique_ptr<AutofillField>& field : form.fields()) {
-    field->set_possible_types(GetPossibleFieldTypes(
-        *field, profiles, credit_cards, entities, loyalty_cards,
-        fields_that_match_state, app_locale));
+  for (auto [field, types] : base::zip(form.fields(), possible_types)) {
+    types.types = GetPossibleFieldTypes(*field, profiles, credit_cards,
+                                        entities, loyalty_cards,
+                                        fields_that_match_state, app_locale);
   }
 
   // Date detection is not part of the above loop because dates can span
   // multiple fields.
   FindAndSetPossibleDateFieldTypes(entities, dates_and_formats, app_locale,
-                                   form);
+                                   form, possible_types);
 
   // As CVCs are not stored, run special heuristics to detect CVC-like values.
-  if (AutofillField* cvc_field = GetBestPossibleCVCFieldForUpload(
-          form, last_unlocked_credit_card_cvc)) {
-    FieldTypeSet possible_types = cvc_field->possible_types();
-    possible_types.insert(CREDIT_CARD_VERIFICATION_CODE);
-    cvc_field->set_possible_types(possible_types);
-  }
+  FindAndSetPossibleCvcFieldTypes(last_unlocked_credit_card_cvc, form,
+                                  possible_types);
 
-  for (const std::unique_ptr<AutofillField>& field : form.fields()) {
-    if (field->possible_types().empty()) {
-      field->set_possible_types({UNKNOWN_TYPE});
+  for (auto [field, pt] : base::zip(form.fields(), possible_types)) {
+    if (pt.types.empty()) {
+      pt.types = {UNKNOWN_TYPE};
     }
   }
 
-  DisambiguatePossibleFieldTypes(form);
+  return DisambiguatePossibleFieldTypes(form, std::move(possible_types));
 }
 
 FieldTypeSet DetermineAvailableFieldTypes(
diff --git a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.h b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.h
index fadf005..fd8563d 100644
--- a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.h
+++ b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types.h
@@ -25,6 +25,24 @@
 class FormStructure;
 class LoyaltyCard;
 
+// The result of DeterminePossibleFieldTypesForUpload() for a specific
+// AutofillField.
+struct PossibleTypes {
+  PossibleTypes();
+  PossibleTypes(const PossibleTypes&) = delete;
+  PossibleTypes& operator=(const PossibleTypes&) = delete;
+  PossibleTypes(PossibleTypes&&);
+  PossibleTypes& operator=(PossibleTypes&&);
+  ~PossibleTypes();
+
+  // The FieldTypes for which data on file matches the field's value.
+  FieldTypeSet types;
+
+  // Indicates if the value is a known CVC value.
+  // TODO(crbug.com/429655113): Do we need this? If not, remove.
+  bool known_value = false;
+};
+
 // Note that the `dates` and `formats` are not aligned (i.e., do not base::zip()
 // them!). They may even be of distinct size (see Example 2 of
 // ExtractDatesInFields()).
@@ -76,12 +94,12 @@
     const std::string& app_locale);
 
 // Determines the `FieldType`s for which profiles etc. define non-empty
-// values. The result is stored in FormStructure::possible_types().
+// values.
 //
 // This is potentially expensive -- on the order of 50ms even for a small set of
 // `stored_data`. Hence, it should not run on the UI thread -- to avoid
 // locking up the UI -- nor on the IO thread -- to avoid blocking IPC calls.
-void DeterminePossibleFieldTypesForUpload(
+[[nodiscard]] std::vector<PossibleTypes> DeterminePossibleFieldTypesForUpload(
     base::span<const AutofillProfile> profiles,
     base::span<const CreditCard> credit_cards,
     base::span<const EntityInstance> entities,
@@ -90,7 +108,7 @@
     std::u16string_view last_unlocked_credit_card_cvc,
     const std::map<FieldGlobalId, DatesAndFormats>& dates_and_formats,
     const std::string& app_locale,
-    FormStructure& form);
+    const FormStructure& form);
 
 // Returns the set of `FieldType`s for which the given profiles etc. contain
 // non-empty values.
diff --git a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc
index 71c2441a..07f5c2d 100644
--- a/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc
+++ b/components/autofill/core/browser/crowdsourcing/determine_possible_field_types_unittest.cc
@@ -31,6 +31,7 @@
 using ::autofill::test::CreateTestFormField;
 using ::autofill::test::CreateTestSelectField;
 using ::testing::Contains;
+using ::testing::Each;
 using ::testing::ElementsAre;
 using ::testing::Field;
 using ::testing::IsEmpty;
@@ -56,30 +57,21 @@
 }
 
 void CheckThatOnlyFieldByIndexHasThisPossibleType(
-    const FormStructure& form_structure,
+    base::span<const PossibleTypes> possible_types,
     size_t field_index,
     FieldType type,
-    FieldPropertiesMask mask) {
-  EXPECT_TRUE(field_index < form_structure.field_count());
-
-  for (size_t i = 0; i < form_structure.field_count(); i++) {
+    bool known_value) {
+  EXPECT_LT(field_index, possible_types.size());
+  for (size_t i = 0; i < possible_types.size(); i++) {
     if (i == field_index) {
-      EXPECT_THAT(form_structure.field(i)->possible_types(), ElementsAre(type));
-      EXPECT_EQ(mask, form_structure.field(i)->properties_mask());
+      EXPECT_THAT(possible_types[i].types, ElementsAre(type)) << "i=" << i;
+      EXPECT_EQ(possible_types[i].known_value, known_value) << "i=" << i;
     } else {
-      EXPECT_THAT(form_structure.field(i)->possible_types(),
-                  Not(Contains(type)));
+      EXPECT_THAT(possible_types[i].types, Not(Contains(type))) << "i=" << i;
     }
   }
 }
 
-void CheckThatNoFieldHasThisPossibleType(const FormStructure& form_structure,
-                                         FieldType type) {
-  for (size_t i = 0; i < form_structure.field_count(); i++) {
-    EXPECT_THAT(form_structure.field(i)->possible_types(), Not(Contains(type)));
-  }
-}
-
 struct TestAddressFillData {
   TestAddressFillData(const char* first,
                       const char* middle,
@@ -310,13 +302,10 @@
   test::SetProfileInfo(&profiles[3], "Vincent", "Wilhelm", "van Gogh", "NL");
   profiles[3].set_guid(MakeGuid(4));
 
-  // Set up the test credit cards.
-  std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Doe", "4234-5678-9012-3456", "04",
                           "2999", "1");
   credit_card.set_guid(MakeGuid(3));
-  credit_cards.push_back(credit_card);
 
   FormData form;
   form.set_name(u"MyForm");
@@ -328,17 +317,17 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  DeterminePossibleFieldTypesForUpload(
-      profiles, credit_cards, std::vector<EntityInstance>(),
-      std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/u"", /*dates_and_formats=*/{}, "en-us",
-      *form_structure);
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          profiles, {credit_card}, std::vector<EntityInstance>(),
+          std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/u"", /*dates_and_formats=*/{},
+          "en-us", *form_structure);
 
-  ASSERT_EQ(1U, form_structure->field_count());
-
-  FieldTypeSet possible_types = form_structure->field(0)->possible_types();
-  EXPECT_EQ(possible_types, expected_possible_types);
+  ASSERT_EQ(form_structure->field_count(), possible_types.size());
+  EXPECT_THAT(possible_types[0].types,
+              UnorderedElementsAreArray(expected_possible_types));
 }
 
 INSTANTIATE_TEST_SUITE_P(DeterminePossibleFieldTypesForUploadTest,
@@ -365,9 +354,6 @@
 // BrowserAutofillManager reuses the CVC value to identify a potentially
 // existing CVC form field to cast a |CREDIT_CARD_VERIFICATION_CODE|-type vote.
 TEST_F(DeterminePossibleFieldTypesForUploadTest, CrowdsourceCVCFieldByValue) {
-  std::vector<AutofillProfile> profiles;
-  std::vector<CreditCard> credit_cards;
-
   constexpr char kCvc[] = "1234";
   constexpr char16_t kCvc16[] = u"1234";
   constexpr char kFourDigitButNotCvc[] = "6676";
@@ -388,18 +374,18 @@
 
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
-  form_structure->field(0)->set_possible_types({CREDIT_CARD_NUMBER});
 
-  DeterminePossibleFieldTypesForUpload(
-      profiles, credit_cards, std::vector<EntityInstance>(),
-      std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/kCvc16, /*dates_and_formats=*/{},
-      "en-us", *form_structure);
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), std::vector<CreditCard>(),
+          std::vector<EntityInstance>(), std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/kCvc16, /*dates_and_formats=*/{},
+          "en-us", *form_structure);
 
-  CheckThatOnlyFieldByIndexHasThisPossibleType(
-      *form_structure, 2, CREDIT_CARD_VERIFICATION_CODE,
-      FieldPropertiesFlags::kKnownValue);
+  CheckThatOnlyFieldByIndexHasThisPossibleType(possible_types, 2,
+                                               CREDIT_CARD_VERIFICATION_CODE,
+                                               /*known_value=*/true);
 }
 
 // Expiration year field was detected by the server. The other field with a
@@ -426,32 +412,22 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  // Set the field types.
-  form_structure->field(0)->set_possible_types({CREDIT_CARD_NUMBER});
-  form_structure->field(1)->set_possible_types({CREDIT_CARD_EXP_4_DIGIT_YEAR});
-  form_structure->field(2)->set_possible_types({UNKNOWN_TYPE});
-
-  // Set up the test credit cards.
-  std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Doe", credit_card_number, "04",
                           actual_credit_card_exp_year, "1");
   credit_card.set_guid(MakeGuid(3));
-  credit_cards.push_back(credit_card);
 
-  // Set up the test profiles.
-  std::vector<AutofillProfile> profiles;
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), {credit_card},
+          std::vector<EntityInstance>(), std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/std::u16string(),
+          /*dates_and_formats=*/{}, "en-us", *form_structure);
 
-  DeterminePossibleFieldTypesForUpload(
-      profiles, credit_cards, std::vector<EntityInstance>(),
-      std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/std::u16string(),
-      /*dates_and_formats=*/{}, "en-us", *form_structure);
-
-  CheckThatOnlyFieldByIndexHasThisPossibleType(*form_structure, 2,
+  CheckThatOnlyFieldByIndexHasThisPossibleType(possible_types, 2,
                                                CREDIT_CARD_VERIFICATION_CODE,
-                                               FieldPropertiesFlags::kNoFlags);
+                                               /*known_value=*/false);
 }
 
 // Tests if the CVC field is heuristically detected if it appears after the
@@ -478,32 +454,22 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  // Set the field types.
-  form_structure->field(0)->set_possible_types({CREDIT_CARD_NUMBER});
-  form_structure->field(1)->set_possible_types({CREDIT_CARD_EXP_4_DIGIT_YEAR});
-  form_structure->field(2)->set_possible_types({UNKNOWN_TYPE});
-
-  // Set up the test credit cards.
-  std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Doe", credit_card_number, "04",
                           actual_credit_card_exp_year, "1");
   credit_card.set_guid(MakeGuid(3));
-  credit_cards.push_back(credit_card);
 
-  // Set up the test profiles.
-  std::vector<AutofillProfile> profiles;
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), {credit_card},
+          std::vector<EntityInstance>(), std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/std::u16string(),
+          /*dates_and_formats=*/{}, "en-us", *form_structure);
 
-  DeterminePossibleFieldTypesForUpload(
-      profiles, credit_cards, std::vector<EntityInstance>(),
-      std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/std::u16string(),
-      /*dates_and_formats=*/{}, "en-us", *form_structure);
-
-  CheckThatOnlyFieldByIndexHasThisPossibleType(*form_structure, 2,
+  CheckThatOnlyFieldByIndexHasThisPossibleType(possible_types, 2,
                                                CREDIT_CARD_VERIFICATION_CODE,
-                                               FieldPropertiesFlags::kNoFlags);
+                                               /*known_value=*/false);
 }
 
 // Tests if the CVC field is heuristically detected if it contains a value which
@@ -529,32 +495,22 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  // Set the field types.
-  form_structure->field(0)->set_possible_types({CREDIT_CARD_NUMBER});
-  form_structure->field(1)->set_possible_types({UNKNOWN_TYPE});
-  form_structure->field(2)->set_possible_types({UNKNOWN_TYPE});
-
-  // Set up the test credit cards.
-  std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Doe", credit_card_number, "04",
                           actual_credit_card_exp_year, "1");
   credit_card.set_guid(MakeGuid(3));
-  credit_cards.push_back(credit_card);
 
-  // Set up the test profiles.
-  std::vector<AutofillProfile> profiles;
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), {credit_card},
+          std::vector<EntityInstance>(), std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/std::u16string(),
+          /*dates_and_formats=*/{}, "en-us", *form_structure);
 
-  DeterminePossibleFieldTypesForUpload(
-      profiles, credit_cards, std::vector<EntityInstance>(),
-      std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/std::u16string(),
-      /*dates_and_formats=*/{}, "en-us", *form_structure);
-
-  CheckThatOnlyFieldByIndexHasThisPossibleType(*form_structure, 1,
+  CheckThatOnlyFieldByIndexHasThisPossibleType(possible_types, 1,
                                                CREDIT_CARD_VERIFICATION_CODE,
-                                               FieldPropertiesFlags::kNoFlags);
+                                               /*known_value=*/false);
 }
 
 // Tests if no CVC field is heuristically detected due to the missing of a
@@ -580,30 +536,21 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  // Set the field types.
-  form_structure->field(0)->set_possible_types({UNKNOWN_TYPE});
-  form_structure->field(1)->set_possible_types({CREDIT_CARD_EXP_4_DIGIT_YEAR});
-  form_structure->field(2)->set_possible_types({UNKNOWN_TYPE});
-
-  // Set up the test credit cards.
-  std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Doe", credit_card_number, "04",
                           actual_credit_card_exp_year, "1");
   credit_card.set_guid(MakeGuid(3));
-  credit_cards.push_back(credit_card);
 
-  // Set up the test profiles.
-  std::vector<AutofillProfile> profiles;
-
-  DeterminePossibleFieldTypesForUpload(
-      profiles, credit_cards, std::vector<EntityInstance>(),
-      std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/std::u16string(),
-      /*dates_and_formats=*/{}, "en-us", *form_structure);
-  CheckThatNoFieldHasThisPossibleType(*form_structure,
-                                      CREDIT_CARD_VERIFICATION_CODE);
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), {credit_card},
+          std::vector<EntityInstance>(), std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/std::u16string(),
+          /*dates_and_formats=*/{}, "en-us", *form_structure);
+  EXPECT_THAT(possible_types,
+              Each(Field(&PossibleTypes::types,
+                         Not(Contains(CREDIT_CARD_VERIFICATION_CODE)))));
 }
 
 // Test if no CVC is found because the candidate has no valid CVC value.
@@ -627,32 +574,21 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  // Set the field types.
-  form_structure->field(0)->set_possible_types(
-      {CREDIT_CARD_NUMBER, UNKNOWN_TYPE});
-  form_structure->field(1)->set_possible_types({CREDIT_CARD_EXP_4_DIGIT_YEAR});
-  form_structure->field(2)->set_possible_types({UNKNOWN_TYPE});
-
-  // Set up the test credit cards.
-  std::vector<CreditCard> credit_cards;
   CreditCard credit_card;
   test::SetCreditCardInfo(&credit_card, "John Doe", credit_card_number, "04",
                           credit_card_exp_year, "1");
   credit_card.set_guid(MakeGuid(3));
-  credit_cards.push_back(credit_card);
 
-  // Set up the test profiles.
-  std::vector<AutofillProfile> profiles;
-
-  DeterminePossibleFieldTypesForUpload(
-      profiles, credit_cards, std::vector<EntityInstance>(),
-      std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/u"", /*dates_and_formats=*/{}, "en-us",
-      *form_structure);
-
-  CheckThatNoFieldHasThisPossibleType(*form_structure,
-                                      CREDIT_CARD_VERIFICATION_CODE);
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), {credit_card},
+          std::vector<EntityInstance>(), std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/u"", /*dates_and_formats=*/{},
+          "en-us", *form_structure);
+  EXPECT_THAT(possible_types,
+              Each(Field(&PossibleTypes::types,
+                         Not(Contains(CREDIT_CARD_VERIFICATION_CODE)))));
 }
 
 // Tests if the loyalty card field detected.
@@ -672,24 +608,19 @@
   std::unique_ptr<FormStructure> form_structure =
       ConstructFormStructureFromFormData(form);
 
-  // Set the field types.
-  form_structure->field(0)->set_possible_types({LOYALTY_MEMBERSHIP_PROGRAM});
-
-  // Set up the test loyalty cards.
-  std::vector<LoyaltyCard> loyalty_cards;
   LoyaltyCard loyalty_card = test::CreateLoyaltyCard();
   loyalty_card.set_loyalty_card_number(loyalty_card_number);
-  loyalty_cards.push_back(loyalty_card);
-  DeterminePossibleFieldTypesForUpload(
-      std::vector<AutofillProfile>(), std::vector<CreditCard>(),
-      std::vector<EntityInstance>(), loyalty_cards,
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/u"", /*dates_and_formats=*/{}, "en-us",
-      *form_structure);
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), std::vector<CreditCard>(),
+          std::vector<EntityInstance>(), {loyalty_card},
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/u"", /*dates_and_formats=*/{},
+          "en-us", *form_structure);
 
-  CheckThatOnlyFieldByIndexHasThisPossibleType(*form_structure, 1,
+  CheckThatOnlyFieldByIndexHasThisPossibleType(possible_types, 1,
                                                LOYALTY_MEMBERSHIP_ID,
-                                               FieldPropertiesFlags::kNoFlags);
+                                               /*known_value=*/false);
 }
 
 // Tests if the Autofill AI field types are crowdsourced.
@@ -724,29 +655,28 @@
       .issue_date = u"2010-09-01",
   });
 
-  DeterminePossibleFieldTypesForUpload(
-      std::vector<AutofillProfile>(), std::vector<CreditCard>(),
-      base::span_from_ref(entity), std::vector<LoyaltyCard>(),
-      /*fields_that_match_state=*/{},
-      /*last_unlocked_credit_card_cvc=*/u"",
-      ExtractDatesInFields(form_structure->fields()), "en-US", *form_structure);
+  std::vector<PossibleTypes> possible_types =
+      DeterminePossibleFieldTypesForUpload(
+          std::vector<AutofillProfile>(), std::vector<CreditCard>(),
+          base::span_from_ref(entity), std::vector<LoyaltyCard>(),
+          /*fields_that_match_state=*/{},
+          /*last_unlocked_credit_card_cvc=*/u"",
+          ExtractDatesInFields(form_structure->fields()), "en-US",
+          *form_structure);
 
-  EXPECT_THAT(form_structure->fields()[0]->possible_types(),
-              UnorderedElementsAre(NAME_FIRST));
-  EXPECT_THAT(form_structure->fields()[1]->possible_types(),
+  EXPECT_THAT(possible_types[0].types, UnorderedElementsAre(NAME_FIRST));
+  EXPECT_THAT(possible_types[1].types,
               UnorderedElementsAre(NAME_LAST, NAME_LAST_SECOND));
-  EXPECT_THAT(form_structure->fields()[2]->possible_types(),
-              UnorderedElementsAre(PASSPORT_NUMBER));
-  EXPECT_THAT(form_structure->fields()[3]->possible_types(),
+  EXPECT_THAT(possible_types[2].types, UnorderedElementsAre(PASSPORT_NUMBER));
+  EXPECT_THAT(possible_types[3].types,
               UnorderedElementsAre(PASSPORT_EXPIRATION_DATE));
-  EXPECT_THAT(form_structure->fields()[4]->possible_types(),
+  EXPECT_THAT(possible_types[4].types,
               UnorderedElementsAre(PASSPORT_ISSUE_DATE));
-  EXPECT_THAT(form_structure->fields()[5]->possible_types(),
+  EXPECT_THAT(possible_types[5].types,
               UnorderedElementsAre(PASSPORT_ISSUE_DATE));
-  EXPECT_THAT(form_structure->fields()[6]->possible_types(),
+  EXPECT_THAT(possible_types[6].types,
               UnorderedElementsAre(PASSPORT_ISSUE_DATE));
-  EXPECT_THAT(form_structure->fields()[7]->possible_types(),
-              UnorderedElementsAre(UNKNOWN_TYPE));
+  EXPECT_THAT(possible_types[7].types, UnorderedElementsAre(UNKNOWN_TYPE));
 }
 
 // Test fixture for PreProcessStateMatchingTypes().
diff --git a/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.cc b/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.cc
index 492d71c..b114bbe 100644
--- a/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.cc
+++ b/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.cc
@@ -5,7 +5,9 @@
 #include "components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.h"
 
 #include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/crowdsourcing/determine_possible_field_types.h"
 #include "components/autofill/core/browser/field_type_utils.h"
+#include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/common/autofill_features.h"
 
@@ -13,11 +15,6 @@
 
 namespace {
 
-// Returns `true` if `field` contains multiple possible types.
-bool MayHaveAmbiguousPossibleTypes(const AutofillField& field) {
-  return field.possible_types().size() > 1;
-}
-
 // Returns whether the `field` is predicted as being any kind of name.
 bool IsNameType(const AutofillField& field) {
   return field.Type().group() == FieldTypeGroup::kName ||
@@ -31,28 +28,31 @@
 // address name types are discarded. If `is_credit_card` is false, the opposite
 // happens. This is called when we have multiple possible name types from mixed
 // field type groups.
-void SelectProbableNameTypes(AutofillField& field,
-                             bool select_credit_card_names) {
+[[nodiscard]] FieldTypeSet SelectProbableNameTypes(
+    FieldTypeSet possible_types,
+    bool select_credit_card_names) {
   FieldTypeSet types_to_keep;
-  for (FieldType type : field.possible_types()) {
-    FieldTypeGroup group = GroupTypeOfFieldType(type);
-    if ((select_credit_card_names && group == FieldTypeGroup::kCreditCard) ||
-        (!select_credit_card_names && group == FieldTypeGroup::kName)) {
+  for (FieldType type : possible_types) {
+    if (GroupTypeOfFieldType(type) == (select_credit_card_names
+                                           ? FieldTypeGroup::kCreditCard
+                                           : FieldTypeGroup::kName)) {
       types_to_keep.insert(type);
     }
   }
-
-  field.set_possible_types(types_to_keep);
+  return types_to_keep;
 }
 
 // If a field was autofilled on form submission and the value was accepted, set
 // possible types to the autofilled type.
-void SetPossibleTypesToAutofilledTypeIfAvailable(AutofillField& field) {
+[[nodiscard]] FieldTypeSet OverridePossibleTypesToAutofilledTypeIfAvailable(
+    const AutofillField& field,
+    FieldTypeSet possible_types) {
   if (field.is_autofilled() && field.autofilled_type() &&
       base::FeatureList::IsEnabled(
           features::kAutofillDisambiguateContradictingFieldTypes)) {
-    field.set_possible_types({*field.autofilled_type()});
+    return {*field.autofilled_type()};
   }
+  return possible_types;
 }
 
 // Disambiguates name types from mixed field type groups when the name exists in
@@ -61,22 +61,22 @@
 // Note that generally a name field can legitimately have multiple types
 // according to the types tree structure, e.g. `NAME_FULL`, `NAME_LAST` and
 // `NAME_LAST_{FIRST,SECOND}` at the same time.
-void MaybeDisambiguateNameTypes(FormStructure& form,
-                                size_t current_field_index) {
+[[nodiscard]] FieldTypeSet MaybeDisambiguateNameTypes(
+    const FormStructure& form,
+    size_t current_field_index,
+    FieldTypeSet possible_types) {
   // Disambiguation is only applicable if there is a mixture of one or more
   // address related name types ('one or more' because e.g. NAME_LAST and
   // NAME_LAST_SECOND can be identical) and exactly one credit card related name
   // type ('exactly one' because credit cards names only support a single last
   // name).
-  AutofillField& field = *form.field(current_field_index);
-  const size_t credit_card_type_count =
-      NumberOfPossibleFieldTypesInGroup(field, FieldTypeGroup::kCreditCard);
-  const size_t name_type_count =
-      NumberOfPossibleFieldTypesInGroup(field, FieldTypeGroup::kName);
-  if (field.possible_types().size() !=
-          (credit_card_type_count + name_type_count) ||
+  const size_t credit_card_type_count = std::ranges::count(
+      possible_types, FieldTypeGroup::kCreditCard, GroupTypeOfFieldType);
+  const size_t name_type_count = std::ranges::count(
+      possible_types, FieldTypeGroup::kName, GroupTypeOfFieldType);
+  if (possible_types.size() != credit_card_type_count + name_type_count ||
       credit_card_type_count != 1 || name_type_count == 0) {
-    return;
+    return possible_types;
   }
 
   // This case happens when both a profile and a credit card have the same
@@ -96,11 +96,11 @@
   size_t index = current_field_index;
   while (index != 0 && !has_found_previous_type) {
     --index;
-    AutofillField* prev_field = form.field(index);
-    if (!IsNameType(*prev_field)) {
+    const AutofillField& prev_field = *form.field(index);
+    if (!IsNameType(prev_field)) {
       has_found_previous_type = true;
       is_previous_credit_card =
-          prev_field->Type().group() == FieldTypeGroup::kCreditCard;
+          prev_field.Type().group() == FieldTypeGroup::kCreditCard;
     }
   }
 
@@ -109,11 +109,11 @@
   bool is_next_credit_card = false;
   index = current_field_index;
   while (++index < form.field_count() && !has_found_next_type) {
-    AutofillField* next_field = form.field(index);
-    if (!IsNameType(*next_field)) {
+    const AutofillField& next_field = *form.field(index);
+    if (!IsNameType(next_field)) {
       has_found_next_type = true;
       is_next_credit_card =
-          next_field->Type().group() == FieldTypeGroup::kCreditCard;
+          next_field.Type().group() == FieldTypeGroup::kCreditCard;
     }
   }
 
@@ -124,37 +124,39 @@
     // name group there is no sure way to disambiguate.
     if (has_found_previous_type && has_found_next_type &&
         (is_previous_credit_card != is_next_credit_card)) {
-      return;
+      return possible_types;
     }
 
     // Otherwise, use the previous (if it was found) or next field group to
     // decide whether the field is a name or a credit card name.
-    if (has_found_previous_type) {
-      SelectProbableNameTypes(field, is_previous_credit_card);
-    } else {
-      SelectProbableNameTypes(field, is_next_credit_card);
-    }
+    possible_types = SelectProbableNameTypes(
+        possible_types, has_found_previous_type ? is_previous_credit_card
+                                                : is_next_credit_card);
   }
+  return possible_types;
 }
 
 }  // namespace
 
-void DisambiguatePossibleFieldTypes(FormStructure& form) {
+std::vector<PossibleTypes> DisambiguatePossibleFieldTypes(
+    const FormStructure& form,
+    std::vector<PossibleTypes> possible_types) {
   for (size_t i = 0; i < form.field_count(); ++i) {
-    AutofillField& field = *form.field(i);
-    if (!MayHaveAmbiguousPossibleTypes(field)) {
+    if (possible_types[i].types.size() <= 1) {
       continue;
     }
     // Setting possible types to an autofilled value that was accepted by the
     // user should have the highest priority among disambiguation methods
     // because it represents user intent the most.
-    SetPossibleTypesToAutofilledTypeIfAvailable(field);
-
-    if (!MayHaveAmbiguousPossibleTypes(field)) {
+    possible_types[i].types = OverridePossibleTypesToAutofilledTypeIfAvailable(
+        *form.field(i), possible_types[i].types);
+    if (possible_types[i].types.size() <= 1) {
       continue;
     }
-    MaybeDisambiguateNameTypes(form, i);
+    possible_types[i].types =
+        MaybeDisambiguateNameTypes(form, i, possible_types[i].types);
   }
+  return possible_types;
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.h b/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.h
index 5395f3c..10b5e667 100644
--- a/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.h
+++ b/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types.h
@@ -5,16 +5,23 @@
 #ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_CROWDSOURCING_DISAMBIGUATE_POSSIBLE_FIELD_TYPES_H_
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_CROWDSOURCING_DISAMBIGUATE_POSSIBLE_FIELD_TYPES_H_
 
+#include <vector>
+
+#include "base/containers/span.h"
+
 namespace autofill {
 
 class FormStructure;
+struct PossibleTypes;
 
 // Applies several heuristics to select the most probable types for fields with
 // ambiguous possible types. Heuristics are run in order of priority which is
 // based on reflecting user intent the most.
 // Note that the case where a single-line street address is ambiguous to address
 // line 1 is handled on the server.
-void DisambiguatePossibleFieldTypes(FormStructure& form);
+std::vector<PossibleTypes> DisambiguatePossibleFieldTypes(
+    const FormStructure& form,
+    std::vector<PossibleTypes> possible_types);
 
 }  // namespace autofill
 
diff --git a/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types_unittest.cc b/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types_unittest.cc
index 56f5968e..f882a58 100644
--- a/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types_unittest.cc
+++ b/components/autofill/core/browser/crowdsourcing/disambiguate_possible_field_types_unittest.cc
@@ -6,8 +6,10 @@
 
 #include <algorithm>
 
+#include "base/containers/to_vector.h"
 #include "base/types/zip.h"
 #include "components/autofill/core/browser/autofill_field.h"
+#include "components/autofill/core/browser/crowdsourcing/determine_possible_field_types.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/form_data.h"
@@ -16,18 +18,12 @@
 
 namespace autofill {
 
-using test::CreateTestFormField;
 using ::testing::ElementsAre;
 using ::testing::UnorderedElementsAre;
 
 // Tests that `DisambiguatePossibleFieldTypes` makes the correct choices.
 class DisambiguatePossibleFieldTypesTest : public ::testing::Test {
  protected:
-  DisambiguatePossibleFieldTypesTest() {
-    feature_list_.InitAndEnableFeature(
-        features::kAutofillDisambiguateContradictingFieldTypes);
-  }
-
   struct TestFieldData {
     FieldType predicted_type;
     FieldTypeSet ambiguous_possible_field_types;
@@ -41,35 +37,31 @@
     FormData form;
     for (size_t i = 0; i < test_fields.size(); ++i) {
       test_api(form).Append(
-          CreateTestFormField("", "", "", FormControlType::kInputText));
+          test::CreateTestFormField("", "", "", FormControlType::kInputText));
     }
+
     FormStructure form_structure(form);
-    for (auto [field, test_field] :
-         base::zip(form_structure.fields(), test_fields)) {
-      field->set_possible_types(test_field.ambiguous_possible_field_types);
+    std::vector<PossibleTypes> possible_types(form_structure.fields().size());
+    for (auto [test_field, field, pt] :
+         base::zip(test_fields, form_structure.fields(), possible_types)) {
       field->set_server_predictions(
-          {::autofill::test::CreateFieldPrediction(test_field.predicted_type)});
+          {test::CreateFieldPrediction(test_field.predicted_type)});
       if (test_field.is_autofilled) {
         field->set_autofilled_type(test_field.predicted_type);
         field->set_is_autofilled(true);
       }
+      pt.types = test_field.ambiguous_possible_field_types;
     }
 
-    DisambiguatePossibleFieldTypes(form_structure);
-
-    std::vector<FieldTypeSet> disambiguated_possible_field_types;
-    std::ranges::transform(
-        form_structure.fields(),
-        std::back_inserter(disambiguated_possible_field_types),
-        [](const std::unique_ptr<AutofillField>& field) {
-          return field->possible_types();
-        });
-    return disambiguated_possible_field_types;
+    return base::ToVector(DisambiguatePossibleFieldTypes(
+                              form_structure, std::move(possible_types)),
+                          &PossibleTypes::types);
   }
 
- protected:
+ private:
   test::AutofillUnitTestEnvironment autofill_test_environment_;
-  base::test::ScopedFeatureList feature_list_;
+  base::test::ScopedFeatureList feature_list_{
+      features::kAutofillDisambiguateContradictingFieldTypes};
 };
 
 // Name disambiguation.
@@ -82,11 +74,7 @@
       {CREDIT_CARD_NAME_FIRST, {NAME_FIRST, CREDIT_CARD_NAME_FIRST}},
       {CREDIT_CARD_NAME_LAST,
        {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}}};
-
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
-  EXPECT_THAT(kDisambiguatedPossibleFieldTypes,
+  EXPECT_THAT(GetDisambiguatedPossibleFieldTypes(kTestFields),
               ElementsAre(UnorderedElementsAre(ADDRESS_HOME_CITY),
                           UnorderedElementsAre(NAME_FIRST),
                           UnorderedElementsAre(NAME_LAST, NAME_LAST_SECOND)));
@@ -101,11 +89,7 @@
       {CREDIT_CARD_NAME_FIRST, {NAME_FIRST, CREDIT_CARD_NAME_FIRST}},
       {CREDIT_CARD_NAME_LAST,
        {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}}};
-
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
-  EXPECT_THAT(kDisambiguatedPossibleFieldTypes,
+  EXPECT_THAT(GetDisambiguatedPossibleFieldTypes(kTestFields),
               ElementsAre(UnorderedElementsAre(CREDIT_CARD_NUMBER),
                           UnorderedElementsAre(CREDIT_CARD_NAME_FIRST),
                           UnorderedElementsAre(CREDIT_CARD_NAME_LAST)));
@@ -120,11 +104,7 @@
       {CREDIT_CARD_NAME_LAST,
        {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}},
       {ADDRESS_HOME_CITY, {ADDRESS_HOME_CITY}}};
-
-  const std::vector<FieldTypeSet> disambiguated_possible_field_types =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
-  EXPECT_THAT(disambiguated_possible_field_types,
+  EXPECT_THAT(GetDisambiguatedPossibleFieldTypes(kTestFields),
               ElementsAre(UnorderedElementsAre(NAME_FIRST),
                           UnorderedElementsAre(NAME_LAST, NAME_LAST_SECOND),
                           UnorderedElementsAre(ADDRESS_HOME_CITY)));
@@ -138,11 +118,7 @@
       {NAME_FIRST, {NAME_FIRST, CREDIT_CARD_NAME_FIRST}},
       {NAME_LAST, {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}},
       {CREDIT_CARD_NUMBER, {CREDIT_CARD_NUMBER}}};
-
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
-  EXPECT_THAT(kDisambiguatedPossibleFieldTypes,
+  EXPECT_THAT(GetDisambiguatedPossibleFieldTypes(kTestFields),
               ElementsAre(UnorderedElementsAre(CREDIT_CARD_NAME_FIRST),
                           UnorderedElementsAre(CREDIT_CARD_NAME_LAST),
                           UnorderedElementsAre(CREDIT_CARD_NUMBER)));
@@ -158,11 +134,7 @@
       {CREDIT_CARD_NAME_LAST,
        {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}},
       {ADDRESS_HOME_STATE, {ADDRESS_HOME_STATE}}};
-
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
-  EXPECT_THAT(kDisambiguatedPossibleFieldTypes,
+  EXPECT_THAT(GetDisambiguatedPossibleFieldTypes(kTestFields),
               ElementsAre(UnorderedElementsAre(ADDRESS_HOME_CITY),
                           UnorderedElementsAre(NAME_FIRST),
                           UnorderedElementsAre(NAME_LAST, NAME_LAST_SECOND),
@@ -178,11 +150,7 @@
       {NAME_FIRST, {NAME_FIRST, CREDIT_CARD_NAME_FIRST}},
       {NAME_LAST, {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}},
       {CREDIT_CARD_EXP_4_DIGIT_YEAR, {CREDIT_CARD_EXP_4_DIGIT_YEAR}}};
-
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
-  EXPECT_THAT(kDisambiguatedPossibleFieldTypes,
+  EXPECT_THAT(GetDisambiguatedPossibleFieldTypes(kTestFields),
               ElementsAre(UnorderedElementsAre(CREDIT_CARD_NUMBER),
                           UnorderedElementsAre(CREDIT_CARD_NAME_FIRST),
                           UnorderedElementsAre(CREDIT_CARD_NAME_LAST),
@@ -199,11 +167,8 @@
       {NAME_LAST, {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}},
       {CREDIT_CARD_EXP_4_DIGIT_YEAR, {CREDIT_CARD_EXP_4_DIGIT_YEAR}}};
 
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
   EXPECT_THAT(
-      kDisambiguatedPossibleFieldTypes,
+      GetDisambiguatedPossibleFieldTypes(kTestFields),
       ElementsAre(UnorderedElementsAre(ADDRESS_HOME_CITY),
                   UnorderedElementsAre(NAME_FIRST, CREDIT_CARD_NAME_FIRST),
                   UnorderedElementsAre(NAME_LAST, CREDIT_CARD_NAME_LAST,
@@ -220,12 +185,8 @@
       {NAME_FIRST, {NAME_FIRST, CREDIT_CARD_NAME_FIRST}},
       {NAME_LAST, {NAME_LAST, CREDIT_CARD_NAME_LAST, NAME_LAST_SECOND}},
       {ADDRESS_HOME_CITY, {ADDRESS_HOME_CITY}}};
-
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
   EXPECT_THAT(
-      kDisambiguatedPossibleFieldTypes,
+      GetDisambiguatedPossibleFieldTypes(kTestFields),
       ElementsAre(UnorderedElementsAre(CREDIT_CARD_EXP_4_DIGIT_YEAR),
                   UnorderedElementsAre(NAME_FIRST, CREDIT_CARD_NAME_FIRST),
                   UnorderedElementsAre(NAME_LAST, CREDIT_CARD_NAME_LAST,
@@ -239,11 +200,7 @@
       {ADDRESS_HOME_LINE1,
        {CREDIT_CARD_EXP_4_DIGIT_YEAR, NAME_FULL, COMPANY_NAME},
        true}};
-
-  const std::vector<FieldTypeSet> kDisambiguatedPossibleFieldTypes =
-      GetDisambiguatedPossibleFieldTypes(kTestFields);
-
-  EXPECT_THAT(kDisambiguatedPossibleFieldTypes,
+  EXPECT_THAT(GetDisambiguatedPossibleFieldTypes(kTestFields),
               ElementsAre(UnorderedElementsAre(ADDRESS_HOME_LINE1)));
 }
 
diff --git a/components/autofill/core/browser/crowdsourcing/votes_uploader.cc b/components/autofill/core/browser/crowdsourcing/votes_uploader.cc
index c601578..2331236 100644
--- a/components/autofill/core/browser/crowdsourcing/votes_uploader.cc
+++ b/components/autofill/core/browser/crowdsourcing/votes_uploader.cc
@@ -9,6 +9,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/types/zip.h"
 #include "components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.h"
 #include "components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_manager.h"
 #include "components/autofill/core/browser/crowdsourcing/determine_possible_field_types.h"
@@ -263,10 +264,19 @@
                 dates_and_formats_by_field =
                     ExtractDatesInFields(form->fields());
 
-            DeterminePossibleFieldTypesForUpload(
-                profiles, credit_cards, entities, loyalty_cards,
-                fields_that_match_state, last_unlocked_credit_card_cvc,
-                dates_and_formats_by_field, app_locale, *form);
+            std::vector<PossibleTypes> possible_types =
+                DeterminePossibleFieldTypesForUpload(
+                    profiles, credit_cards, entities, loyalty_cards,
+                    fields_that_match_state, last_unlocked_credit_card_cvc,
+                    dates_and_formats_by_field, app_locale, *form);
+
+            for (auto [field, pt] : base::zip(form->fields(), possible_types)) {
+              field->set_possible_types(pt.types);
+              if (pt.known_value) {
+                field->set_properties_mask(field->properties_mask() |
+                                           FieldPropertiesFlags::kKnownValue);
+              }
+            }
 
             EncodeUploadRequestOptions options;
             options.encoder = std::move(randomized_encoder);
diff --git a/components/autofill/core/browser/field_type_utils.cc b/components/autofill/core/browser/field_type_utils.cc
index 04f7511d..1934c82 100644
--- a/components/autofill/core/browser/field_type_utils.cc
+++ b/components/autofill/core/browser/field_type_utils.cc
@@ -12,12 +12,6 @@
 
 namespace autofill {
 
-size_t NumberOfPossibleFieldTypesInGroup(const AutofillField& field,
-                                         FieldTypeGroup group) {
-  return std::ranges::count(field.possible_types(), group,
-                            GroupTypeOfFieldType);
-}
-
 bool FieldHasMeaningfulPossibleFieldTypes(const AutofillField& field) {
   // This function should only be invoked when the possible types have been
   // determined.
diff --git a/components/autofill/core/browser/field_type_utils.h b/components/autofill/core/browser/field_type_utils.h
index 94dc1f7..0ae36c3f 100644
--- a/components/autofill/core/browser/field_type_utils.h
+++ b/components/autofill/core/browser/field_type_utils.h
@@ -17,11 +17,6 @@
 // determined.
 bool FieldHasMeaningfulPossibleFieldTypes(const AutofillField& field);
 
-// Returns the number of possible field types (type votes) of a `field` that are
-// in a specific `group`.
-size_t NumberOfPossibleFieldTypesInGroup(const AutofillField& field,
-                                         FieldTypeGroup group);
-
 // Returns true if the type of `field` is a possible type.
 bool TypeOfFieldIsPossibleType(const AutofillField& field);
 
diff --git a/components/autofill/core/browser/field_type_utils_unittest.cc b/components/autofill/core/browser/field_type_utils_unittest.cc
index 2c4748a3..4603103 100644
--- a/components/autofill/core/browser/field_type_utils_unittest.cc
+++ b/components/autofill/core/browser/field_type_utils_unittest.cc
@@ -12,24 +12,6 @@
 
 namespace {
 
-TEST(AutofillFieldTypeUtils, NumberOfPossibleTypesInGroup) {
-  AutofillField field;
-  field.set_possible_types({NAME_FIRST, NAME_LAST, CREDIT_CARD_NAME_FIRST});
-
-  EXPECT_EQ(NumberOfPossibleFieldTypesInGroup(field, FieldTypeGroup::kName),
-            2U);
-
-  EXPECT_EQ(
-      NumberOfPossibleFieldTypesInGroup(field, FieldTypeGroup::kCreditCard),
-      1U);
-
-  EXPECT_EQ(NumberOfPossibleFieldTypesInGroup(field, FieldTypeGroup::kAddress),
-            0U);
-
-  EXPECT_EQ(NumberOfPossibleFieldTypesInGroup(field, FieldTypeGroup::kPhone),
-            0U);
-}
-
 TEST(AutofillFieldTypeUtils, FieldHasMeaningfulFieldTypes) {
   AutofillField field;
 
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index 37e9f4b..a8b6dc6 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit 37e9f4b495cdbb05bd7ec76311668998b6053bf9
+Subproject commit a8b6dc620e36af6428e9953791c2b66ebd6b3641
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 16de85a38..81733f95 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -2773,11 +2773,12 @@
       hdr_metadata.ndwl = gfx::HdrMetadataNdwl(
           current_frame()->display_color_spaces.GetSDRMaxLuminanceNits());
     }
+    // TODO(https://crbug.com/428575083): Change this to use log2 based
+    // headroom.
     cc::ToneMapUtil::AddGlobalToneMapFilterToPaint(
         paint, image, hdr_metadata,
-        quad->dynamic_range_limit.ComputeHdrHeadroom(
-            current_frame()
-                ->display_color_spaces.GetHDRMaxLuminanceRelative()));
+        std::exp2(quad->dynamic_range_limit.ComputeEffectiveHdrHeadroom(
+            current_frame()->display_color_spaces.GetHdrHeadroom())));
   }
 
   // From gl_renderer, the final src color will be
diff --git a/content/browser/preloading/prerender/prerender_attributes.cc b/content/browser/preloading/prerender/prerender_attributes.cc
index cb43cf7..59daf4be 100644
--- a/content/browser/preloading/prerender/prerender_attributes.cc
+++ b/content/browser/preloading/prerender/prerender_attributes.cc
@@ -36,7 +36,8 @@
         url_match_predicate,
     base::RepeatingCallback<void(NavigationHandle&)>
         prerender_navigation_handle_callback,
-    scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info)
+    scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info,
+    bool allow_reuse)
     : prerendering_url(prerendering_url),
       trigger_type(trigger_type),
       embedder_histogram_suffix(embedder_histogram_suffix),
@@ -50,7 +51,8 @@
       url_match_predicate(std::move(url_match_predicate)),
       prerender_navigation_handle_callback(
           std::move(prerender_navigation_handle_callback)),
-      preload_pipeline_info(std::move(preload_pipeline_info)) {
+      preload_pipeline_info(std::move(preload_pipeline_info)),
+      allow_reuse(allow_reuse) {
   if (initiator_render_frame_host) {
     initiator_origin = initiator_render_frame_host->GetLastCommittedOrigin();
     initiator_process_id =
diff --git a/content/browser/preloading/prerender/prerender_attributes.h b/content/browser/preloading/prerender/prerender_attributes.h
index 96014f60..88d10db 100644
--- a/content/browser/preloading/prerender/prerender_attributes.h
+++ b/content/browser/preloading/prerender/prerender_attributes.h
@@ -55,7 +55,8 @@
           url_match_predicate,
       base::RepeatingCallback<void(NavigationHandle&)>
           prerender_navigation_handle_callback,
-      scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info);
+      scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info,
+      bool allow_reuse);
 
   ~PrerenderAttributes();
 
@@ -144,6 +145,9 @@
   // Information of preload pipeline that this prerender belongs to.
   scoped_refptr<PreloadPipelineInfoImpl> preload_pipeline_info;
 
+  // Whether the created prerender host can be reused for future navigations.
+  bool allow_reuse = false;
+
   // This is std::nullopt when prerendering is initiated by the browser.
   std::optional<base::UnguessableToken> initiator_devtools_navigation_token;
 
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index e1fde5e..2328a567 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -512,7 +512,8 @@
             /*planned_max_preloading_type=*/PreloadingType::kPrerender),
         preloading_attempt,
         /*url_match_predicate=*/{},
-        /*prerender_navigation_handle_callback=*/{});
+        /*prerender_navigation_handle_callback=*/{},
+        /*allow_reuse=*/false);
   }
 
   bool AddTestUtilJS(RenderFrameHost* host) {
@@ -12388,7 +12389,8 @@
           PreloadPipelineInfo::Create(
               /*planned_max_preloading_type=*/PreloadingType::kPrerender),
           /*preloading_attempt=*/nullptr, /*url_match_predicate=*/{},
-          /*prerender_navigation_handle_callback=*/{});
+          /*prerender_navigation_handle_callback=*/{},
+          /*allow_reuse=*/false);
   EXPECT_TRUE(prerender_handle);
   test::PrerenderTestHelper::WaitForPrerenderLoadCompletion(web_contents,
                                                             prerendering_url);
diff --git a/content/browser/preloading/prerender/prerender_host_registry_unittest.cc b/content/browser/preloading/prerender/prerender_host_registry_unittest.cc
index 33edc9c5..5fa79d7 100644
--- a/content/browser/preloading/prerender/prerender_host_registry_unittest.cc
+++ b/content/browser/preloading/prerender/prerender_host_registry_unittest.cc
@@ -211,7 +211,8 @@
             /*url_match_predicate=*/{},
             /*prerender_navigation_handle_callback=*/{},
             PreloadPipelineInfoImpl::Create(
-                /*planned_max_preloading_type=*/PreloadingType::kPrerender));
+                /*planned_max_preloading_type=*/PreloadingType::kPrerender),
+            /*allow_reuse=*/false);
       case PreloadingTriggerType::kEmbedder:
         return PrerenderAttributes(
             url, trigger_type, embedder_histogram_suffix,
@@ -225,7 +226,8 @@
             /*url_match_predicate=*/{},
             /*prerender_navigation_handle_callback=*/{},
             PreloadPipelineInfoImpl::Create(
-                /*planned_max_preloading_type=*/PreloadingType::kPrerender));
+                /*planned_max_preloading_type=*/PreloadingType::kPrerender),
+            /*allow_reuse=*/false);
     }
   }
 
diff --git a/content/browser/preloading/prerender/prerender_host_unittest.cc b/content/browser/preloading/prerender/prerender_host_unittest.cc
index 40edb29..5112e69 100644
--- a/content/browser/preloading/prerender/prerender_host_unittest.cc
+++ b/content/browser/preloading/prerender/prerender_host_unittest.cc
@@ -284,7 +284,8 @@
         /*should_prepare_paint_tree=*/false, std::move(url_match_predicate),
         /*prerender_navigation_handle_callback=*/{},
         PreloadPipelineInfoImpl::Create(
-            /*planned_max_preloading_type=*/PreloadingType::kPrerender));
+            /*planned_max_preloading_type=*/PreloadingType::kPrerender),
+        /*allow_reuse=*/false);
   }
 
   void ExpectFinalStatus(PrerenderFinalStatus status) {
diff --git a/content/browser/preloading/prerenderer_impl.cc b/content/browser/preloading/prerenderer_impl.cc
index 26c55b5..e63628a 100644
--- a/content/browser/preloading/prerenderer_impl.cc
+++ b/content/browser/preloading/prerenderer_impl.cc
@@ -372,7 +372,8 @@
       /*url_match_predicate=*/{},
       /*prerender_navigation_handle_callback=*/{},
       PreloadPipelineInfoImpl::Create(
-          /*planned_max_preloading_type=*/PreloadingType::kPrerender));
+          /*planned_max_preloading_type=*/PreloadingType::kPrerender),
+      /*allow_reuse=*/false);
 
   PreloadingTriggerType trigger_type =
       PreloadingTriggerTypeFromSpeculationInjectionType(
diff --git a/content/browser/renderer_host/back_forward_cache_metrics.cc b/content/browser/renderer_host/back_forward_cache_metrics.cc
index cd265b01..117bb10 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics.cc
+++ b/content/browser/renderer_host/back_forward_cache_metrics.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/renderer_host/back_forward_cache_metrics.h"
 
+#include "base/debug/alias.h"
 #include "base/debug/crash_logging.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/metrics/histogram_functions.h"
@@ -231,6 +232,7 @@
                  page_store_result_->ToString());
     RecordHistoryNavigationUMA(navigation, back_forward_cache_allowed);
     RecordHistoryNavigationUKM(navigation);
+    SCOPED_CRASH_KEY_BOOL("crbug/427426299", "pstr", !!page_store_tree_result_);
     if (!navigation->IsServedFromBackForwardCache()) {
       devtools_instrumentation::BackForwardCacheNotUsed(
           navigation, page_store_result_.get(), page_store_tree_result_.get());
@@ -241,10 +243,12 @@
           std::move(page_store_tree_result_));
     }
   }
+  SCOPED_CRASH_KEY_BOOL("crbug/427426299", "before_gni", true);
   // Save the information about the last cross-document main frame navigation
   // that uses this metrics object.
   last_committed_cross_document_main_frame_navigation_id_ =
       navigation->GetNavigationId();
+  SCOPED_CRASH_KEY_BOOL("crbug/427426299", "after_gni", true);
 
   // BackForwardCacheMetrics can be reused in some cases. Reset fields for UKM
   // for the next navigation.
diff --git a/content/browser/renderer_host/back_forward_cache_metrics.h b/content/browser/renderer_host/back_forward_cache_metrics.h
index 040ac7d..28ad3a1 100644
--- a/content/browser/renderer_host/back_forward_cache_metrics.h
+++ b/content/browser/renderer_host/back_forward_cache_metrics.h
@@ -139,8 +139,10 @@
   // Notifies that an associated entry has committed a navigation.
   // |back_forward_cache_allowed| indicates whether back-forward cache is
   // allowed for the URL of |navigation_request|.
-  void DidCommitNavigation(NavigationRequest* navigation_request,
-                           bool back_forward_cache_allowed);
+  // TODO(https://crbug.com/427426299): Remove these annotations.
+  NOINLINE NOT_TAIL_CALLED void DidCommitNavigation(
+      NavigationRequest* navigation_request,
+      bool back_forward_cache_allowed);
 
   // Records when another navigation commits away from the most recent entry
   // associated with |this|.  This is the point in time that the previous
diff --git a/content/browser/renderer_host/page_lifecycle_state_manager.cc b/content/browser/renderer_host/page_lifecycle_state_manager.cc
index c5613ed01c..2441c3d 100644
--- a/content/browser/renderer_host/page_lifecycle_state_manager.cc
+++ b/content/browser/renderer_host/page_lifecycle_state_manager.cc
@@ -10,6 +10,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/state_transitions.h"
+#include "base/strings/stringprintf.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
@@ -98,7 +99,8 @@
 }
 
 void PageLifecycleStateManager::SetBackForwardCacheEntered(
-    BackForwardCacheEntered entered) {
+    BackForwardCacheEntered entered,
+    char context_for_bug_427316606) {
   static const base::NoDestructor<
       base::StateTransitions<BackForwardCacheEntered>>
       transitions(base::StateTransitions<BackForwardCacheEntered>({
@@ -109,17 +111,9 @@
       }));
   // TODO(https://crbug.com/427316606): Remove this when we understand how this
   // transition sometimes occurs.
-  switch (entered) {
-    case BackForwardCacheEntered::kNo:
-      back_forward_cache_state_counts_.no++;
-      break;
-    case BackForwardCacheEntered::kEntering:
-      back_forward_cache_state_counts_.entering++;
-      break;
-    case BackForwardCacheEntered::kEntered:
-      back_forward_cache_state_counts_.entered++;
-      break;
-  }
+  back_forward_cache_state_tracker_.append(
+      base::StringPrintf("%c%d", context_for_bug_427316606, entered));
+
   if (back_forward_cache_entered_ == BackForwardCacheEntered::kNo &&
       entered == BackForwardCacheEntered::kEntered) {
     DumpWithoutCrashForBug427316606();
@@ -130,12 +124,8 @@
 }
 
 void PageLifecycleStateManager::DumpWithoutCrashForBug427316606() {
-  SCOPED_CRASH_KEY_NUMBER("bfcache", "count_no",
-                          back_forward_cache_state_counts_.no);
-  SCOPED_CRASH_KEY_NUMBER("bfcache", "count_entering",
-                          back_forward_cache_state_counts_.entering);
-  SCOPED_CRASH_KEY_NUMBER("bfcache", "count_entered",
-                          back_forward_cache_state_counts_.entered);
+  SCOPED_CRASH_KEY_STRING64("bfcache", "tracker",
+                            back_forward_cache_state_tracker_);
   base::debug::DumpWithoutCrashing();
 }
 
@@ -151,7 +141,7 @@
          !last_acknowledged_state_->eviction_enabled);
   eviction_enabled_ = is_in_back_forward_cache;
   if (is_in_back_forward_cache) {
-    SetBackForwardCacheEntered(BackForwardCacheEntered::kEntering);
+    SetBackForwardCacheEntered(BackForwardCacheEntered::kEntering, 'S');
     // When a page is put into BackForwardCache, the page can run a busy loop.
     // Set a timeout monitor to check that the transition finishes within the
     // time limit.
@@ -165,7 +155,7 @@
     // When a page is restored from the back-forward cache, we should reset this
     // state so that it behaves correctly next time navigation occurs.
     pagehide_dispatch_ = blink::mojom::PagehideDispatch::kNotDispatched;
-    SetBackForwardCacheEntered(BackForwardCacheEntered::kNo);
+    SetBackForwardCacheEntered(BackForwardCacheEntered::kNo, 'S');
   }
 
   SendUpdatesToRendererIfNeeded(std::move(page_restore_params),
@@ -283,7 +273,10 @@
       back_forward_cache_entered_ != BackForwardCacheEntered::kEntered) {
     SCOPED_CRASH_KEY_NUMBER("bfcache", "current_entered_",
                             static_cast<int>(back_forward_cache_entered_));
-    SetBackForwardCacheEntered(BackForwardCacheEntered::kEntered);
+    SCOPED_CRASH_KEY_NUMBER("bfcache", "splsr",
+                            set_page_lifecycle_state_response);
+    SetBackForwardCacheEntered(BackForwardCacheEntered::kEntered,
+                               set_page_lifecycle_state_response ? 'R' : 'C');
     // TODO(https://crbug.com/427316606): Remove this.
     // This should only happen during a response to SetPageLifecycleState.
     if (!set_page_lifecycle_state_response) {
diff --git a/content/browser/renderer_host/page_lifecycle_state_manager.h b/content/browser/renderer_host/page_lifecycle_state_manager.h
index ea4b6ff..dcca589 100644
--- a/content/browser/renderer_host/page_lifecycle_state_manager.h
+++ b/content/browser/renderer_host/page_lifecycle_state_manager.h
@@ -126,7 +126,10 @@
   friend std::ostream& operator<<(std::ostream&,
                                   const BackForwardCacheEntered&);
 
-  void SetBackForwardCacheEntered(BackForwardCacheEntered entered);
+  // TODO(https://crbug.com/427316606): Remove `context_for_bug_427316606` after
+  // debugging.
+  void SetBackForwardCacheEntered(BackForwardCacheEntered entered,
+                                  char context_for_bug_427316606);
 
   BackForwardCacheEntered back_forward_cache_entered_ =
       BackForwardCacheEntered::kNo;
@@ -158,11 +161,7 @@
   raw_ptr<TestDelegate> test_delegate_{nullptr};
 
   // TODO(https://crbug.com/427316606): Remove this after debugging.
-  struct {
-    unsigned int no = 0;
-    unsigned int entering = 0;
-    unsigned int entered = 0;
-  } back_forward_cache_state_counts_;
+  std::string back_forward_cache_state_tracker_;
 
   // NOTE: This must be the last member.
   base::WeakPtrFactory<PageLifecycleStateManager> weak_ptr_factory_{this};
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 378f8e5..f4cb3ab0 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -11973,7 +11973,8 @@
                                  const std::optional<UrlMatchType>&)>
         url_match_predicate,
     base::RepeatingCallback<void(NavigationHandle&)>
-        prerender_navigation_handle_callback) {
+        prerender_navigation_handle_callback,
+    bool allow_reuse) {
   PrerenderAttributes attributes(
       prerendering_url, trigger_type, embedder_histogram_suffix,
       /*speculation_rules_params=*/std::nullopt, content::Referrer(),
@@ -11983,7 +11984,8 @@
       std::move(url_match_predicate),
       std::move(prerender_navigation_handle_callback),
       base::WrapRefCounted(
-          static_cast<PreloadPipelineInfoImpl*>(preload_pipeline_info.get())));
+          static_cast<PreloadPipelineInfoImpl*>(preload_pipeline_info.get())),
+      allow_reuse);
 #if BUILDFLAG(IS_ANDROID)
   attributes.additional_headers = std::move(additional_headers);
 #else
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 6687021..3ca82fa 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1010,7 +1010,8 @@
       PreloadingAttempt* preloading_attempt,
       base::RepeatingCallback<bool(const GURL&,
                                    const std::optional<UrlMatchType>&)>,
-      base::RepeatingCallback<void(NavigationHandle&)>) override;
+      base::RepeatingCallback<void(NavigationHandle&)>,
+      bool allow_reuse) override;
   void CancelAllPrerendering() override;
   bool IsAllowedToStartPrerendering() override;
   void BackNavigationLikely(PreloadingPredictor predictor,
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index adf555c..d1147d661 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -740,6 +740,7 @@
     "junit/src/org/chromium/content/browser/webcontents/WebContentsImplTest.java",
     "junit/src/org/chromium/content/browser/webcontents/WebContentsObserverProxyTest.java",
     "junit/src/org/chromium/content_public/browser/MessagePayloadTest.java",
+    "junit/src/org/chromium/content_public/browser/media/capture/ImageHandlerTest.java",
   ]
 
   deps = [
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ImageHandler.java b/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ImageHandler.java
index 5cdf3b7..c166fa6 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ImageHandler.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ImageHandler.java
@@ -14,6 +14,7 @@
 import android.view.Surface;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import org.chromium.build.annotations.NullMarked;
 
@@ -51,15 +52,27 @@
      * @param handler The handler on which to run callbacks.
      */
     ImageHandler(ScreenCapture.CaptureState captureState, Delegate delegate, Handler handler) {
-        mCaptureState = captureState;
-        mDelegate = delegate;
-        mHandler = handler;
-        mImageReader =
+        this(
+                captureState,
+                delegate,
+                handler,
                 ImageReader.newInstance(
                         captureState.width,
                         captureState.height,
                         captureState.format,
-                        /* maxImages= */ 2);
+                        /* maxImages= */ 2));
+    }
+
+    @VisibleForTesting
+    ImageHandler(
+            ScreenCapture.CaptureState captureState,
+            Delegate delegate,
+            Handler handler,
+            ImageReader imageReader) {
+        mCaptureState = captureState;
+        mDelegate = delegate;
+        mHandler = handler;
+        mImageReader = imageReader;
         mImageReader.setOnImageAvailableListener(this, mHandler);
     }
 
@@ -178,4 +191,14 @@
                 throw new IllegalStateException("Unexpected image format: " + image.getFormat());
         }
     }
+
+    /** Returns the current number of images acquired but not yet released. */
+    int getAcquiredImageCountForTesting() {
+        return mAcquiredImageCount;
+    }
+
+    /** Returns true if close() has been called but images are still pending release. */
+    boolean isClosingForTesting() {
+        return mClosing;
+    }
 }
diff --git a/content/public/android/junit/src/org/chromium/content_public/browser/media/capture/ImageHandlerTest.java b/content/public/android/junit/src/org/chromium/content_public/browser/media/capture/ImageHandlerTest.java
new file mode 100644
index 0000000..80f1b58
--- /dev/null
+++ b/content/public/android/junit/src/org/chromium/content_public/browser/media/capture/ImageHandlerTest.java
@@ -0,0 +1,107 @@
+// 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.content_public.browser.media.capture;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.os.Looper;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.content_public.browser.media.capture.ScreenCapture.CaptureState;
+
+/** Unit tests for {@link ImageHandler}. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class ImageHandlerTest {
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private static final int TEST_WIDTH = 100;
+    private static final int TEST_HEIGHT = 200;
+    private static final int TEST_DPI = 300;
+    private static final Rect TEST_CROP_RECT = new Rect(0, 0, TEST_WIDTH, TEST_HEIGHT);
+    private static final long TEST_TIMESTAMP = 123456789L;
+
+    @Mock private ImageHandler.Delegate mDelegate;
+    @Mock private ImageReader mImageReader;
+
+    private ImageHandler mImageHandler;
+
+    @Before
+    public void setUp() {
+        mImageHandler =
+                new ImageHandler(
+                        new CaptureState(TEST_WIDTH, TEST_HEIGHT, TEST_DPI, PixelFormat.RGBA_8888),
+                        mDelegate,
+                        new Handler(Looper.getMainLooper()),
+                        mImageReader);
+
+        when(mImageReader.getMaxImages()).thenReturn(2);
+    }
+
+    private Image createMockImage() {
+        final Image image = mock(Image.class);
+        final Plane plane = mock(Plane.class);
+        when(image.getPlanes()).thenReturn(new Plane[] {plane});
+        when(image.getFormat()).thenReturn(PixelFormat.RGBA_8888);
+        when(image.getCropRect()).thenReturn(TEST_CROP_RECT);
+        when(image.getTimestamp()).thenReturn(TEST_TIMESTAMP);
+        return image;
+    }
+
+    private void onImageAvailable(Image image) {
+        // Return `image` once, then nothing.
+        when(mImageReader.acquireLatestImage()).thenReturn(image, (Image) null);
+        mImageHandler.onImageAvailable(mImageReader);
+    }
+
+    @Test
+    public void testImageAcquireAndRelease() throws Exception {
+        final Image image = createMockImage();
+        final Plane plane = image.getPlanes()[0];
+
+        // Acquire an `Image`.
+        onImageAvailable(image);
+
+        final ArgumentCaptor<Runnable> releaseCb = ArgumentCaptor.forClass(Runnable.class);
+        verify(mDelegate)
+                .onRgbaFrameAvailable(
+                        eq(mImageHandler),
+                        releaseCb.capture(),
+                        eq(TEST_TIMESTAMP),
+                        eq(plane),
+                        eq(TEST_CROP_RECT));
+        assertEquals(1, mImageHandler.getAcquiredImageCountForTesting());
+
+        // Run the release callback.
+        releaseCb.getValue().run();
+
+        // Image should be closed now.
+        verify(image).close();
+        assertEquals(0, mImageHandler.getAcquiredImageCountForTesting());
+
+        verifyNoMoreInteractions(mDelegate);
+    }
+}
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index c5daa64..4a0bc0e 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -1687,7 +1687,8 @@
                                    const std::optional<UrlMatchType>&)>
           url_match_predicate,
       base::RepeatingCallback<void(NavigationHandle&)>
-          prerender_navigation_handle_callback) = 0;
+          prerender_navigation_handle_callback,
+      bool allow_reuse) = 0;
 
   // Cancels all prerendering hosted on this WebContents.
   virtual void CancelAllPrerendering() = 0;
diff --git a/content/public/test/prerender_test_util.cc b/content/public/test/prerender_test_util.cc
index 0d1384c..75caba25 100644
--- a/content/public/test/prerender_test_util.cc
+++ b/content/public/test/prerender_test_util.cc
@@ -708,7 +708,8 @@
       PreloadPipelineInfo::Create(
           /*planned_max_preloading_type=*/PreloadingType::kPrerender),
       /*preloading_attempt=*/nullptr, /*url_match_predicate=*/{},
-      /*prerender_navigation_handle_callback=*/{});
+      /*prerender_navigation_handle_callback=*/{},
+      /*allow_reuse=*/false);
 }
 
 std::unique_ptr<PrerenderHandle>
diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc
index 78fc9981..0ec3b11 100644
--- a/content/test/test_web_contents.cc
+++ b/content/test/test_web_contents.cc
@@ -510,7 +510,8 @@
       /*url_match_predicate=*/{},
       /*prerender_navigation_handle_callback=*/{},
       PreloadPipelineInfoImpl::Create(
-          /*planned_max_preloading_type=*/PreloadingType::kPrerender)));
+          /*planned_max_preloading_type=*/PreloadingType::kPrerender),
+      /*allow_reuse=*/false));
 }
 
 TestRenderFrameHost* TestWebContents::AddPrerenderAndCommitNavigation(
diff --git a/infra/config/generated/builder-owners/chrome-build-team@google.com.txt b/infra/config/generated/builder-owners/chrome-build-team@google.com.txt
index 0066db0..044e2cd 100644
--- a/infra/config/generated/builder-owners/chrome-build-team@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-build-team@google.com.txt
@@ -12,6 +12,8 @@
 build/linux-build-perf-siso
 build/linux-chromeos-build-perf-ninja
 build/linux-chromeos-build-perf-siso
+build/linux-rbe-trusted-test
+build/linux-rbe-untrusted-test
 build/mac-build-perf-developer
 build/mac-build-perf-ninja
 build/mac-build-perf-siso
@@ -19,6 +21,8 @@
 build/win-build-perf-developer
 build/win-build-perf-ninja
 build/win-build-perf-siso
+build/win-rbe-trusted-test
+build/win-rbe-untrusted-test
 ci/Deterministic Linux
 ci/Deterministic Linux (dbg)
 ci/linux-modules-compile-fyi-rel
diff --git a/infra/config/generated/builders/build/linux-rbe-trusted-test/gn-args.json b/infra/config/generated/builders/build/linux-rbe-trusted-test/gn-args.json
new file mode 100644
index 0000000..95e40ac
--- /dev/null
+++ b/infra/config/generated/builders/build/linux-rbe-trusted-test/gn-args.json
@@ -0,0 +1,19 @@
+{
+  "phases": {
+    "builtin": {
+      "gn_args": {
+        "dcheck_always_on": false,
+        "devtools_skip_typecheck": false,
+        "ffmpeg_branding": "Chrome",
+        "is_component_build": false,
+        "is_debug": false,
+        "proprietary_codecs": true,
+        "target_cpu": "x64",
+        "target_os": "linux",
+        "use_reclient": false,
+        "use_remoteexec": true,
+        "use_siso": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/build/linux-rbe-trusted-test/properties.json b/infra/config/generated/builders/build/linux-rbe-trusted-test/properties.json
new file mode 100644
index 0000000..e223897
--- /dev/null
+++ b/infra/config/generated/builders/build/linux-rbe-trusted-test/properties.json
@@ -0,0 +1,68 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/build/linux-rbe-trusted-test/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "build",
+              "builder": "linux-rbe-trusted-test",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.build.test",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "config": "chromium",
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "siso_latest"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "build",
+          "builder": "linux-rbe-trusted-test",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/siso": {
+    "configs": [
+      "builder",
+      "remote-link"
+    ],
+    "enable_cloud_monitoring": true,
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [
+      "no-fallback"
+    ],
+    "metrics_project": "chromium-reclient-metrics",
+    "project": "rbe-chromium-trusted-test",
+    "remote_jobs": 250
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.build.test",
+  "recipe": "chrome_build/build_perf_siso"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/build/linux-rbe-untrusted-test/gn-args.json b/infra/config/generated/builders/build/linux-rbe-untrusted-test/gn-args.json
new file mode 100644
index 0000000..de244b67
--- /dev/null
+++ b/infra/config/generated/builders/build/linux-rbe-untrusted-test/gn-args.json
@@ -0,0 +1,22 @@
+{
+  "phases": {
+    "builtin": {
+      "gn_args": {
+        "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
+        "dcheck_always_on": true,
+        "devtools_skip_typecheck": false,
+        "ffmpeg_branding": "Chrome",
+        "is_component_build": false,
+        "is_debug": false,
+        "proprietary_codecs": true,
+        "symbol_level": 0,
+        "target_cpu": "x64",
+        "target_os": "linux",
+        "use_clang_coverage": true,
+        "use_reclient": false,
+        "use_remoteexec": true,
+        "use_siso": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/build/linux-rbe-untrusted-test/properties.json b/infra/config/generated/builders/build/linux-rbe-untrusted-test/properties.json
new file mode 100644
index 0000000..668f2ea
--- /dev/null
+++ b/infra/config/generated/builders/build/linux-rbe-untrusted-test/properties.json
@@ -0,0 +1,71 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/build/linux-rbe-untrusted-test/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "build",
+              "builder": "linux-rbe-untrusted-test",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.build.test",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "config": "chromium",
+                "target_platform": "linux"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "siso_latest"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "build",
+          "builder": "linux-rbe-untrusted-test",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/code_coverage": {
+    "use_clang_coverage": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder",
+      "remote-link"
+    ],
+    "enable_cloud_monitoring": true,
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [
+      "no-fallback"
+    ],
+    "metrics_project": "chromium-reclient-metrics",
+    "project": "rbe-chromium-untrusted-test",
+    "remote_jobs": -1
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.build.test",
+  "recipe": "chrome_build/build_perf_siso"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/build/win-rbe-trusted-test/gn-args.json b/infra/config/generated/builders/build/win-rbe-trusted-test/gn-args.json
new file mode 100644
index 0000000..6a1b1493
--- /dev/null
+++ b/infra/config/generated/builders/build/win-rbe-trusted-test/gn-args.json
@@ -0,0 +1,19 @@
+{
+  "phases": {
+    "builtin": {
+      "gn_args": {
+        "dcheck_always_on": false,
+        "ffmpeg_branding": "Chrome",
+        "is_component_build": false,
+        "is_debug": false,
+        "proprietary_codecs": true,
+        "symbol_level": 1,
+        "target_cpu": "x64",
+        "target_os": "win",
+        "use_reclient": false,
+        "use_remoteexec": true,
+        "use_siso": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/build/win-rbe-trusted-test/properties.json b/infra/config/generated/builders/build/win-rbe-trusted-test/properties.json
new file mode 100644
index 0000000..80026a7
--- /dev/null
+++ b/infra/config/generated/builders/build/win-rbe-trusted-test/properties.json
@@ -0,0 +1,68 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/build/win-rbe-trusted-test/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "build",
+              "builder": "win-rbe-trusted-test",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.build.test",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "config": "chromium",
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "siso_latest"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "build",
+          "builder": "win-rbe-trusted-test",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/siso": {
+    "configs": [
+      "builder",
+      "remote-link"
+    ],
+    "enable_cloud_monitoring": true,
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [
+      "no-fallback"
+    ],
+    "metrics_project": "chromium-reclient-metrics",
+    "project": "rbe-chromium-trusted-test",
+    "remote_jobs": 250
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.build.test",
+  "recipe": "chrome_build/build_perf_siso"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/build/win-rbe-untrusted-test/gn-args.json b/infra/config/generated/builders/build/win-rbe-untrusted-test/gn-args.json
new file mode 100644
index 0000000..7d282522
--- /dev/null
+++ b/infra/config/generated/builders/build/win-rbe-untrusted-test/gn-args.json
@@ -0,0 +1,24 @@
+{
+  "phases": {
+    "builtin": {
+      "gn_args": {
+        "coverage_instrumentation_input_file": "//.code-coverage/files_to_instrument.txt",
+        "dcheck_always_on": true,
+        "enable_dangling_raw_ptr_checks": true,
+        "enable_dangling_raw_ptr_feature_flag": true,
+        "enable_resource_allowlist_generation": false,
+        "ffmpeg_branding": "Chrome",
+        "is_component_build": false,
+        "is_debug": false,
+        "proprietary_codecs": true,
+        "symbol_level": 0,
+        "target_cpu": "x64",
+        "target_os": "win",
+        "use_clang_coverage": true,
+        "use_reclient": false,
+        "use_remoteexec": true,
+        "use_siso": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/build/win-rbe-untrusted-test/properties.json b/infra/config/generated/builders/build/win-rbe-untrusted-test/properties.json
new file mode 100644
index 0000000..e190a76
--- /dev/null
+++ b/infra/config/generated/builders/build/win-rbe-untrusted-test/properties.json
@@ -0,0 +1,71 @@
+{
+  "$build/chromium_tests_builder_config": {
+    "builder_config": {
+      "additional_exclusions": [
+        "infra/config/generated/builders/build/win-rbe-untrusted-test/gn-args.json"
+      ],
+      "builder_db": {
+        "entries": [
+          {
+            "builder_id": {
+              "bucket": "build",
+              "builder": "win-rbe-untrusted-test",
+              "project": "chromium"
+            },
+            "builder_spec": {
+              "builder_group": "chromium.build.test",
+              "execution_mode": "COMPILE_AND_TEST",
+              "legacy_chromium_config": {
+                "apply_configs": [
+                  "mb"
+                ],
+                "config": "chromium",
+                "target_platform": "win"
+              },
+              "legacy_gclient_config": {
+                "apply_configs": [
+                  "siso_latest"
+                ],
+                "config": "chromium"
+              }
+            }
+          }
+        ]
+      },
+      "builder_ids": [
+        {
+          "bucket": "build",
+          "builder": "win-rbe-untrusted-test",
+          "project": "chromium"
+        }
+      ]
+    }
+  },
+  "$build/code_coverage": {
+    "use_clang_coverage": true
+  },
+  "$build/siso": {
+    "configs": [
+      "builder",
+      "remote-link"
+    ],
+    "enable_cloud_monitoring": true,
+    "enable_cloud_profiler": true,
+    "enable_cloud_trace": true,
+    "experiments": [
+      "no-fallback"
+    ],
+    "metrics_project": "chromium-reclient-metrics",
+    "project": "rbe-chromium-untrusted-test",
+    "remote_jobs": -1
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.build.test",
+  "recipe": "chrome_build/build_perf_siso"
+}
\ 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 afffd66..906571b 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -127,6 +127,12 @@
   "chromium.build.fyi": {
     "Mac Builder Siso FYI": "build/Mac Builder Siso FYI/gn-args.json"
   },
+  "chromium.build.test": {
+    "linux-rbe-trusted-test": "build/linux-rbe-trusted-test/gn-args.json",
+    "linux-rbe-untrusted-test": "build/linux-rbe-untrusted-test/gn-args.json",
+    "win-rbe-trusted-test": "build/win-rbe-trusted-test/gn-args.json",
+    "win-rbe-untrusted-test": "build/win-rbe-untrusted-test/gn-args.json"
+  },
   "chromium.cft": {
     "linux-rel-cft": "ci/linux-rel-cft/gn-args.json",
     "mac-rel-cft": "ci/mac-rel-cft/gn-args.json",
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 4876977..71f2438 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -1062,6 +1062,152 @@
       }
     }
     builders {
+      name: "linux-rbe-trusted-test"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      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/build/linux-rbe-trusted-test/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.build.test",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chrome_build/build_perf_siso"'
+        '}'
+      priority: 35
+      execution_timeout_secs: 36000
+      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
+      }
+      description_html: "This builder builds Linux CI build with rbe-chroimum-trusted-test.<br/>The build configs and the bot specs should be in sync with <a href=\"https://ci.chromium.org/p/chromium/builders/ci/Linux Builder\">Linux Builder</a><br/>Builder owner: <a href=mailto:chrome-build-team@google.com>chrome-build-team@google.com</a>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      }
+      contact_team_email: "chrome-build-team@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+    }
+    builders {
+      name: "linux-rbe-untrusted-test"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      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/build/linux-rbe-untrusted-test/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.build.test",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chrome_build/build_perf_siso"'
+        '}'
+      priority: 35
+      execution_timeout_secs: 36000
+      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
+      }
+      description_html: "This builder builds Linux CQ build with rbe-chroimum-untrusted-test.<br/>The build configs and the bot specs should be in sync with <a href=\"https://ci.chromium.org/p/chromium/builders/try/linux-rel-compilator\">linux-rel-compilator</a><br/>Builder owner: <a href=mailto:chrome-build-team@google.com>chrome-build-team@google.com</a>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      }
+      contact_team_email: "chrome-build-team@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+    }
+    builders {
       name: "mac-build-perf-developer"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:mac-build-perf-developer"
@@ -1547,6 +1693,152 @@
         predicates: "string(build.output.properties.is_cached) == \"false\""
       }
     }
+    builders {
+      name: "win-rbe-trusted-test"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "os:Windows-10"
+      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/build/win-rbe-trusted-test/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.build.test",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chrome_build/build_perf_siso"'
+        '}'
+      priority: 35
+      execution_timeout_secs: 36000
+      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
+      }
+      description_html: "This builder builds Windows CI build with rbe-chroimum-trusted-test.<br/>The build configs and the bot specs should be in sync with <a href=\"https://ci.chromium.org/p/chromium/builders/ci/Win x64 Builder\">Win x64 Builder</a><br/>Builder owner: <a href=mailto:chrome-build-team@google.com>chrome-build-team@google.com</a>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      }
+      contact_team_email: "chrome-build-team@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+    }
+    builders {
+      name: "win-rbe-untrusted-test"
+      swarming_host: "chromium-swarm.appspot.com"
+      dimensions: "builderless:1"
+      dimensions: "os:Windows-10"
+      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/build/win-rbe-untrusted-test/properties.json",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
+        '  },'
+        '  "builder_group": "chromium.build.test",'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chrome_build/build_perf_siso"'
+        '}'
+      priority: 35
+      execution_timeout_secs: 36000
+      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
+      }
+      description_html: "This builder builds Windows CQ build with rbe-chroimum-untrusted-test.<br/>The build configs and the bot specs should be in sync with <a href=\"https://ci.chromium.org/p/chromium/builders/try/win-rel-compilator\">win-rel-compilator</a><br/>Builder owner: <a href=mailto:chrome-build-team@google.com>chrome-build-team@google.com</a>"
+      shadow_builder_adjustments {
+        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
+      }
+      contact_team_email: "chrome-build-team@google.com"
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/cached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"true\""
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
+        predicates: "has(build.output.properties.ran_tests_retry_shard)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
+        predicates: "has(build.output.properties.ran_tests_without_patch)"
+      }
+      custom_metric_definitions {
+        name: "/chrome/infra/browser/builds/uncached_count"
+        predicates: "has(build.output.properties.is_cached)"
+        predicates: "string(build.output.properties.is_cached) == \"false\""
+      }
+    }
   }
   shadow: "build.shadow"
 }
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 32ddd85b..ebbe4c0 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -11412,6 +11412,33 @@
   }
 }
 consoles {
+  id: "chromium.build.test"
+  name: "chromium.build.test"
+  repo_url: "https://chromium.googlesource.com/chromium/src"
+  refs: "regexp:refs/heads/main"
+  manifest_name: "REVISION"
+  builders {
+    name: "buildbucket/luci.chromium.build/linux-rbe-trusted-test"
+    category: "trusted"
+    short_name: "lin"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.build/win-rbe-trusted-test"
+    category: "trusted"
+    short_name: "win"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.build/linux-rbe-untrusted-test"
+    category: "untrusted"
+    short_name: "lin"
+  }
+  builders {
+    name: "buildbucket/luci.chromium.build/win-rbe-untrusted-test"
+    category: "untrusted"
+    short_name: "win"
+  }
+}
+consoles {
   id: "chromium.cft"
   name: "chromium.cft"
   repo_url: "https://chromium.googlesource.com/chromium/src"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 4a3fb168..ea8b52c23 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -4777,6 +4777,24 @@
   }
 }
 job {
+  id: "linux-rbe-trusted-test"
+  realm: "build"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "build"
+    builder: "linux-rbe-trusted-test"
+  }
+}
+job {
+  id: "linux-rbe-untrusted-test"
+  realm: "build"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "build"
+    builder: "linux-rbe-untrusted-test"
+  }
+}
+job {
   id: "linux-rel-cft"
   realm: "ci"
   buildbucket {
@@ -5733,6 +5751,24 @@
   }
 }
 job {
+  id: "win-rbe-trusted-test"
+  realm: "build"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "build"
+    builder: "win-rbe-trusted-test"
+  }
+}
+job {
+  id: "win-rbe-untrusted-test"
+  realm: "build"
+  buildbucket {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "build"
+    builder: "win-rbe-untrusted-test"
+  }
+}
+job {
   id: "win-rel-cft"
   realm: "ci"
   buildbucket {
@@ -6114,6 +6150,8 @@
   triggers: "linux-build-perf-siso"
   triggers: "linux-chromeos-build-perf-ninja"
   triggers: "linux-chromeos-build-perf-siso"
+  triggers: "linux-rbe-trusted-test"
+  triggers: "linux-rbe-untrusted-test"
   triggers: "mac-build-perf-developer"
   triggers: "mac-build-perf-ninja"
   triggers: "mac-build-perf-siso"
@@ -6121,6 +6159,8 @@
   triggers: "win-build-perf-developer"
   triggers: "win-build-perf-ninja"
   triggers: "win-build-perf-siso"
+  triggers: "win-rbe-trusted-test"
+  triggers: "win-rbe-untrusted-test"
   gitiles {
     repo: "https://chromium.googlesource.com/chromium/src"
     refs: "regexp:refs/heads/main"
diff --git a/infra/config/subprojects/build/build.test.star b/infra/config/subprojects/build/build.test.star
new file mode 100644
index 0000000..33598ef
--- /dev/null
+++ b/infra/config/subprojects/build/build.test.star
@@ -0,0 +1,179 @@
+# 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.
+"""Definitions of builders with test RBE instances."""
+
+load("//lib/builder_config.star", "builder_config")
+load("//lib/builders.star", "os", "siso")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+load("//lib/html.star", "linkify_builder")
+load("//project.star", "settings")
+
+luci.gitiles_poller(
+    name = "chrome-build-gitiles-trigger",
+    bucket = "build",
+    repo = "https://chromium.googlesource.com/chromium/src",
+    refs = [settings.ref],
+)
+
+ci.defaults.set(
+    bucket = "build",
+    triggered_by = ["chrome-build-gitiles-trigger"],
+    builder_group = "chromium.build.test",
+    pool = ci.DEFAULT_POOL,
+    builderless = True,
+    build_numbers = True,
+    contact_team_email = "chrome-build-team@google.com",
+    execution_timeout = 10 * time.hour,
+    priority = ci.DEFAULT_FYI_PRIORITY,
+    resultdb_enable = False,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    shadow_service_account = ci.DEFAULT_SHADOW_SERVICE_ACCOUNT,
+    siso_configs = ["builder", "remote-link"],
+    siso_experiments = ["no-fallback"],
+)
+
+consoles.console_view(
+    name = "chromium.build.test",
+    repo = "https://chromium.googlesource.com/chromium/src",
+)
+
+def cq_build_perf_builder(**kwargs):
+    # Use CQ RBE instance and high remote_jobs to simulate CQ builds.
+    return ci.builder(
+        siso_remote_jobs = siso.remote_jobs.HIGH_JOBS_FOR_CQ,
+        siso_project = siso.project.TEST_UNTRUSTED,
+        use_clang_coverage = True,
+        **kwargs
+    )
+
+def ci_build_perf_builder(**kwargs):
+    # Use CI RBE instance to simulate CI builds.
+    return ci.builder(
+        siso_remote_jobs = siso.remote_jobs.DEFAULT,
+        siso_project = siso.project.TEST_TRUSTED,
+        **kwargs
+    )
+
+# Builders with rbe-chromium-untrusted-test.
+cq_build_perf_builder(
+    name = "linux-rbe-untrusted-test",
+    description_html = "This builder builds Linux CQ build with rbe-chroimum-untrusted-test.<br/>" +
+                       "The build configs and the bot specs should be in sync with " + linkify_builder("try", "linux-rel-compilator", "chromium"),
+    executable = "recipe:chrome_build/build_perf_siso",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "siso_latest",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            target_platform = builder_config.target_platform.LINUX,
+        ),
+    ),
+    gn_args = {
+        "builtin": "try/linux-rel",
+    },
+    os = os.LINUX_DEFAULT,
+    console_view_entry = consoles.console_view_entry(
+        category = "untrusted",
+        short_name = "lin",
+    ),
+)
+
+cq_build_perf_builder(
+    name = "win-rbe-untrusted-test",
+    description_html = "This builder builds Windows CQ build with rbe-chroimum-untrusted-test.<br/>" +
+                       "The build configs and the bot specs should be in sync with " + linkify_builder("try", "win-rel-compilator", "chromium"),
+    executable = "recipe:chrome_build/build_perf_siso",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "siso_latest",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            target_platform = builder_config.target_platform.WIN,
+        ),
+    ),
+    gn_args = {
+        "builtin": "try/win-rel",
+    },
+    os = os.WINDOWS_DEFAULT,
+    console_view_entry = consoles.console_view_entry(
+        category = "untrusted",
+        short_name = "win",
+    ),
+)
+
+# Builders with rbe-chromium-trusted-test.
+ci_build_perf_builder(
+    name = "linux-rbe-trusted-test",
+    description_html = "This builder builds Linux CI build with rbe-chroimum-trusted-test.<br/>" +
+                       "The build configs and the bot specs should be in sync with " + linkify_builder("ci", "Linux Builder", "chromium"),
+    executable = "recipe:chrome_build/build_perf_siso",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "siso_latest",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            target_platform = builder_config.target_platform.LINUX,
+        ),
+    ),
+    gn_args = {
+        "builtin": "ci/Linux Builder",
+    },
+    os = os.LINUX_DEFAULT,
+    console_view_entry = consoles.console_view_entry(
+        category = "trusted",
+        short_name = "lin",
+    ),
+)
+
+ci_build_perf_builder(
+    name = "win-rbe-trusted-test",
+    description_html = "This builder builds Windows CI build with rbe-chroimum-trusted-test.<br/>" +
+                       "The build configs and the bot specs should be in sync with " + linkify_builder("ci", "Win x64 Builder", "chromium"),
+    executable = "recipe:chrome_build/build_perf_siso",
+    builder_spec = builder_config.builder_spec(
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = [
+                "siso_latest",
+            ],
+        ),
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = [
+                "mb",
+            ],
+            target_platform = builder_config.target_platform.WIN,
+        ),
+    ),
+    gn_args = {
+        "builtin": "ci/Win x64 Builder",
+    },
+    os = os.WINDOWS_DEFAULT,
+    console_view_entry = consoles.console_view_entry(
+        category = "trusted",
+        short_name = "win",
+    ),
+)
diff --git a/infra/config/subprojects/build/subproject.star b/infra/config/subprojects/build/subproject.star
index d7a548f..fb3ffa9 100644
--- a/infra/config/subprojects/build/subproject.star
+++ b/infra/config/subprojects/build/subproject.star
@@ -14,3 +14,4 @@
 
 exec("./build.fyi.star")
 exec("./build.star")
+exec("./build.test.star")
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 5d8d0612..f920a08 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 @@
-04ae9a93503e1c10bade48c9766b0cfa41e0de72
\ No newline at end of file
+855267be987acdfe6f973d9c9ea4ccc7a124fe26
\ 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 886700e6..12ad1ade 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 @@
-a21e68b032e304fa21ba1cdc730473a37cdd45f6
\ No newline at end of file
+4440ed82fd7dc0dee553d5023055b97bfedbfa8e
\ 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 4cbcfe59..913507d5 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 @@
-caa37b5cfb15a545f7eab1d167f33e26cf1b75d6
\ No newline at end of file
+863153f893f07394d90833ad5d7b5f62ee3d6383
\ 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 80a7932..e5b4adb 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 @@
-68f1b42af48b31b44b2e4e7dde237a36ba54646a
\ No newline at end of file
+2a2f53fd8928703d9020fae668aa1c8ed24b3f04
\ 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 8849e647e..c7094d53 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 @@
-ced12991698cfadf98df107ec096de797912a1a3
\ No newline at end of file
+798417fd25389e432da7ddbf9e9dc5902e2484a0
\ 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 873a096..905e600e 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 @@
-76a96e7b0a23c0669f1b943d4fc703cf015cf3ef
\ No newline at end of file
+7cc64cc7dbbf6b9d623bc83f8270bba3c89b29e2
\ 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 15ccaf4..e0b3922 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 @@
-2a5d9089c49951b517841516a639e4ae7875906b
\ No newline at end of file
+752338ee44a68c79658c391b013a75977a29dbb3
\ 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 ead16289..2fc2625 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 @@
-93ef5ef40ae93f25eb5bf06df915cb0c6e8e3ae1
\ No newline at end of file
+76427e1a5e5c1b467bff96a701b5ead5b65f539a
\ 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 9aa17a9f..8178ab7 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 @@
-f0b53cdfe5cf87875241cab331173e1fc9072c97
\ No newline at end of file
+febd50b587ca6a86512f9fd0f450240a71f918d6
\ 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 60d322ca..063f3417 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 @@
-75ac393d943f2138edebdcd86d56b547f31d3a06
\ No newline at end of file
+4e49d2a9fcea9e612ebcbf2fce4046ad7cdfc77c
\ 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 72895d5..9aefa24 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 @@
-894816f0e4c9cb1bce917f47890586be19329072
\ No newline at end of file
+4b7ef546f4a710cdd40d344fde7ea01f41a178b5
\ 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 cefe756..282ce97 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 @@
-312185c4f7efea9f119ebbcaf561dc89503bfc14
\ No newline at end of file
+20a960156299cdb8bb85218488a7af84010b8f78
\ 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 8df4e8d..21f0ce64 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 @@
-e5a0b7f52df9f91fcab4fc2b8a85e07451610713
\ No newline at end of file
+83a1bfc5940bbaafb3e63aa744def8ca3869ec7c
\ 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 f37597b8..367b143 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 @@
-2d65accd84e04e06277202641453989feb848088
\ No newline at end of file
+d53b0c7a6acd7e58d5a4f3a86a18a48a6a75dbba
\ 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 ba149f5..51cd680 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 @@
-a02cf4958849307d47e4f9ee67cb9637b511cf91
\ No newline at end of file
+6d032fdd8548ea7b11cca72d0e40997d8be8cf4c
\ 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 4d2c667..d31e4560 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 @@
-9bd3e7f0a5daa44486379e300d34132c44a07fcf
\ No newline at end of file
+b2f0f89808a3aae3432a17d509beea9792491610
\ 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 7e8b708..28b46c5 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 @@
-478955e7b7669d685ecc9b4dcc3a9fe1ebf56c94
\ No newline at end of file
+3955286bd17f8303b8a71cce4cecc0918560daf9
\ 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 5746729..35e5d41 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 @@
-694bf6a9fbcdb0183f06007df8cdccd267834ee9
\ No newline at end of file
+e2f06aa71ebd935935ee2143178dc69bb17c56f5
\ 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 f642f5fb..9348372 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 @@
-17967e4df3156d6588436a349dc4cf4ff8aaf09d
\ No newline at end of file
+c191ac24056d8241d6932f238528e54363162d86
\ 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 3cab0f7..0f959df 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 @@
-cd2c515620e78abcc4b35d44f441ac3d4bc67eba
\ No newline at end of file
+abe5080ac9a799e899cce42aa9fb8a07428dcccc
\ 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 a057107..9cfed494 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 @@
-4e63e4b315709c6c830ba9cf6acbc72301965152
\ No newline at end of file
+4dbfb708f455f41eea83addf67153941a7b580c9
\ 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 6d47aa9..7aa54b9d 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 @@
-680d9ef360c9c16a750c3e72a1de92007e2d9035
\ No newline at end of file
+aa7095001d1457c1ecf2c38f7e6ac60073377ab8
\ 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 2585f79a..250e085d 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 @@
-87a97e03c91b11144295a6b1d888e19e6b1c2476
\ No newline at end of file
+267556bf7429a2257131190a9296a2e13ce5d0ee
\ 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 ee232d2..cc4bdf94 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 @@
-1ce8288c700269bd6dfa58619abde73ed22094b9
\ No newline at end of file
+915d2d1b13d1e09d319665c6f19ad7456821fd6d
\ 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 e1592e39..114cc0b 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 @@
-15c016a3366c4d161f3d337933b4b739e7a08514
\ No newline at end of file
+3020bdcfb33ef9c8c8812ac9a1e3c4bcd77ce45c
\ 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 68061e91..42c0fbaf 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 @@
-7ca35463967f7965de30fc75c15933e29f6006e4
\ No newline at end of file
+40e6fbf3b6bc01029225a9ee6e765e5dc15d3746
\ 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 c16f0a6..4c052e1 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 @@
-5e230e64bb1e5a461633e21dfafdc17d379e70d9
\ No newline at end of file
+81a41d8cbc85a48208adb1d70623af490bb315d2
\ 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 527de78..10f55e2e 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 @@
-2108901c0d1073f11ba1bab2eca1075de9f5742b
\ No newline at end of file
+2b32acbf8202f830832d0e36a2321a5d4826e05c
\ No newline at end of file
diff --git a/ios_internal b/ios_internal
index 97f5aa2..91544a5 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 97f5aa28bc4471c48f2f4488c5671bee87b9922a
+Subproject commit 91544a50032a3960c09f26b99b4aab8efbaee788
diff --git a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
index f96af4d9..b033f9c 100644
--- a/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_jpeg_encode_accelerator.cc
@@ -25,12 +25,12 @@
 #include "base/task/bind_post_task.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
-#include "gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h"
 #include "media/gpu/chromeos/fourcc.h"
 #include "media/gpu/chromeos/platform_video_frame_utils.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/v4l2/v4l2_device.h"
 #include "third_party/libyuv/include/libyuv.h"
+#include "ui/gfx/native_pixmap.h"
 #include "ui/ozone/public/ozone_platform.h"
 
 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value, type_name) \
@@ -577,26 +577,26 @@
   // In this case, we use the R_8 buffer with height == 1 to represent a data
   // container. As a result, we use plane.stride as size of the data here since
   // plane.size might be larger due to height alignment.
-  const gfx::Size output_gmb_buffer_size(
+  const gfx::Size native_pixmap_size(
       base::checked_cast<int32_t>(output_frame->layout().planes()[0].stride),
       1);
 
-  auto output_gmb_buffer =
-      gpu::GpuMemoryBufferImplNativePixmap::CreateFromHandle(
-          client_native_pixmap_factory_.get(), std::move(output_gmb_handle),
-          output_gmb_buffer_size, gfx::BufferFormat::R_8,
-          gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, base::DoNothing());
-  if (!output_gmb_buffer) {
-    VLOGF(1) << "Failed to import gmb buffer";
+  std::unique_ptr<gfx::ClientNativePixmap> native_pixmap =
+      client_native_pixmap_factory_->ImportFromHandle(
+          std::move(output_gmb_handle).native_pixmap_handle(),
+          native_pixmap_size, gfx::BufferFormat::R_8,
+          gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE);
+  if (!native_pixmap) {
+    VLOGF(1) << "Failed to import native pixmap";
     return 0;
   }
 
-  bool isMapped = output_gmb_buffer->Map();
+  bool isMapped = native_pixmap->Map();
   if (!isMapped) {
-    VLOGF(1) << "Failed to map gmb buffer";
+    VLOGF(1) << "Failed to map native pixmap";
     return 0;
   }
-  uint8_t* dst_ptr = static_cast<uint8_t*>(output_gmb_buffer->memory(0));
+  uint8_t* dst_ptr = static_cast<uint8_t*>(native_pixmap->GetMemoryAddress(0));
 
   // Fill SOI and EXIF markers.
   static const uint8_t kJpegStart[] = {0xFF, JPEG_SOI};
@@ -654,7 +654,7 @@
       NOTREACHED() << "Unsupported output pixel format";
   }
 
-  output_gmb_buffer->Unmap();
+  native_pixmap->Unmap();
 
   return idx;
 }
diff --git a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
index 50908b2b..cf52037 100644
--- a/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc
@@ -23,7 +23,6 @@
 #include "base/task/thread_pool.h"
 #include "base/thread_annotations.h"
 #include "base/trace_event/trace_event.h"
-#include "gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h"
 #include "media/base/video_frame.h"
 #include "media/gpu/chromeos/platform_video_frame_utils.h"
 #include "media/gpu/macros.h"
@@ -31,6 +30,7 @@
 #include "media/gpu/vaapi/vaapi_utils.h"
 #include "media/parsers/jpeg_parser.h"
 #include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
+#include "ui/gfx/native_pixmap.h"
 #include "ui/ozone/public/client_native_pixmap_factory_ozone.h"
 #include "ui/ozone/public/ozone_platform.h"
 
@@ -289,7 +289,7 @@
     return;
   }
 
-  // Create gmb buffer from output VideoFrame. Since the JPEG VideoFrame's coded
+  // Create GMB handle from output VideoFrame. Since the JPEG VideoFrame's coded
   // size is the 2D image size, we should use (buffer_size, 1) as the R8 gmb's
   // size, where buffer_size can be obtained from the first plane's size.
   auto output_gmb_handle = CreateGpuMemoryBufferHandle(output_frame.get());
@@ -298,35 +298,36 @@
   // In this case, we use the R_8 buffer with height == 1 to represent a data
   // container. As a result, we use plane.stride as size of the data here since
   // plane.size might be larger due to height alignment.
-  const gfx::Size output_gmb_buffer_size(
+  const gfx::Size native_pixmap_size(
       base::checked_cast<int32_t>(output_frame->layout().planes()[0].stride),
       1);
-  auto output_gmb_buffer =
-      gpu::GpuMemoryBufferImplNativePixmap::CreateFromHandle(
-          client_native_pixmap_factory_.get(), std::move(output_gmb_handle),
-          output_gmb_buffer_size, gfx::BufferFormat::R_8,
-          gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE, base::DoNothing());
-  if (output_gmb_buffer == nullptr) {
-    VLOGF(1) << "Failed to create GpuMemoryBufferImpl from handle";
+  std::unique_ptr<gfx::ClientNativePixmap> native_pixmap =
+      client_native_pixmap_factory_->ImportFromHandle(
+          std::move(output_gmb_handle).native_pixmap_handle(),
+          native_pixmap_size, gfx::BufferFormat::R_8,
+          gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE);
+  if (native_pixmap == nullptr) {
+    VLOGF(1) << "Failed to create NativePixmap from handle";
     notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
     return;
   }
 
-  const bool is_mapped = output_gmb_buffer->Map();
+  const bool is_mapped = native_pixmap->Map();
   if (!is_mapped) {
-    VLOGF(1) << "Map the output gmb buffer failed";
+    VLOGF(1) << "Map the native pixmap failed";
     notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
     return;
   }
-  absl::Cleanup output_gmb_buffer_unmapper = [&output_gmb_buffer] {
-    output_gmb_buffer->Unmap();
+  absl::Cleanup native_pixmap_unmapper = [&native_pixmap] {
+    native_pixmap->Unmap();
   };
 
   // Get the encoded output. DownloadFromVABuffer() is a blocking call. It
   // would wait until encoding is finished.
-  uint8_t* output_memory = static_cast<uint8_t*>(output_gmb_buffer->memory(0));
+  uint8_t* output_memory =
+      static_cast<uint8_t*>(native_pixmap->GetMemoryAddress(0));
   size_t encoded_size = 0;
-  // Since the format of |output_gmb_buffer| is gfx::BufferFormat::R_8, we can
+  // Since the format of |native_pixmap| is gfx::BufferFormat::R_8, we can
   // use its area as the maximum bytes we need to download to avoid buffer
   // overflow.
   // Since we didn't supply EXIF data to the JPEG encoder, it creates a default
@@ -349,7 +350,7 @@
   const size_t output_offset =
       exif_buffer_size > 0 ? exif_buffer_size - kApp0DataSize : 0;
   const size_t output_size =
-      base::checked_cast<size_t>(output_gmb_buffer->GetSize().GetArea());
+      base::checked_cast<size_t>(native_pixmap_size.GetArea());
   if (output_offset >= output_size) {
     VLOGF(1) << "Output buffer size (" << output_size << ") is too small";
     notify_error_cb_.Run(task_id, PLATFORM_FAILURE);
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 3bcb0859..b207e436 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -895,6 +895,10 @@
     "quic/quic_session_alias_key.h",
     "quic/quic_session_attempt.cc",
     "quic/quic_session_attempt.h",
+    "quic/quic_session_attempt_manager.cc",
+    "quic/quic_session_attempt_manager.h",
+    "quic/quic_session_attempt_request.cc",
+    "quic/quic_session_attempt_request.h",
     "quic/quic_session_key.cc",
     "quic/quic_session_key.h",
     "quic/quic_session_pool.cc",
@@ -2939,6 +2943,7 @@
     "quic/quic_proxy_client_socket_test_base.h",
     "quic/quic_proxy_client_socket_unittest.cc",
     "quic/quic_proxy_datagram_client_socket_unittest.cc",
+    "quic/quic_session_attempt_manager_unittest.cc",
     "quic/quic_session_key_unittest.cc",
     "quic/quic_session_pool_peer.cc",
     "quic/quic_session_pool_peer.h",
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index fcad1f5e..26a1950 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -1691,7 +1691,7 @@
   // slow. Currently we just cancel them for simplicity. If we want to keep
   // these attempts in the draining `this`, Group::ConnectingStreamSocketCount()
   // should check draining AttemptManagers.
-  CancelTcpBasedAttempts(StreamSocketCloseReason::kAbort);
+  CancelTcpBasedAttempts(StreamSocketCloseReason::kAttemptManagerDraining);
 
   if (quic_attempt_ && quic_attempt_->is_slow()) {
     CancelQuicAttempt(ERR_ABORTED);
diff --git a/net/http/http_stream_pool_attempt_manager_unittest.cc b/net/http/http_stream_pool_attempt_manager_unittest.cc
index c73e06ff..3cc89ba 100644
--- a/net/http/http_stream_pool_attempt_manager_unittest.cc
+++ b/net/http/http_stream_pool_attempt_manager_unittest.cc
@@ -5165,7 +5165,9 @@
   ASSERT_EQ(quic_session1, quic_session2);
 }
 
-// Regression test for crbug.com/421877252.
+// Regression test for crbug.com/421877252. Ensure that a late-arriving QUIC
+// is used by subsequent requests after the first request completes with a
+// TLS session.
 TEST_F(HttpStreamPoolAttemptManagerTest,
        QuicExistingSessionAfterAttemptComplete) {
   base::test::ScopedFeatureList feature_list;
@@ -5192,11 +5194,6 @@
   EXPECT_THAT(requester1.result(), Optional(IsOk()));
   EXPECT_NE(requester1.negotiated_protocol(), NextProto::kProtoQUIC);
 
-  MockConnectCompleter quic_completer2;
-  AddQuicData(/*host=*/kDefaultDestination, &quic_completer2,
-              quic::QUIC_CONNECTION_IP_POOLED,
-              "An active session exists for the given IP.");
-
   SequencedSocketData tcp_data2;
   tcp_data2.set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   socket_factory()->AddSocketDataProvider(&tcp_data2);
@@ -5207,7 +5204,6 @@
       .RequestStream(pool());
 
   quic_completer1.Complete(OK);
-  quic_completer2.Complete(OK);
 
   requester2.WaitForResult();
   EXPECT_THAT(requester2.result(), Optional(IsOk()));
diff --git a/net/http/http_stream_pool_quic_attempt.cc b/net/http/http_stream_pool_quic_attempt.cc
index cb6e1063..a464dc8 100644
--- a/net/http/http_stream_pool_quic_attempt.cc
+++ b/net/http/http_stream_pool_quic_attempt.cc
@@ -25,6 +25,8 @@
 #include "net/log/net_log_source_type.h"
 #include "net/log/net_log_with_source.h"
 #include "net/quic/quic_session_alias_key.h"
+#include "net/quic/quic_session_attempt_manager.h"
+#include "net/quic/quic_session_attempt_request.h"
 #include "net/quic/quic_session_key.h"
 #include "net/quic/quic_session_pool.h"
 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
@@ -62,30 +64,8 @@
       NetLogEventType::HTTP_STREAM_POOL_ATTEMPT_MANAGER_QUIC_ATTEMPT_BOUND,
       net_log_.source());
 
-  SSLConfig ssl_config;
-  ssl_config.disable_cert_verification_network_fetches =
-      stream_key().disable_cert_network_fetches();
-  int cert_verify_flags = ssl_config.GetCertVerifyFlags();
-
-  base::TimeTicks dns_resolution_start_time =
-      manager_->dns_resolution_start_time();
-  // The DNS resolution end time could be null when the resolution is still
-  // ongoing. In that case, use the current time to make sure the connect
-  // start time is already greater than the DNS resolution end time.
-  base::TimeTicks dns_resolution_end_time =
-      manager_->dns_resolution_end_time().is_null()
-          ? base::TimeTicks::Now()
-          : manager_->dns_resolution_end_time();
-
-  std::set<std::string> dns_aliases =
-      manager_->service_endpoint_request()->GetDnsAliasResults();
-
-  session_attempt_ = GetQuicSessionPool()->CreateSessionAttempt(
-      this, GetKey().session_key(), quic_endpoint_, cert_verify_flags,
-      dns_resolution_start_time, dns_resolution_end_time,
-      /*use_dns_aliases=*/true, std::move(dns_aliases),
-      manager_->CalculateMultiplexedSessionCreationInitiator(),
-      /*connection_management_config=*/std::nullopt);
+  request_ =
+      GetQuicSessionPool()->session_attempt_manager()->CreateRequest(GetKey());
 }
 
 HttpStreamPool::QuicAttempt::~QuicAttempt() {
@@ -103,8 +83,31 @@
     manager_->MaybeRunTcpBasedAttemptDelayTimer();
   }
 
-  int rv = session_attempt_->Start(base::BindOnce(
-      &QuicAttempt::OnSessionAttemptComplete, weak_ptr_factory_.GetWeakPtr()));
+  SSLConfig ssl_config;
+  ssl_config.disable_cert_verification_network_fetches =
+      stream_key().disable_cert_network_fetches();
+  int cert_verify_flags = ssl_config.GetCertVerifyFlags();
+
+  base::TimeTicks dns_resolution_start_time =
+      manager_->dns_resolution_start_time();
+  // The DNS resolution end time could be null when the resolution is still
+  // ongoing. In that case, use the current time to make sure the connect
+  // start time is already greater than the DNS resolution end time.
+  base::TimeTicks dns_resolution_end_time =
+      manager_->dns_resolution_end_time().is_null()
+          ? base::TimeTicks::Now()
+          : manager_->dns_resolution_end_time();
+
+  std::set<std::string> dns_aliases =
+      manager_->service_endpoint_request()->GetDnsAliasResults();
+  int rv = request_->RequestSession(
+      quic_endpoint_, cert_verify_flags, dns_resolution_start_time,
+      dns_resolution_end_time, /*use_dns_aliases=*/true, std::move(dns_aliases),
+      manager_->CalculateMultiplexedSessionCreationInitiator(),
+      /*connection_management_config=*/std::nullopt, net_log_,
+      base::BindOnce(&QuicAttempt::OnSessionAttemptComplete,
+                     weak_ptr_factory_.GetWeakPtr()));
+
   if (rv == ERR_IO_PENDING) {
     slow_timer_.Start(FROM_HERE, HttpStreamPool::GetConnectionAttemptDelay(),
                       base::BindOnce(&QuicAttempt::OnSessionAttemptSlow,
@@ -165,11 +168,11 @@
 
   result_ = rv;
   QuicAttemptOutcome outcome(rv);
-  if (session_attempt_) {
-    outcome.session = session_attempt_->session();
-    session_attempt_->PopulateNetErrorDetails(&outcome.error_details);
+  if (request_) {
+    outcome.session = request_->session();
+    outcome.error_details = request_->error_details();
   }
-  session_attempt_.reset();
+  request_.reset();
   manager_->OnQuicAttemptComplete(std::move(outcome));
   // `this` is deleted.
 }
diff --git a/net/http/http_stream_pool_quic_attempt.h b/net/http/http_stream_pool_quic_attempt.h
index 585d50af2..6fd8968 100644
--- a/net/http/http_stream_pool_quic_attempt.h
+++ b/net/http/http_stream_pool_quic_attempt.h
@@ -18,6 +18,7 @@
 #include "net/http/http_stream_pool.h"
 #include "net/http/http_stream_pool_attempt_manager.h"
 #include "net/quic/quic_session_attempt.h"
+#include "net/quic/quic_session_attempt_request.h"
 #include "net/quic/quic_session_pool.h"
 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
 
@@ -67,7 +68,7 @@
   const perfetto::Track track_;
   const perfetto::Flow flow_;
 
-  std::unique_ptr<QuicSessionAttempt> session_attempt_;
+  std::unique_ptr<QuicSessionAttemptRequest> request_;
   base::OneShotTimer slow_timer_;
   bool is_slow_ = false;
   std::optional<int> result_;
diff --git a/net/http/http_stream_pool_tcp_based_attempt.cc b/net/http/http_stream_pool_tcp_based_attempt.cc
index 5b4091b..02f84fd 100644
--- a/net/http/http_stream_pool_tcp_based_attempt.cc
+++ b/net/http/http_stream_pool_tcp_based_attempt.cc
@@ -60,6 +60,8 @@
       return "ExistingSpdySession";
     case StreamSocketCloseReason::kUsingExistingQuicSession:
       return "ExistingQuicSession";
+    case StreamSocketCloseReason::kAttemptManagerDraining:
+      return "AttemptManagerDraining";
     case StreamSocketCloseReason::kUnspecified:
     case StreamSocketCloseReason::kCloseAllConnections:
     case StreamSocketCloseReason::kIpAddressChanged:
diff --git a/net/quic/quic_session_attempt.h b/net/quic/quic_session_attempt.h
index b702cdd..1781433 100644
--- a/net/quic/quic_session_attempt.h
+++ b/net/quic/quic_session_attempt.h
@@ -102,6 +102,12 @@
 
   bool session_creation_finished() const { return session_creation_finished_; }
 
+  const quic::ParsedQuicVersion& quic_version() const { return quic_version_; }
+
+  const IPEndPoint& ip_endpoint() const { return ip_endpoint_; }
+
+  const ConnectionEndpointMetadata& metadata() const { return metadata_; }
+
   QuicChromiumClientSession* session() const { return session_.get(); }
 
   void PopulateNetErrorDetails(NetErrorDetails* details) const;
diff --git a/net/quic/quic_session_attempt_manager.cc b/net/quic/quic_session_attempt_manager.cc
new file mode 100644
index 0000000..13fdf44
--- /dev/null
+++ b/net/quic/quic_session_attempt_manager.cc
@@ -0,0 +1,244 @@
+// 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 "net/quic/quic_session_attempt_manager.h"
+
+#include <memory>
+#include <optional>
+#include <set>
+#include <string>
+
+#include "base/check.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_export.h"
+#include "net/base/reconnect_notifier.h"
+#include "net/log/net_log_with_source.h"
+#include "net/quic/quic_endpoint.h"
+#include "net/quic/quic_session_alias_key.h"
+#include "net/quic/quic_session_attempt.h"
+#include "net/quic/quic_session_attempt_request.h"
+#include "net/quic/quic_session_pool.h"
+
+namespace net {
+
+// A Job is responsible for creating a QUIC session for a specific
+// QuicSessionAliasKey. It manages multiple concurrent connection attempts
+// (`QuicSessionAttempt`) to different endpoints and notifies multiple clients
+// (`QuicSessionAttemptRequest`) upon completion.
+//
+// If any attempt succeeds, the Job immediately notifies all waiting requests
+// and cancels any other ongoing attempts. If an attempt fails, the Job will
+// wait for other attempts to complete. Only when the last attempt fails does
+// the Job notify all waiting requests of the failure.
+//
+// The Job is owned by the QuicSessionAttemptManager and is destroyed once the
+// session is created or all attempts have failed.
+class QuicSessionAttemptManager::Job : public QuicSessionAttempt::Delegate {
+ public:
+  Job(QuicSessionAttemptManager* manager,
+      QuicSessionAliasKey key,
+      const NetLogWithSource& net_log)
+      : manager_(manager), key_(std::move(key)), net_log_(net_log) {}
+
+  ~Job() override {
+    // Notify all pending requests that the job is aborted.
+    if (!requests_.empty()) {
+      NotifyRequests(ERR_ABORTED, /*session=*/nullptr, NetErrorDetails());
+    }
+  }
+
+  Job(const Job&) = delete;
+  Job& operator=(const Job&) = delete;
+
+  // Attempts to create a QUIC session for the given endpoint. If an attempt
+  // already exists for the endpoint, returns ERR_IO_PENDING and the request
+  // will be notified when the attempt completes. Otherwise, a new attempt is
+  // created and started, and the request will be notified when the attempt
+  // completes.
+  //
+  // The request will be added to the job and notified upon completion.
+  int MaybeAttemptEndpoint(
+      QuicSessionAttemptRequest* request,
+      QuicEndpoint endpoint,
+      int cert_verify_flags,
+      base::TimeTicks dns_resolution_start_time,
+      base::TimeTicks dns_resolution_end_time,
+      bool use_dns_aliases,
+      std::set<std::string> dns_aliases,
+      MultiplexedSessionCreationInitiator session_creation_initiator,
+      std::optional<ConnectionManagementConfig> connection_management_config) {
+    AddRequest(request);
+
+    if (FindAttempt(endpoint)) {
+      return ERR_IO_PENDING;
+    }
+
+    std::unique_ptr<QuicSessionAttempt> attempt =
+        manager_->pool_->CreateSessionAttempt(
+            this, key_.session_key(), endpoint, cert_verify_flags,
+            dns_resolution_start_time, dns_resolution_end_time, use_dns_aliases,
+            std::move(dns_aliases), session_creation_initiator,
+            std::move(connection_management_config));
+    QuicSessionAttempt* raw_attempt = attempt.get();
+    auto [_, inserted] = attempts_.emplace(std::move(attempt));
+    CHECK(inserted);
+    int rv = raw_attempt->Start(base::BindOnce(
+        &Job::OnAttemptComplete, base::Unretained(this), raw_attempt));
+    if (rv != ERR_IO_PENDING) {
+      OnAttemptComplete(raw_attempt, rv);
+    }
+    return rv;
+  }
+
+  // Called by QuicSessionAttemptRequest to remove itself from the job.
+  void RemoveRequest(QuicSessionAttemptRequest* request) {
+    auto it = requests_.find(request);
+    CHECK(it != requests_.end());
+    requests_.erase(it);
+
+    if (requests_.empty()) {
+      manager_->OnJobComplete(this);
+      // `this` is deleted.
+    }
+  }
+
+  // QuicSessionAttempt::Delegate implementation.
+  QuicSessionPool* GetQuicSessionPool() override { return manager_->pool_; }
+  const QuicSessionAliasKey& GetKey() override { return key_; }
+  const NetLogWithSource& GetNetLog() override { return net_log_; }
+
+ private:
+  void OnAttemptComplete(QuicSessionAttempt* raw_attempt, int rv) {
+    auto it = attempts_.find(raw_attempt);
+    CHECK(it != attempts_.end());
+
+    NetErrorDetails error_details;
+    if (rv == OK) {
+      QuicChromiumClientSession* session = raw_attempt->session();
+      attempts_.erase(it);
+      NotifyRequestsAndComplete(rv, session, std::move(error_details));
+      return;
+    }
+
+    raw_attempt->PopulateNetErrorDetails(&error_details);
+    attempts_.erase(it);
+    if (!attempts_.empty()) {
+      // Wait for other attempts to complete.
+      return;
+    }
+
+    NotifyRequestsAndComplete(rv, /*session=*/nullptr,
+                              std::move(error_details));
+  }
+
+  void AddRequest(QuicSessionAttemptRequest* request) {
+    auto [_, inserted] = requests_.insert(request);
+    CHECK(inserted);
+  }
+
+  QuicSessionAttempt* FindAttempt(const QuicEndpoint& endpoint) {
+    auto it = std::ranges::find_if(attempts_, [&](const auto& attempt) {
+      return attempt->quic_version() == endpoint.quic_version &&
+             attempt->ip_endpoint() == endpoint.ip_endpoint &&
+             attempt->metadata() == endpoint.metadata;
+    });
+    return it == attempts_.end() ? nullptr : it->get();
+  }
+
+  // Notifies all requests that the job is complete.
+  void NotifyRequests(int rv,
+                      QuicChromiumClientSession* session,
+                      NetErrorDetails error_details) {
+    // Cancel other attempts.
+    attempts_.clear();
+
+    while (!requests_.empty()) {
+      raw_ptr<QuicSessionAttemptRequest> request =
+          requests_.extract(requests_.begin()).value();
+      // Use ExtractAsDangling() because `request` may delete itself.
+      request.ExtractAsDangling()->Complete(rv, session, error_details);
+    }
+    CHECK(requests_.empty());
+  }
+
+  void NotifyRequestsAndComplete(int rv,
+                                 QuicChromiumClientSession* session,
+                                 NetErrorDetails error_details) {
+    NotifyRequests(rv, session, std::move(error_details));
+    manager_->OnJobComplete(this);
+    // `this` is deleted.
+  }
+
+  raw_ptr<QuicSessionAttemptManager> manager_;
+  QuicSessionAliasKey key_;
+
+  NetLogWithSource net_log_;
+
+  std::set<raw_ptr<QuicSessionAttemptRequest>> requests_;
+
+  base::flat_set<std::unique_ptr<QuicSessionAttempt>, base::UniquePtrComparator>
+      attempts_;
+};
+
+QuicSessionAttemptManager::QuicSessionAttemptManager(QuicSessionPool* pool)
+    : pool_(pool) {}
+
+QuicSessionAttemptManager::~QuicSessionAttemptManager() {
+  // Clear the active jobs, first moving out of the instance variable so that
+  // calls to RemoveRequest for any pending requests do not cause recursion.
+  base::flat_map<QuicSessionAliasKey, std::unique_ptr<Job>> active_jobs =
+      std::move(active_jobs_);
+  active_jobs.clear();
+}
+
+std::unique_ptr<QuicSessionAttemptRequest>
+QuicSessionAttemptManager::CreateRequest(QuicSessionAliasKey key) {
+  return base::WrapUnique(new QuicSessionAttemptRequest(this, std::move(key)));
+}
+
+int QuicSessionAttemptManager::RequestSession(
+    QuicSessionAttemptRequest* request,
+    QuicEndpoint endpoint,
+    int cert_verify_flags,
+    base::TimeTicks dns_resolution_start_time,
+    base::TimeTicks dns_resolution_end_time,
+    bool use_dns_aliases,
+    std::set<std::string> dns_aliases,
+    MultiplexedSessionCreationInitiator session_creation_initiator,
+    std::optional<ConnectionManagementConfig> connection_management_config,
+    const NetLogWithSource& net_log) {
+  auto it = active_jobs_.find(request->key_);
+  if (it == active_jobs_.end()) {
+    it = active_jobs_
+             .try_emplace(request->key_,
+                          std::make_unique<Job>(this, request->key_, net_log))
+             .first;
+  }
+
+  return it->second->MaybeAttemptEndpoint(
+      request, endpoint, cert_verify_flags, dns_resolution_start_time,
+      dns_resolution_end_time, use_dns_aliases, std::move(dns_aliases),
+      session_creation_initiator, std::move(connection_management_config));
+}
+
+void QuicSessionAttemptManager::RemoveRequest(
+    QuicSessionAttemptRequest* request) {
+  auto it = active_jobs_.find(request->key_);
+  if (it == active_jobs_.end()) {
+    return;
+  }
+  it->second->RemoveRequest(request);
+}
+
+void QuicSessionAttemptManager::OnJobComplete(Job* job) {
+  auto it = active_jobs_.find(job->GetKey());
+  CHECK(it != active_jobs_.end());
+  active_jobs_.erase(it);
+}
+
+}  // namespace net
diff --git a/net/quic/quic_session_attempt_manager.h b/net/quic/quic_session_attempt_manager.h
new file mode 100644
index 0000000..79c6579bb
--- /dev/null
+++ b/net/quic/quic_session_attempt_manager.h
@@ -0,0 +1,92 @@
+// 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 NET_QUIC_QUIC_SESSION_ATTEMPT_MANAGER_H_
+#define NET_QUIC_QUIC_SESSION_ATTEMPT_MANAGER_H_
+
+#include <memory>
+#include <optional>
+#include <set>
+#include <string>
+
+#include "base/containers/flat_map.h"
+#include "base/memory/raw_ptr.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+#include "net/base/reconnect_notifier.h"
+#include "net/quic/quic_endpoint.h"
+#include "net/quic/quic_session_alias_key.h"
+#include "net/quic/quic_session_pool.h"
+
+namespace net {
+
+class QuicSessionAttemptRequest;
+class NetLogWithSource;
+
+// Manages all in-flight QUIC session attempts. For each QuicSessionAliasKey
+// that a client has requested, there can be at most one active
+// QuicSessionAttemptManager::Job. A Job manages all attempts for the
+// QuicSessionAliasKey (e.g. to different IP addresses) and all clients waiting
+// for the result.
+//
+// The relationship between the manager, jobs, requests, and attempts is as
+// follows:
+//
+//        +-------------- QuicSessionAttemptManager -------------+
+//        |                         |                            |
+//       Job                       Job                          Job
+//    (for Key1)                (for Key2)                   (for KeyX)
+//    /       \                 /       \                    /       \
+// Requests   Attempts       Requests   Attempts         Requests   Attempts
+//    |          |              |          |                |          |
+// Request... Attempt...    Request... Attempt...        Request... Attempt...
+// (client A) (endpoint 1)  (client C) (endpoint 3)      (client X) (endpoint X)
+// (client B) (endpoint 2)             (endpoint 4)
+//
+// Owned by the QuicSessionPool.
+class NET_EXPORT_PRIVATE QuicSessionAttemptManager {
+ public:
+  explicit QuicSessionAttemptManager(QuicSessionPool* pool);
+
+  ~QuicSessionAttemptManager();
+
+  // Creates a new QuicSessionAttemptRequest for the given key.
+  std::unique_ptr<QuicSessionAttemptRequest> CreateRequest(
+      QuicSessionAliasKey key);
+
+  // Called by QuicSessionAttemptRequest to request a session. See
+  // QuicSessionAttemptRequest for more details.
+  int RequestSession(
+      QuicSessionAttemptRequest* request,
+      QuicEndpoint endpoint,
+      int cert_verify_flags,
+      base::TimeTicks dns_resolution_start_time,
+      base::TimeTicks dns_resolution_end_time,
+      bool use_dns_aliases,
+      std::set<std::string> dns_aliases,
+      MultiplexedSessionCreationInitiator session_creation_initiator,
+      std::optional<ConnectionManagementConfig> connection_management_config,
+      const NetLogWithSource& net_log);
+
+  // Called by QuicSessionAttemptRequest to remove itself from the manager.
+  void RemoveRequest(QuicSessionAttemptRequest* request);
+
+  bool HasActiveJobForTesting(const QuicSessionAliasKey& key) const {
+    return active_jobs_.find(key) != active_jobs_.end();
+  }
+
+ private:
+  class Job;
+
+  // Called by Job when the last request is completed.
+  void OnJobComplete(Job* job);
+
+  const raw_ptr<QuicSessionPool> pool_;
+
+  base::flat_map<QuicSessionAliasKey, std::unique_ptr<Job>> active_jobs_;
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_QUIC_SESSION_ATTEMPT_MANAGER_H_
diff --git a/net/quic/quic_session_attempt_manager_unittest.cc b/net/quic/quic_session_attempt_manager_unittest.cc
new file mode 100644
index 0000000..5d105ae
--- /dev/null
+++ b/net/quic/quic_session_attempt_manager_unittest.cc
@@ -0,0 +1,480 @@
+// 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 "net/quic/quic_session_attempt_manager.h"
+
+#include <memory>
+#include <optional>
+#include <set>
+
+#include "base/functional/callback_forward.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/time/time.h"
+#include "net/base/connection_endpoint_metadata.h"
+#include "net/base/features.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_error_details.h"
+#include "net/base/net_errors.h"
+#include "net/base/network_anonymization_key.h"
+#include "net/base/privacy_mode.h"
+#include "net/base/proxy_chain.h"
+#include "net/base/reconnect_notifier.h"
+#include "net/base/session_usage.h"
+#include "net/dns/public/secure_dns_policy.h"
+#include "net/log/net_log_source_type.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/mock_quic_data.h"
+#include "net/quic/quic_chromium_client_session.h"
+#include "net/quic/quic_endpoint.h"
+#include "net/quic/quic_session_alias_key.h"
+#include "net/quic/quic_session_attempt_request.h"
+#include "net/quic/quic_session_pool_test_base.h"
+#include "net/socket/socket_tag.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/multiplexed_session_creation_initiator.h"
+#include "net/test/gtest_util.h"
+#include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/scheme_host_port.h"
+#include "url/url_constants.h"
+
+namespace net::test {
+
+namespace {
+
+IPEndPoint MakeIPEndPoint(std::string_view addr, uint16_t port = 443) {
+  return IPEndPoint(*IPAddress::FromIPLiteral(addr), port);
+}
+
+class SessionRequester {
+ public:
+  explicit SessionRequester(QuicSessionAttemptManager* manager,
+                            quic::ParsedQuicVersion version)
+      : manager_(manager),
+        quic_version_(version),
+        net_log_(NetLogWithSource::Make(NetLog::Get(),
+                                        NetLogSourceType::URL_REQUEST)) {}
+
+  SessionRequester(SessionRequester&&) = default;
+  SessionRequester& operator=(SessionRequester&&) = default;
+  SessionRequester(const SessionRequester&) = delete;
+  SessionRequester& operator=(const SessionRequester&) = delete;
+
+  ~SessionRequester() = default;
+
+  SessionRequester& SetIPEndPoint(IPEndPoint ip_endpoint) {
+    endpoint_ = QuicEndpoint(quic_version_, std::move(ip_endpoint),
+                             ConnectionEndpointMetadata());
+    return *this;
+  }
+
+  int Request() {
+    QuicSessionAliasKey key(
+        destination_,
+        QuicSessionKey(destination_.host(), destination_.port(), privacy_mode_,
+                       proxy_chain_, session_usage_, socket_tag_,
+                       network_anonymization_key_, secure_dns_policy_,
+                       require_dns_https_alpn_));
+    request_ = manager_->CreateRequest(key);
+    int rv = request_->RequestSession(
+        endpoint_, cert_verify_flags_, dns_resolution_start_time_,
+        dns_resolution_end_time_, /*use_dns_aliases=*/true, dns_aliases_,
+        session_creation_initiator_, connection_management_config_, net_log_,
+        base::BindOnce(&SessionRequester::OnComplete, base::Unretained(this)));
+    if (rv != ERR_IO_PENDING) {
+      OnComplete(rv);
+    }
+    return rv;
+  }
+
+  int WaitForResult() {
+    if (result_.has_value()) {
+      return *result_;
+    }
+
+    CHECK(!wait_for_result_closure_);
+    base::RunLoop run_loop;
+    wait_for_result_closure_ = run_loop.QuitClosure();
+    run_loop.Run();
+    CHECK(result_.has_value());
+    return *result_;
+  }
+
+  void ResetRequest() { request_.reset(); }
+
+  const QuicSessionAliasKey& key() const {
+    CHECK(request_);
+    return request_->key();
+  }
+
+  std::optional<int> result() const { return result_; }
+  const QuicChromiumClientSession* session() const { return session_; }
+  const NetErrorDetails& error_details() const { return error_details_; }
+
+ private:
+  void OnComplete(int result) {
+    CHECK(!result_.has_value());
+    CHECK(request_);
+
+    result_ = result;
+    if (result_ == OK) {
+      session_ = request_->session();
+    } else {
+      error_details_ = request_->error_details();
+    }
+    request_.reset();
+    // Clear the request to avoid dangling pointer.
+    manager_ = nullptr;
+
+    if (wait_for_result_closure_) {
+      std::move(wait_for_result_closure_).Run();
+    }
+  }
+
+  raw_ptr<QuicSessionAttemptManager> manager_;
+  quic::ParsedQuicVersion quic_version_;
+
+  // For calculating the session key.
+  url::SchemeHostPort destination_{
+      url::kHttpsScheme, QuicSessionPoolTestBase::kDefaultServerHostName,
+      QuicSessionPoolTestBase::kDefaultServerPort};
+  PrivacyMode privacy_mode_ = PRIVACY_MODE_DISABLED;
+  ProxyChain proxy_chain_ = ProxyChain::Direct();
+  SessionUsage session_usage_ = SessionUsage::kDestination;
+  SocketTag socket_tag_;
+  NetworkAnonymizationKey network_anonymization_key_;
+  SecureDnsPolicy secure_dns_policy_ = SecureDnsPolicy::kAllow;
+  bool require_dns_https_alpn_ = false;
+
+  // For calling RequestSession().
+  QuicEndpoint endpoint_{quic_version_,
+                         IPEndPoint(IPAddress::IPv4Localhost(), 443),
+                         ConnectionEndpointMetadata()};
+  int cert_verify_flags_ = 0;
+  base::TimeTicks dns_resolution_start_time_ = base::TimeTicks::Now();
+  base::TimeTicks dns_resolution_end_time_ = base::TimeTicks::Now();
+  std::set<std::string> dns_aliases_;
+  MultiplexedSessionCreationInitiator session_creation_initiator_ =
+      MultiplexedSessionCreationInitiator::kUnknown;
+  std::optional<ConnectionManagementConfig> connection_management_config_;
+  NetLogWithSource net_log_;
+
+  std::unique_ptr<QuicSessionAttemptRequest> request_;
+
+  base::OnceClosure wait_for_result_closure_;
+
+  std::optional<int> result_;
+  raw_ptr<QuicChromiumClientSession> session_ = nullptr;
+  NetErrorDetails error_details_;
+};
+
+}  // namespace
+
+class QuicSessionAttemptManagerTest
+    : public QuicSessionPoolTestBase,
+      public ::testing::TestWithParam<quic::ParsedQuicVersion> {
+ protected:
+  QuicSessionAttemptManagerTest() : QuicSessionPoolTestBase(GetParam()) {}
+
+  void InitializeWithDefaultProofVerifyDetails() {
+    Initialize();
+    crypto_client_stream_factory_.AddProofVerifyDetails(
+        &default_verify_details_);
+  }
+
+  SessionRequester CreateRequester() {
+    return SessionRequester(pool_->session_attempt_manager(), GetParam());
+  }
+
+  QuicSessionAttemptManager* session_attempt_manager() {
+    return pool_->session_attempt_manager();
+  }
+
+ private:
+  ProofVerifyDetailsChromium default_verify_details_ =
+      DefaultProofVerifyDetails();
+};
+
+INSTANTIATE_TEST_SUITE_P(/**/,
+                         QuicSessionAttemptManagerTest,
+                         ::testing::ValuesIn(AllSupportedQuicVersions()));
+
+TEST_P(QuicSessionAttemptManagerTest, RequestSessionSync) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(net::features::kAsyncQuicSession);
+
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data(version_);
+  socket_data.AddReadPauseForever();
+  socket_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  SessionRequester requester = CreateRequester();
+  int result = requester.Request();
+  EXPECT_THAT(result, IsOk());
+  EXPECT_TRUE(requester.session());
+}
+
+TEST_P(QuicSessionAttemptManagerTest, RequestSessionAsync) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data(version_);
+  socket_data.AddReadPauseForever();
+  socket_data.AddWrite(ASYNC, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  SessionRequester requester = CreateRequester();
+  int result = requester.Request();
+  EXPECT_THAT(result, IsError(ERR_IO_PENDING));
+
+  result = requester.WaitForResult();
+  EXPECT_THAT(result, IsOk());
+}
+
+TEST_P(QuicSessionAttemptManagerTest, MultipleRequestsSameSessionSuccess) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data(version_);
+  socket_data.AddReadPauseForever();
+  socket_data.AddWrite(ASYNC, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create three requesters for the same session.
+  SessionRequester requester1 = CreateRequester();
+  SessionRequester requester2 = CreateRequester();
+  SessionRequester requester3 = CreateRequester();
+
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester3.Request(), IsError(ERR_IO_PENDING));
+
+  // All requesters should succeed and get the same session.
+  EXPECT_THAT(requester1.WaitForResult(), IsOk());
+  EXPECT_THAT(requester2.WaitForResult(), IsOk());
+  EXPECT_THAT(requester3.WaitForResult(), IsOk());
+
+  EXPECT_TRUE(requester1.session());
+  EXPECT_EQ(requester1.session(), requester2.session());
+  EXPECT_EQ(requester1.session(), requester3.session());
+}
+
+TEST_P(QuicSessionAttemptManagerTest, MultipleRequestsSameSessionFailure) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data(version_);
+  socket_data.AddConnect(ASYNC, ERR_ADDRESS_IN_USE);
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create three requesters for the same session.
+  SessionRequester requester1 = CreateRequester();
+  SessionRequester requester2 = CreateRequester();
+  SessionRequester requester3 = CreateRequester();
+
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester3.Request(), IsError(ERR_IO_PENDING));
+
+  // All requesters should fail with the same error.
+  EXPECT_THAT(requester1.WaitForResult(), IsError(ERR_ADDRESS_IN_USE));
+  EXPECT_THAT(requester2.WaitForResult(), IsError(ERR_ADDRESS_IN_USE));
+  EXPECT_THAT(requester3.WaitForResult(), IsError(ERR_ADDRESS_IN_USE));
+
+  EXPECT_FALSE(requester1.session());
+  EXPECT_FALSE(requester2.session());
+  EXPECT_FALSE(requester3.session());
+}
+
+// Test multiple endpoints for the same session: One succeeds, one fails.
+TEST_P(QuicSessionAttemptManagerTest, MultipleEndpointsSuccessAndFailure) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  // Create two mock sockets for two different endpoints.
+  MockQuicData socket_data1(version_);
+  socket_data1.AddConnect(ASYNC, ERR_CONNECTION_FAILED);
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  MockQuicData socket_data2(version_);
+  socket_data2.AddReadPauseForever();
+  socket_data2.AddWrite(ASYNC, ConstructInitialSettingsPacket());
+  socket_data2.AddSocketDataToFactory(socket_factory_.get());
+
+  SessionRequester requester1 = CreateRequester();
+  requester1.SetIPEndPoint(MakeIPEndPoint("192.0.2.1"));
+  SessionRequester requester2 = CreateRequester();
+  requester2.SetIPEndPoint(MakeIPEndPoint("192.0.2.2"));
+
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+
+  // First endpoint fails but second succeeds, so both requesters should
+  // receive the successful session.
+  EXPECT_THAT(requester1.WaitForResult(), IsOk());
+  EXPECT_THAT(requester2.WaitForResult(), IsOk());
+
+  EXPECT_TRUE(requester1.session());
+  EXPECT_TRUE(requester2.session());
+  EXPECT_EQ(requester1.session(), requester2.session());
+}
+
+// Test multiple endpoints for the same session: All fail.
+TEST_P(QuicSessionAttemptManagerTest, MultipleEndpointsAllFail) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  // Create three mock sockets for three different endpoints, all fail
+  MockQuicData socket_data1(version_);
+  socket_data1.AddConnect(ASYNC, ERR_CONNECTION_FAILED);
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  MockQuicData socket_data2(version_);
+  socket_data2.AddConnect(ASYNC, ERR_ADDRESS_UNREACHABLE);
+  socket_data2.AddSocketDataToFactory(socket_factory_.get());
+
+  MockQuicData socket_data3(version_);
+  socket_data3.AddConnect(ASYNC, ERR_CONNECTION_REFUSED);
+  socket_data3.AddSocketDataToFactory(socket_factory_.get());
+
+  SessionRequester requester1 = CreateRequester();
+  requester1.SetIPEndPoint(MakeIPEndPoint("2001:db8::1"));
+  SessionRequester requester2 = CreateRequester();
+  requester2.SetIPEndPoint(MakeIPEndPoint("2001:db8::2"));
+  SessionRequester requester3 = CreateRequester();
+  requester3.SetIPEndPoint(MakeIPEndPoint("2001:db8::3"));
+
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester3.Request(), IsError(ERR_IO_PENDING));
+
+  // All should fail with the last error.
+  EXPECT_THAT(requester1.WaitForResult(), IsError(ERR_CONNECTION_REFUSED));
+  EXPECT_THAT(requester2.WaitForResult(), IsError(ERR_CONNECTION_REFUSED));
+  EXPECT_THAT(requester3.WaitForResult(), IsError(ERR_CONNECTION_REFUSED));
+
+  // No sessions should be created.
+  EXPECT_FALSE(requester1.session());
+  EXPECT_FALSE(requester2.session());
+  EXPECT_FALSE(requester3.session());
+}
+
+// Test multiple endpoints for the same session: All sessions succeed,
+// but only the first one is used.
+TEST_P(QuicSessionAttemptManagerTest, MultipleEndpointsAllSuccess) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data1(version_);
+  socket_data1.AddReadPauseForever();
+  socket_data1.AddWrite(ASYNC, ConstructInitialSettingsPacket());
+  socket_data1.AddSocketDataToFactory(socket_factory_.get());
+
+  // This would succeed but should be cancelled.
+  MockQuicData socket_data2(version_);
+  socket_data2.AddReadPauseForever();
+  socket_data2.AddWrite(ASYNC, ConstructInitialSettingsPacket());
+  socket_data2.AddSocketDataToFactory(socket_factory_.get());
+
+  SessionRequester requester1 = CreateRequester();
+  requester1.SetIPEndPoint(MakeIPEndPoint("2001:db8::1"));
+  SessionRequester requester2 = CreateRequester();
+  requester2.SetIPEndPoint(MakeIPEndPoint("192.0.2.1"));
+
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+
+  // Both should succeed and have the same session.
+  EXPECT_THAT(requester1.WaitForResult(), IsOk());
+  EXPECT_THAT(requester2.WaitForResult(), IsOk());
+
+  EXPECT_TRUE(requester1.session());
+  EXPECT_TRUE(requester2.session());
+  EXPECT_EQ(requester1.session(), requester2.session());
+}
+
+TEST_P(QuicSessionAttemptManagerTest, JobCompletesWhenAllRequestsCancelled) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data(version_);
+  socket_data.AddConnect(ASYNC, ERR_IO_PENDING);
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create multiple pending requests.
+  SessionRequester requester1 = CreateRequester();
+  SessionRequester requester2 = CreateRequester();
+  SessionRequester requester3 = CreateRequester();
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester3.Request(), IsError(ERR_IO_PENDING));
+
+  const QuicSessionAliasKey key = requester1.key();
+
+  // Cancel first two requests.
+  requester1.ResetRequest();
+  requester2.ResetRequest();
+
+  // At this point, the Job should still exist because requester3 is active.
+  EXPECT_TRUE(session_attempt_manager()->HasActiveJobForTesting(key));
+
+  // Cancel the last request. This should destroy the Job.
+  requester3.ResetRequest();
+  EXPECT_FALSE(session_attempt_manager()->HasActiveJobForTesting(key));
+}
+
+TEST_P(QuicSessionAttemptManagerTest, CancelSomeRequestsWhileOthersComplete) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data(version_);
+  socket_data.AddReadPauseForever();
+  socket_data.AddWrite(ASYNC, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create multiple pending requests.
+  SessionRequester requester1 = CreateRequester();
+  SessionRequester requester2 = CreateRequester();
+  SessionRequester requester3 = CreateRequester();
+  SessionRequester requester4 = CreateRequester();
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester3.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester4.Request(), IsError(ERR_IO_PENDING));
+
+  // Cancel two requests before connection completes.
+  requester1.ResetRequest();
+  requester2.ResetRequest();
+
+  // Complete connection attempt. Remaining requests should succeed.
+  EXPECT_THAT(requester3.WaitForResult(), IsOk());
+  EXPECT_THAT(requester4.WaitForResult(), IsOk());
+
+  EXPECT_TRUE(requester3.session());
+  EXPECT_TRUE(requester4.session());
+  EXPECT_EQ(requester3.session(), requester4.session());
+}
+
+TEST_P(QuicSessionAttemptManagerTest, DestroyManagerWithPendingRequests) {
+  InitializeWithDefaultProofVerifyDetails();
+
+  MockQuicData socket_data(version_);
+  socket_data.AddReadPauseForever();
+  socket_data.AddWrite(ASYNC, ConstructInitialSettingsPacket());
+  socket_data.AddSocketDataToFactory(socket_factory_.get());
+
+  // Create multiple pending requests.
+  SessionRequester requester1 = CreateRequester();
+  SessionRequester requester2 = CreateRequester();
+  SessionRequester requester3 = CreateRequester();
+
+  EXPECT_THAT(requester1.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester2.Request(), IsError(ERR_IO_PENDING));
+  EXPECT_THAT(requester3.Request(), IsError(ERR_IO_PENDING));
+
+  // Destroy the pool (which contains the manager) while requests are
+  // pending.
+  pool_.reset();
+
+  EXPECT_THAT(requester1.WaitForResult(), IsError(ERR_ABORTED));
+  EXPECT_THAT(requester2.WaitForResult(), IsError(ERR_ABORTED));
+  EXPECT_THAT(requester3.WaitForResult(), IsError(ERR_ABORTED));
+}
+
+}  // namespace net::test
diff --git a/net/quic/quic_session_attempt_request.cc b/net/quic/quic_session_attempt_request.cc
new file mode 100644
index 0000000..12c7b0df
--- /dev/null
+++ b/net/quic/quic_session_attempt_request.cc
@@ -0,0 +1,70 @@
+// 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 "net/quic/quic_session_attempt_request.h"
+
+#include <optional>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/check.h"
+#include "net/base/completion_once_callback.h"
+#include "net/base/net_error_details.h"
+#include "net/base/net_errors.h"
+#include "net/quic/quic_session_attempt_manager.h"
+
+namespace net {
+
+QuicSessionAttemptRequest::QuicSessionAttemptRequest(
+    QuicSessionAttemptManager* manager,
+    QuicSessionAliasKey key)
+    : manager_(manager), key_(std::move(key)) {}
+
+QuicSessionAttemptRequest::~QuicSessionAttemptRequest() {
+  if (manager_ && callback_) {
+    manager_->RemoveRequest(this);
+  }
+}
+
+int QuicSessionAttemptRequest::RequestSession(
+    QuicEndpoint endpoint,
+    int cert_verify_flags,
+    base::TimeTicks dns_resolution_start_time,
+    base::TimeTicks dns_resolution_end_time,
+    bool use_dns_aliases,
+    std::set<std::string> dns_aliases,
+    MultiplexedSessionCreationInitiator session_creation_initiator,
+    std::optional<ConnectionManagementConfig> connection_management_config,
+    const NetLogWithSource& net_log,
+    CompletionOnceCallback callback) {
+  int rv = manager_->RequestSession(
+      this, std::move(endpoint), cert_verify_flags, dns_resolution_start_time,
+      dns_resolution_end_time, use_dns_aliases, std::move(dns_aliases),
+      session_creation_initiator, std::move(connection_management_config),
+      net_log);
+  if (rv == ERR_IO_PENDING) {
+    CHECK(!completed_);
+    callback_ = std::move(callback);
+  } else {
+    CHECK(completed_);
+  }
+  return rv;
+}
+
+void QuicSessionAttemptRequest::Complete(int rv,
+                                         QuicChromiumClientSession* session,
+                                         NetErrorDetails error_details) {
+  CHECK(!completed_);
+  completed_ = true;
+  session_ = session;
+  error_details_ = std::move(error_details);
+
+  manager_ = nullptr;
+  if (callback_) {
+    std::move(callback_).Run(rv);
+  }
+}
+
+}  // namespace net
diff --git a/net/quic/quic_session_attempt_request.h b/net/quic/quic_session_attempt_request.h
new file mode 100644
index 0000000..77e1cd51
--- /dev/null
+++ b/net/quic/quic_session_attempt_request.h
@@ -0,0 +1,89 @@
+// 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 NET_QUIC_QUIC_SESSION_ATTEMPT_REQUEST_H_
+#define NET_QUIC_QUIC_SESSION_ATTEMPT_REQUEST_H_
+
+#include <optional>
+#include <set>
+#include <string>
+
+#include "base/check.h"
+#include "base/memory/raw_ptr.h"
+#include "net/base/completion_once_callback.h"
+#include "net/base/net_error_details.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_session_alias_key.h"
+#include "net/quic/quic_session_pool.h"
+
+namespace net {
+
+class QuicSessionAttemptManager;
+
+// Represents a request to attempt creation of a new QUIC session. This class
+// is owned by the creator of the request. If the request is still pending when
+// the request is destroyed, it will be cancelled.
+class NET_EXPORT_PRIVATE QuicSessionAttemptRequest {
+ public:
+  QuicSessionAttemptRequest(const QuicSessionAttemptRequest&) = delete;
+  QuicSessionAttemptRequest& operator=(const QuicSessionAttemptRequest&) =
+      delete;
+
+  ~QuicSessionAttemptRequest();
+
+  // Requests a QUIC session. If the request is completed synchronously, returns
+  // the result. If the request is completed asynchronously, returns
+  // ERR_IO_PENDING and `callback` will be invoked later. See also
+  // `QuicSessionAttempt`.
+  int RequestSession(
+      QuicEndpoint endpoint,
+      int cert_verify_flags,
+      base::TimeTicks dns_resolution_start_time,
+      base::TimeTicks dns_resolution_end_time,
+      bool use_dns_aliases,
+      std::set<std::string> dns_aliases,
+      MultiplexedSessionCreationInitiator session_creation_initiator,
+      std::optional<ConnectionManagementConfig> connection_management_config,
+      const NetLogWithSource& net_log,
+      CompletionOnceCallback callback);
+
+  const QuicSessionAliasKey& key() const { return key_; }
+
+  // Returns the error details of the request. Populated only if the request is
+  // failed. Only valid to call after the request is completed.
+  const NetErrorDetails& error_details() const {
+    CHECK(completed_);
+    return error_details_;
+  }
+
+  // Returns the session of the request. Can be nullptr if the request is
+  // failed. Only valid to call after the request is completed.
+  raw_ptr<QuicChromiumClientSession> session() const {
+    CHECK(completed_);
+    return session_;
+  }
+
+ private:
+  friend class QuicSessionAttemptManager;
+
+  explicit QuicSessionAttemptRequest(QuicSessionAttemptManager* manager,
+                                     QuicSessionAliasKey key);
+
+  void Complete(int rv,
+                QuicChromiumClientSession* session,
+                NetErrorDetails error_details);
+
+  raw_ptr<QuicSessionAttemptManager> manager_;
+  const QuicSessionAliasKey key_;
+
+  bool completed_ = false;
+  CompletionOnceCallback callback_;
+
+  NetErrorDetails error_details_;
+  raw_ptr<QuicChromiumClientSession> session_;
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_QUIC_SESSION_ATTEMPT_REQUEST_H_
diff --git a/net/quic/quic_session_pool.cc b/net/quic/quic_session_pool.cc
index aa02cdbf..7b13f539 100644
--- a/net/quic/quic_session_pool.cc
+++ b/net/quic/quic_session_pool.cc
@@ -64,6 +64,7 @@
 #include "net/quic/quic_context.h"
 #include "net/quic/quic_crypto_client_stream_factory.h"
 #include "net/quic/quic_server_info.h"
+#include "net/quic/quic_session_attempt_manager.h"
 #include "net/quic/quic_session_key.h"
 #include "net/quic/quic_session_pool_direct_job.h"
 #include "net/quic/quic_session_pool_job.h"
@@ -656,7 +657,9 @@
           quic_context->params()->skip_dns_with_origin_frame),
       ignore_ip_matching_when_finding_existing_sessions_(
           quic_context->params()
-              ->ignore_ip_matching_when_finding_existing_sessions) {
+              ->ignore_ip_matching_when_finding_existing_sessions),
+      session_attempt_manager_(
+          std::make_unique<QuicSessionAttemptManager>(this)) {
   DCHECK(transport_security_state_);
   DCHECK(http_server_properties_);
   if (params_.disable_tls_zero_rtt) {
@@ -673,6 +676,10 @@
   CloseAllSessions(ERR_ABORTED, quic::QUIC_CONNECTION_CANCELLED);
   all_sessions_.clear();
 
+  // Reset session attempt manager to ensure there is no active crypto config
+  // map.
+  session_attempt_manager_.reset();
+
   // Clear the active jobs, first moving out of the instance variable so that
   // calls to CancelRequest for any pending requests do not cause recursion.
   JobMap active_jobs = std::move(active_jobs_);
diff --git a/net/quic/quic_session_pool.h b/net/quic/quic_session_pool.h
index 6281b5da..93b34b2 100644
--- a/net/quic/quic_session_pool.h
+++ b/net/quic/quic_session_pool.h
@@ -97,6 +97,7 @@
 class QuicCryptoClientStreamFactory;
 class QuicServerInfo;
 class QuicSessionPool;
+class QuicSessionAttemptManager;
 class QuicContext;
 class SCTAuditingDelegate;
 class SocketPerformanceWatcherFactory;
@@ -540,6 +541,10 @@
     return host_resolver_->IsHappyEyeballsV3Enabled();
   }
 
+  QuicSessionAttemptManager* session_attempt_manager() {
+    return session_attempt_manager_.get();
+  }
+
   struct QuicCryptoClientConfigKey;
 
  private:
@@ -919,6 +924,8 @@
   quic::DeterministicConnectionIdGenerator connection_id_generator_{
       quic::kQuicDefaultConnectionIdLength};
 
+  std::unique_ptr<QuicSessionAttemptManager> session_attempt_manager_;
+
   std::optional<base::TimeDelta> time_delay_for_waiting_job_for_testing_;
 
   base::WeakPtrFactory<QuicSessionPool> weak_factory_{this};
diff --git a/net/socket/stream_socket_close_reason.cc b/net/socket/stream_socket_close_reason.cc
index 29410ab..fda9451 100644
--- a/net/socket/stream_socket_close_reason.cc
+++ b/net/socket/stream_socket_close_reason.cc
@@ -31,6 +31,8 @@
       return "UsingExistingQuicSession";
     case StreamSocketCloseReason::kAbort:
       return "Abort";
+    case StreamSocketCloseReason::kAttemptManagerDraining:
+      return "AttemptManagerDraining";
   }
 }
 
diff --git a/net/socket/stream_socket_close_reason.h b/net/socket/stream_socket_close_reason.h
index 39ba86ac..58b90cb 100644
--- a/net/socket/stream_socket_close_reason.h
+++ b/net/socket/stream_socket_close_reason.h
@@ -25,7 +25,8 @@
   kUsingExistingSpdySession = 7,
   kUsingExistingQuicSession = 8,
   kAbort = 9,
-  kMaxValue = kAbort,
+  kAttemptManagerDraining = 10,
+  kMaxValue = kAttemptManagerDraining,
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:StreamSocketCloseReason)
 
diff --git a/net/third_party/quiche/src b/net/third_party/quiche/src
index 203951a..1852058 160000
--- a/net/third_party/quiche/src
+++ b/net/third_party/quiche/src
@@ -1 +1 @@
-Subproject commit 203951a0d6e8a190839bf7a59e00d4df1b45835f
+Subproject commit 1852058ed84fc551bd4cb9dba8330446d7d02411
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index e77a21b..c479d1ec 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -306,7 +306,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/13738694/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/13739506/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index ec6731de..d8d459ea 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -3386,6 +3386,8 @@
       optional BackendNodeId backendNodeId
       # JavaScript object id of the node wrapper.
       optional Runtime.RemoteObjectId objectId
+      # Include all shadow roots. Equals to false if not specified.
+      experimental optional boolean includeShadowDOM
     returns
       # Outer HTML markup.
       string outerHTML
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 368f1661..b4a5aea 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -4912,6 +4912,7 @@
   kHTMLCanvasElementLowLatency_WebGL = 5602,
   kHTMLCanvasElementLowLatency_WebGL_Preserve = 5603,
   kHTMLCanvasElementLowLatency_WebGL_Discard = 5604,
+  kAnimatedImageUsedMoreThanOnce = 5605,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots. Also don't add extra
diff --git a/third_party/blink/renderer/core/dom/qualified_name.cc b/third_party/blink/renderer/core/dom/qualified_name.cc
index 9be3d6d..e708c0a 100644
--- a/third_party/blink/renderer/core/dom/qualified_name.cc
+++ b/third_party/blink/renderer/core/dom/qualified_name.cc
@@ -117,7 +117,7 @@
 DEFINE_GLOBAL(, QualifiedName, g_any_name);
 DEFINE_GLOBAL(, QualifiedName, g_null_name);
 
-void QualifiedName::InitAndReserveCapacityForSize(unsigned size) {
+void QualifiedName::InitAndReserveCapacityForSize(wtf_size_t size) {
   DCHECK(g_star_atom.Impl());
   GetQualifiedNameCache().ReserveCapacityForSize(
       size + 2 /*g_star_atom and g_null_atom */);
diff --git a/third_party/blink/renderer/core/dom/qualified_name.h b/third_party/blink/renderer/core/dom/qualified_name.h
index f63cb9f..0a10d76 100644
--- a/third_party/blink/renderer/core/dom/qualified_name.h
+++ b/third_party/blink/renderer/core/dom/qualified_name.h
@@ -205,7 +205,7 @@
   QualifiedNameImpl* Impl() const { return impl_.get(); }
 
   // Init routine for globals
-  static void InitAndReserveCapacityForSize(unsigned size);
+  static void InitAndReserveCapacityForSize(wtf_size_t size);
 
   static const QualifiedName& Null() { return g_null_name; }
 
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
index 91cc584..16f2b75 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
@@ -631,6 +631,8 @@
           return no_serialization;
         }
         break;
+      case Behavior::kIncludeAllShadowRootsForInspector:
+        break;
     }
   }
 
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.h b/third_party/blink/renderer/core/editing/serializers/serialization.h
index a923233..70965aa 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.h
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.h
@@ -62,6 +62,8 @@
     // Include any shadow root (open or closed) marked `serializable`. Also
     // include any shadow root in the include_shadow_roots list.
     kIncludeAnySerializableShadowRoots,
+    // Include all shadow roots for requests by the inspector.
+    kIncludeAllShadowRootsForInspector,
   };
 
   ShadowRootInclusion() = default;
diff --git a/third_party/blink/renderer/core/editing/visible_units.cc b/third_party/blink/renderer/core/editing/visible_units.cc
index 74a164c..b7d1b352 100644
--- a/third_party/blink/renderer/core/editing/visible_units.cc
+++ b/third_party/blink/renderer/core/editing/visible_units.cc
@@ -495,6 +495,10 @@
     // [2] editing/inserting/return-with-object-element.html
     if (const InlineNodeData* inline_data = block_flow->GetInlineNodeData()) {
       if (inline_data->ItemsData(false).text_content.empty() &&
+          // Out-of-flow objects (floating and out-of-flow positioned) used to
+          // represent a U+FFFC Object Replacement Character. Keep the
+          // historical behavior.
+          !inline_data->HasFloatingOrOutOfFlowPositioned() &&
           block_flow->HasLineIfEmpty()) {
         return false;
       }
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics.cc b/third_party/blink/renderer/core/html/anchor_element_metrics.cc
index 8f67a14..dfca3d01 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics.cc
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics.cc
@@ -118,10 +118,10 @@
 
   int source_number =
       CharactersToInt(StringView(source, left, source_right - left),
-                      WTF::NumberParsingOptions(), /*ok=*/nullptr);
+                      NumberParsingOptions(), /*ok=*/nullptr);
   int target_number =
       CharactersToInt(StringView(target, left, target_right - left),
-                      WTF::NumberParsingOptions(), /*ok=*/nullptr);
+                      NumberParsingOptions(), /*ok=*/nullptr);
 
   // The second number should increment by one and the rest of the strings
   // should be the same.
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index d0d04bb2..2ff3450 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -268,11 +268,4 @@
   return preferred_2d_raster_mode_ == RasterModeHint::kPreferGPU && CanUseGPU();
 }
 
-void CanvasRenderingContextHost::FlushRecordingForCanvas2D(FlushReason reason) {
-  CHECK(IsRenderingContext2D());
-  if (auto* provider = GetResourceProviderForCanvas2D()) {
-    provider->FlushCanvas(reason);
-  }
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
index 5b01bdf..7ea7d75e 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
@@ -153,14 +153,8 @@
   bool ShouldTryToUseGpuRaster() const;
   void SetPreferred2DRasterMode(RasterModeHint);
 
-  virtual std::unique_ptr<CanvasResourceProvider>
-      ReplaceResourceProviderForCanvas2D(
-          std::unique_ptr<CanvasResourceProvider>) = 0;
-
   virtual void DiscardResources() = 0;
 
-  void FlushRecordingForCanvas2D(FlushReason reason);
-
  protected:
   ~CanvasRenderingContextHost() override = default;
 
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index 9ebe263a..238920f 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -389,7 +389,7 @@
   if (printed_in_current_task || IsPrinting()) {
     reason = FlushReason::kCanvasPushFrameWhilePrinting;
   }
-  FlushRecordingForCanvas2D(reason);
+  GetResourceProviderForCanvas2D()->FlushCanvas(reason);
 
   // If the context is lost, we don't know if we should be producing GPU or
   // software frames, until we get a new context, since the compositor will
@@ -1235,7 +1235,7 @@
   if (IsPrinting() && IsRenderingContext2D() &&
       GetResourceProviderForCanvas2D()) {
     auto* provider = GetResourceProviderForCanvas2D();
-    FlushRecordingForCanvas2D(FlushReason::kPrinting);
+    provider->FlushCanvas(FlushReason::kPrinting);
     // `FlushRecording` might be a no-op if a flush already happened before.
     // Fortunately, the last flush recording was kept by the provider.
     const std::optional<cc::PaintRecord>& last_recording =
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
index 8cd093e7b..3a394c49 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -269,8 +269,9 @@
   bool IsHibernating() const override;
   void SetTransferToGPUTextureWasInvoked() override;
   UkmParameters GetUkmParameters() override;
+
   std::unique_ptr<CanvasResourceProvider> ReplaceResourceProviderForCanvas2D(
-      std::unique_ptr<CanvasResourceProvider>) override;
+      std::unique_ptr<CanvasResourceProvider>);
 
   // This method attempts to ensure that the canvas' resource exists on the GPU.
   // A HTMLCanvasElement can downgrade itself from GPU to CPU when readback
diff --git a/third_party/blink/renderer/core/html/html_dimension.cc b/third_party/blink/renderer/core/html/html_dimension.cc
index 8a611cf..418c337b 100644
--- a/third_party/blink/renderer/core/html/html_dimension.cc
+++ b/third_party/blink/renderer/core/html/html_dimension.cc
@@ -62,7 +62,7 @@
     bool ok = false;
     unsigned integer_value = CharactersToUInt(
         characters.subspan(digits_start, position - digits_start),
-        WTF::NumberParsingOptions(), &ok);
+        NumberParsingOptions(), &ok);
     if (!ok)
       return HTMLDimension(0., HTMLDimension::kRelative);
     value += integer_value;
@@ -78,8 +78,8 @@
       }
 
       if (fraction_numbers.size()) {
-        double fraction_value = CharactersToUInt(
-            base::span(fraction_numbers), WTF::NumberParsingOptions(), &ok);
+        double fraction_value = CharactersToUInt(base::span(fraction_numbers),
+                                                 NumberParsingOptions(), &ok);
         if (!ok)
           return HTMLDimension(0., HTMLDimension::kRelative);
 
diff --git a/third_party/blink/renderer/core/html/html_font_element.cc b/third_party/blink/renderer/core/html/html_font_element.cc
index 7ebee421..f41c343 100644
--- a/third_party/blink/renderer/core/html/html_font_element.cc
+++ b/third_party/blink/renderer/core/html/html_font_element.cc
@@ -85,7 +85,7 @@
   int value = CharactersToInt(
       characters.subspan(digits_start,
                          static_cast<size_t>(position - digits_start)),
-      WTF::NumberParsingOptions(), nullptr);
+      NumberParsingOptions(), nullptr);
 
   // Step 9
   if (mode == kRelativePlus) {
diff --git a/third_party/blink/renderer/core/html/parser/html_parser_idioms.cc b/third_party/blink/renderer/core/html/parser/html_parser_idioms.cc
index 9f9495a8..69a3c53 100644
--- a/third_party/blink/renderer/core/html/parser/html_parser_idioms.cc
+++ b/third_party/blink/renderer/core/html/parser/html_parser_idioms.cc
@@ -196,7 +196,7 @@
     DCHECK_LT(position, chars.size());
 
     bool ok;
-    constexpr auto kOptions = WTF::NumberParsingOptions()
+    constexpr auto kOptions = NumberParsingOptions()
                                   .SetAcceptTrailingGarbage()
                                   .SetAcceptLeadingPlus();
     int wtf_value = CharactersToInt(chars.subspan(position), kOptions, &ok);
@@ -237,7 +237,7 @@
         DCHECK_LT(position, chars.size());
 
         NumberParsingResult result;
-        constexpr auto kOptions = WTF::NumberParsingOptions()
+        constexpr auto kOptions = NumberParsingOptions()
                                       .SetAcceptTrailingGarbage()
                                       .SetAcceptLeadingPlus()
                                       .SetAcceptMinusZeroForUnsigned();
diff --git a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
index 48f4384..9861a9f 100644
--- a/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
+++ b/third_party/blink/renderer/core/html/parser/html_srcset_parser.cc
@@ -88,7 +88,7 @@
     }
     return CharactersToInt(
         attribute.subspan(start, length_excluding_descriptor),
-        WTF::NumberParsingOptions(), &is_valid);
+        NumberParsingOptions(), &is_valid);
   }
 
   template <typename CharType>
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.cc
index 23432df..d38b105 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_scanner.cc
@@ -86,8 +86,7 @@
     auto [number_data, rest] = buf.split_at(num_digits);
     // Consume the digits.
     buf = rest;
-    return CharactersToUInt(number_data, WTF::NumberParsingOptions(),
-                            &valid_number);
+    return CharactersToUInt(number_data, NumberParsingOptions(), &valid_number);
   });
 
   // Since we know that scanDigits only scanned valid (ASCII) digits (and
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index 26afc21..2b636a4 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -1211,6 +1211,7 @@
     std::optional<int> node_id,
     std::optional<int> backend_node_id,
     std::optional<String> object_id,
+    std::optional<bool> include_shadow_dom,
     WTF::String* outer_html) {
   Node* node = nullptr;
   protocol::Response response =
@@ -1218,7 +1219,12 @@
   if (!response.IsSuccess())
     return response;
 
-  *outer_html = CreateMarkup(node);
+  ShadowRootInclusion shadow_roots{
+      include_shadow_dom.value_or(false)
+          ? ShadowRootInclusion::Behavior::kIncludeAllShadowRootsForInspector
+          : ShadowRootInclusion::Behavior::kOnlyProvidedShadowRoots};
+  *outer_html =
+      CreateMarkup(node, kIncludeNode, kDoNotResolveURLs, shadow_roots);
   return protocol::Response::Success();
 }
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.h b/third_party/blink/renderer/core/inspector/inspector_dom_agent.h
index 6a5a0c1b..9ad2455 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.h
@@ -148,6 +148,7 @@
   protocol::Response getOuterHTML(std::optional<int> node_id,
                                   std::optional<int> backend_node_id,
                                   std::optional<String> object_id,
+                                  std::optional<bool> include_shadow_dom,
                                   String* outer_html) override;
   protocol::Response setOuterHTML(int node_id,
                                   const String& outer_html) override;
diff --git a/third_party/blink/renderer/core/layout/inline/inline_item.cc b/third_party/blink/renderer/core/layout/inline/inline_item.cc
index 7ea4f33..2d84084 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_item.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_item.cc
@@ -208,30 +208,52 @@
 unsigned InlineItem::SetBidiLevel(InlineItems& items,
                                   unsigned index,
                                   unsigned end_offset,
-                                  UBiDiLevel level) {
-  for (; items[index]->end_offset_ < end_offset; index++) {
-    items[index]->SetBidiLevel(level);
-  }
-  InlineItem* item = items[index];
-  item->SetBidiLevel(level);
-
-  if (item->end_offset_ == end_offset) {
-    // Let close items have the same bidi-level as the previous item.
-    while (index + 1 < items.size() &&
-           items[index + 1]->Type() == InlineItem::kCloseTag) {
-      items[++index]->SetBidiLevel(level);
+                                  UBiDiLevel level,
+                                  wtf_size_t num_out_of_flow) {
+  DCHECK(!num_out_of_flow ||
+         RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled());
+  InlineItem* item;
+  for (;; ++index) {
+    item = items[index];
+    item->SetBidiLevel(level);
+    if (num_out_of_flow && item->IsFloatingOrOutOfFlowPositioned()) {
+      --num_out_of_flow;
     }
-  } else {
+    if (item->end_offset_ >= end_offset) {
+      break;
+    }
+  }
+
+  // If `end_offset` is in the middle of an `InlineItem`, split it.
+  if (item->end_offset_ > end_offset) {
     // If a reused item needs to split, |SetNeedsLayout| to ensure the line is
     // not reused.
     LayoutObject* layout_object = item->GetLayoutObject();
-    if (layout_object->EverHadLayout() && !layout_object->NeedsLayout())
+    if (layout_object->EverHadLayout() && !layout_object->NeedsLayout()) {
       layout_object->SetNeedsLayout(layout_invalidation_reason::kStyleChange);
+    }
 
     Split(items, index, end_offset);
+    return index + 1;
   }
+  DCHECK_EQ(end_offset, item->end_offset_);
 
-  return index + 1;
+  // Let trailing items have the same bidi-level as the previous item.
+  for (++index; index < items.size(); ++index) {
+    item = items[index];
+    const bool is_trailing =
+        !item->Length() &&
+        (item->Type() == InlineItem::kCloseTag || num_out_of_flow);
+    if (!is_trailing) {
+      break;
+    }
+    item->SetBidiLevel(level);
+    if (num_out_of_flow && item->IsFloatingOrOutOfFlowPositioned()) {
+      --num_out_of_flow;
+    }
+  }
+  DCHECK_EQ(num_out_of_flow, 0u);  // Check if `num_out_of_flow` is consumed.
+  return index;
 }
 
 const Font& InlineItem::FontWithSvgScaling() const {
diff --git a/third_party/blink/renderer/core/layout/inline/inline_item.h b/third_party/blink/renderer/core/layout/inline/inline_item.h
index 804f5e67..46a2bc0 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_item.h
+++ b/third_party/blink/renderer/core/layout/inline/inline_item.h
@@ -89,6 +89,9 @@
   void SetTextType(TextItemType text_type) {
     text_type_ = static_cast<unsigned>(text_type);
   }
+  bool IsFloatingOrOutOfFlowPositioned() const {
+    return Type() == kFloating || Type() == kOutOfFlowPositioned;
+  }
   bool IsSymbolMarker() const {
     return TextType() == TextItemType::kSymbolMarker;
   }
@@ -272,7 +275,8 @@
   static unsigned SetBidiLevel(InlineItems&,
                                unsigned index,
                                unsigned end_offset,
-                               UBiDiLevel);
+                               UBiDiLevel,
+                               wtf_size_t num_out_of_flow = 0);
 
   // Update `InlineItem::Index()` for the given list.
   static void UpdateIndex(base::span<Member<InlineItem>> items);
diff --git a/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc b/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc
index 9c9862c..8a57042 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_items_builder.cc
@@ -1285,8 +1285,14 @@
 template <typename MappingBuilder>
 void InlineItemsBuilderTemplate<MappingBuilder>::AppendFloating(
     LayoutObject* layout_object) {
-  AppendOpaque(InlineItem::kFloating, uchar::kObjectReplacementCharacter,
-               layout_object);
+  if (RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) {
+    // Out-of-flow elements should be ignored for text processing.
+    // https://drafts.csswg.org/css-text-3/#text-encoding
+    AppendOpaque(InlineItem::kFloating, layout_object);
+  } else {
+    AppendOpaque(InlineItem::kFloating, uchar::kObjectReplacementCharacter,
+                 layout_object);
+  }
   has_floats_ = true;
   // Floats/exclusions require computing line heights, which is currently
   // skipped during the bisect. See `ParagraphLineBreaker`.
@@ -1297,8 +1303,15 @@
 template <typename MappingBuilder>
 void InlineItemsBuilderTemplate<MappingBuilder>::AppendOutOfFlowPositioned(
     LayoutObject* layout_object) {
-  AppendOpaque(InlineItem::kOutOfFlowPositioned,
-               uchar::kObjectReplacementCharacter, layout_object);
+  if (RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) {
+    // Out-of-flow elements should be ignored for text processing.
+    // https://drafts.csswg.org/css-text-3/#text-encoding
+    AppendOpaque(InlineItem::kOutOfFlowPositioned, layout_object);
+  } else {
+    AppendOpaque(InlineItem::kOutOfFlowPositioned,
+                 uchar::kObjectReplacementCharacter, layout_object);
+  }
+  has_out_of_flow_positioned_ = true;
 }
 
 template <typename MappingBuilder>
@@ -1698,6 +1711,7 @@
       HasBidiControls() ||
       (has_non_orc_16bit_ && Character::MaybeBidiRtl(data->text_content));
   data->has_floats_ = has_floats_;
+  data->has_out_of_flow_positioned_ = has_out_of_flow_positioned_;
   data->has_initial_letter_box_ = has_initial_letter_box_;
   data->has_ruby_ = has_ruby_;
   data->is_block_level_ = IsBlockLevel();
diff --git a/third_party/blink/renderer/core/layout/inline/inline_items_builder.h b/third_party/blink/renderer/core/layout/inline/inline_items_builder.h
index 3773a7d..08cfe006 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_items_builder.h
+++ b/third_party/blink/renderer/core/layout/inline/inline_items_builder.h
@@ -195,6 +195,7 @@
   const bool is_text_combine_;
   bool has_bidi_controls_ = false;
   bool has_floats_ = false;
+  bool has_out_of_flow_positioned_ = false;
   bool has_initial_letter_box_ = false;
   bool has_ruby_ = false;
   bool is_block_level_ = true;
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node.cc b/third_party/blink/renderer/core/layout/inline/inline_node.cc
index e78c07c..b9246c11 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_node.cc
@@ -1288,10 +1288,10 @@
   }
   BidiParagraph bidi;
   data->text_content.Ensure16Bit();
-  if (!bidi.SetParagraph(data->text_content, base_direction)) {
+  const String& text_content = data->text_content;
+  if (!bidi.SetParagraph(text_content, base_direction)) {
     // On failure, give up bidi resolving and reordering.
-    data->is_bidi_enabled_ = false;
-    data->SetBaseDirection(TextDirection::kLtr);
+    data->DisableBidi();
     return;
   }
 
@@ -1303,16 +1303,100 @@
     return;
   }
 
+  // If this IFC has out-of-flow objects, create a text with them represented by
+  // the U+FFFC OBJECT REPLACEMENT CHARACTER. The [CSS Text] defines that
+  // out-of-flow elements must be ignored for text processing, but many existing
+  // tests require them to act as a [neutral] like U+FFFC OBJECT REPLACEMENT
+  // CHARACTER, and all browsers match.
+  //
+  // [CSS Text]: https://drafts.csswg.org/css-text-3/#text-encoding
+  // [neutral]: https://unicode.org/reports/tr9/#ON
+  struct OutOfFlowItem {
+    wtf_size_t text_offset;
+#if EXPENSIVE_DCHECKS_ARE_ON()
+    UBiDiLevel level = 0;
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
+  };
+  Vector<OutOfFlowItem> out_of_flow_items;
+  String text_content_with_out_of_flow;
+  wtf_size_t text_len = text_content.length();
   InlineItems& items = data->items;
+  if (data->HasFloatingOrOutOfFlowPositioned() &&
+      RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) [[unlikely]] {
+    StringBuilder builder;
+    wtf_size_t last_offset = 0;
+    for (const auto item_ptr : items) {
+      const InlineItem& item = *item_ptr;
+      if (item.IsFloatingOrOutOfFlowPositioned()) [[unlikely]] {
+        const wtf_size_t offset = item.StartOffset();
+        if (builder.empty()) {
+          builder.Reserve16BitCapacity(text_len + 16);
+        }
+        builder.Append(text_content, last_offset, offset - last_offset);
+        last_offset = offset;
+        out_of_flow_items.push_back(OutOfFlowItem{builder.length()});
+        builder.Append(uchar::kObjectReplacementCharacter);
+      }
+    }
+    DCHECK_EQ(builder.empty(), out_of_flow_items.empty());
+    if (!builder.empty()) {
+      builder.Append(StringView{text_content, last_offset});
+      text_content_with_out_of_flow = builder.ReleaseString();
+      if (!bidi.SetParagraph(text_content_with_out_of_flow, base_direction)) {
+        data->DisableBidi();
+        return;
+      }
+      text_len = text_content_with_out_of_flow.length();
+
+      // Add a sentinel to help the loop below.
+      out_of_flow_items.push_back(
+          OutOfFlowItem{std::numeric_limits<wtf_size_t>::max()});
+    }
+  }
+
+  // Copy resolved `BidiLevel`s in the `bidi` to the `items`.
+  // If a boundary is within an `InlineItem`, this involves splitting.
+  wtf_size_t out_of_flow_item_index = 0;
   unsigned item_index = 0;
-  for (unsigned start = 0; start < data->text_content.length();) {
+  for (unsigned start = 0; start < text_len;) {
+    DCHECK_EQ(items[item_index]->start_offset_, start - out_of_flow_item_index);
     UBiDiLevel level;
-    unsigned end = bidi.GetLogicalRun(start, &level);
-    DCHECK_EQ(items[item_index]->start_offset_, start);
-    item_index = InlineItem::SetBidiLevel(items, item_index, end, level);
+    const unsigned end = bidi.GetLogicalRun(start, &level);
+    if (out_of_flow_items.empty()) {
+      item_index = InlineItem::SetBidiLevel(items, item_index, end, level);
+    } else {
+      DCHECK(RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled());
+      wtf_size_t num_out_of_flow_in_this_run = 0;
+      while (end > out_of_flow_items[out_of_flow_item_index].text_offset) {
+#if EXPENSIVE_DCHECKS_ARE_ON()
+        out_of_flow_items[out_of_flow_item_index].level = level;
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
+        ++out_of_flow_item_index;
+        ++num_out_of_flow_in_this_run;
+      }
+      item_index = InlineItem::SetBidiLevel(items, item_index,
+                                            end - out_of_flow_item_index, level,
+                                            num_out_of_flow_in_this_run);
+    }
     start = end;
   }
-#if DCHECK_IS_ON()
+
+#if EXPENSIVE_DCHECKS_ARE_ON()
+  if (!out_of_flow_items.empty()) {
+    // Check the BiDi level for OOF items are set correctly.
+    DCHECK(RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled());
+    DCHECK_EQ(out_of_flow_item_index, out_of_flow_items.size() - 1);
+    out_of_flow_item_index = 0;
+    for (const auto item_ptr : items) {
+      const InlineItem& item = *item_ptr;
+      if (item.IsFloatingOrOutOfFlowPositioned()) {
+        DCHECK_EQ(item.BidiLevel(),
+                  out_of_flow_items[out_of_flow_item_index].level);
+        ++out_of_flow_item_index;
+      }
+    }
+  }
+
   // Check all items have bidi levels, except trailing non-length items.
   // Items that do not create break opportunities such as kOutOfFlowPositioned
   // do not have corresponding characters, and that they do not have bidi level
@@ -1321,7 +1405,7 @@
     item_index++;
   }
   DCHECK_EQ(item_index, items.size());
-#endif
+#endif  // EXPENSIVE_DCHECKS_ARE_ON()
 }
 
 bool InlineNode::IsNGShapeCacheAllowed(
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node_data.cc b/third_party/blink/renderer/core/layout/inline/inline_node_data.cc
index b22fdec6..50862514 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node_data.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_node_data.cc
@@ -8,6 +8,11 @@
 
 namespace blink {
 
+void InlineNodeData::DisableBidi() {
+  is_bidi_enabled_ = false;
+  SetBaseDirection(TextDirection::kLtr);
+}
+
 void InlineNodeData::Trace(Visitor* visitor) const {
   visitor->Trace(first_line_items_);
   visitor->Trace(svg_node_data_);
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node_data.h b/third_party/blink/renderer/core/layout/inline/inline_node_data.h
index 681c6eb..b092cc5f 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node_data.h
+++ b/third_party/blink/renderer/core/layout/inline/inline_node_data.h
@@ -25,8 +25,13 @@
   TextDirection BaseDirection() const {
     return static_cast<TextDirection>(base_direction_);
   }
+  void DisableBidi();
 
   bool HasFloats() const { return has_floats_; }
+  bool HasOutOfFlowPositioned() const { return has_out_of_flow_positioned_; }
+  bool HasFloatingOrOutOfFlowPositioned() const {
+    return HasFloats() || HasOutOfFlowPositioned();
+  }
   bool HasInitialLetterBox() const { return has_initial_letter_box_; }
   bool HasRuby() const { return has_ruby_; }
 
@@ -79,6 +84,7 @@
   unsigned base_direction_ : 1;  // TextDirection
 
   unsigned has_floats_ : 1;
+  unsigned has_out_of_flow_positioned_ : 1;
 
   // True if this node contains initial letter box. This value is used for
   // clearing. To control whether subsequent blocks overlap with initial
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node_test.cc b/third_party/blink/renderer/core/layout/inline/inline_node_test.cc
index f64db41..270699e5 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node_test.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_node_test.cc
@@ -233,15 +233,25 @@
             "</div>");
   InlineNodeForTest node = CreateInlineNode();
   node.CollectInlines();
-  EXPECT_EQ("abc\uFFFCghi\uFFFCmno", node.Text())
+  EXPECT_EQ(RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()
+                ? "abcghimno"
+                : "abc\uFFFCghi\uFFFCmno",
+            node.Text())
       << "floats are appeared as an object replacement character";
   InlineItems& items = node.Items();
   ASSERT_EQ(5u, items.size());
   TEST_ITEM_TYPE_OFFSET(items[0], kText, 0u, 3u);
-  TEST_ITEM_TYPE_OFFSET(items[1], kFloating, 3u, 4u);
-  TEST_ITEM_TYPE_OFFSET(items[2], kText, 4u, 7u);
-  TEST_ITEM_TYPE_OFFSET(items[3], kFloating, 7u, 8u);
-  TEST_ITEM_TYPE_OFFSET(items[4], kText, 8u, 11u);
+  if (RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) {
+    TEST_ITEM_TYPE_OFFSET(items[1], kFloating, 3u, 3u);
+    TEST_ITEM_TYPE_OFFSET(items[2], kText, 3u, 6u);
+    TEST_ITEM_TYPE_OFFSET(items[3], kFloating, 6u, 6u);
+    TEST_ITEM_TYPE_OFFSET(items[4], kText, 6u, 9u);
+  } else {
+    TEST_ITEM_TYPE_OFFSET(items[1], kFloating, 3u, 4u);
+    TEST_ITEM_TYPE_OFFSET(items[2], kText, 4u, 7u);
+    TEST_ITEM_TYPE_OFFSET(items[3], kFloating, 7u, 8u);
+    TEST_ITEM_TYPE_OFFSET(items[4], kText, 8u, 11u);
+  }
 }
 
 TEST_F(InlineNodeTest, CollectInlinesInlineBlock) {
@@ -1476,7 +1486,11 @@
             "</div>");
   GetElementById("remove")->remove();
   UpdateAllLifecyclePhasesForTest();
-  EXPECT_EQ(String(u"abc \uFFFCx"), GetText());
+  if (RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) {
+    EXPECT_EQ(String(u"abc x"), GetText());
+  } else {
+    EXPECT_EQ(String(u"abc \uFFFCx"), GetText());
+  }
 }
 
 // https://crbug.com/109654
diff --git a/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc b/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc
index ff7375ca..1ad7b27 100644
--- a/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc
+++ b/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc
@@ -624,6 +624,7 @@
     }
     levels.push_back(item.bidi_level);
   }
+  DCHECK_EQ(line_box->size(), levels.size());
 
   // For opaque items, copy bidi levels from adjacent items.
   if (has_opaque_items) {
diff --git a/third_party/blink/renderer/core/layout/inline/offset_mapping_test.cc b/third_party/blink/renderer/core/layout/inline/offset_mapping_test.cc
index f87bbc9..f0aafd05 100644
--- a/third_party/blink/renderer/core/layout/inline/offset_mapping_test.cc
+++ b/third_party/blink/renderer/core/layout/inline/offset_mapping_test.cc
@@ -936,8 +936,13 @@
 
   const OffsetMapping& remaining_text_result = *mapping1;
   ASSERT_EQ(1u, remaining_text_result.GetUnits().size());
-  TEST_UNIT(remaining_text_result.GetUnits()[0],
-            OffsetMappingUnitType::kIdentity, text_node, 1u, 3u, 1u, 3u);
+  if (RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) {
+    TEST_UNIT(remaining_text_result.GetUnits()[0],
+              OffsetMappingUnitType::kIdentity, text_node, 1u, 3u, 0u, 2u);
+  } else {
+    TEST_UNIT(remaining_text_result.GetUnits()[0],
+              OffsetMappingUnitType::kIdentity, text_node, 1u, 3u, 1u, 3u);
+  }
   ASSERT_EQ(1u, remaining_text_result.GetRanges().size());
   TEST_RANGE(remaining_text_result.GetRanges(), text_node, 0u, 1u);
 
@@ -956,17 +961,34 @@
 
   EXPECT_EQ(0u,
             *first_letter_result.GetTextContentOffset(Position(text_node, 0)));
-  EXPECT_EQ(
-      1u, *remaining_text_result.GetTextContentOffset(Position(text_node, 1)));
-  EXPECT_EQ(
-      2u, *remaining_text_result.GetTextContentOffset(Position(text_node, 2)));
-  EXPECT_EQ(
-      3u, *remaining_text_result.GetTextContentOffset(Position(text_node, 3)));
+
+  if (RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) {
+    EXPECT_EQ(0u, *remaining_text_result.GetTextContentOffset(
+                      Position(text_node, 1)));
+    EXPECT_EQ(1u, *remaining_text_result.GetTextContentOffset(
+                      Position(text_node, 2)));
+    EXPECT_EQ(2u, *remaining_text_result.GetTextContentOffset(
+                      Position(text_node, 3)));
+  } else {
+    EXPECT_EQ(1u, *remaining_text_result.GetTextContentOffset(
+                      Position(text_node, 1)));
+    EXPECT_EQ(2u, *remaining_text_result.GetTextContentOffset(
+                      Position(text_node, 2)));
+    EXPECT_EQ(3u, *remaining_text_result.GetTextContentOffset(
+                      Position(text_node, 3)));
+  }
 
   EXPECT_EQ(Position(text_node, 1), first_letter_result.GetFirstPosition(1));
   EXPECT_EQ(Position(text_node, 1), first_letter_result.GetLastPosition(1));
-  EXPECT_EQ(Position(text_node, 1), remaining_text_result.GetFirstPosition(1));
-  EXPECT_EQ(Position(text_node, 1), remaining_text_result.GetLastPosition(1));
+  if (RuntimeEnabledFeatures::LineBreakOofNoOrcEnabled()) {
+    EXPECT_EQ(Position(text_node, 1),
+              remaining_text_result.GetFirstPosition(0));
+    EXPECT_EQ(Position(text_node, 1), remaining_text_result.GetLastPosition(0));
+  } else {
+    EXPECT_EQ(Position(text_node, 1),
+              remaining_text_result.GetFirstPosition(1));
+    EXPECT_EQ(Position(text_node, 1), remaining_text_result.GetLastPosition(1));
+  }
 }
 
 TEST_F(OffsetMappingTest, WhiteSpaceTextNodeWithoutLayoutText) {
diff --git a/third_party/blink/renderer/core/layout/layout_image.cc b/third_party/blink/renderer/core/layout/layout_image.cc
index 417b86cf..b96e6e91 100644
--- a/third_party/blink/renderer/core/layout/layout_image.cc
+++ b/third_party/blink/renderer/core/layout/layout_image.cc
@@ -281,6 +281,16 @@
 void LayoutImage::Paint(const PaintInfo& paint_info) const {
   NOT_DESTROYED();
   ImagePainter(*this).Paint(paint_info);
+
+  if (image_resource_ && image_resource_->MaybeAnimated()) {
+    if (const auto* cached_image = image_resource_->CachedImage();
+        cached_image && (cached_image->NumberOfObservers() > 2)) {
+      // Images have 2 observers HTMLImageLoader and LayoutImage, when they're
+      // repeated in the same document they'll have more than 2.
+      UseCounter::Count(GetDocument(),
+                        WebFeature::kAnimatedImageUsedMoreThanOnce);
+    }
+  }
 }
 
 void LayoutImage::AreaElementFocusChanged(HTMLAreaElement* area_element) {
diff --git a/third_party/blink/renderer/core/loader/resource/image_resource_content.h b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
index ff9ab3b..0cef5da 100644
--- a/third_party/blink/renderer/core/loader/resource/image_resource_content.h
+++ b/third_party/blink/renderer/core/loader/resource/image_resource_content.h
@@ -206,6 +206,9 @@
   bool HasObservers() const {
     return !observers_.empty() || !finished_observers_.empty();
   }
+  wtf_size_t NumberOfObservers() const {
+    return observers_.size() + finished_observers_.size();
+  }
   bool CanBeSpeculativelyDecoded() const;
   ImageDecoder::CompressionFormat GetCompressionFormat() const;
 
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
index e358b59c..5763b64 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.h
@@ -128,7 +128,7 @@
     transfer_to_gpu_texture_was_invoked_ = true;
   }
   std::unique_ptr<CanvasResourceProvider> ReplaceResourceProviderForCanvas2D(
-      std::unique_ptr<CanvasResourceProvider>) override;
+      std::unique_ptr<CanvasResourceProvider>);
   void DiscardResources() override;
   CanvasResourceProvider* GetResourceProviderForCanvas2D() const override {
     CHECK(IsRenderingContext2D());
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index d43ec16..858ab092 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -161,7 +161,7 @@
         value_string == "true") {
       value = 1;
     } else {
-      value = CharactersToInt(value_string, WTF::NumberParsingOptions::Loose(),
+      value = CharactersToInt(value_string, NumberParsingOptions::Loose(),
                               /*ok=*/nullptr);
     }
 
diff --git a/third_party/blink/renderer/core/streams/readable_stream.idl b/third_party/blink/renderer/core/streams/readable_stream.idl
index d5f1bc5f..141cce37 100644
--- a/third_party/blink/renderer/core/streams/readable_stream.idl
+++ b/third_party/blink/renderer/core/streams/readable_stream.idl
@@ -12,7 +12,7 @@
 [
     Exposed=*
 ] interface ReadableStream {
-    [RuntimeEnabled=ReadableStreamAsyncIterable, HasAsyncIteratorReturnAlgorithm] async iterable<any>(optional ReadableStreamIteratorOptions options = {});
+    [HasAsyncIteratorReturnAlgorithm] async iterable<any>(optional ReadableStreamIteratorOptions options = {});
 
     [CallWith=ScriptState, RaisesException] constructor(optional any underlyingSource, optional any strategy);
     readonly attribute boolean locked;
diff --git a/third_party/blink/renderer/core/url_pattern/url_pattern.cc b/third_party/blink/renderer/core/url_pattern/url_pattern.cc
index 9cff9c4..67482209 100644
--- a/third_party/blink/renderer/core/url_pattern/url_pattern.cc
+++ b/third_party/blink/renderer/core/url_pattern/url_pattern.cc
@@ -80,7 +80,7 @@
     return false;
 
   bool port_ok = false;
-  int port_number = port.Impl()->ToInt(WTF::NumberParsingOptions(), &port_ok);
+  int port_number = port.Impl()->ToInt(NumberParsingOptions(), &port_ok);
   if (!port_ok)
     return false;
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
index 22384cb..c2290dc3 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc
@@ -292,7 +292,7 @@
       !host) {
     return;
   }
-  DCHECK(!host->GetResourceProviderForCanvas2D());
+  DCHECK(!GetResourceProviderForCanvas2D());
 
   if (host->IsValidImageSize()) {
     if (dispatch_context_lost_event_timer_.IsActive()) {
@@ -784,7 +784,7 @@
 }
 
 bool BaseRenderingContext2D::IsAccelerated() const {
-  auto* resource_provider = Host()->GetResourceProviderForCanvas2D();
+  auto* resource_provider = GetResourceProviderForCanvas2D();
   return resource_provider ? resource_provider->IsAccelerated()
                            : Host()->ShouldTryToUseGpuRaster();
 }
@@ -802,7 +802,7 @@
 BaseRenderingContext2D::PaintRenderingResultsToSnapshot(
     SourceDrawingBuffer source_buffer,
     FlushReason reason) {
-  CanvasResourceProvider* provider = Host()->GetResourceProviderForCanvas2D();
+  CanvasResourceProvider* provider = GetResourceProviderForCanvas2D();
   return provider ? provider->Snapshot(reason) : nullptr;
 }
 
@@ -1509,7 +1509,7 @@
   gpu::SyncToken canvas_access_sync_token;
   bool performed_copy = false;
   scoped_refptr<gpu::ClientSharedImage> client_si =
-      host->GetResourceProviderForCanvas2D()
+      GetResourceProviderForCanvas2D()
           ->GetBackingClientSharedImageForExternalWrite(
               &canvas_access_sync_token,
               gpu::SHARED_IMAGE_USAGE_WEBGPU_READ |
@@ -1556,7 +1556,7 @@
   // It also gives us a mechanism to detect post-transfer-out draws, which is
   // used in `transferBackFromWebGPU` to raise an exception.
   resource_provider_from_webgpu_access_ =
-      host->ReplaceResourceProviderForCanvas2D(nullptr);
+      ReplaceResourceProviderForCanvas2D(nullptr);
 
   // The user isn't obligated to ever transfer back, which means this resource
   // provider might stick around for while. Jettison any unnecessary resources.
@@ -1592,7 +1592,7 @@
   // If this canvas already has a resource provider, this means that drawing has
   // occurred after `transferToWebGPU`. We disallow transferring back in this
   // case, and raise an exception instead.
-  if (host->GetResourceProviderForCanvas2D()) {
+  if (GetResourceProviderForCanvas2D()) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidStateError,
         "The canvas was touched after transferToGPUTexture.");
@@ -1615,7 +1615,7 @@
   // surrendering our temporary ownership of the provider.
   CanvasResourceProvider* resource_provider =
       resource_provider_from_webgpu_access_.get();
-  host->ReplaceResourceProviderForCanvas2D(
+  ReplaceResourceProviderForCanvas2D(
       std::move(resource_provider_from_webgpu_access_));
   resource_provider->SetDelegate(host);
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
index a10a2b1..7179a09 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h
@@ -273,7 +273,11 @@
   void TryRestoreContextEvent(TimerBase*);
   void RestoreFromInvalidSizeIfNeeded() override;
 
+  virtual CanvasResourceProvider* GetResourceProviderForCanvas2D() const = 0;
   virtual CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() = 0;
+  virtual std::unique_ptr<CanvasResourceProvider>
+      ReplaceResourceProviderForCanvas2D(
+          std::unique_ptr<CanvasResourceProvider>) = 0;
 
   static const char kInheritString[];
 
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
index 26403a9..eefee3a 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d_test.cc
@@ -169,6 +169,10 @@
     return true;
   }
 
+  CanvasResourceProvider* GetResourceProviderForCanvas2D() const override {
+    return nullptr;
+  }
+
   CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() override {
     return nullptr;
   }
@@ -177,6 +181,10 @@
   scoped_refptr<StaticBitmapImage> GetImage(FlushReason) override {
     return nullptr;
   }
+  std::unique_ptr<CanvasResourceProvider> ReplaceResourceProviderForCanvas2D(
+      std::unique_ptr<CanvasResourceProvider>) override {
+    return nullptr;
+  }
 
   bool IsComposited() const override { return false; }
   bool IsPaintable() const override { return true; }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
index dd19ad4..5dadd6ea 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -285,7 +285,7 @@
       recorder.RestartRecording();
     }
   } else {
-    host->FlushRecordingForCanvas2D(FlushReason::kWritePixels);
+    provider->FlushCanvas(FlushReason::kWritePixels);
 
     // Short-circuit out if an error occurred while flushing the recording.
     if (!provider->IsValid()) {
@@ -685,7 +685,7 @@
   if (!provider) {
     return nullptr;
   }
-  Host()->FlushRecordingForCanvas2D(reason);
+  provider->FlushCanvas(reason);
   return provider->Snapshot(reason);
 }
 
@@ -881,7 +881,7 @@
   HTMLCanvasElement* host = canvas();
   CHECK(host);
 
-  host->FlushRecordingForCanvas2D(reason);
+  GetResourceProviderForCanvas2D()->FlushCanvas(reason);
   if (reason == FlushReason::kCanvasPushFrame) {
     if (host->IsDisplayed()) {
       // Make sure the GPU is never more than two animation frames behind.
@@ -1126,6 +1126,11 @@
 }
 
 CanvasResourceProvider*
+CanvasRenderingContext2D::GetResourceProviderForCanvas2D() const {
+  return Host()->GetResourceProviderForCanvas2D();
+}
+
+CanvasResourceProvider*
 CanvasRenderingContext2D::GetOrCreateCanvas2DResourceProvider() {
   HTMLCanvasElement* const element = canvas();
   if (!element) [[unlikely]] {
@@ -1134,4 +1139,10 @@
   return element->GetOrCreateCanvasResourceProviderForCanvas2D();
 }
 
+std::unique_ptr<CanvasResourceProvider>
+CanvasRenderingContext2D::ReplaceResourceProviderForCanvas2D(
+    std::unique_ptr<CanvasResourceProvider> provider) {
+  return canvas()->ReplaceResourceProviderForCanvas2D(std::move(provider));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
index 52d14bc..57d80b1 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
@@ -29,6 +29,7 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <optional>
 
 #include "base/check.h"
@@ -317,7 +318,10 @@
 
   void ColorSchemeMayHaveChanged() override;
 
+  CanvasResourceProvider* GetResourceProviderForCanvas2D() const override;
   CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() override;
+  std::unique_ptr<CanvasResourceProvider> ReplaceResourceProviderForCanvas2D(
+      std::unique_ptr<CanvasResourceProvider>) override;
 
   FilterOperations filter_operations_;
   HashMap<String, FontDescription> fonts_resolved_using_current_style_;
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index 1d8a92a..abbdaa2 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -133,7 +133,7 @@
   if (!GetOrCreateCanvas2DResourceProvider()) {
     return;
   }
-  Host()->FlushRecordingForCanvas2D(reason);
+  GetResourceProviderForCanvas2D()->FlushCanvas(reason);
 }
 
 // BaseRenderingContext2D implementation
@@ -172,8 +172,7 @@
     return nullptr;
   }
 
-  if (CanvasResourceProvider* provider =
-          host->GetResourceProviderForCanvas2D()) {
+  if (CanvasResourceProvider* provider = GetResourceProviderForCanvas2D()) {
     if (!provider->IsValid()) {
       // The canvas context is not lost but the provider is invalid. This
       // happens if the GPU process dies in the middle of a render task. The
@@ -251,21 +250,27 @@
 
   host->SetResourceProviderForCanvas2D(std::move(provider));
 
-  if (host->GetResourceProviderForCanvas2D() &&
-      host->GetResourceProviderForCanvas2D()->IsValid()) {
+  if (GetResourceProviderForCanvas2D() &&
+      GetResourceProviderForCanvas2D()->IsValid()) {
     base::UmaHistogramBoolean(
         "Blink.Canvas.ResourceProviderIsAccelerated",
-        host->GetResourceProviderForCanvas2D()->IsAccelerated());
-    base::UmaHistogramEnumeration(
-        "Blink.Canvas.ResourceProviderType",
-        host->GetResourceProviderForCanvas2D()->GetType());
+        GetResourceProviderForCanvas2D()->IsAccelerated());
+    base::UmaHistogramEnumeration("Blink.Canvas.ResourceProviderType",
+                                  GetResourceProviderForCanvas2D()->GetType());
     host->DidDraw();
   }
-  return host->GetResourceProviderForCanvas2D();
+  return GetResourceProviderForCanvas2D();
+}
+
+std::unique_ptr<CanvasResourceProvider>
+OffscreenCanvasRenderingContext2D::ReplaceResourceProviderForCanvas2D(
+    std::unique_ptr<CanvasResourceProvider> provider) {
+  return HostAsOffscreenCanvas()->ReplaceResourceProviderForCanvas2D(
+      std::move(provider));
 }
 
 CanvasResourceProvider*
-OffscreenCanvasRenderingContext2D::GetCanvasResourceProvider() const {
+OffscreenCanvasRenderingContext2D::GetResourceProviderForCanvas2D() const {
   return Host()->GetResourceProviderForCanvas2D();
 }
 
@@ -351,7 +356,7 @@
   if (!IsPaintable())
     return nullptr;
   scoped_refptr<StaticBitmapImage> image =
-      GetCanvasResourceProvider()->Snapshot(reason);
+      GetResourceProviderForCanvas2D()->Snapshot(reason);
 
   return image;
 }
@@ -382,7 +387,7 @@
   if (!is_valid_size_ || isContextLost()) [[unlikely]] {
     return nullptr;
   }
-  CanvasResourceProvider* const provider = GetCanvasResourceProvider();
+  CanvasResourceProvider* const provider = GetResourceProviderForCanvas2D();
   if (provider == nullptr) [[unlikely]] {
     return nullptr;
   }
@@ -391,7 +396,7 @@
 
 const MemoryManagedPaintRecorder* OffscreenCanvasRenderingContext2D::Recorder()
     const {
-  const CanvasResourceProvider* provider = GetCanvasResourceProvider();
+  const CanvasResourceProvider* provider = GetResourceProviderForCanvas2D();
   if (provider == nullptr) [[unlikely]] {
     return nullptr;
   }
@@ -409,8 +414,7 @@
   } else {
     Host()->DidDraw(dirty_rect_for_commit_);
   }
-  if (CanvasResourceProvider* provider =
-          Host()->GetResourceProviderForCanvas2D();
+  if (CanvasResourceProvider* provider = GetResourceProviderForCanvas2D();
       layer_count_ == 0 && provider != nullptr) [[likely]] {
     // TODO(crbug.com/1246486): Make auto-flushing layer friendly.
     provider->FlushIfRecordingLimitExceeded();
@@ -436,7 +440,7 @@
 }
 
 bool OffscreenCanvasRenderingContext2D::IsPaintable() const {
-  return Host()->GetResourceProviderForCanvas2D();
+  return GetResourceProviderForCanvas2D();
 }
 
 bool OffscreenCanvasRenderingContext2D::WritePixels(
@@ -447,15 +451,15 @@
     int y) {
   DCHECK(IsCanvas2DBufferValid());
 
-  Host()->FlushRecordingForCanvas2D(FlushReason::kWritePixels);
+  GetResourceProviderForCanvas2D()->FlushCanvas(FlushReason::kWritePixels);
 
   // Short-circuit out if an error occurred while flushing the recording.
-  if (!Host()->GetResourceProviderForCanvas2D()->IsValid()) {
+  if (!GetResourceProviderForCanvas2D()->IsValid()) {
     return false;
   }
 
-  return Host()->GetResourceProviderForCanvas2D()->WritePixels(
-      orig_info, pixels, row_bytes, x, y);
+  return GetResourceProviderForCanvas2D()->WritePixels(orig_info, pixels,
+                                                       row_bytes, x, y);
 }
 
 bool OffscreenCanvasRenderingContext2D::ResolveFont(const String& new_font) {
@@ -486,13 +490,13 @@
 
 bool OffscreenCanvasRenderingContext2D::IsCanvas2DBufferValid() {
   if (IsPaintable())
-    return GetCanvasResourceProvider()->IsValid();
+    return GetResourceProviderForCanvas2D()->IsValid();
   return false;
 }
 
 std::optional<cc::PaintRecord> OffscreenCanvasRenderingContext2D::FlushCanvas(
     FlushReason reason) {
-  if (CanvasResourceProvider* provider = GetCanvasResourceProvider())
+  if (CanvasResourceProvider* provider = GetResourceProviderForCanvas2D())
       [[likely]] {
     return provider->FlushCanvas(reason);
   }
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
index 7ac5a50..9483efb 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_OFFSCREENCANVAS2D_OFFSCREEN_CANVAS_RENDERING_CONTEXT_2D_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_CANVAS_OFFSCREENCANVAS2D_OFFSCREEN_CANVAS_RENDERING_CONTEXT_2D_H_
 
+#include <memory>
+
 #include "base/notreached.h"
 #include "third_party/blink/renderer/core/canvas_interventions/canvas_interventions_enums.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_2d_color_params.h"
@@ -81,7 +83,7 @@
   int Height() const final;
 
   bool CanCreateCanvas2dResourceProvider() final;
-  CanvasResourceProvider* GetCanvasResourceProvider() const;
+  CanvasResourceProvider* GetResourceProviderForCanvas2D() const override;
 
   // Offscreen canvas doesn't have any notion of image orientation.
   RespectImageOrientationEnum RespectImageOrientation() const final {
@@ -163,6 +165,8 @@
   scoped_refptr<CanvasResource> ProduceCanvasResource(FlushReason);
 
   CanvasResourceProvider* GetOrCreateCanvas2DResourceProvider() override;
+  std::unique_ptr<CanvasResourceProvider> ReplaceResourceProviderForCanvas2D(
+      std::unique_ptr<CanvasResourceProvider>) override;
 
   SkIRect dirty_rect_for_commit_;
 
diff --git a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
index 63af1543..128c75bb 100644
--- a/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
+++ b/third_party/blink/renderer/platform/blink_platform_unittests_bundle_data.filelist
@@ -241,6 +241,7 @@
 ../../web_tests/images/resources/animated-10color.gif
 ../../web_tests/images/resources/animated-gif-with-offsets.gif
 ../../web_tests/images/resources/animated.gif
+../../web_tests/images/resources/animated.png
 ../../web_tests/images/resources/animated2.gif
 ../../web_tests/images/resources/apng-test-suite-dispose-op-none-basic.png
 ../../web_tests/images/resources/apng00.png
diff --git a/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc b/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc
index ebabb50f..69a94afc 100644
--- a/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc
+++ b/third_party/blink/renderer/platform/exported/web_icon_sizes_parser.cc
@@ -45,7 +45,7 @@
                                      wtf_size_t end) {
   return WTF::VisitCharacters(
       StringView(string, start, end - start), [](auto chars) {
-        return CharactersToInt(chars, WTF::NumberParsingOptions(), nullptr);
+        return CharactersToInt(chars, NumberParsingOptions(), nullptr);
       });
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h b/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h
index 8bfa7e58..1ddc927 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_cache.h
@@ -195,7 +195,7 @@
     static bool IsEmptyValue(const SmallStringKey& key) {
       return key.IsHashTableEmptyValue();
     }
-    static const unsigned kMinimumTableSize = 16;
+    static const wtf_size_t kMinimumTableSize = 16;
   };
 
   friend bool operator==(const SmallStringKey&, const SmallStringKey&);
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 382c9749..219755b8 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2862,6 +2862,11 @@
       base_feature: "none",
     },
     {
+      // Stop emitting U+FFFC Object Replacement Character for out-of-flow
+      // objects such as floating or out-of-flow positioned. crbgu.com/40658609
+      name: "LineBreakOofNoOrc",
+    },
+    {
       name: "ListItemWithCounterSetNotSetExplicitValue",
       status: "stable",
     },
@@ -3783,10 +3788,6 @@
       }
     },
     {
-      name: "ReadableStreamAsyncIterable",
-      status: "stable",
-    },
-    {
       // Enables support for a 'min' option in ReadableStream BYOBReader.read(),
       // allowing callers to specify the minimum number of bytes to be read into
       // the buffer before resolving the read request.
@@ -4424,6 +4425,10 @@
       // Android does not yet support SharedWorker. crbug.com/154571
       status: {"Android": "", "default": "stable"},
       base_feature: "none",
+
+      // Allow Android to run the origin trial.
+      origin_trial_feature_name: "SharedWorkerOnAndroid",
+      origin_trial_os: ["android"],
     },
     {
       // If enabled, SharedWorker supports extendedLifetime
diff --git a/third_party/blink/renderer/platform/text/bidi_paragraph.cc b/third_party/blink/renderer/platform/text/bidi_paragraph.cc
index f27f4f40..93b1e0a 100644
--- a/third_party/blink/renderer/platform/text/bidi_paragraph.cc
+++ b/third_party/blink/renderer/platform/text/bidi_paragraph.cc
@@ -14,8 +14,9 @@
 bool BidiParagraph::SetParagraph(const String& text,
                                  std::optional<TextDirection> base_direction) {
   DCHECK(!text.IsNull());
-  DCHECK(!ubidi_);
-  ubidi_ = UBidiPtr(ubidi_open());
+  if (!ubidi_) {
+    ubidi_ = UBidiPtr(ubidi_open());
+  }
 
   UBiDiLevel para_level;
   if (base_direction) {
diff --git a/third_party/blink/renderer/platform/wtf/hash_counted_set.h b/third_party/blink/renderer/platform/wtf/hash_counted_set.h
index 1be7295..c4abeada 100644
--- a/third_party/blink/renderer/platform/wtf/hash_counted_set.h
+++ b/third_party/blink/renderer/platform/wtf/hash_counted_set.h
@@ -56,8 +56,8 @@
 
   void swap(HashCountedSet& other) { impl_.swap(other.impl_); }
 
-  unsigned size() const { return impl_.size(); }
-  unsigned Capacity() const { return impl_.capacity(); }
+  wtf_size_t size() const { return impl_.size(); }
+  wtf_size_t Capacity() const { return impl_.capacity(); }
   bool empty() const { return impl_.empty(); }
 
   // Iterators iterate over pairs of values (called key) and counts (called
diff --git a/third_party/blink/renderer/platform/wtf/hash_map.h b/third_party/blink/renderer/platform/wtf/hash_map.h
index b52171d..f616814c 100644
--- a/third_party/blink/renderer/platform/wtf/hash_map.h
+++ b/third_party/blink/renderer/platform/wtf/hash_map.h
@@ -144,7 +144,7 @@
 
   wtf_size_t size() const;
   wtf_size_t Capacity() const;
-  void ReserveCapacityForSize(unsigned size) {
+  void ReserveCapacityForSize(wtf_size_t size) {
     impl_.ReserveCapacityForSize(size);
   }
 
diff --git a/third_party/blink/renderer/platform/wtf/hash_set.h b/third_party/blink/renderer/platform/wtf/hash_set.h
index 77c222a..47c38fa 100644
--- a/third_party/blink/renderer/platform/wtf/hash_set.h
+++ b/third_party/blink/renderer/platform/wtf/hash_set.h
@@ -88,11 +88,11 @@
 
   void swap(HashSet& ref) { impl_.swap(ref.impl_); }
 
-  unsigned size() const;
-  unsigned Capacity() const;
+  wtf_size_t size() const;
+  wtf_size_t Capacity() const;
   bool empty() const;
 
-  void ReserveCapacityForSize(unsigned size) {
+  void ReserveCapacityForSize(wtf_size_t size) {
     impl_.ReserveCapacityForSize(size);
   }
 
@@ -267,12 +267,12 @@
 }
 
 template <typename T, typename U, typename V>
-inline unsigned HashSet<T, U, V>::size() const {
+inline wtf_size_t HashSet<T, U, V>::size() const {
   return impl_.size();
 }
 
 template <typename T, typename U, typename V>
-inline unsigned HashSet<T, U, V>::Capacity() const {
+inline wtf_size_t HashSet<T, U, V>::Capacity() const {
   return impl_.Capacity();
 }
 
diff --git a/third_party/blink/renderer/platform/wtf/hash_set_test.cc b/third_party/blink/renderer/platform/wtf/hash_set_test.cc
index 6ac25d2..aef07c4 100644
--- a/third_party/blink/renderer/platform/wtf/hash_set_test.cc
+++ b/third_party/blink/renderer/platform/wtf/hash_set_test.cc
@@ -113,11 +113,11 @@
   EXPECT_EQ(1U, set.size());
 }
 
-template <unsigned size>
+template <wtf_size_t size>
 void TestReserveCapacity();
 template <>
 void TestReserveCapacity<0>() {}
-template <unsigned size>
+template <wtf_size_t size>
 void TestReserveCapacity() {
   HashSet<int> test_set;
 
@@ -125,8 +125,8 @@
   EXPECT_EQ(0UL, test_set.Capacity());
 
   test_set.ReserveCapacityForSize(size);
-  const unsigned initial_capacity = test_set.Capacity();
-  const unsigned kMinimumTableSize = HashTraits<int>::kMinimumTableSize;
+  const wtf_size_t initial_capacity = test_set.Capacity();
+  constexpr wtf_size_t kMinimumTableSize = HashTraits<int>::kMinimumTableSize;
 
   // reserveCapacityForSize should respect minimumTableSize.
   EXPECT_GE(initial_capacity, kMinimumTableSize);
@@ -139,7 +139,7 @@
 
   // Adding items up to less than half the capacity should not change the
   // capacity.
-  unsigned capacity_limit = initial_capacity / 2 - 1;
+  wtf_size_t capacity_limit = initial_capacity / 2 - 1;
   for (wtf_size_t i = size; i < capacity_limit; ++i) {
     test_set.insert(i + 1);
     EXPECT_EQ(initial_capacity, test_set.Capacity());
diff --git a/third_party/blink/renderer/platform/wtf/hash_table.h b/third_party/blink/renderer/platform/wtf/hash_table.h
index 6e7e3b6..7e168e5 100644
--- a/third_party/blink/renderer/platform/wtf/hash_table.h
+++ b/third_party/blink/renderer/platform/wtf/hash_table.h
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/platform/wtf/construct_traits.h"
 #include "third_party/blink/renderer/platform/wtf/hash_traits.h"
 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
 #if !defined(DUMP_HASHTABLE_STATS)
 #define DUMP_HASHTABLE_STATS 0
@@ -690,11 +691,11 @@
     return MakeKnownGoodConstIterator(table_ + table_size_);
   }
 
-  unsigned size() const {
+  wtf_size_t size() const {
     DCHECK(!AccessForbidden());
     return key_count_;
   }
-  unsigned Capacity() const {
+  wtf_size_t Capacity() const {
     DCHECK(!AccessForbidden());
     return table_size_;
   }
@@ -703,7 +704,7 @@
     return !key_count_;
   }
 
-  void ReserveCapacityForSize(unsigned size);
+  void ReserveCapacityForSize(wtf_size_t size);
 
   template <typename IncomingValueType>
   AddResult insert(IncomingValueType&& value) {
@@ -812,8 +813,8 @@
     requires Allocator::kIsGarbageCollected;
 
  private:
-  static ValueType* AllocateTable(unsigned size);
-  static void DeleteAllBucketsAndDeallocate(ValueType* table, unsigned size);
+  static ValueType* AllocateTable(wtf_size_t size);
+  static void DeleteAllBucketsAndDeallocate(ValueType* table, wtf_size_t size);
 
   struct LookupResult {
     ValueType* entry;
@@ -841,11 +842,11 @@
   ValueType* Expand(ValueType* entry = nullptr);
   void Shrink() { Rehash(table_size_ / 2, nullptr); }
 
-  ValueType* ExpandBuffer(unsigned new_table_size, ValueType* entry, bool&);
+  ValueType* ExpandBuffer(wtf_size_t new_table_size, ValueType* entry, bool&);
   ValueType* RehashTo(ValueType* new_table,
-                      unsigned new_table_size,
+                      wtf_size_t new_table_size,
                       ValueType* entry);
-  ValueType* Rehash(unsigned new_table_size, ValueType* entry);
+  ValueType* Rehash(wtf_size_t new_table_size, ValueType* entry);
   ValueType* Reinsert(ValueType&&);
 
   static void ReinitializeBucket(ValueType& bucket);
@@ -890,32 +891,18 @@
 
   // Constructor for hash tables with raw storage.
   struct RawStorageTag {};
-  HashTable(RawStorageTag, ValueType* table, unsigned size)
-      : table_(table),
-        table_size_(size),
-        key_count_(0),
-        deleted_count_(0)
-#if DCHECK_IS_ON()
-        ,
-        access_forbidden_(0),
-        modifications_(0)
-#endif
-#if DUMP_HASHTABLE_STATS_PER_TABLE
-        ,
-        stats_(nullptr)
-#endif
-  {
-  }
+  HashTable(RawStorageTag, ValueType* table, wtf_size_t size)
+      : table_(table), table_size_(size) {}
 
   ValueType* table_;
-  unsigned table_size_;
-  unsigned key_count_;
+  wtf_size_t table_size_;
+  wtf_size_t key_count_ = 0;
 #if DCHECK_IS_ON()
-  unsigned deleted_count_ : 30;
-  unsigned access_forbidden_ : 1;
-  unsigned modifications_;
+  wtf_size_t deleted_count_ : 30 = 0;
+  wtf_size_t access_forbidden_ : 1 = 0;
+  wtf_size_t modifications_ = 0;
 #else
-  unsigned deleted_count_ : 31;
+  wtf_size_t deleted_count_ : 31 = 0;
 #endif
 
 #if DUMP_HASHTABLE_STATS_PER_TABLE
@@ -923,7 +910,8 @@
   mutable
       typename std::conditional<Allocator::kIsGarbageCollected,
                                 HashTableStats*,
-                                std::unique_ptr<HashTableStats>>::type stats_;
+                                std::unique_ptr<HashTableStats>>::type stats_ =
+          nullptr;
   void DumpStats() {
     if (stats_) {
       stats_->DumpStats();
@@ -984,9 +972,10 @@
 {
 }
 
-inline unsigned CalculateCapacity(unsigned size) {
-  for (unsigned mask = size; mask; mask >>= 1)
+inline wtf_size_t CalculateCapacity(wtf_size_t size) {
+  for (wtf_size_t mask = size; mask; mask >>= 1) {
     size |= mask;         // 00110101010 -> 00111111111
+  }
   return (size + 1) * 2;  // 00111111111 -> 10000000000
 }
 
@@ -996,14 +985,9 @@
           typename Traits,
           typename KeyTraits,
           typename Allocator>
-void HashTable<Key,
-               Value,
-               Extractor,
-
-               Traits,
-               KeyTraits,
-               Allocator>::ReserveCapacityForSize(unsigned new_size) {
-  unsigned new_capacity = CalculateCapacity(new_size);
+void HashTable<Key, Value, Extractor, Traits, KeyTraits, Allocator>::
+    ReserveCapacityForSize(wtf_size_t new_size) {
+  wtf_size_t new_capacity = CalculateCapacity(new_size);
   if (new_capacity < KeyTraits::kMinimumTableSize)
     new_capacity = KeyTraits::kMinimumTableSize;
 
@@ -1152,7 +1136,7 @@
   }
 
   template <typename HashTable>
-  static Value* AllocateTable(unsigned size, size_t alloc_size) {
+  static Value* AllocateTable(wtf_size_t size, size_t alloc_size) {
     Value* result =
         Allocator::template AllocateHashTableBacking<Value, HashTable>(
             alloc_size);
@@ -1160,8 +1144,8 @@
     return result;
   }
 
-  static void InitializeTable(Value* table, unsigned size) {
-    for (unsigned i = 0; i < size; i++) {
+  static void InitializeTable(Value* table, wtf_size_t size) {
+    for (wtf_size_t i = 0; i < size; i++) {
       Reinitialize(table[i]);
     }
   }
@@ -1185,7 +1169,7 @@
   }
 
   template <typename HashTable>
-  static Value* AllocateTable(unsigned size, size_t alloc_size) {
+  static Value* AllocateTable(wtf_size_t size, size_t alloc_size) {
     Value* result =
         Allocator::template AllocateZeroedHashTableBacking<Value, HashTable>(
             alloc_size);
@@ -1193,15 +1177,15 @@
     return result;
   }
 
-  static void InitializeTable(Value* table, unsigned size) {
+  static void InitializeTable(Value* table, wtf_size_t size) {
     AtomicMemzero(table, size * sizeof(Value));
     CheckEmptyValues(table, size);
   }
 
  private:
-  static void CheckEmptyValues(Value* values, unsigned size) {
+  static void CheckEmptyValues(Value* values, wtf_size_t size) {
 #if EXPENSIVE_DCHECKS_ARE_ON()
-    for (unsigned i = 0; i < size; i++) {
+    for (wtf_size_t i = 0; i < size; i++) {
       DCHECK(IsHashTraitsEmptyValue<Traits>(values[i]));
     }
 #endif
@@ -1498,7 +1482,7 @@
   RegisterModification();
   EnterAccessForbiddenScope();
 
-  for (unsigned i = 0; i < table_size_; ++i) {
+  for (wtf_size_t i = 0; i < table_size_; ++i) {
     if (!IsEmptyOrDeletedBucket(table_[i]) && pred(table_[i])) {
       DeleteBucket(table_[i]);
 #if DUMP_HASHTABLE_STATS
@@ -1568,7 +1552,7 @@
           typename Allocator>
 Value*
 HashTable<Key, Value, Extractor, Traits, KeyTraits, Allocator>::AllocateTable(
-    unsigned size) {
+    wtf_size_t size) {
   // Assert that we will not use memset on things with a vtable entry.  The
   // compiler will also check this on some platforms. We would like to check
   // this on the whole value (key-value pair), but std::is_polymorphic will
@@ -1597,7 +1581,7 @@
           typename KeyTraits,
           typename Allocator>
 void HashTable<Key, Value, Extractor, Traits, KeyTraits, Allocator>::
-    DeleteAllBucketsAndDeallocate(ValueType* table, unsigned size) {
+    DeleteAllBucketsAndDeallocate(ValueType* table, wtf_size_t size) {
   // We delete a bucket in the following cases:
   // - It is not trivially destructible.
   // - The table is weak (thus garbage collected) and we are currently marking.
@@ -1609,7 +1593,7 @@
       !std::is_trivially_destructible<ValueType>::value ||
       (WTF::IsWeak<ValueType>::value && Allocator::IsIncrementalMarking());
   if (needs_bucket_deletion) {
-    for (unsigned i = 0; i < size; ++i) {
+    for (wtf_size_t i = 0; i < size; ++i) {
       // This code is called when the hash table is cleared or resized. We
       // have allocated a new backing store and we need to run the
       // destructors on the old backing store, as it is being freed. If we
@@ -1638,7 +1622,7 @@
           typename Allocator>
 Value* HashTable<Key, Value, Extractor, Traits, KeyTraits, Allocator>::Expand(
     Value* entry) {
-  unsigned new_size;
+  wtf_size_t new_size;
   if (!table_size_) {
     new_size = KeyTraits::kMinimumTableSize;
   } else if (MustRehashInPlace()) {
@@ -1659,7 +1643,7 @@
           typename Allocator>
 Value*
 HashTable<Key, Value, Extractor, Traits, KeyTraits, Allocator>::ExpandBuffer(
-    unsigned new_table_size,
+    wtf_size_t new_table_size,
     Value* entry,
     bool& success) {
   success = false;
@@ -1673,11 +1657,11 @@
   success = true;
 
   Value* new_entry = nullptr;
-  unsigned old_table_size = table_size_;
+  wtf_size_t old_table_size = table_size_;
   ValueType* original_table = table_;
 
   ValueType* temporary_table = AllocateTable(old_table_size);
-  for (unsigned i = 0; i < old_table_size; i++) {
+  for (wtf_size_t i = 0; i < old_table_size; i++) {
     if (&table_[i] == entry)
       new_entry = &temporary_table[i];
     if (IsEmptyOrDeletedBucket(table_[i])) {
@@ -1709,7 +1693,7 @@
           typename Allocator>
 Value* HashTable<Key, Value, Extractor, Traits, KeyTraits, Allocator>::RehashTo(
     ValueType* new_table,
-    unsigned new_table_size,
+    wtf_size_t new_table_size,
     Value* entry) {
 #if DUMP_HASHTABLE_STATS
   if (table_size_ != 0) {
@@ -1730,7 +1714,7 @@
 #endif
 
   Value* new_entry = nullptr;
-  for (unsigned i = 0; i != table_size_; ++i) {
+  for (wtf_size_t i = 0; i != table_size_; ++i) {
     if (IsEmptyOrDeletedBucket(table_[i])) {
       DCHECK_NE(&table_[i], entry);
       continue;
@@ -1745,7 +1729,7 @@
   Allocator::TraceBackingStoreIfMarked(new_hash_table.table_);
 
   ValueType* old_table = table_;
-  unsigned old_table_size = table_size_;
+  wtf_size_t old_table_size = table_size_;
 
   // This swaps the newly allocated buffer with the current one. The store to
   // the current table has to be atomic to prevent races with concurrent marker.
@@ -1779,9 +1763,9 @@
           typename KeyTraits,
           typename Allocator>
 Value* HashTable<Key, Value, Extractor, Traits, KeyTraits, Allocator>::Rehash(
-    unsigned new_table_size,
+    wtf_size_t new_table_size,
     Value* entry) {
-  unsigned old_table_size = table_size_;
+  wtf_size_t old_table_size = table_size_;
 
 #if DUMP_HASHTABLE_STATS
   if (old_table_size != 0) {
@@ -1861,7 +1845,7 @@
   key_count_ = other.key_count_;
   deleted_count_ = other.deleted_count_;
 
-  for (unsigned i = 0; i < table_size_; i++) {
+  for (wtf_size_t i = 0; i < table_size_; i++) {
     if (other.IsEmptyBucket(other.table_[i])) {
       // Do nothing. All entries are initially empty by AllocateTable().
     } else if (other.IsDeletedBucket(other.table_[i])) {
@@ -1929,7 +1913,7 @@
   std::swap(table_size_, other.table_size_);
   std::swap(key_count_, other.key_count_);
   // std::swap does not work for bit fields.
-  unsigned deleted = deleted_count_;
+  wtf_size_t deleted = deleted_count_;
   deleted_count_ = other.deleted_count_;
   other.deleted_count_ = deleted;
 
diff --git a/third_party/blink/renderer/platform/wtf/hash_traits.h b/third_party/blink/renderer/platform/wtf/hash_traits.h
index 8a8422e..4604401 100644
--- a/third_party/blink/renderer/platform/wtf/hash_traits.h
+++ b/third_party/blink/renderer/platform/wtf/hash_traits.h
@@ -36,6 +36,7 @@
 #include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
 namespace WTF {
 
@@ -173,9 +174,9 @@
   // The allocation pool for nodes is one big chunk that ASAN has no insight
   // into, so it can cloak errors. Make it as small as possible to force nodes
   // to be allocated individually where ASAN can see them.
-  static constexpr unsigned kMinimumTableSize = 1;
+  static constexpr wtf_size_t kMinimumTableSize = 1;
 #else
-  static constexpr unsigned kMinimumTableSize = 8;
+  static constexpr wtf_size_t kMinimumTableSize = 8;
 #endif
 
   // The NeedsToForbidGCOnMove flag is used to make the hash table move
@@ -519,7 +520,8 @@
     return IsHashTraitsDeletedValue<FieldTraits>(value.*field);
   }
 
-  static constexpr unsigned kMinimumTableSize = FieldTraits::kMinimumTableSize;
+  static constexpr wtf_size_t kMinimumTableSize =
+      FieldTraits::kMinimumTableSize;
 
   template <typename U = void>
   struct NeedsToForbidGCOnMove {
diff --git a/third_party/blink/renderer/platform/wtf/size_assertions.cc b/third_party/blink/renderer/platform/wtf/size_assertions.cc
index 9d61c72..7c5e2584 100644
--- a/third_party/blink/renderer/platform/wtf/size_assertions.cc
+++ b/third_party/blink/renderer/platform/wtf/size_assertions.cc
@@ -48,17 +48,17 @@
   // Don't add anything here because this should stay small.
 };
 
-template <typename T, unsigned inlineCapacity = 0>
+template <typename T, wtf_size_t inlineCapacity = 0>
 struct SameSizeAsVectorWithInlineCapacity;
 
 template <typename T>
 struct SameSizeAsVectorWithInlineCapacity<T, 0> {
   void* buffer_pointer;
-  unsigned capacity;
-  unsigned size;
+  wtf_size_t capacity;
+  wtf_size_t size;
 };
 
-template <typename T, unsigned inlineCapacity>
+template <typename T, wtf_size_t inlineCapacity>
 struct SameSizeAsVectorWithInlineCapacity {
   SameSizeAsVectorWithInlineCapacity<T, 0> base_capacity;
 #if !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
diff --git a/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h b/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
index 3cf6950..d0e7edd 100644
--- a/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
+++ b/third_party/blink/renderer/platform/wtf/text/number_parsing_options.h
@@ -11,18 +11,18 @@
 
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 
-namespace WTF {
+namespace blink {
 
 // Copyable and immutable object representing number parsing flags.
 class NumberParsingOptions final {
   STACK_ALLOCATED();
 
  public:
-  // 'Strict' behavior for WTF::String.
+  // 'Strict' behavior for blink::String.
   static constexpr NumberParsingOptions Strict() {
     return NumberParsingOptions().SetAcceptLeadingPlus().SetAcceptWhiteSpace();
   }
-  // Non-'Strict' behavior for WTF::String.
+  // Non-'Strict' behavior for blink::String.
   static constexpr NumberParsingOptions Loose() {
     return Strict().SetAcceptTrailingGarbage();
   }
@@ -77,10 +77,6 @@
   unsigned accept_minus_zero_for_unsigned_ : 1;
 };
 
-}  // namespace WTF
-
-namespace blink {
-using WTF::NumberParsingOptions;
-}
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_NUMBER_PARSING_OPTIONS_H_
diff --git a/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png-expected.html b/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png-expected.html
index 4fc57ea..f033a7b 100644
--- a/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png-expected.html
+++ b/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png-expected.html
@@ -4,7 +4,9 @@
 <div>This file tests the image pasting functionality of DataTransferItems.
 To try the test manually, right-click on the image &gt; Copy Image, click
 anywhere in the background of the main page and paste.</div>
-<iframe id="src" src="resources/mozilla.gif"></iframe><br>
+<div style="height:150px;">
+  <img src="resources/mozilla.gif" style="display:block;">
+</div>
 <img src="resources/mozilla.gif">
 </body>
 </html>
diff --git a/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png.html b/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png.html
index 43c2034..d0d8d7c 100644
--- a/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png.html
+++ b/third_party/blink/web_tests/editing/pasteboard/data-transfer-items-image-png.html
@@ -3,27 +3,30 @@
 <head>
 <script src="../editing.js"></script>
 <script>
-function paste(event)
+function pasted(event)
 {
     var items = event.clipboardData.items;
     for (var i = 0; i < items.length; ++i) {
         if (items[i].kind == 'file' && items[i].type == 'image/png') {
+            let dest = document.createElement("img");
+            dest.onload = function() {
+                if (window.testRunner) {
+                    testRunner.notifyDone();
+                }
+            }
             var blob = items[i].getAsFile();
             var url = window.URL.createObjectURL(blob);
-            document.getElementById('dest').src = url;
+            document.body.appendChild(dest);
+            dest.src = url;
+            break;
         }
     }
-    window.setTimeout(function () {
-        if (window.testRunner)
-            testRunner.notifyDone();
-    }, 0);
 }
 
 function runTest()
 {
     if (!window.testRunner)
         return;
-    testRunner.waitUntilDone();
     var srcElement = document.getElementById('src');
     srcElement.contentWindow.document.execCommand('copy');
     eventSender.mouseMoveTo(1, 1);
@@ -31,13 +34,14 @@
     eventSender.mouseUp();
     document.execCommand('paste');
 }
+
+testRunner.waitUntilDone();
 </script>
 </head>
-<body onload="runTest()" onpaste="paste(event)">
+<body onpaste="pasted(event)">
 <div>This file tests the image pasting functionality of DataTransferItems.
 To try the test manually, right-click on the image &gt; Copy Image, click
 anywhere in the background of the main page and paste.</div>
-<iframe id="src" src="resources/mozilla.gif"></iframe><br>
-<img id="dest">
+<iframe id="src" src="resources/mozilla.gif" onload="runTest()" style="display:block; border:none;"></iframe>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/text/white-space-processing-049-ref.xht b/third_party/blink/web_tests/external/wpt/css/CSS2/text/white-space-processing-049-ref.xht
index 0903a615..2c61f82 100644
--- a/third_party/blink/web_tests/external/wpt/css/CSS2/text/white-space-processing-049-ref.xht
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/text/white-space-processing-049-ref.xht
@@ -9,7 +9,7 @@
   <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" />
 
   <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
-  <style type="text/css"><![CDATA[
+  <style type="text/css">
   div
   {
   background-color: orange;
@@ -18,7 +18,6 @@
   padding: 0em 2em;
   width: 3em;
   }
-  ]]>
   </style>
 
  </head>
@@ -30,4 +29,4 @@
   <div>123</div>
 
  </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/third_party/blink/web_tests/images/animated-images-use-counter-001.html b/third_party/blink/web_tests/images/animated-images-use-counter-001.html
new file mode 100644
index 0000000..039e52a
--- /dev/null
+++ b/third_party/blink/web_tests/images/animated-images-use-counter-001.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script>
+const kAnimatedImageUsedMoreThanOnce = 5605;
+
+async_test(function(t) {
+  internals.clearUseCounter(document, kAnimatedImageUsedMoreThanOnce);
+
+  let gif1 = document.createElement("img");
+  gif1.src = "resources/animated.gif";
+  document.body.appendChild(gif1);
+  let gif2 = document.createElement("img");
+  gif2.src = "resources/animated.gif";
+  document.body.appendChild(gif2);
+
+  onload = t.step_func(() => {
+    requestAnimationFrame(t.step_func(() => {
+      requestAnimationFrame(t.step_func_done(() => {
+        assert_true(internals.isUseCounted(document, kAnimatedImageUsedMoreThanOnce));
+      }));
+    }));
+  });
+}, 'Check that kAnimatedImageUsedMoreThanOnce is counted with a duplicated animated image');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/images/animated-images-use-counter-002.html b/third_party/blink/web_tests/images/animated-images-use-counter-002.html
new file mode 100644
index 0000000..42de3798
--- /dev/null
+++ b/third_party/blink/web_tests/images/animated-images-use-counter-002.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script>
+const kAnimatedImageUsedMoreThanOnce = 5605;
+
+async_test(function(t) {
+  internals.clearUseCounter(document, kAnimatedImageUsedMoreThanOnce);
+
+  let gif1 = document.createElement("img");
+  gif1.src = "resources/animated.gif";
+  document.body.appendChild(gif1);
+
+  onload = t.step_func(() => {
+    requestAnimationFrame(t.step_func(() => {
+      requestAnimationFrame(t.step_func_done(() => {
+        assert_false(internals.isUseCounted(document, kAnimatedImageUsedMoreThanOnce));
+      }));
+    }));
+  });
+}, 'Check that kAnimatedImageUsedMoreThanOnce is NOT counted with a single animated image');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/images/animated-images-use-counter-003.html b/third_party/blink/web_tests/images/animated-images-use-counter-003.html
new file mode 100644
index 0000000..75a1e3f
--- /dev/null
+++ b/third_party/blink/web_tests/images/animated-images-use-counter-003.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script>
+const kAnimatedImageUsedMoreThanOnce = 5605;
+
+async_test(function(t) {
+  internals.clearUseCounter(document, kAnimatedImageUsedMoreThanOnce);
+
+  let gif1 = document.createElement("img");
+  gif1.src = "resources/animated.gif";
+  document.body.appendChild(gif1);
+  let gif2 = document.createElement("img");
+  gif2.src = "resources/animated2.gif";
+  document.body.appendChild(gif2);
+
+  onload = t.step_func(() => {
+    requestAnimationFrame(t.step_func(() => {
+      requestAnimationFrame(t.step_func_done(() => {
+        assert_false(internals.isUseCounted(document, kAnimatedImageUsedMoreThanOnce));
+      }));
+    }));
+  });
+}, 'Check that kAnimatedImageUsedMoreThanOnce is NOT counted with different animated image');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/images/animated-images-use-counter-004.html b/third_party/blink/web_tests/images/animated-images-use-counter-004.html
new file mode 100644
index 0000000..933fd95
--- /dev/null
+++ b/third_party/blink/web_tests/images/animated-images-use-counter-004.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script>
+const kAnimatedImageUsedMoreThanOnce = 5605;
+
+async_test(function(t) {
+  internals.clearUseCounter(document, kAnimatedImageUsedMoreThanOnce);
+
+  let png1 = document.createElement("img");
+  png1.src = "resources/png-simple.png";
+  document.body.appendChild(png1);
+  let png2 = document.createElement("img");
+  png2.src = "resources/png-simple.png";
+  document.body.appendChild(png2);
+
+  onload = t.step_func(() => {
+    requestAnimationFrame(t.step_func(() => {
+      requestAnimationFrame(t.step_func_done(() => {
+        assert_false(internals.isUseCounted(document, kAnimatedImageUsedMoreThanOnce));
+      }));
+    }));
+  });
+}, 'Check that kAnimatedImageUsedMoreThanOnce is NOT counted with duplicated images not animated');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/images/animated-images-use-counter-005.html b/third_party/blink/web_tests/images/animated-images-use-counter-005.html
new file mode 100644
index 0000000..7a9c3a8
--- /dev/null
+++ b/third_party/blink/web_tests/images/animated-images-use-counter-005.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body>
+<script>
+const kAnimatedImageUsedMoreThanOnce = 5605;
+
+async_test(function(t) {
+  internals.clearUseCounter(document, kAnimatedImageUsedMoreThanOnce);
+
+  let apng1 = document.createElement("img");
+  apng1.src = "resources/animated.png";
+  document.body.appendChild(apng1);
+  let apng2 = document.createElement("img");
+  apng2.src = "resources/animated.png";
+  document.body.appendChild(apng2);
+
+  onload = t.step_func(() => {
+    requestAnimationFrame(t.step_func(() => {
+      requestAnimationFrame(t.step_func_done(() => {
+        assert_true(internals.isUseCounted(document, kAnimatedImageUsedMoreThanOnce));
+      }));
+    }));
+  });
+}, 'Check that kAnimatedImageUsedMoreThanOnce is counted with a duplicated animated image (APNG)');
+</script>
+</body>
diff --git a/third_party/blink/web_tests/images/resources/animated.png b/third_party/blink/web_tests/images/resources/animated.png
new file mode 100644
index 0000000..69f3c05
--- /dev/null
+++ b/third_party/blink/web_tests/images/resources/animated.png
Binary files differ
diff --git a/third_party/blink/web_tests/inspector-protocol/dom/getOuterHTML-expected.txt b/third_party/blink/web_tests/inspector-protocol/dom/getOuterHTML-expected.txt
new file mode 100644
index 0000000..712d7d1
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/dom/getOuterHTML-expected.txt
@@ -0,0 +1,38 @@
+Tests how DOM domain works with getOuterHTML.
+
+getOuterHTML(declarativeId, includeShadowDOM=false):
+{
+    id : <number>
+    result : {
+        outerHTML : <div id="declarative">            </div>
+    }
+    sessionId : <string>
+}
+
+getOuterHTML(declarativeId, includeShadowDOM=true):
+{
+    id : <number>
+    result : {
+        outerHTML : <div id="declarative"><template shadowrootmode="open">         <div>contents</div>       </template>            </div>
+    }
+    sessionId : <string>
+}
+
+getOuterHTML(jsId, includeShadowDOM=false):
+{
+    id : <number>
+    result : {
+        outerHTML : <div id="js">     </div>
+    }
+    sessionId : <string>
+}
+
+getOuterHTML(jsId, includeShadowDOM=true):
+{
+    id : <number>
+    result : {
+        outerHTML : <div id="js"><template shadowrootmode="open"><div>more contents</div></template>     </div>
+    }
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/inspector-protocol/dom/getOuterHTML.js b/third_party/blink/web_tests/inspector-protocol/dom/getOuterHTML.js
new file mode 100644
index 0000000..f4beb95a
--- /dev/null
+++ b/third_party/blink/web_tests/inspector-protocol/dom/getOuterHTML.js
@@ -0,0 +1,41 @@
+(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
+  var {page, session, dp} = await testRunner.startHTML(
+      `
+    <div id="declarative">
+      <template shadowrootmode="open">
+        <div>contents</div>
+      </template>
+    </div>
+    <div id="js">
+    </div>
+    <script>
+      const div = document.createElement('div');
+      div.textContent = 'more contents';
+      document.getElementById('js').attachShadow({mode: "open"}).appendChild(div);
+    </script>
+  `,
+      'Tests how DOM domain works with getOuterHTML.');
+
+  const {result: {root: {nodeId: docId}}} = await dp.DOM.getDocument();
+  const {result: {nodeId: declarativeId}} =
+      await dp.DOM.querySelector({nodeId: docId, selector: '#declarative'});
+  const {result: {nodeId: jsId}} =
+      await dp.DOM.querySelector({nodeId: docId, selector: '#js'});
+
+  testRunner.log('\ngetOuterHTML(declarativeId, includeShadowDOM=false):');
+  testRunner.log(await dp.DOM.getOuterHTML(
+      {nodeId: declarativeId, includeShadowDOM: false}));
+
+  testRunner.log('\ngetOuterHTML(declarativeId, includeShadowDOM=true):');
+  testRunner.log(await dp.DOM.getOuterHTML(
+      {nodeId: declarativeId, includeShadowDOM: true}));
+
+  testRunner.log('\ngetOuterHTML(jsId, includeShadowDOM=false):');
+  testRunner.log(
+      await dp.DOM.getOuterHTML({nodeId: jsId, includeShadowDOM: false}));
+
+  testRunner.log('\ngetOuterHTML(jsId, includeShadowDOM=true):');
+  testRunner.log(
+      await dp.DOM.getOuterHTML({nodeId: jsId, includeShadowDOM: true}));
+  testRunner.completeTest();
+});
diff --git a/third_party/blink/web_tests/webaudio/AudioParam/audioparam-setValueCurve-end.html b/third_party/blink/web_tests/webaudio/AudioParam/audioparam-setValueCurve-end.html
index 5ede1750..c9e43f9 100644
--- a/third_party/blink/web_tests/webaudio/AudioParam/audioparam-setValueCurve-end.html
+++ b/third_party/blink/web_tests/webaudio/AudioParam/audioparam-setValueCurve-end.html
@@ -1,162 +1,143 @@
 <!DOCTYPE html>
 <html>
-  <head>
-    <title>
-      Test Automation Following setValueCurveAtTime Automations
-    </title>
-    <script src="../../resources/testharness.js"></script>
-    <script src="../../resources/testharnessreport.js"></script>
-    <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audit.js"></script>
-    <script src="../resources/audio-param.js"></script>
-  </head>
-  <body>
-    <script id="layout-test-code">
-      let sampleRate = 12800;
-      // Some short duration because we don't need to run the test for very
-      // long.
-      let testDurationFrames = 256;
-      let testDurationSec = testDurationFrames / sampleRate;
-      let curveDuration = testDurationSec / 2;
+<head>
+  <title>Test Automation Following setValueCurveAtTime Automations</title>
+  <script src="../../resources/testharness.js"></script>
+  <script src="../../resources/testharnessreport.js"></script>
+  <script src="../resources/audit-util.js"></script>
+  <script src="../resources/audio-param.js"></script>
+</head>
+<body>
+<script>
+  const sampleRate = 12800;
 
-      let audit = Audit.createTaskRunner();
+  // Short duration since the test doesn't require long audio
+  const testDurationFrames = 256;
+  const testDurationSec = testDurationFrames / sampleRate;
+  const curveDuration = testDurationSec / 2;
 
-      // Configuration for each test.
-      //
-      // Required options:
-      //   automation - Name of automation method to test
-      //   time       - Time for the automation method.
-      // Optional options:
-      //   extraDuration - extra time for the duration of the setValueCurve
-      //                   duration.  Default is 0. This should not be on a
-      //                   sample frame boundary. This is for testing that
-      //                   curves that don't end on a frame boundary are handled
-      //                   correctly.
-      //   threshold     - Error threshold for the test; default is 0.
-      let testConfigs = [
-        {
-          automation: 'linearRampToValueAtTime',
-          time: testDurationSec,
-          threshold: 3.9737e-8
-        },
-        {
-          automation: 'linearRampToValueAtTime',
-          time: testDurationSec,
-          extraDuration: 0.5 / sampleRate,
-          threshold: 1.8141e-8
-        },
-        {
-          automation: 'exponentialRampToValueAtTime',
-          time: testDurationSec,
-          threshold: 3.9737e-8
-        },
-        {
-          automation: 'exponentialRampToValueAtTime',
-          time: testDurationSec,
-          extraDuration: 0.5 / sampleRate,
-          threshold: 7.8294e-8
-        },
-        {
-          automation: 'setTargetAtTime',
-          time: curveDuration,
-          threshold: 1.5895e-7
-        },
-        {
-          automation: 'setTargetAtTime',
-          time: curveDuration + 0.5 / sampleRate,
-          extraDuration: 0.5 / sampleRate,
-          threshold: 1.3278e-7
-        }
-      ];
+  // Configuration for each test.
+  //
+  // Required options:
+  //   automation - Name of automation method to test
+  //   time       - Time for the automation method.
+  // Optional options:
+  //   extraDuration - extra time for the duration of the setValueCurve
+  //                   duration.  Default is 0. This should not be on a
+  //                   sample frame boundary. This is for testing that
+  //                   curves that don't end on a frame boundary are handled
+  //                   correctly.
+  //   threshold     - Error threshold for the test; default is 0.
+  const testConfigs = [
+    {
+      automation: 'linearRampToValueAtTime',
+      time: testDurationSec,
+      threshold: 3.9737e-8
+    },
+    {
+      automation: 'linearRampToValueAtTime',
+      time: testDurationSec,
+      extraDuration: 0.5 / sampleRate,
+      threshold: 1.8141e-8
+    },
+    {
+      automation: 'exponentialRampToValueAtTime',
+      time: testDurationSec,
+      threshold: 3.9737e-8
+    },
+    {
+      automation: 'exponentialRampToValueAtTime',
+      time: testDurationSec,
+      extraDuration: 0.5 / sampleRate,
+      threshold: 7.8294e-8
+    },
+    {
+      automation: 'setTargetAtTime',
+      time: curveDuration,
+      threshold: 1.5895e-7
+    },
+    {
+      automation: 'setTargetAtTime',
+      time: curveDuration + 0.5 / sampleRate,
+      extraDuration: 0.5 / sampleRate,
+      threshold: 1.3278e-7
+    }
+  ];
 
-      // Define tests from the configs
-      for (k in testConfigs) {
-        audit.define(k + ': ' + testConfigs[k].automation, (function(config) {
-                       return (task, should) => {
-                         runTest(should, config).then(() => task.done());
-                       };
-                     })(testConfigs[k]));
-      }
+  testConfigs.forEach((config, index) => {
+    const title =
+        `Automation after setValueCurve: ${config.automation} ` +
+            ` (config ${index + 1})`;
+    promise_test(async t => {
+      await runTest(config);
+    }, title);
+  });
 
-      audit.run();
+  async function runTest(options) {
+    // For the test, use a gain node with a constant input to test the
+    // automations.
+    const context = new OfflineAudioContext(1, testDurationFrames, sampleRate);
 
-      function runTest(should, options) {
-        // For the test, use a gain node with a constant input to test the
-        // automations.
-        let context =
-            new OfflineAudioContext(1, testDurationFrames, sampleRate);
-        let source = context.createBufferSource();
-        source.buffer = createConstantBuffer(context, 1, 1);
-        source.loop = true;
+    const source = context.createBufferSource();
+    source.buffer = createConstantBuffer(context, 1, 1);
+    source.loop = true;
 
-        let gain = context.createGain();
+    const gain = context.createGain();
 
-        // Any valid curve is ok.  We only use the last value for testing.
-        let curve = [0, 2, 0.3];
-        let actualDuration = curveDuration + (options.extraDuration || 0);
-        gain.gain.setValueCurveAtTime(
-            Float32Array.from(curve), 0, actualDuration);
+    // Any valid curve is ok.  We only use the last value for testing.
+    const curve = [0, 2, 0.3];
+    const actualDuration = curveDuration + (options.extraDuration || 0);
+    gain.gain.setValueCurveAtTime(Float32Array.from(curve), 0, actualDuration);
 
-        // Run the desired test automation.  The extra parameter (0.01) is only
-        // used for setTargetAtTime tests; it's ignored for other tests.
-        let automationValue = 2;
-        gain.gain[options.automation](automationValue, options.time, 0.01);
+    // Run the desired test automation.  The extra parameter (0.01) is only
+    // used for setTargetAtTime tests; it's ignored for other tests.
+    const automationValue = 2;
+    gain.gain[options.automation](automationValue, options.time, 0.01);
 
-        source.connect(gain);
-        gain.connect(context.destination);
+    source.connect(gain).connect(context.destination);
+    source.start();
 
-        source.start();
+    const resultBuffer = await context.startRendering();
+    const result = resultBuffer.getChannelData(0);
 
-        return context.startRendering().then(function(resultBuffer) {
-          let result = resultBuffer.getChannelData(0);
+    // Only need to verify that the ramp started at the right
+    // value. Figure the nearest sample frame to the end curve.
+    const curveEndFrame = Math.ceil(actualDuration * sampleRate);
+    const curveEndTime = curveEndFrame / sampleRate;
+    const finalCurveValue = curve[curve.length - 1];
 
-          // Only need to verify that the ramp started at the right
-          // value. Figure the nearest sample frame to the end curve.
-          let curveEndFrame = Math.ceil(actualDuration * sampleRate);
+    let expectedResult;
+    // Determine the expected value after the end of the setValueCurve
+    // event.
+    if (options.automation === 'linearRampToValueAtTime') {
+      expectedResult = audioParamLinearRamp(
+        curveEndTime, finalCurveValue, actualDuration,
+        automationValue, testDurationSec
+      );
+    } else if (options.automation === 'exponentialRampToValueAtTime') {
+      expectedResult = audioParamExponentialRamp(
+        curveEndTime, finalCurveValue, actualDuration,
+        automationValue, testDurationSec
+      );
+    } else if (options.automation === 'setTargetAtTime') {
+      expectedResult = audioParamSetTarget(
+        curveEndTime, finalCurveValue, actualDuration,
+        automationValue, 0.01
+      );
+    }
 
-          let expectedResult = curve[curve.length - 1];
+    const message =
+    `After setValueCurveAtTime(..., 0, ${actualDuration}), ` +
+    `${options.automation}(2, ${options.time}` +
+    `${options.automation === 'setTargetAtTime' ? ', 0.01' : ''})`;
 
-          // Determine the expected value after the end of the setValueCurve
-          // event.
-          if (options.automation == 'linearRampToValueAtTime') {
-            expectedResult = audioParamLinearRamp(
-                curveEndFrame / sampleRate, curve[curve.length - 1],
-                actualDuration, automationValue, testDurationSec);
-          } else if (options.automation == 'exponentialRampToValueAtTime') {
-            expectedResult = audioParamExponentialRamp(
-                curveEndFrame / sampleRate, curve[curve.length - 1],
-                actualDuration, automationValue, testDurationSec);
-          } else if (options.automation == 'setTargetAtTime') {
-            expectedResult = audioParamSetTarget(
-                curveEndFrame / sampleRate, curve[curve.length - 1],
-                actualDuration, automationValue, 0.01);
-          }
+    assert_approx_equals(
+      result[curveEndFrame], expectedResult,
+      options.threshold || 0,
+      `${message} — expected value ≈ ${expectedResult} at time ${curveEndTime}`
+    );
+  }
 
-          let message = 'setValueCurve(..., ' + 0 + ', ' + actualDuration +
-              ').' + options.automation + '(2, ' + testDurationSec;
-
-          if (options.automation == 'setTargetAtTime')
-            message += ', 0.01';
-          message += ')';
-
-          should(
-              result[curveEndFrame],
-              message + ': value at time ' + curveEndFrame / sampleRate)
-              .beCloseTo(expectedResult, {threshold: options.threshold || 0});
-        });
-      }
-
-      function linearRampValue(t, t0, v0, t1, v1) {
-        return v0 + (v1 - v0) * (t - t0) / (t1 - t0);
-      }
-
-      function exponentialRampValue(t, t0, v0, t1, v1) {
-        return v0 * Math.pow(v1 / v0, (t - t0) / (t1 - t0));
-      }
-
-      function setTargetValue(t, t0, v0, v1, timeConstant) {
-        return v1 + (v0 - v1) * Math.exp(-(t - t0) / timeConstant)
-      }
-    </script>
-  </body>
+</script>
+</body>
 </html>
diff --git a/third_party/cros-components/src b/third_party/cros-components/src
index 97dc8c7..e7f1a1f 160000
--- a/third_party/cros-components/src
+++ b/third_party/cros-components/src
@@ -1 +1 @@
-Subproject commit 97dc8c7a1df880206cc54d9913a7e9d73677072a
+Subproject commit e7f1a1f42262790f48a9b69761c6c3e45ef225ca
diff --git a/third_party/skia b/third_party/skia
index e84f5ed..d37ac42 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit e84f5ed7d152063a4efd1399eb379305ebe5d3d6
+Subproject commit d37ac42bd8d6ff5a91f6bef83a09260524cae274
diff --git a/tools/clang/spanify/SpanifyManualPathsToIgnore.h b/tools/clang/spanify/SpanifyManualPathsToIgnore.h
index ec3e8995..5808b97 100644
--- a/tools/clang/spanify/SpanifyManualPathsToIgnore.h
+++ b/tools/clang/spanify/SpanifyManualPathsToIgnore.h
@@ -166,6 +166,18 @@
     // Included inside a class declaration. Adding top-level #includes (e.g.,
     // for span.h, <vector>) here will cause compilation errors.
     "gpu/command_buffer/client/gles2_interface_autogen.h",
+
+    // This test seems to deliberately go out of bounds into other contiguous
+    // regions of memory.
+    "remoting/base/typed_buffer_unittest.cc",
+
+    // This test is explicitly testing unsafe buffers.
+    "base/unsafe_buffers_unittest.cc",
+
+    // This test does weird things having a heap of size zero, allocating it
+    // somewhere else and then assuming they can index it without knowing the
+    // bounds.
+    "third_party/blink/renderer/platform/heap/test/heap_test.cc",
 };
 
 #endif  // TOOLS_CLANG_SPANIFY_SPANIFYMANUALPATHSTOIGNORE_H_
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 1d4c07c..9f842db 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -10044,7 +10044,6 @@
   <int value="-1473878093" label="HideArcMediaNotifications:disabled"/>
   <int value="-1473668019" label="token-binding:disabled"/>
   <int value="-1473537658" label="DeprecateLowUsageCodecs:disabled"/>
-  <int value="-1473179394" label="CrOSLateBootCameraAngleBackend:enabled"/>
   <int value="-1473136627" label="enable-web-payments"/>
   <int value="-1472825316" label="ContextualSearchLongpressResolve:enabled"/>
   <int value="-1472171434" label="HistoryPageHistorySyncPromo:disabled"/>
@@ -16400,7 +16399,6 @@
   <int value="880510010" label="enable-permissions-bubbles"/>
   <int value="880552542" label="ImeUsEnglishModelUpdate:enabled"/>
   <int value="880638328" label="DriveFsShowCSEFiles:enabled"/>
-  <int value="881202472" label="CrOSLateBootCameraAngleBackend:disabled"/>
   <int value="881920916" label="CrosWebAppInstallDialog:disabled"/>
   <int value="882103442" label="FloatWindow:disabled"/>
   <int value="882893584" label="UseOfHashAffiliationFetcher:disabled"/>
diff --git a/tools/metrics/histograms/metadata/blink/enums.xml b/tools/metrics/histograms/metadata/blink/enums.xml
index 95468c5a9..580f701 100644
--- a/tools/metrics/histograms/metadata/blink/enums.xml
+++ b/tools/metrics/histograms/metadata/blink/enums.xml
@@ -6235,6 +6235,7 @@
   <int value="5602" label="HTMLCanvasElementLowLatency_WebGL"/>
   <int value="5603" label="HTMLCanvasElementLowLatency_WebGL_Preserve"/>
   <int value="5604" label="HTMLCanvasElementLowLatency_WebGL_Discard"/>
+  <int value="5605" label="AnimatedImageUsedMoreThanOnce"/>
 </enum>
 
 <!-- LINT.ThenChange(//third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom:WebFeature) -->
diff --git a/tools/metrics/histograms/metadata/cros_ml/histograms.xml b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
index a7d75dc..139fcab 100644
--- a/tools/metrics/histograms/metadata/cros_ml/histograms.xml
+++ b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
@@ -5,7 +5,7 @@
 -->
 
 <!--
-This file is used to generate a comprehensive list of Power histograms
+This file is used to generate a comprehensive list of ML Service histograms
 along with a detailed description for each histogram.
 
 For best practices on writing histogram descriptions, see
@@ -201,7 +201,7 @@
 
 <histogram name="MachineLearningService.MlCore.DlcFinalInstallResult"
     enum="MachineLearningServiceDlcFinalInstallResult"
-    expires_after="2025-08-01">
+    expires_after="2026-02-01">
   <owner>amoylan@chromium.org</owner>
   <owner>chenjih@google.com</owner>
   <owner>nbowe@chromium.org</owner>
@@ -360,7 +360,7 @@
 <histogram
     name="MachineLearningService.{ModelName}.CreateGraphExecutorResult.Event"
     enum="MachineLearningServiceCreateGraphExecutorResultEvent"
-    expires_after="2025-08-01">
+    expires_after="2026-02-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
@@ -371,7 +371,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.{ModelName}.ExecuteResult.Event"
-    enum="MachineLearningServiceExecuteResultEvent" expires_after="2025-08-24">
+    enum="MachineLearningServiceExecuteResultEvent" expires_after="2026-02-01">
   <owner>amoylan@chromium.org</owner>
   <owner>alanlxl@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/net/enums.xml b/tools/metrics/histograms/metadata/net/enums.xml
index 5e7988d..19a3868b8c 100644
--- a/tools/metrics/histograms/metadata/net/enums.xml
+++ b/tools/metrics/histograms/metadata/net/enums.xml
@@ -2990,6 +2990,7 @@
   <int value="7" label="Using existing SPDY session"/>
   <int value="8" label="Using existing QUIC session"/>
   <int value="9" label="Abort"/>
+  <int value="10" label="AttemptManagerDraining"/>
 </enum>
 
 <!-- LINT.ThenChange(//net/socket/stream_socket_close_reason.h:StreamSocketCloseReason) -->
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 65ae014..e6469ea 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -3401,6 +3401,7 @@
     Recorded for each stream attempt upon destruction of the canceled attempt.
   </summary>
   <token key="Reason">
+    <variant name="AttemptManagerDraining"/>
     <variant name="ExistingQuicSession"/>
     <variant name="ExistingSpdySession"/>
     <variant name="NewQuicSession"/>
diff --git a/tools/metrics/histograms/metadata/password/enums.xml b/tools/metrics/histograms/metadata/password/enums.xml
index f7f3cda..2718b83 100644
--- a/tools/metrics/histograms/metadata/password/enums.xml
+++ b/tools/metrics/histograms/metadata/password/enums.xml
@@ -767,6 +767,8 @@
 
 <!-- LINT.ThenChange(/chrome/browser/ui/passwords/password_change_ui_controller.h:PasswordChangeDialogAction) -->
 
+<!-- LINT.IfChange(PasswordChangeFlowState) -->
+
 <enum name="PasswordChangeFlowState">
   <int value="0" label="Offering password change"/>
   <int value="1" label="Waiting for privacy notice acceptance"/>
@@ -775,8 +777,12 @@
   <int value="4" label="Changing password"/>
   <int value="5" label="Password successfully changed"/>
   <int value="6" label="Password change failed"/>
+  <int value="7" label="OTP detected"/>
+  <int value="8" label="Canceled by the user"/>
 </enum>
 
+<!-- LINT.ThenChange(/chrome/browser/password_manager/password_change_delegate.h) -->
+
 <!-- LINT.IfChange(PasswordChangeToastEvent) -->
 
 <enum name="PasswordChangeToastEvent">
diff --git a/ui/gfx/display_color_spaces.cc b/ui/gfx/display_color_spaces.cc
index aa7add2..7b310ec 100644
--- a/ui/gfx/display_color_spaces.cc
+++ b/ui/gfx/display_color_spaces.cc
@@ -10,6 +10,7 @@
 #include "ui/gfx/display_color_spaces.h"
 
 #include <array>
+#include <cmath>
 
 #include "build/build_config.h"
 #include "skia/ext/skcolorspace_primaries.h"
@@ -132,6 +133,10 @@
          hdr_max_luminance_relative_ > 1.f;
 }
 
+float DisplayColorSpaces::GetHdrHeadroom() const {
+  return std::log2(hdr_max_luminance_relative_);
+}
+
 ColorSpace DisplayColorSpaces::GetScreenInfoColorSpace() const {
   return GetOutputColorSpace(ContentColorUsage::kHDR, false /* needs_alpha */);
 }
diff --git a/ui/gfx/display_color_spaces.h b/ui/gfx/display_color_spaces.h
index 0592fa49..9094eca 100644
--- a/ui/gfx/display_color_spaces.h
+++ b/ui/gfx/display_color_spaces.h
@@ -95,6 +95,9 @@
     return hdr_max_luminance_relative_;
   }
 
+  // Returns log2 of GetHDRMaxLuminanceRelative.
+  float GetHdrHeadroom() const;
+
   // TODO(crbug.com/40144904): These helper functions exist temporarily
   // to handle the transition of display::ScreenInfo off of ColorSpace. All
   // calls to these functions are to be eliminated.