diff --git a/DEPS b/DEPS
index 2c37ba3c..fd8737d 100644
--- a/DEPS
+++ b/DEPS
@@ -268,7 +268,7 @@
   'screen_ai_windows_386': 'version:138.06',
 
   # siso CIPD package version.
-  'siso_version': 'git_revision:7e7d85fc69f9084d2168385fb504b31e830dbfff',
+  'siso_version': 'git_revision:c23de742b8483ddc09d8b68ef845efc7f5c6192f',
 
   # download libaom test data
   'download_libaom_testdata': False,
@@ -294,11 +294,11 @@
   # 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': '35d5edfa0d50984c22ff94f5438c31e0db12c6f8',
+  'skia_revision': '47766ee60ccc6e9e0a024348192ba72391e5785e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '9541b18ce700cdef8cce07fef49f1ac9d74a1dd2',
+  'v8_revision': '4d7c9f183f3c41ac42eb0d622a4d8e58a0982693',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
@@ -310,7 +310,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b6457bcccc5c86ac7159f957fa8dbea3d5e13f49',
+  'pdfium_revision': '1dbcd29c45a96d9717e040662bba05395d790145',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -362,7 +362,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': '6acc0ec6a547f0d9cfc307fedd515459d239fa24',
+  'crossbench_revision': '749d1a4f48bb0ae63aa8559ef96180a677b65307',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -378,7 +378,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'ea58b6707370f3b440b362fe178e7f6cd4a9170b',
+  'devtools_frontend_revision': 'fee355f9b7f54daccee81b9ef82034656243e834',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -402,7 +402,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'd03fe7eda12dfce1578bf69da2814f89fd78c1e9',
+  'dawn_revision': '830dc86845102bb7c45f45ca14251b92add5dbb9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -510,7 +510,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling llvm-libc
   # and whatever else without interference from each other.
-  'compiler_rt_revision': 'd3919a20084ce47e31983ffa3724f6318f34390c',
+  'compiler_rt_revision': '2a4f69a118bdc5d03c415de1b9b166b2f1d4084f',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
@@ -1563,7 +1563,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '9e7cc1ffbb07d1afe8f3528f10d621b54993c515',
+    '37813b0fe2b96138bf8f715b42ac30fba422b9d2',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1717,7 +1717,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '-KXWut6mOUpWy_b-s5qaRRUSsLCznTQ6aiZoYO3b4zsC',
+          'version': 'RMTIxBSE0B55RT6WlvZAfFwWK9rBMMW14yPXf2AzfysC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -2053,7 +2053,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ae917185501cda874f12f3e1d870de8505b0807c',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8f878438075a8d5d76f57bd10ab866d58d706319',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2593,7 +2593,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'cca1748cb16b9dc0cac21515813c3855a983917d',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'faeba3e4b27b5adb924ece5548a61bef8013faed',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -3143,7 +3143,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'pNSGXXsmfoCc9iNOIrFiMFPhvTk348XeiQ7uuRP3KIYC',
+        'version': 'AbrE_oILb_zjITzM-b71AVUd4sYJZCam8HpaxgVNMj8C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/browser/prefetch/aw_prefetch_manager.cc b/android_webview/browser/prefetch/aw_prefetch_manager.cc
index b52a6be..2ff4508d 100644
--- a/android_webview/browser/prefetch/aw_prefetch_manager.cc
+++ b/android_webview/browser/prefetch/aw_prefetch_manager.cc
@@ -144,8 +144,9 @@
         browser_context_->StartBrowserPrefetchRequest(
             pf_url, AW_PREFETCH_METRICS_SUFFIX,
             GetIsJavaScriptEnabledFromPrefetchParameters(env, prefetch_params),
-            expected_no_vary_search, additional_headers,
-            std::move(request_status_listener), base::Seconds(ttl_in_sec_),
+            expected_no_vary_search, /*priority=*/std::nullopt,
+            additional_headers, std::move(request_status_listener),
+            base::Seconds(ttl_in_sec_),
             /*should_append_variations_header=*/false,
             /*should_disable_block_until_head_timeout=*/true);
 
diff --git a/ash/birch/birch_model_unittest.cc b/ash/birch/birch_model_unittest.cc
index 61aec25..97ca989c 100644
--- a/ash/birch/birch_model_unittest.cc
+++ b/ash/birch/birch_model_unittest.cc
@@ -722,17 +722,9 @@
   EXPECT_TRUE(model->IsDataFresh());
 }
 
-// TODO(https://crbug.com/324963992): Fix `BirchModel*Test.DataFetchTimeout`
-// for debug builds.
-#if defined(NDEBUG)
-#define MAYBE_DataFetchTimeout DataFetchTimeout
-#else
-#define MAYBE_DataFetchTimeout DISABLED_DataFetchTimeout
-#endif
-
 // Test that consumer is notified when waiting a set amount of time after
 // requesting birch data.
-TEST_F(BirchModelTest, MAYBE_DataFetchTimeout) {
+TEST_F(BirchModelTest, DataFetchTimeout) {
   BirchModel* model = Shell::Get()->birch_model();
   TestModelConsumer consumer;
   EXPECT_TRUE(model);
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index aaf4a8c..1d93406 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -346,12 +346,10 @@
   // No crash.
 }
 
-// TODO(crbug.com/40867071): Enable when the bug is fixed.
 // Verifies that the display rotation from the long side to the short side
 // should not break the scrollable shelf's UI behavior
 // (https://crbug.com/1000764).
-TEST_P(ScrollableShelfViewRTLTest,
-       DISABLED_CorrectUIAfterDisplayRotationLongToShort) {
+TEST_P(ScrollableShelfViewRTLTest, CorrectUIAfterDisplayRotationLongToShort) {
   // Changes the display setting in order that the display's width is greater
   // than the height.
   UpdateDisplay("600x300");
diff --git a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_perftest.cc b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_perftest.cc
index 7da2f69..716d86a9 100644
--- a/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_perftest.cc
+++ b/base/allocator/partition_allocator/src/partition_alloc/partition_alloc_perftest.cc
@@ -71,7 +71,8 @@
 enum class AllocatorType {
   kSystem,
   kPartitionAlloc,
-  kPartitionAllocWithThreadCache,
+  kPartitionAllocWithThreadCache, /* NormalBucketDist */
+  kPartitionAllocWithThreadCacheAndDenserBucketDist,
 #if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER)
   kPartitionAllocWithAllocationStackTraceRecorder,
 #endif
@@ -362,8 +363,7 @@
   return timer.LapsPerSecond();
 }
 
-std::unique_ptr<Allocator> CreateAllocator(AllocatorType type,
-                                           bool use_denser_bucket_dist) {
+std::unique_ptr<Allocator> CreateAllocator(AllocatorType type) {
   switch (type) {
     case AllocatorType::kSystem:
       return std::make_unique<SystemAllocator>();
@@ -371,7 +371,10 @@
       return std::make_unique<PartitionAllocator>();
     case AllocatorType::kPartitionAllocWithThreadCache:
       return std::make_unique<PartitionAllocatorWithThreadCache>(
-          use_denser_bucket_dist);
+          /*use_denser_bucket_dist=*/false);
+    case AllocatorType::kPartitionAllocWithThreadCacheAndDenserBucketDist:
+      return std::make_unique<PartitionAllocatorWithThreadCache>(
+          /*use_denser_bucket_dist=*/true);
 #if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER)
     case AllocatorType::kPartitionAllocWithAllocationStackTraceRecorder:
       return std::make_unique<
@@ -389,13 +392,37 @@
                << "," << min_laps_per_second;
 }
 
+std::string MakeTestName(int thread_count, AllocatorType alloc_type) {
+  char const* alloc_type_str;
+  switch (alloc_type) {
+    case AllocatorType::kSystem:
+      alloc_type_str = "System";
+      break;
+    case AllocatorType::kPartitionAlloc:
+      alloc_type_str = "PartitionAlloc";
+      break;
+    case AllocatorType::kPartitionAllocWithThreadCache:
+      alloc_type_str = "PartitionAllocWithThreadCache";
+      break;
+    case AllocatorType::kPartitionAllocWithThreadCacheAndDenserBucketDist:
+      alloc_type_str = "PartitionAllocWithThreadCacheAndDenserBucketDist";
+      break;
+#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER)
+    case AllocatorType::kPartitionAllocWithAllocationStackTraceRecorder:
+      alloc_type_str = "PartitionAllocWithAllocationStackTraceRecorder";
+      break;
+#endif
+  }
+
+  return base::TruncatingStringPrintf("%s_%d", alloc_type_str, thread_count);
+}
+
 void RunTest(int thread_count,
-             bool use_denser_bucket_dist,
              AllocatorType alloc_type,
              float (*test_fn)(Allocator*),
              float (*noisy_neighbor_fn)(Allocator*),
              const char* story_base_name) {
-  auto alloc = CreateAllocator(alloc_type, use_denser_bucket_dist);
+  auto alloc = CreateAllocator(alloc_type);
 
   std::unique_ptr<TestLoopThread> noisy_neighbor_thread = nullptr;
   if (noisy_neighbor_fn) {
@@ -420,27 +447,10 @@
     noisy_neighbor_thread->Run();
   }
 
-  char const* alloc_type_str;
-  switch (alloc_type) {
-    case AllocatorType::kSystem:
-      alloc_type_str = "System";
-      break;
-    case AllocatorType::kPartitionAlloc:
-      alloc_type_str = "PartitionAlloc";
-      break;
-    case AllocatorType::kPartitionAllocWithThreadCache:
-      alloc_type_str = "PartitionAllocWithThreadCache";
-      break;
-#if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER)
-    case AllocatorType::kPartitionAllocWithAllocationStackTraceRecorder:
-      alloc_type_str = "PartitionAllocWithAllocationStackTraceRecorder";
-      break;
-#endif
-  }
-
-  std::string name = base::TruncatingStringPrintf(
-      "%s%s_%s_%d", kMetricPrefixMemoryAllocation, story_base_name,
-      alloc_type_str, thread_count);
+  std::string test_name = MakeTestName(thread_count, alloc_type);
+  std::string name =
+      base::TruncatingStringPrintf("%s%s_%s", kMetricPrefixMemoryAllocation,
+                                   story_base_name, test_name.c_str());
 
   DisplayResults(name + "_total", total_laps_per_second);
   DisplayResults(name + "_worst", min_laps_per_second);
@@ -449,7 +459,7 @@
 }
 
 class PartitionAllocMemoryAllocationPerfTest
-    : public testing::TestWithParam<std::tuple<int, bool, AllocatorType>> {
+    : public testing::TestWithParam<std::tuple<int, AllocatorType>> {
 #if PA_CONFIG(ENABLE_SHADOW_METADATA)
   void SetUp() override {
     PartitionRoot::EnableShadowMetadata(
@@ -466,64 +476,63 @@
     PartitionAllocMemoryAllocationPerfTest,
     ::testing::Combine(
         ::testing::Values(1, 2, 3, 4),
-        ::testing::Values(false, true),
         ::testing::Values(
             AllocatorType::kSystem,
             AllocatorType::kPartitionAlloc,
-            AllocatorType::kPartitionAllocWithThreadCache
+            AllocatorType::kPartitionAllocWithThreadCache,
+            AllocatorType::kPartitionAllocWithThreadCacheAndDenserBucketDist
 #if BUILDFLAG(ENABLE_ALLOCATION_STACK_TRACE_RECORDER)
             ,
             AllocatorType::kPartitionAllocWithAllocationStackTraceRecorder
 #endif
-            )));
+            )),
+    [](const testing::TestParamInfo<
+        PartitionAllocMemoryAllocationPerfTest::ParamType>& info) {
+      return MakeTestName(std::get<0>(info.param), std::get<1>(info.param));
+    });
 
 // This test (and the other one below) allocates a large amount of memory, which
 // can cause issues on Android.
 #if !defined(MEMORY_CONSTRAINED)
 TEST_P(PartitionAllocMemoryAllocationPerfTest, SingleBucket) {
   auto params = GetParam();
-  RunTest(std::get<int>(params), std::get<bool>(params),
-          std::get<AllocatorType>(params), SingleBucket, nullptr,
-          "SingleBucket");
+  RunTest(std::get<int>(params), std::get<AllocatorType>(params), SingleBucket,
+          nullptr, "SingleBucket");
 }
 #endif  // defined(MEMORY_CONSTRAINED)
 
 TEST_P(PartitionAllocMemoryAllocationPerfTest, SingleBucketWithFree) {
   auto params = GetParam();
-  RunTest(std::get<int>(params), std::get<bool>(params),
-          std::get<AllocatorType>(params), SingleBucketWithFree, nullptr,
-          "SingleBucketWithFree");
+  RunTest(std::get<int>(params), std::get<AllocatorType>(params),
+          SingleBucketWithFree, nullptr, "SingleBucketWithFree");
 }
 
 #if !defined(MEMORY_CONSTRAINED)
 TEST_P(PartitionAllocMemoryAllocationPerfTest, MultiBucket) {
   auto params = GetParam();
-  RunTest(std::get<int>(params), std::get<bool>(params),
-          std::get<AllocatorType>(params), MultiBucket, nullptr, "MultiBucket");
+  RunTest(std::get<int>(params), std::get<AllocatorType>(params), MultiBucket,
+          nullptr, "MultiBucket");
 }
 #endif  // defined(MEMORY_CONSTRAINED)
 
 TEST_P(PartitionAllocMemoryAllocationPerfTest, MultiBucketWithFree) {
   auto params = GetParam();
-  RunTest(std::get<int>(params), std::get<bool>(params),
-          std::get<AllocatorType>(params), MultiBucketWithFree, nullptr,
-          "MultiBucketWithFree");
+  RunTest(std::get<int>(params), std::get<AllocatorType>(params),
+          MultiBucketWithFree, nullptr, "MultiBucketWithFree");
 }
 
 TEST_P(PartitionAllocMemoryAllocationPerfTest, DirectMapped) {
   auto params = GetParam();
-  RunTest(std::get<int>(params), std::get<bool>(params),
-          std::get<AllocatorType>(params), DirectMapped, nullptr,
-          "DirectMapped");
+  RunTest(std::get<int>(params), std::get<AllocatorType>(params), DirectMapped,
+          nullptr, "DirectMapped");
 }
 
 #if !defined(MEMORY_CONSTRAINED)
 TEST_P(PartitionAllocMemoryAllocationPerfTest,
        DISABLED_MultiBucketWithNoisyNeighbor) {
   auto params = GetParam();
-  RunTest(std::get<int>(params), std::get<bool>(params),
-          std::get<AllocatorType>(params), MultiBucket, DirectMapped,
-          "MultiBucketWithNoisyNeighbor");
+  RunTest(std::get<int>(params), std::get<AllocatorType>(params), MultiBucket,
+          DirectMapped, "MultiBucketWithNoisyNeighbor");
 }
 #endif  // !defined(MEMORY_CONSTRAINED)
 
diff --git a/base/android/base_feature_map.cc b/base/android/base_feature_map.cc
index c1cfeb15..f6c4c746 100644
--- a/base/android/base_feature_map.cc
+++ b/base/android/base_feature_map.cc
@@ -19,6 +19,7 @@
     &features::kBackgroundNotPerceptibleBinding,
     &features::kPostPowerMonitorBroadcastReceiverInitToBackground,
     &features::kPostGetMyMemoryStateToBackground,
+    &features::kUpdateStateBeforeUnbinding,
     &features::kUseSharedRebindServiceConnection,
 };
 
diff --git a/base/android/java/src/org/chromium/base/BaseFeatureList.java b/base/android/java/src/org/chromium/base/BaseFeatureList.java
index e1750dbbb..b5e58d5 100644
--- a/base/android/java/src/org/chromium/base/BaseFeatureList.java
+++ b/base/android/java/src/org/chromium/base/BaseFeatureList.java
@@ -11,6 +11,12 @@
 public class BaseFeatureList {
     private BaseFeatureList() {}
 
+    public static final MutableFlagWithSafeDefault sUpdateStateBeforeUnbinding =
+            new MutableFlagWithSafeDefault(
+                    BaseFeatureMap.getInstance(),
+                    BaseFeatures.UPDATE_STATE_BEFORE_UNBINDING,
+                    false);
+
     public static final MutableFlagWithSafeDefault sUseSharedRebindServiceConnection =
             new MutableFlagWithSafeDefault(
                     BaseFeatureMap.getInstance(),
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
index 3fca572..08883be 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessConnection.java
@@ -1094,15 +1094,22 @@
         mService = null;
         mConnectionParams = null;
         mUnbound = true;
-        mStrongBinding.unbindServiceConnection();
+        if (BaseFeatureList.sUpdateStateBeforeUnbinding.isEnabled()) {
+            // Update binding state to ChildBindingState.UNBOUND before unbinding
+            // actual bindings below.
+            updateBindingState();
+        }
+        mStrongBinding.unbindServiceConnection(null);
         // We must clear shared waived binding when we unbind a waived binding.
         clearSharedWaivedBinding();
-        mWaivedBinding.unbindServiceConnection();
+        mWaivedBinding.unbindServiceConnection(null);
         if (mNotPerceptibleBinding != null) {
-            mNotPerceptibleBinding.unbindServiceConnection();
+            mNotPerceptibleBinding.unbindServiceConnection(null);
         }
-        mVisibleBinding.unbindServiceConnection();
-        updateBindingState();
+        mVisibleBinding.unbindServiceConnection(null);
+        if (!BaseFeatureList.sUpdateStateBeforeUnbinding.isEnabled()) {
+            updateBindingState();
+        }
 
         if (mMemoryPressureCallback != null) {
             final MemoryPressureCallback callback = mMemoryPressureCallback;
@@ -1171,8 +1178,12 @@
         assert mStrongBindingCount > 0;
         mStrongBindingCount--;
         if (mStrongBindingCount == 0) {
-            mStrongBinding.unbindServiceConnection();
-            updateBindingState();
+            if (BaseFeatureList.sUpdateStateBeforeUnbinding.isEnabled()) {
+                mStrongBinding.unbindServiceConnection(() -> updateBindingState());
+            } else {
+                mStrongBinding.unbindServiceConnection(null);
+                updateBindingState();
+            }
         }
     }
 
@@ -1207,8 +1218,12 @@
         assert mVisibleBindingCount > 0;
         mVisibleBindingCount--;
         if (mVisibleBindingCount == 0) {
-            mVisibleBinding.unbindServiceConnection();
-            updateBindingState();
+            if (BaseFeatureList.sUpdateStateBeforeUnbinding.isEnabled()) {
+                mVisibleBinding.unbindServiceConnection(() -> updateBindingState());
+            } else {
+                mVisibleBinding.unbindServiceConnection(null);
+                updateBindingState();
+            }
         }
     }
 
@@ -1245,8 +1260,13 @@
         assert mNotPerceptibleBindingCount > 0;
         mNotPerceptibleBindingCount--;
         if (mNotPerceptibleBindingCount == 0) {
-            assumeNonNull(mNotPerceptibleBinding).unbindServiceConnection();
-            updateBindingState();
+            if (BaseFeatureList.sUpdateStateBeforeUnbinding.isEnabled()) {
+                assumeNonNull(mNotPerceptibleBinding)
+                        .unbindServiceConnection(() -> updateBindingState());
+            } else {
+                assumeNonNull(mNotPerceptibleBinding).unbindServiceConnection(null);
+                updateBindingState();
+            }
         }
     }
 
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java
index aca11232..c6701a7 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnection.java
@@ -5,13 +5,20 @@
 package org.chromium.base.process_launcher;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 
 /** Interface representing a connection to the Android service. Can be mocked in unit-tests. */
 @NullMarked
 /* package */ interface ChildServiceConnection {
     boolean bindServiceConnection();
 
-    void unbindServiceConnection();
+    /**
+     * Unbinds the underlying service connection.
+     *
+     * @param onUnbindCallback A callback to be run after isBound() become false and before the
+     *     service is unbound.
+     */
+    void unbindServiceConnection(@Nullable Runnable onStateChangeCallback);
 
     boolean isBound();
 
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java
index 73375d08..564e3b9 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildServiceConnectionImpl.java
@@ -70,10 +70,13 @@
     }
 
     @Override
-    public void unbindServiceConnection() {
+    public void unbindServiceConnection(@Nullable Runnable onStateChangeCallback) {
         if (mBound) {
-            mContext.unbindService(this);
             mBound = false;
+            if (onStateChangeCallback != null) {
+                onStateChangeCallback.run();
+            }
+            mContext.unbindService(this);
         }
     }
 
@@ -109,7 +112,7 @@
     @Override
     public void retire() {
         mDelegate = null;
-        unbindServiceConnection();
+        unbindServiceConnection(null);
     }
 
     @Override
diff --git a/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
index d16d4c5..3d1c53c 100644
--- a/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
+++ b/base/android/junit/src/org/chromium/base/process_launcher/ChildProcessConnectionTest.java
@@ -44,6 +44,7 @@
 import org.chromium.base.ChildBindingState;
 import org.chromium.base.library_loader.IRelroLibInfo;
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.build.annotations.Nullable;
 
 import java.util.ArrayList;
 
@@ -73,8 +74,11 @@
         }
 
         @Override
-        public void unbindServiceConnection() {
+        public void unbindServiceConnection(@Nullable Runnable onStateChangeCallback) {
             mBound = false;
+            if (onStateChangeCallback != null) {
+                onStateChangeCallback.run();
+            }
         }
 
         @Override
@@ -604,11 +608,96 @@
         // Kill and verify state.
         connection.kill();
         verify(mIChildProcessService).forceKill();
+        assertEquals(ChildBindingState.UNBOUND, connection.bindingStateCurrent());
         assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());
         Assert.assertTrue(connection.isKilledByUs());
     }
 
     @Test
+    public void testBindingDowngrade() throws RemoteException {
+        ChildProcessConnection connection = createDefaultTestConnection();
+        assertNotNull(mFirstServiceConnection);
+        connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
+        mFirstServiceConnection.notifyServiceConnected(mChildProcessServiceBinder);
+        connection.setupConnection(
+                /* childProcessArgs= */ null,
+                /* clientInterfaces= */ null,
+                /* binderBox= */ null,
+                mConnectionCallback,
+                /* zygoteInfoCallback= */ null);
+        verify(mConnectionCallback, never()).onConnected(any());
+        ShadowLooper.runUiThreadTasks();
+        assertNotNull(mConnectionParentProcess);
+        sendPid(34);
+        verify(mConnectionCallback, times(1)).onConnected(connection);
+        connection.removeVisibleBinding();
+
+        // Add all bindings
+        connection.addStrongBinding();
+        connection.addVisibleBinding();
+        if (ChildProcessConnection.supportNotPerceptibleBinding()) {
+            connection.addNotPerceptibleBinding();
+        }
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());
+
+        // Remove binding from the highest.
+        connection.removeStrongBinding();
+        assertEquals(ChildBindingState.VISIBLE, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.VISIBLE, connection.bindingStateCurrentOrWhenDied());
+        connection.removeVisibleBinding();
+        if (ChildProcessConnection.supportNotPerceptibleBinding()) {
+            assertEquals(ChildBindingState.NOT_PERCEPTIBLE, connection.bindingStateCurrent());
+            assertEquals(
+                    ChildBindingState.NOT_PERCEPTIBLE, connection.bindingStateCurrentOrWhenDied());
+
+            connection.removeNotPerceptibleBinding();
+        }
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrentOrWhenDied());
+
+        // Add all bindings
+        connection.addStrongBinding();
+        connection.addVisibleBinding();
+        if (ChildProcessConnection.supportNotPerceptibleBinding()) {
+            connection.addNotPerceptibleBinding();
+        }
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());
+
+        // Remove the binding in the middle priority.
+        connection.removeVisibleBinding();
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());
+        connection.removeStrongBinding();
+        if (ChildProcessConnection.supportNotPerceptibleBinding()) {
+            assertEquals(ChildBindingState.NOT_PERCEPTIBLE, connection.bindingStateCurrent());
+            assertEquals(
+                    ChildBindingState.NOT_PERCEPTIBLE, connection.bindingStateCurrentOrWhenDied());
+
+            connection.removeNotPerceptibleBinding();
+        }
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrentOrWhenDied());
+
+        // Add strong binding only
+        connection.addStrongBinding();
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.STRONG, connection.bindingStateCurrentOrWhenDied());
+        // Remove the strong binding
+        connection.removeStrongBinding();
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrentOrWhenDied());
+
+        // Kill and verify state from waived binding only.
+        connection.kill();
+        verify(mIChildProcessService).forceKill();
+        assertEquals(ChildBindingState.UNBOUND, connection.bindingStateCurrent());
+        assertEquals(ChildBindingState.WAIVED, connection.bindingStateCurrentOrWhenDied());
+        Assert.assertTrue(connection.isKilledByUs());
+    }
+
+    @Test
     public void testUpdateGroupImportanceSmoke() throws RemoteException {
         ChildProcessConnection connection = createDefaultTestConnection();
         connection.start(/* useStrongBinding= */ false, /* serviceCallback= */ null);
diff --git a/base/containers/auto_spanification_helper.h b/base/containers/auto_spanification_helper.h
index 155d50d..7a8d677 100644
--- a/base/containers/auto_spanification_helper.h
+++ b/base/containers/auto_spanification_helper.h
@@ -24,6 +24,19 @@
   return sizeof(Element) * N;
 }
 
+// This helper is used to rewrite code that passes the address of a single
+// variable or object member (e.g. `&my_var` or `&obj.member`) to a function
+// that expects a `span` representing a single element.
+//
+// WARNING: This function should only be used by the auto-spanification tool.
+// Do not use this helper outside of the tool.
+template <typename T>
+span<T, 1> SpanFromSingleElement(T& ref) {
+  // This is a single element and the address is always valid as long as the
+  // reference is valid.
+  return UNSAFE_TODO(span<T, 1u>(&ref, 1u));
+}
+
 // Modifies the input span by removing its first element (if not empty)
 // and returns the modified span.
 // Used to rewrite pre-increment (++ptr).
diff --git a/base/features.cc b/base/features.cc
index a5c96cba..f75a71a 100644
--- a/base/features.cc
+++ b/base/features.cc
@@ -133,6 +133,11 @@
              "PostGetMyMemoryStateToBackground",
              FEATURE_ENABLED_BY_DEFAULT);
 
+// Update child process binding state before unbinding.
+BASE_FEATURE(kUpdateStateBeforeUnbinding,
+            "UpdateStateBeforeUnbinding",
+            FEATURE_DISABLED_BY_DEFAULT);
+
 // Use shared service connection to rebind a service binding to update the LRU
 // in the ProcessList of OomAdjuster.
 BASE_FEATURE(kUseSharedRebindServiceConnection,
diff --git a/base/features.h b/base/features.h
index 6d13f4a..b745510 100644
--- a/base/features.h
+++ b/base/features.h
@@ -43,6 +43,7 @@
 BASE_EXPORT BASE_DECLARE_FEATURE(
     kPostPowerMonitorBroadcastReceiverInitToBackground);
 BASE_EXPORT BASE_DECLARE_FEATURE(kPostGetMyMemoryStateToBackground);
+BASE_EXPORT BASE_DECLARE_FEATURE(kUpdateStateBeforeUnbinding);
 BASE_EXPORT BASE_DECLARE_FEATURE(kUseSharedRebindServiceConnection);
 
 BASE_EXPORT BASE_DECLARE_FEATURE(kBackgroundThreadPoolFieldTrial);
diff --git a/base/memory/raw_ptr.md b/base/memory/raw_ptr.md
index 9ab540d..62870e2 100644
--- a/base/memory/raw_ptr.md
+++ b/base/memory/raw_ptr.md
@@ -94,7 +94,7 @@
       Make sure to look at
       [the "Extra pointer rules" section](#Extra-pointer-rules)
       before resorting to this exclusion.
-- [RawPtrManualPathsToIgnore.h](../../tools/clang/plugins/RawPtrManualPathsToIgnore.h)
+- [RawPtrManualPathsToIgnore.h](../../tools/clang/raw_ptr_plugin/RawPtrManualPathsToIgnore.h)
   to exclude at a directory level (NOTE, use it as last resort, and be aware
   it'll require a Clang plugin roll).  Examples:
     - Renderer-only code (i.e. code in paths that contain `/renderer/` or
diff --git a/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java b/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java
index 9fe0b962..fa76734 100644
--- a/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java
+++ b/base/test/android/javatests/src/org/chromium/base/process_launcher/TestChildProcessConnection.java
@@ -8,6 +8,8 @@
 import android.content.Intent;
 import android.os.Bundle;
 
+import org.chromium.build.annotations.Nullable;
+
 /** An implementation of ChildProcessConnection that does not connect to a real service. */
 public class TestChildProcessConnection extends ChildProcessConnection {
     private static class MockChildServiceConnection implements ChildServiceConnection {
@@ -20,8 +22,11 @@
         }
 
         @Override
-        public void unbindServiceConnection() {
+        public void unbindServiceConnection(@Nullable Runnable onStateChangeCallback) {
             mBound = false;
+            if (onStateChangeCallback != null) {
+                onStateChangeCallback.run();
+            }
         }
 
         @Override
diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
index 031b87d..ada28949 100644
--- a/build/config/BUILDCONFIG.gn
+++ b/build/config/BUILDCONFIG.gn
@@ -540,36 +540,15 @@
 }
 
 DEFAULT_MODULE_DEPS = [
-  "//buildtools/third_party/libc++:_Builtin_float",
   "//buildtools/third_party/libc++:_Builtin_intrinsics",
-  "//buildtools/third_party/libc++:_Builtin_inttypes",
   "//buildtools/third_party/libc++:_Builtin_limits",
-  "//buildtools/third_party/libc++:_Builtin_stdalign",
   "//buildtools/third_party/libc++:_Builtin_stdarg",
-  "//buildtools/third_party/libc++:_Builtin_stdatomic",
   "//buildtools/third_party/libc++:_Builtin_stdbool",
-  "//buildtools/third_party/libc++:_Builtin_stddef",
   "//buildtools/third_party/libc++:_Builtin_stdint",
   "//buildtools/third_party/libc++:_Builtin_unwind",
   "//buildtools/third_party/libc++:std",
-  "//buildtools/third_party/libc++:std_config",
-  "//buildtools/third_party/libc++:std_core",
-  "//buildtools/third_party/libc++:std_ctype_h",
-  "//buildtools/third_party/libc++:std_errno_h",
-  "//buildtools/third_party/libc++:std_fenv_h",
-  "//buildtools/third_party/libc++:std_float_h",
-  "//buildtools/third_party/libc++:std_inttypes_h",
-  "//buildtools/third_party/libc++:std_math_h",
-  "//buildtools/third_party/libc++:std_private_mbstate_t",
   "//buildtools/third_party/libc++:std_stdatomic_h",
-  "//buildtools/third_party/libc++:std_stdbool_h",
-  "//buildtools/third_party/libc++:std_string_h",
-  "//buildtools/third_party/libc++:std_uchar_h",
-  "//buildtools/third_party/libc++:std_wchar_h",
-  "//buildtools/third_party/libc++:std_wctype_h",
-  "//buildtools/third_party/libc++:sysroot_bits",
   "//buildtools/third_party/libc++:sysroot_features",
-  "//buildtools/third_party/libc++:sysroot_time",
   "//buildtools/third_party/libc++:sysroot_types",
 ]
 
diff --git a/buildtools/third_party/libc++/BUILD.gn b/buildtools/third_party/libc++/BUILD.gn
index 1e2cb5c..88aefac1 100644
--- a/buildtools/third_party/libc++/BUILD.gn
+++ b/buildtools/third_party/libc++/BUILD.gn
@@ -89,8 +89,6 @@
 
   sysroot_modules("sysroot_types") {
     public_deps = [
-      ":_Builtin_stddef",
-      ":std_config",
       ":sysroot_bits",
       ":sysroot_features",
       ":sysroot_time",
@@ -120,7 +118,6 @@
   }
 
   builtin_modules("_Builtin_inttypes") {
-    public_deps = [ ":_Builtin_stdint" ]
   }
 
   builtin_modules("_Builtin_limits") {
@@ -134,10 +131,6 @@
   }
 
   builtin_modules("_Builtin_stdatomic") {
-    public_deps = [
-      ":_Builtin_stddef",
-      ":_Builtin_stdint",
-    ]
   }
 
   builtin_modules("_Builtin_stdbool") {
@@ -198,9 +191,6 @@
 
   libcxx_modules("std") {
     public_deps = [
-      ":_Builtin_stdarg",
-      ":_Builtin_stddef",
-      ":std_config",
       ":std_core",
       ":std_ctype_h",
       ":std_errno_h",
@@ -211,12 +201,7 @@
       ":std_private_mbstate_t",
       ":std_string_h",
       ":std_uchar_h",
-      ":std_wchar_h",
       ":std_wctype_h",
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-      ":sysroot_types",
     ]
   }
 
@@ -224,11 +209,6 @@
   }
 
   libcxx_modules("std_core") {
-    public_deps = [
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-    ]
   }
 
   libcxx_modules("std_ctype_h") {
@@ -244,40 +224,16 @@
   }
 
   libcxx_modules("std_inttypes_h") {
-    public_deps = [
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-    ]
   }
 
   libcxx_modules("std_math_h") {
-    public_deps = [
-      ":std_core",
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-    ]
+    public_deps = [ ":std_core" ]
   }
 
   libcxx_modules("std_private_mbstate_t") {
-    public_deps = [
-      ":_Builtin_stdarg",
-      ":_Builtin_stddef",
-      ":std_config",
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-      ":sysroot_types",
-    ]
   }
 
   libcxx_modules("std_stdatomic_h") {
-    public_deps = [
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-    ]
   }
 
   libcxx_modules("std_stdbool_h") {
@@ -290,30 +246,10 @@
   }
 
   libcxx_modules("std_uchar_h") {
-    public_deps = [
-      ":_Builtin_stddef",
-      ":std_config",
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-      ":sysroot_types",
-    ]
-    if (is_mac) {
-      public_deps += [ ":std_private_mbstate_t" ]
-    }
+    public_deps = [ ":std_private_mbstate_t" ]
   }
 
   libcxx_modules("std_wchar_h") {
-    public_deps = [
-      ":_Builtin_stdarg",
-      ":_Builtin_stddef",
-      ":std_config",
-      ":std_private_mbstate_t",
-      ":sysroot_bits",
-      ":sysroot_features",
-      ":sysroot_time",
-      ":sysroot_types",
-    ]
   }
 
   libcxx_modules("std_wctype_h") {
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 45835b8..2c684f7 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -1314,9 +1314,6 @@
            !layer_tree_impl()->IsReadyToActivate());
     return true;
   }
-  // We can recreate the tiling if we would invalidate all of its tiles.
-  if (high_res.may_contain_low_resolution_tiles())
-    return true;
   // Keep the non-ideal raster translation unchanged for transform animations
   // to avoid re-rasterization during animation.
   if (draw_properties().screen_space_transform_is_animating ||
@@ -1397,13 +1394,6 @@
     // We always need a high res tiling, so create one if it doesn't exist.
     high_res = AddTiling(gfx::AxisTransform2d::FromScaleAndTranslation(
         raster_contents_scale_, raster_translation));
-  } else if (high_res->may_contain_low_resolution_tiles()) {
-    // If the tiling we find here was LOW_RESOLUTION previously, it may not be
-    // fully rastered, so destroy the old tiles.
-    high_res->Reset();
-    // Reset the flag now that we'll make it high res, it will have fully
-    // rastered content.
-    high_res->reset_may_contain_low_resolution_tiles();
   }
   high_res->set_resolution(HIGH_RESOLUTION);
 
diff --git a/cc/tiles/picture_layer_tiling.h b/cc/tiles/picture_layer_tiling.h
index 7f1e62e..92395953 100644
--- a/cc/tiles/picture_layer_tiling.h
+++ b/cc/tiles/picture_layer_tiling.h
@@ -123,12 +123,6 @@
     resolution_ = resolution;
   }
   TileResolution resolution() const { return resolution_; }
-  bool may_contain_low_resolution_tiles() const {
-    return may_contain_low_resolution_tiles_;
-  }
-  void reset_may_contain_low_resolution_tiles() {
-    may_contain_low_resolution_tiles_ = false;
-  }
   void set_can_require_tiles_for_activation(bool can_require_tiles) {
     can_require_tiles_for_activation_ = can_require_tiles;
   }
@@ -455,7 +449,6 @@
   const float min_preraster_distance_;
   const float max_preraster_distance_;
   TileResolution resolution_ = NON_IDEAL_RESOLUTION;
-  bool may_contain_low_resolution_tiles_ = false;
 
   // Internal data.
   TilingData tiling_data_{gfx::Size(), gfx::Rect(), kBorderTexels};
diff --git a/cc/tiles/picture_layer_tiling_set.cc b/cc/tiles/picture_layer_tiling_set.cc
index 34386f9a..d5c8601 100644
--- a/cc/tiles/picture_layer_tiling_set.cc
+++ b/cc/tiles/picture_layer_tiling_set.cc
@@ -611,7 +611,6 @@
   // compute them only when the tiling set has changed instead.
   size_t tilings_size = tilings_.size();
   TilingRange high_res_range(0, 0);
-  TilingRange low_res_range(tilings_size, tilings_size);
   for (size_t i = 0; i < tilings_size; ++i) {
     const PictureLayerTiling* tiling = tilings_[i].get();
     if (tiling->resolution() == HIGH_RESOLUTION)
@@ -627,22 +626,7 @@
       range = high_res_range;
       break;
     case BETWEEN_HIGH_AND_LOW_RES:
-      // TODO(vmpstr): This code assumes that high res tiling will come before
-      // low res tiling, however there are cases where this assumption is
-      // violated. As a result, it's better to be safe in these situations,
-      // since otherwise we can end up accessing a tiling that doesn't exist.
-      // See crbug.com/429397 for high res tiling appearing after low res
-      // tiling discussion/fixes.
-      if (high_res_range.start <= low_res_range.start)
-        range = TilingRange(high_res_range.end, low_res_range.start);
-      else
-        range = TilingRange(low_res_range.end, high_res_range.start);
-      break;
-    case LOW_RES:
-      range = low_res_range;
-      break;
-    case LOWER_THAN_LOW_RES:
-      range = TilingRange(low_res_range.end, tilings_size);
+      range = TilingRange(high_res_range.end, tilings_size);
       break;
   }
 
diff --git a/cc/tiles/picture_layer_tiling_set.h b/cc/tiles/picture_layer_tiling_set.h
index d817df2..f3e2a67 100644
--- a/cc/tiles/picture_layer_tiling_set.h
+++ b/cc/tiles/picture_layer_tiling_set.h
@@ -32,8 +32,6 @@
     HIGHER_THAN_HIGH_RES,
     HIGH_RES,
     BETWEEN_HIGH_AND_LOW_RES,
-    LOW_RES,
-    LOWER_THAN_LOW_RES
   };
   struct TilingRange {
     TilingRange(size_t start, size_t end) : start(start), end(end) {}
diff --git a/cc/tiles/picture_layer_tiling_set_unittest.cc b/cc/tiles/picture_layer_tiling_set_unittest.cc
index af0e5adc..dea3c88 100644
--- a/cc/tiles/picture_layer_tiling_set_unittest.cc
+++ b/cc/tiles/picture_layer_tiling_set_unittest.cc
@@ -97,14 +97,6 @@
   EXPECT_EQ(2u, between_high_and_low_res_range.start);
   EXPECT_EQ(4u, between_high_and_low_res_range.end);
 
-  low_res_range =
-      set_without_low_res->GetTilingRange(PictureLayerTilingSet::LOW_RES);
-  EXPECT_EQ(0u, low_res_range.end - low_res_range.start);
-
-  lower_than_low_res_range = set_without_low_res->GetTilingRange(
-      PictureLayerTilingSet::LOWER_THAN_LOW_RES);
-  EXPECT_EQ(0u, lower_than_low_res_range.end - lower_than_low_res_range.start);
-
   std::unique_ptr<TestablePictureLayerTilingSet> set_with_only_high_res =
       CreateTilingSet(&client);
   high_res_tiling =
@@ -125,14 +117,6 @@
       PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES);
   EXPECT_EQ(0u, between_high_and_low_res_range.end -
                     between_high_and_low_res_range.start);
-
-  low_res_range =
-      set_with_only_high_res->GetTilingRange(PictureLayerTilingSet::LOW_RES);
-  EXPECT_EQ(0u, low_res_range.end - low_res_range.start);
-
-  lower_than_low_res_range = set_with_only_high_res->GetTilingRange(
-      PictureLayerTilingSet::LOWER_THAN_LOW_RES);
-  EXPECT_EQ(0u, lower_than_low_res_range.end - lower_than_low_res_range.start);
 }
 
 class PictureLayerTilingSetTestWithResources : public testing::Test {
diff --git a/cc/tiles/tiling_set_eviction_queue.cc b/cc/tiles/tiling_set_eviction_queue.cc
index e3c80a9..dd0decc1 100644
--- a/cc/tiles/tiling_set_eviction_queue.cc
+++ b/cc/tiles/tiling_set_eviction_queue.cc
@@ -44,14 +44,6 @@
       tilings_.push_back(tiling);
   }
 
-  range = tiling_set->GetTilingRange(PictureLayerTilingSet::LOWER_THAN_LOW_RES);
-  for (size_t i = range.start; i < range.end; ++i) {
-    size_t index = range.start + (range.end - 1 - i);
-    PictureLayerTiling* tiling = tiling_set->tiling_at(index);
-    if (tiling->has_tiles())
-      tilings_.push_back(tiling);
-  }
-
   range = tiling_set->GetTilingRange(
       PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES);
   for (size_t i = range.start; i < range.end; ++i) {
@@ -61,13 +53,6 @@
       tilings_.push_back(tiling);
   }
 
-  range = tiling_set->GetTilingRange(PictureLayerTilingSet::LOW_RES);
-  for (size_t index = range.start; index < range.end; ++index) {
-    PictureLayerTiling* tiling = tiling_set->tiling_at(index);
-    if (tiling->has_tiles())
-      tilings_.push_back(tiling);
-  }
-
   range = tiling_set->GetTilingRange(PictureLayerTilingSet::HIGH_RES);
   for (size_t index = range.start; index < range.end; ++index) {
     PictureLayerTiling* tiling = tiling_set->tiling_at(index);
diff --git a/chrome/VERSION b/chrome/VERSION
index cc4fd5a3..21c4eb4 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=139
 MINOR=0
-BUILD=7256
+BUILD=7257
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
index 622498e..607c9a6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
@@ -15,6 +15,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewStub;
+import android.view.Window;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -26,6 +27,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
+import org.chromium.components.browser_ui.edge_to_edge.EdgeToEdgeStateProvider;
 import org.chromium.components.browser_ui.widget.ContextMenuDialog;
 import org.chromium.components.embedder_support.contextmenu.ChipDelegate;
 import org.chromium.components.embedder_support.contextmenu.ChipRenderParams;
@@ -106,6 +108,35 @@
         dismissDialog();
     }
 
+    // Calculate true top content offset to be used to compute the AnchorRect used by
+    // AnchoredPopupWindow, with origin below the system decoration which may or may not be merged
+    // with the tabstrip.
+    private static float topContentOffset(float offset, WindowAndroid windowAndroid) {
+        // If edge-to-edge mode is disabled, the input offset i.e. height of tabstrip plus toolbar
+        // is correct.
+        if (!EdgeToEdgeStateProvider.isEdgeToEdgeEnabledForWindow(windowAndroid)) return offset;
+
+        // Otherwise, the system decoration is tabstrip, so the input offset should only be height
+        // of toolbar.
+        // Compute the height of system decoration to get height of tabstrip, and subtract it from
+        // the input offset.
+        Window window = windowAndroid.getWindow();
+        if (window == null) return offset;
+        View view = window.getDecorView();
+        // The rect of the window without system decoration, see
+        // https://developer.android.com/reference/android/view/View#getWindowVisibleDisplayFrame(android.graphics.Rect)
+        Rect windowVisibleRect = new Rect();
+        view.getWindowVisibleDisplayFrame(windowVisibleRect);
+        // The coordinates of the window root (with system decoration), see
+        // https://developer.android.com/reference/android/view/View#getLocationOnScreen(int[])
+        int[] windowRootCoordinates = new int[2];
+        view.getLocationOnScreen(windowRootCoordinates);
+        // Difference of the two top-left y-coordinates is the height of the system decoration.
+        float systemDecorHeight = windowVisibleRect.top - windowRootCoordinates[1];
+
+        return offset - systemDecorHeight;
+    }
+
     // Shows the menu with chip.
     void displayMenuWithChip(
             final WindowAndroid window,
@@ -147,7 +178,7 @@
                         window.getWindow(),
                         webContents,
                         params,
-                        mTopContentOffsetPx,
+                        topContentOffset(mTopContentOffsetPx, window),
                         usePopupWindow,
                         layout);
         boolean shouldRemoveScrim = ContextMenuUtils.isPopupSupported(activity);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
index 65b9922..a7c642d5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webauth/Fido2CredentialRequestTest.java
@@ -108,7 +108,6 @@
 import org.chromium.content_public.browser.test.mock.MockRenderFrameHost;
 import org.chromium.content_public.browser.test.mock.MockWebContents;
 import org.chromium.content_public.common.ContentSwitches;
-import org.chromium.device.DeviceFeatureList;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.ui.test.util.GmsCoreVersionRestriction;
 import org.chromium.url.GURL;
@@ -2705,7 +2704,6 @@
     @Test
     @SmallTest
     @UseMethodParameter(SameOriginTestParams.class)
-    @EnableFeatures(DeviceFeatureList.WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS)
     public void testMakeCredential_remoteDesktopClientOverride_generatesCorrectClientDataJson(
             boolean sameOriginWithAncestors) {
         mIntentSender.setNextResultIntent(
@@ -2769,7 +2767,6 @@
     @Test
     @SmallTest
     @UseMethodParameter(SameOriginTestParams.class)
-    @EnableFeatures(DeviceFeatureList.WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS)
     public void testGetAssertion_remoteDesktopClientOverride_generatesCorrectClientDataJson(
             boolean sameOriginWithAncestors) {
         mIntentSender.setNextResultIntent(Fido2ApiTestHelper.createSuccessfulGetAssertionIntent());
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index d7c51be..4b850185 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -7813,14 +7813,19 @@
         "//services/accessibility/public/mojom",
         "//ui/events/ozone/layout:layout",
 
-        # TODO(crbug.com/40227502): Currently EnterpriseDeviceAttributes
-        # internally uses profile manager. We should get rid of it.
+        # For allow_circular_includes_from below.
+        "//chrome/browser/chromeos/extensions/login_screen/login",
         "//chrome/browser/extensions/api/enterprise_device_attributes",
+        "//chrome/browser/extensions/api/enterprise_login",
       ]
       allow_circular_includes_from += [
         # TODO(crbug.com/40227502): Currently EnterpriseDeviceAttributes
         # internally uses profile manager. We should get rid of it.
         "//chrome/browser/extensions/api/enterprise_device_attributes",
+
+        # For chrome::AttemptUserExit().
+        "//chrome/browser/chromeos/extensions/login_screen/login",
+        "//chrome/browser/extensions/api/enterprise_login",
       ]
     }
   }
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 48cbba3..f032d34d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4856,6 +4856,9 @@
     {"webrtc-hw-encoding", flag_descriptions::kWebrtcHwEncodingName,
      flag_descriptions::kWebrtcHwEncodingDescription, kOsAndroid | kOsCrOS,
      FEATURE_VALUE_TYPE(features::kWebRtcHWEncoding)},
+    {"webrtc-pqc-for-dtls", flag_descriptions::kWebRtcPqcForDtlsName,
+     flag_descriptions::kWebRtcPqcForDtlsDescription, kOsAll,
+     FEATURE_VALUE_TYPE(blink::features::kWebRtcPqcForDtls)},
     {"enable-webrtc-allow-input-volume-adjustment",
      flag_descriptions::kWebRtcAllowInputVolumeAdjustmentName,
      flag_descriptions::kWebRtcAllowInputVolumeAdjustmentDescription,
diff --git a/chrome/browser/ash/account_manager/BUILD.gn b/chrome/browser/ash/account_manager/BUILD.gn
index 37d4f9d..219347b 100644
--- a/chrome/browser/ash/account_manager/BUILD.gn
+++ b/chrome/browser/ash/account_manager/BUILD.gn
@@ -14,7 +14,6 @@
     "account_apps_availability_factory.h",
     "account_manager_edu_coexistence_controller.cc",
     "account_manager_edu_coexistence_controller.h",
-    "account_manager_facade_factory_ash.cc",
     "account_manager_policy_controller.cc",
     "account_manager_policy_controller.h",
     "account_manager_policy_controller_factory.cc",
diff --git a/chrome/browser/ash/account_manager/account_apps_availability_factory.cc b/chrome/browser/ash/account_manager/account_apps_availability_factory.cc
index fb3689ce..17c360c 100644
--- a/chrome/browser/ash/account_manager/account_apps_availability_factory.cc
+++ b/chrome/browser/ash/account_manager/account_apps_availability_factory.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/browser_context.h"
 
@@ -55,7 +55,7 @@
     return nullptr;
 
   return std::make_unique<AccountAppsAvailability>(
-      ::GetAccountManagerFacade(profile->GetPath().value()),
+      GetAccountManagerFacade(profile->GetPath().value()),
       IdentityManagerFactory::GetForProfile(profile), profile->GetPrefs());
 }
 
diff --git a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
index c4774c3..f9d9847c 100644
--- a/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
+++ b/chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller_unittest.cc
@@ -18,12 +18,12 @@
 #include "chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "components/account_id/account_id.h"
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "google_apis/gaia/gaia_id.h"
@@ -117,7 +117,7 @@
                          ->GetAccountManagerFactory()
                          ->GetAccountManager(profile()->GetPath().value());
   account_manager_facade_ =
-      ::GetAccountManagerFacade(profile()->GetPath().value());
+      GetAccountManagerFacade(profile()->GetPath().value());
 
   AddAccount(account_manager(), kPrimaryAccount, kPrimaryAccountGaiaId);
 }
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
index 15694252..21e0344 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller.cc
@@ -12,9 +12,9 @@
 #include "chrome/browser/ash/account_manager/account_manager_edu_coexistence_controller.h"
 #include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/account_manager_core/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "google_apis/gaia/gaia_id.h"
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
index b3219d4..c4dfe37 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_browsertest.cc
@@ -23,13 +23,13 @@
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "components/account_id/account_id.h"
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/account_manager_core/pref_names.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/signin/public/base/consent_level.h"
@@ -91,7 +91,7 @@
         g_browser_process->platform_part()->GetAccountManagerFactory();
     account_manager_ = factory->GetAccountManager(profile()->GetPath().value());
     account_manager_facade_ =
-        ::GetAccountManagerFacade(profile()->GetPath().value());
+        GetAccountManagerFacade(profile()->GetPath().value());
     identity_test_environment_adaptor_ =
         std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile_.get());
 
diff --git a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
index 386aa5c..800a641 100644
--- a/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
+++ b/chrome/browser/ash/account_manager/account_manager_policy_controller_factory.cc
@@ -7,10 +7,10 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/ash/account_manager/account_manager_policy_controller.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/user_manager/user.h"
 
 namespace ash {
@@ -55,7 +55,7 @@
     return nullptr;
 
   auto* account_manager_facade =
-      ::GetAccountManagerFacade(profile->GetPath().value());
+      GetAccountManagerFacade(profile->GetPath().value());
 
   if (!account_manager_facade)
     return nullptr;
diff --git a/chrome/browser/ash/ambient/ambient_client_impl_unittest.cc b/chrome/browser/ash/ambient/ambient_client_impl_unittest.cc
index 05683a8..19a047f 100644
--- a/chrome/browser/ash/ambient/ambient_client_impl_unittest.cc
+++ b/chrome/browser/ash/ambient/ambient_client_impl_unittest.cc
@@ -40,7 +40,7 @@
 
   void SetUp() override {
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &testing_local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
 
     profile_user_manager_controller_ =
diff --git a/chrome/browser/ash/arc/arc_util_unittest.cc b/chrome/browser/ash/arc/arc_util_unittest.cc
index 88f68432..d874857 100644
--- a/chrome/browser/ash/arc/arc_util_unittest.cc
+++ b/chrome/browser/ash/arc/arc_util_unittest.cc
@@ -154,7 +154,7 @@
 
     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
 
     profile_ = profile_manager_->CreateTestingProfile(kTestProfileName);
diff --git a/chrome/browser/ash/arc/auth/BUILD.gn b/chrome/browser/ash/arc/auth/BUILD.gn
index b93977d..5f5eef9d 100644
--- a/chrome/browser/ash/arc/auth/BUILD.gn
+++ b/chrome/browser/ash/arc/auth/BUILD.gn
@@ -34,6 +34,7 @@
     "//chrome/browser/ash/profiles",
     "//chrome/browser/profiles:profile",
     "//chrome/common",
+    "//chromeos/ash/components/account_manager",
     "//chromeos/ash/experiences/arc",
     "//components/account_manager_core",
     "//components/prefs",
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service.cc b/chrome/browser/ash/arc/auth/arc_auth_service.cc
index dc0b354c..3d5ec1a9 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service.cc
+++ b/chrome/browser/ash/arc/auth/arc_auth_service.cc
@@ -36,6 +36,7 @@
 #include "chrome/browser/ui/settings_window_manager_chromeos.h"
 #include "chrome/browser/ui/webui/signin/ash/inline_login_dialog.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/experiences/arc/arc_browser_context_keyed_service_factory_base.h"
 #include "chromeos/ash/experiences/arc/arc_features.h"
 #include "chromeos/ash/experiences/arc/arc_prefs.h"
@@ -45,7 +46,6 @@
 #include "chromeos/ash/experiences/arc/session/arc_management_transition.h"
 #include "chromeos/ash/experiences/arc/session/arc_service_manager.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/user_manager/user_manager.h"
@@ -507,7 +507,7 @@
 void ArcAuthService::HandleAddAccountRequest() {
   DCHECK(ash::IsAccountManagerAvailable(profile_));
 
-  ::GetAccountManagerFacade(profile_->GetPath().value())
+  ash::GetAccountManagerFacade(profile_->GetPath().value())
       ->ShowAddAccountDialog(
           account_manager::AccountManagerFacade::AccountAdditionSource::kArc);
 }
@@ -522,7 +522,7 @@
 void ArcAuthService::HandleUpdateCredentialsRequest(const std::string& email) {
   DCHECK(ash::IsAccountManagerAvailable(profile_));
 
-  ::GetAccountManagerFacade(profile_->GetPath().value())
+  ash::GetAccountManagerFacade(profile_->GetPath().value())
       ->ShowReauthAccountDialog(
           account_manager::AccountManagerFacade::AccountAdditionSource::kArc,
           email, base::DoNothing());
diff --git a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
index 4e9d302..12808f0 100644
--- a/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
+++ b/chrome/browser/ash/arc/auth/arc_auth_service_browsertest.cc
@@ -55,6 +55,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/browser_context_helper/annotated_account_id.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
@@ -68,7 +69,6 @@
 #include "chromeos/ash/experiences/arc/test/connection_holder_util.h"
 #include "chromeos/ash/experiences/arc/test/fake_arc_session.h"
 #include "components/account_id/account_id.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
 #include "components/policy/core/common/policy_switches.h"
@@ -439,7 +439,7 @@
     auth_service_->SetURLLoaderFactoryForTesting(test_shared_loader_factory_);
     arc_availability_setter_ = std::make_unique<AccountAppsAvailabilitySetter>(
         ash::AccountAppsAvailabilityFactory::GetForProfile(profile()),
-        ::GetAccountManagerFacade(profile()->GetPath().value()));
+        ash::GetAccountManagerFacade(profile()->GetPath().value()));
     arc_bridge_service_ = ArcServiceManager::Get()->arc_bridge_service();
     DCHECK(arc_bridge_service_);
     arc_bridge_service_->auth()->SetInstance(&auth_instance_);
diff --git a/chrome/browser/ash/arc/locked_fullscreen/arc_locked_fullscreen_manager_unittest.cc b/chrome/browser/ash/arc/locked_fullscreen/arc_locked_fullscreen_manager_unittest.cc
index 0d91e35..d455f48 100644
--- a/chrome/browser/ash/arc/locked_fullscreen/arc_locked_fullscreen_manager_unittest.cc
+++ b/chrome/browser/ash/arc/locked_fullscreen/arc_locked_fullscreen_manager_unittest.cc
@@ -127,8 +127,7 @@
   ash::ScopedCrosSettingsTestHelper cros_settings_helper_;
   ScopedTestingLocalState local_state_{TestingBrowserProcess::GetGlobal()};
   std::unique_ptr<user_manager::UserManagerImpl> user_manager_;
-  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal(),
-                                         &local_state_};
+  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
   session_manager::SessionManager session_manager_;
   raw_ptr<TestingProfile> profile_;
   std::unique_ptr<ArcSessionManager> arc_session_manager_;
diff --git a/chrome/browser/ash/assistant/assistant_util_unittest.cc b/chrome/browser/ash/assistant/assistant_util_unittest.cc
index ecf24f86..f65c399 100644
--- a/chrome/browser/ash/assistant/assistant_util_unittest.cc
+++ b/chrome/browser/ash/assistant/assistant_util_unittest.cc
@@ -175,7 +175,7 @@
 
     ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
 
     profile_ = profile_manager_->CreateTestingProfile(
diff --git a/chrome/browser/ash/crosapi/crosapi_util_unittest.cc b/chrome/browser/ash/crosapi/crosapi_util_unittest.cc
index ea5a65f..d40f7bd 100644
--- a/chrome/browser/ash/crosapi/crosapi_util_unittest.cc
+++ b/chrome/browser/ash/crosapi/crosapi_util_unittest.cc
@@ -71,7 +71,7 @@
     ash::system::StatisticsProvider::SetTestProvider(&statistics_provider_);
 
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
     testing_profile_ = profile_manager_->CreateTestingProfile(
         TestingProfile::kDefaultProfileUserName);
diff --git a/chrome/browser/ash/crosapi/login_ash.cc b/chrome/browser/ash/crosapi/login_ash.cc
index ec23138a..9a546e7 100644
--- a/chrome/browser/ash/crosapi/login_ash.cc
+++ b/chrome/browser/ash/crosapi/login_ash.cc
@@ -74,34 +74,6 @@
       extensions::login_api_errors::kNoManagedGuestSessionAccounts);
 }
 
-void LoginAsh::ExitCurrentSession(
-    const std::optional<std::string>& data_for_next_login_attempt,
-    ExitCurrentSessionCallback callback) {
-  PrefService* local_state = g_browser_process->local_state();
-  DCHECK(local_state);
-
-  if (data_for_next_login_attempt) {
-    local_state->SetString(prefs::kLoginExtensionApiDataForNextLoginAttempt,
-                           *data_for_next_login_attempt);
-  } else {
-    local_state->ClearPref(prefs::kLoginExtensionApiDataForNextLoginAttempt);
-  }
-
-  chrome::AttemptUserExit();
-  std::move(callback).Run(std::nullopt);
-}
-
-void LoginAsh::FetchDataForNextLoginAttempt(
-    FetchDataForNextLoginAttemptCallback callback) {
-  PrefService* local_state = g_browser_process->local_state();
-  DCHECK(local_state);
-  std::string data_for_next_login_attempt =
-      local_state->GetString(prefs::kLoginExtensionApiDataForNextLoginAttempt);
-  local_state->ClearPref(prefs::kLoginExtensionApiDataForNextLoginAttempt);
-
-  std::move(callback).Run(data_for_next_login_attempt);
-}
-
 void LoginAsh::LockManagedGuestSession(
     LockManagedGuestSessionCallback callback) {
   ui::UserActivityDetector::Get()->HandleExternalUserActivity();
@@ -225,17 +197,6 @@
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
-void LoginAsh::SetDataForNextLoginAttempt(
-    const std::string& data_for_next_login_attempt,
-    SetDataForNextLoginAttemptCallback callback) {
-  PrefService* local_state = g_browser_process->local_state();
-  DCHECK(local_state);
-  local_state->SetString(prefs::kLoginExtensionApiDataForNextLoginAttempt,
-                         data_for_next_login_attempt);
-
-  std::move(callback).Run();
-}
-
 void LoginAsh::AddExternalLogoutRequestObserver(
     mojo::PendingRemote<mojom::ExternalLogoutRequestObserver> observer) {
   mojo::Remote<mojom::ExternalLogoutRequestObserver> remote(
diff --git a/chrome/browser/ash/crosapi/login_ash.h b/chrome/browser/ash/crosapi/login_ash.h
index ee2d085..6909129 100644
--- a/chrome/browser/ash/crosapi/login_ash.h
+++ b/chrome/browser/ash/crosapi/login_ash.h
@@ -42,18 +42,10 @@
   void BindReceiver(mojo::PendingReceiver<mojom::Login> receiver);
 
   // crosapi::mojom::Login:
-  void ExitCurrentSession(
-      const std::optional<std::string>& data_for_next_login_attempt,
-      ExitCurrentSessionCallback callback) override;
-  void FetchDataForNextLoginAttempt(
-      FetchDataForNextLoginAttemptCallback callback) override;
   void LockManagedGuestSession(
       LockManagedGuestSessionCallback callback) override;
   void LockCurrentSession(LockCurrentSessionCallback callback) override;
   void EndSharedSession(EndSharedSessionCallback callback) override;
-  void SetDataForNextLoginAttempt(
-      const std::string& data_for_next_login_attempt,
-      SetDataForNextLoginAttemptCallback callback) override;
   void AddExternalLogoutRequestObserver(
       mojo::PendingRemote<mojom::ExternalLogoutRequestObserver> observer)
       override;
diff --git a/chrome/browser/ash/file_manager/path_util_unittest.cc b/chrome/browser/ash/file_manager/path_util_unittest.cc
index a6542a82..e1c130c7 100644
--- a/chrome/browser/ash/file_manager/path_util_unittest.cc
+++ b/chrome/browser/ash/file_manager/path_util_unittest.cc
@@ -716,7 +716,7 @@
   void SetUp() override {
     fake_user_manager_.Reset(std::make_unique<ash::FakeChromeUserManager>());
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
 
     // Set up fake user manager.
diff --git a/chrome/browser/ash/input_method/japanese/japanese_settings.cc b/chrome/browser/ash/input_method/japanese/japanese_settings.cc
index 62d6a98d..4771fd5 100644
--- a/chrome/browser/ash/input_method/japanese/japanese_settings.cc
+++ b/chrome/browser/ash/input_method/japanese/japanese_settings.cc
@@ -272,6 +272,7 @@
   response->automatically_switch_to_halfwidth =
       prefs.FindBool(kJpPrefAutomaticallySwitchToHalfwidth)
           .value_or(response->automatically_switch_to_halfwidth);
+  RecordJapaneseSettingsMetrics(*response);
   return response;
 }
 
diff --git a/chrome/browser/ash/login/users/avatar/user_image_manager_impl_unittest.cc b/chrome/browser/ash/login/users/avatar/user_image_manager_impl_unittest.cc
index 1296824..2b5f51f 100644
--- a/chrome/browser/ash/login/users/avatar/user_image_manager_impl_unittest.cc
+++ b/chrome/browser/ash/login/users/avatar/user_image_manager_impl_unittest.cc
@@ -102,8 +102,7 @@
  private:
   ScopedTestingLocalState local_state_{TestingBrowserProcess::GetGlobal()};
   content::BrowserTaskEnvironment task_environment_;
-  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal(),
-                                         &local_state_};
+  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
   user_manager::TypedScopedUserManager<FakeChromeUserManager>
       fake_chrome_user_manager_{std::make_unique<FakeChromeUserManager>()};
   raw_ptr<testing::StrictMock<test::MockUserImageLoaderDelegate>>
diff --git a/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc b/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc
index 02ffe31..ceb1b3f7 100644
--- a/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc
+++ b/chrome/browser/ash/login/users/multi_user_sign_in_policy_controller_unittest.cc
@@ -137,7 +137,7 @@
 
   void SetUp() override {
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
 
     for (const auto& account_id : test_users_) {
diff --git a/chrome/browser/ash/login/users/profile_user_manager_controller_unittest.cc b/chrome/browser/ash/login/users/profile_user_manager_controller_unittest.cc
index 8f2ae57..c53b5d20 100644
--- a/chrome/browser/ash/login/users/profile_user_manager_controller_unittest.cc
+++ b/chrome/browser/ash/login/users/profile_user_manager_controller_unittest.cc
@@ -51,7 +51,7 @@
   // pointer first.
   std::unique_ptr<ProfileUserManagerController> controller_;
   TestingProfileManager testing_profile_manager_{
-      TestingBrowserProcess::GetGlobal(), &local_state_};
+      TestingBrowserProcess::GetGlobal()};
 };
 
 TEST_F(ProfileUserManagerControllerTest, GetProfilePrefs) {
diff --git a/chrome/browser/ash/power/extension_event_observer_unittest.cc b/chrome/browser/ash/power/extension_event_observer_unittest.cc
index a27dc766..82264c3 100644
--- a/chrome/browser/ash/power/extension_event_observer_unittest.cc
+++ b/chrome/browser/ash/power/extension_event_observer_unittest.cc
@@ -57,7 +57,7 @@
     testing_local_state_ = std::make_unique<ScopedTestingLocalState>(
         TestingBrowserProcess::GetGlobal());
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), testing_local_state_.get());
+        TestingBrowserProcess::GetGlobal());
 
     // Must be called from ::testing::Test::SetUp.
     ASSERT_TRUE(profile_manager_->SetUp());
diff --git a/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc b/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc
index e6cf1c9..27beb8a 100644
--- a/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc
+++ b/chrome/browser/ash/shimless_rma/chrome_shimless_rma_delegate_unittest.cc
@@ -267,7 +267,7 @@
  protected:
   base::test::ScopedFeatureList feature_list_;
   TestingProfileManager testing_profile_manager_{
-      TestingBrowserProcess::GetGlobal(), &testing_local_state_};
+      TestingBrowserProcess::GetGlobal()};
   variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
       variations::VariationsIdsProvider::Mode::kUseSignedInState};
   std::unique_ptr<FakeDiagnosticsAppProfileHelperDelegate>
diff --git a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_user_provider_impl_unittest.cc b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_user_provider_impl_unittest.cc
index c3923df..d4438e4 100644
--- a/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_user_provider_impl_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/apps/personalization_app/personalization_app_user_provider_impl_unittest.cc
@@ -318,8 +318,7 @@
   user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
       user_manager_{std::make_unique<ash::FakeChromeUserManager>()};
   UserImageManagerRegistry user_image_manager_registry_{user_manager_.Get()};
-  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal(),
-                                         &local_state_};
+  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
   data_decoder::test::InProcessDataDecoder data_decoder_;
   content::TestWebUI web_ui_;
   std::unique_ptr<content::WebContents> web_contents_;
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index a9043a7b..76b8b385 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -1161,7 +1161,7 @@
     // check if a feature is enabled, to avoid tsan data races.
     CHECK(temp_dir_.CreateUniqueTempDir());
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     CHECK(profile_manager_->SetUp(temp_dir_.GetPath()));
     profile_ = profile_manager_->CreateTestingProfile("test_profile",
                                                       GetTestingFactories());
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1a845e2a..2ab396ee 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -346,7 +346,6 @@
 #include "content/public/common/origin_util.h"
 #include "content/public/common/url_utils.h"
 #include "content/public/common/window_container_type.mojom-shared.h"
-#include "device/fido/features.h"
 #include "device/vr/buildflags/buildflags.h"
 #include "extensions/browser/browser_frame_context_data.h"
 #include "extensions/buildflags/buildflags.h"
@@ -2900,9 +2899,7 @@
       // Make the WebAuthenticationRemoteDesktopAllowedOrigins policy enable the
       // experimental WebAuthenticationRemoteDesktopSupport Blink runtime
       // feature.
-      if (base::FeatureList::IsEnabled(
-              device::kWebAuthnRemoteDesktopAllowedOriginsPolicy) &&
-          !prefs->GetList(webauthn::pref_names::kRemoteDesktopAllowedOrigins)
+      if (!prefs->GetList(webauthn::pref_names::kRemoteDesktopAllowedOrigins)
                .empty()) {
         command_line->AppendSwitch(switches::kWebAuthRemoteDesktopSupport);
       }
diff --git a/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc b/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc
index 5061cbef..6334a9f 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_browser_session_unittest.cc
@@ -280,8 +280,7 @@
   KioskBrowserSessionBaseTest()
       : local_state_(std::make_unique<ScopedTestingLocalState>(
             TestingBrowserProcess::GetGlobal())),
-        testing_profile_manager_(TestingBrowserProcess::GetGlobal(),
-                                 local_state_.get()) {}
+        testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {}
 
   KioskBrowserSessionBaseTest(const KioskBrowserSessionBaseTest&) = delete;
   KioskBrowserSessionBaseTest& operator=(const KioskBrowserSessionBaseTest&) =
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn b/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn
index d18f85c78..65d249a 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn
+++ b/chrome/browser/chromeos/extensions/login_screen/login/BUILD.gn
@@ -29,6 +29,7 @@
 
   deps = [
     "//ash/constants",
+    "//chrome/browser:browser_process",
     "//chrome/browser/ash/login",
     "//chrome/browser/ash/login/lock",
     "//chrome/browser/chromeos/extensions/login_screen/login/cleanup",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/DEPS b/chrome/browser/chromeos/extensions/login_screen/login/DEPS
index d179ff0a..5fd419f 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/DEPS
+++ b/chrome/browser/chromeos/extensions/login_screen/login/DEPS
@@ -22,6 +22,7 @@
   "+chrome/browser/browser_process.h",
   "+chrome/browser/chromeos/extensions/login_screen",
   "+chrome/browser/extensions/extension_api_unittest.h",
+  "+chrome/browser/lifetime/application_lifetime.h",
   "+chrome/browser/lifetime/termination_notification.h",
   "+chrome/browser/policy/extension_force_install_mixin.h",
   "+chrome/browser/ui/ash/login",
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
index e214168..192d20ca 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api.cc
@@ -14,7 +14,11 @@
 #include "chrome/browser/ash/crosapi/crosapi_ash.h"
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crosapi/login_ash.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/common/extensions/api/login.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "google_apis/gaia/gaia_id.h"
 
 namespace extensions {
@@ -38,19 +42,6 @@
   return Respond(NoArguments());
 }
 
-ExtensionFunctionWithStringResult::~ExtensionFunctionWithStringResult() =
-    default;
-
-void ExtensionFunctionWithStringResult::OnResult(const std::string& result) {
-  Respond(WithArguments(result));
-}
-
-ExtensionFunctionWithVoidResult::~ExtensionFunctionWithVoidResult() = default;
-
-void ExtensionFunctionWithVoidResult::OnResult() {
-  Respond(NoArguments());
-}
-
 LoginLaunchManagedGuestSessionFunction::
     LoginLaunchManagedGuestSessionFunction() = default;
 LoginLaunchManagedGuestSessionFunction::
@@ -80,17 +71,17 @@
   auto parameters = api::login::ExitCurrentSession::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(parameters);
 
-  auto callback =
-      base::BindOnce(&LoginExitCurrentSessionFunction::OnResult, this);
+  PrefService* local_state = g_browser_process->local_state();
+  CHECK(local_state);
 
-  std::optional<std::string> data_for_next_login_attempt;
   if (parameters->data_for_next_login_attempt) {
-    data_for_next_login_attempt =
-        std::move(*parameters->data_for_next_login_attempt);
+    local_state->SetString(prefs::kLoginExtensionApiDataForNextLoginAttempt,
+                           std::move(*parameters->data_for_next_login_attempt));
+  } else {
+    local_state->ClearPref(prefs::kLoginExtensionApiDataForNextLoginAttempt);
   }
-  GetLoginApi()->ExitCurrentSession(data_for_next_login_attempt,
-                                    std::move(callback));
-  return did_respond() ? AlreadyResponded() : RespondLater();
+  chrome::AttemptUserExit();
+  return RespondNow(NoArguments());
 }
 
 LoginFetchDataForNextLoginAttemptFunction::
@@ -100,11 +91,13 @@
 
 ExtensionFunction::ResponseAction
 LoginFetchDataForNextLoginAttemptFunction::Run() {
-  auto callback = base::BindOnce(
-      &LoginFetchDataForNextLoginAttemptFunction::OnResult, this);
+  PrefService* local_state = g_browser_process->local_state();
+  CHECK(local_state);
 
-  GetLoginApi()->FetchDataForNextLoginAttempt(std::move(callback));
-  return did_respond() ? AlreadyResponded() : RespondLater();
+  std::string data_for_next_login_attempt =
+      local_state->GetString(prefs::kLoginExtensionApiDataForNextLoginAttempt);
+  local_state->ClearPref(prefs::kLoginExtensionApiDataForNextLoginAttempt);
+  return RespondNow(WithArguments(std::move(data_for_next_login_attempt)));
 }
 
 LoginLockManagedGuestSessionFunction::LoginLockManagedGuestSessionFunction() =
@@ -255,12 +248,11 @@
       api::login::SetDataForNextLoginAttempt::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(parameters);
 
-  auto callback =
-      base::BindOnce(&LoginSetDataForNextLoginAttemptFunction::OnResult, this);
-
-  GetLoginApi()->SetDataForNextLoginAttempt(
-      parameters->data_for_next_login_attempt, std::move(callback));
-  return did_respond() ? AlreadyResponded() : RespondLater();
+  PrefService* local_state = g_browser_process->local_state();
+  CHECK(local_state);
+  local_state->SetString(prefs::kLoginExtensionApiDataForNextLoginAttempt,
+                         parameters->data_for_next_login_attempt);
+  return RespondNow(NoArguments());
 }
 
 LoginRequestExternalLogoutFunction::LoginRequestExternalLogoutFunction() =
diff --git a/chrome/browser/chromeos/extensions/login_screen/login/login_api.h b/chrome/browser/chromeos/extensions/login_screen/login/login_api.h
index 4bbfb6c..f482bd4 100644
--- a/chrome/browser/chromeos/extensions/login_screen/login/login_api.h
+++ b/chrome/browser/chromeos/extensions/login_screen/login/login_api.h
@@ -19,20 +19,6 @@
   void OnResult(const std::optional<std::string>& error);
 };
 
-class ExtensionFunctionWithStringResult : public ExtensionFunction {
- protected:
-  ~ExtensionFunctionWithStringResult() override;
-
-  void OnResult(const std::string& result);
-};
-
-class ExtensionFunctionWithVoidResult : public ExtensionFunction {
- protected:
-  ~ExtensionFunctionWithVoidResult() override;
-
-  void OnResult();
-};
-
 class LoginLaunchManagedGuestSessionFunction
     : public ExtensionFunctionWithOptionalErrorResult {
  public:
@@ -75,8 +61,7 @@
   ResponseAction Run() override;
 };
 
-class LoginFetchDataForNextLoginAttemptFunction
-    : public ExtensionFunctionWithStringResult {
+class LoginFetchDataForNextLoginAttemptFunction : public ExtensionFunction {
  public:
   LoginFetchDataForNextLoginAttemptFunction();
 
@@ -283,8 +268,7 @@
   ResponseAction Run() override;
 };
 
-class LoginSetDataForNextLoginAttemptFunction
-    : public ExtensionFunctionWithVoidResult {
+class LoginSetDataForNextLoginAttemptFunction : public ExtensionFunction {
  public:
   LoginSetDataForNextLoginAttemptFunction();
 
diff --git a/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc b/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
index 16780de..f93dfb8e 100644
--- a/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
+++ b/chrome/browser/enterprise/reporting/report_scheduler_unittest.cc
@@ -151,7 +151,7 @@
   ReportSchedulerTest()
       : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
         local_state_(TestingBrowserProcess::GetGlobal()),
-        profile_manager_(TestingBrowserProcess::GetGlobal(), &local_state_) {}
+        profile_manager_(TestingBrowserProcess::GetGlobal()) {}
 
   ReportSchedulerTest(const ReportSchedulerTest&) = delete;
   ReportSchedulerTest& operator=(const ReportSchedulerTest&) = delete;
diff --git a/chrome/browser/extensions/api/enterprise_login/BUILD.gn b/chrome/browser/extensions/api/enterprise_login/BUILD.gn
index ae83943..d43834a6 100644
--- a/chrome/browser/extensions/api/enterprise_login/BUILD.gn
+++ b/chrome/browser/extensions/api/enterprise_login/BUILD.gn
@@ -13,14 +13,13 @@
     "enterprise_login_api.h",
   ]
 
-  public_deps = [
-    "//chromeos/crosapi/mojom",
-    "//extensions/browser",
-  ]
+  public_deps = [ "//extensions/browser" ]
 
   deps = [
     "//base",
-    "//chrome/browser/ash/crosapi",
+    "//chrome/browser:browser_process",
+    "//chrome/browser:browser_public_dependencies",
     "//chrome/common/extensions/api",
+    "//components/prefs",
   ]
 }
diff --git a/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.cc b/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.cc
index 1643afb1..8f5ce1a99 100644
--- a/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.cc
+++ b/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.cc
@@ -4,13 +4,11 @@
 
 #include "chrome/browser/extensions/api/enterprise_login/enterprise_login_api.h"
 
-#include <optional>
-#include <string>
-
-#include "chrome/browser/ash/crosapi/crosapi_ash.h"
-#include "chrome/browser/ash/crosapi/crosapi_manager.h"
-#include "chrome/browser/ash/crosapi/login_ash.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/lifetime/application_lifetime.h"
 #include "chrome/common/extensions/api/enterprise_login.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 
 namespace extensions {
@@ -20,31 +18,18 @@
 EnterpriseLoginExitCurrentManagedGuestSessionFunction::
     ~EnterpriseLoginExitCurrentManagedGuestSessionFunction() = default;
 
-void EnterpriseLoginExitCurrentManagedGuestSessionFunction::OnResult(
-    const std::optional<std::string>& error) {
-  if (error) {
-    Respond(Error(*error));
-    return;
-  }
-
-  return Respond(NoArguments());
-}
-
 ExtensionFunction::ResponseAction
 EnterpriseLoginExitCurrentManagedGuestSessionFunction::Run() {
   if (!user_manager::UserManager::Get() ||
       !user_manager::UserManager::Get()->IsLoggedInAsManagedGuestSession()) {
     return RespondNow(Error("Not a managed guest session."));
   }
-  auto callback = base::BindOnce(
-      &EnterpriseLoginExitCurrentManagedGuestSessionFunction::OnResult, this);
 
-  crosapi::CrosapiManager::Get()
-      ->crosapi_ash()
-      ->login_ash()
-      ->ExitCurrentSession(/*data_for_next_login_attempt=*/std::nullopt,
-                           std::move(callback));
-  return did_respond() ? AlreadyResponded() : RespondLater();
+  g_browser_process->local_state()->ClearPref(
+      prefs::kLoginExtensionApiDataForNextLoginAttempt);
+
+  chrome::AttemptUserExit();
+  return RespondNow(NoArguments());
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.h b/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.h
index c4d8ddd..b7521d3 100644
--- a/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.h
+++ b/chrome/browser/extensions/api/enterprise_login/enterprise_login_api.h
@@ -5,9 +5,6 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_LOGIN_ENTERPRISE_LOGIN_API_H_
 #define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_LOGIN_ENTERPRISE_LOGIN_API_H_
 
-#include <optional>
-#include <string>
-
 #include "extensions/browser/extension_function.h"
 
 namespace extensions {
@@ -29,8 +26,6 @@
  protected:
   ~EnterpriseLoginExitCurrentManagedGuestSessionFunction() override;
 
-  void OnResult(const std::optional<std::string>& error);
-
   // ExtensionFunction:
   ResponseAction Run() override;
 };
diff --git a/chrome/browser/extensions/extension_util_unittest.cc b/chrome/browser/extensions/extension_util_unittest.cc
index b0de5b3a..ea530473 100644
--- a/chrome/browser/extensions/extension_util_unittest.cc
+++ b/chrome/browser/extensions/extension_util_unittest.cc
@@ -210,7 +210,7 @@
     ExtensionUtilUnittest::SetUp();
 
     testing_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &testing_local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(testing_profile_manager_->SetUp());
     auto policy_service = std::make_unique<policy::PolicyServiceImpl>(
         std::vector<
diff --git a/chrome/browser/feature_engagement/tracker_factory.cc b/chrome/browser/feature_engagement/tracker_factory.cc
index ae7e861f..72828b5 100644
--- a/chrome/browser/feature_engagement/tracker_factory.cc
+++ b/chrome/browser/feature_engagement/tracker_factory.cc
@@ -6,11 +6,13 @@
 
 #include "base/files/file_path.h"
 #include "base/no_destructor.h"
+#include "base/path_service.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
 #include "components/feature_engagement/public/configuration_provider.h"
 #include "components/feature_engagement/public/field_trial_configuration_provider.h"
 #include "components/feature_engagement/public/local_configuration_provider.h"
@@ -72,6 +74,11 @@
   base::FilePath storage_dir = profile->GetPath().Append(
       chrome::kFeatureEngagementTrackerStorageDirname);
 
+  base::FilePath device_storage_dir;
+  base::PathService::Get(chrome::DIR_USER_DATA, &device_storage_dir);
+  device_storage_dir = device_storage_dir.Append(
+      chrome::kFeatureEngagementTrackerStorageDirname);
+
   leveldb_proto::ProtoDatabaseProvider* db_provider =
       profile->GetDefaultStoragePartition()->GetProtoDatabaseProvider();
   auto providers =
@@ -86,8 +93,8 @@
 #endif
 
   return feature_engagement::Tracker::Create(
-      storage_dir, background_task_runner, db_provider, nullptr,
-      std::move(providers));
+      storage_dir, device_storage_dir, background_task_runner, db_provider,
+      nullptr, std::move(providers));
 }
 
 }  // namespace feature_engagement
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c474b56..995498c 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -10493,6 +10493,11 @@
     "expiry_milestone": -1
   },
   {
+    "name": "webrtc-pqc-for-dtls",
+    "owners": [ "agpalak@chromium.org", "guidou@chromium.org"],
+    "expiry_milestone": 170
+  },
+  {
     "name": "webrtc-wgc-require-border",
     "owners": [ "eladalon@chromium.org", "kron@chromium.org" ],
     "expiry_milestone": 180
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index a7d53f10..eeff7940f 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -4561,6 +4561,10 @@
 const char kWebrtcHwEncodingDescription[] =
     "Support in WebRTC for encoding video streams using platform hardware.";
 
+const char kWebRtcPqcForDtlsName[] = "WebRTC PQC for DTLS";
+const char kWebRtcPqcForDtlsDescription[] =
+    "Support in WebRTC to enable PQC for DTLS";
+
 const char kWebrtcUseMinMaxVEADimensionsName[] =
     "WebRTC Min/Max Video Encode Accelerator dimensions";
 const char kWebrtcUseMinMaxVEADimensionsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 717e0f8..504496b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2639,6 +2639,9 @@
 extern const char kWebrtcHwEncodingName[];
 extern const char kWebrtcHwEncodingDescription[];
 
+extern const char kWebRtcPqcForDtlsName[];
+extern const char kWebRtcPqcForDtlsDescription[];
+
 extern const char kWebrtcUseMinMaxVEADimensionsName[];
 extern const char kWebrtcUseMinMaxVEADimensionsDescription[];
 
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc b/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc
index b2288df..5502f4e 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_discovery_interface.cc
@@ -23,6 +23,7 @@
 #include "components/signin/public/identity_manager/access_token_info.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/sync/base/features.h"
 #include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -250,13 +251,17 @@
 
   // TODO(crbug.com/40067771): ConsentLevel::kSync is deprecated and should be
   //     removed. See ConsentLevel::kSync documentation for details.
+  const signin::ConsentLevel consent_level =
+      base::FeatureList::IsEnabled(syncer::kReplaceSyncPromosWithSignInPromos)
+          ? signin::ConsentLevel::kSignin
+          : signin::ConsentLevel::kSync;
   return std::make_unique<EndpointFetcher>(
       profile_->GetDefaultStoragePartition()
           ->GetURLLoaderFactoryForBrowserProcess(),
       kDiscoveryOAuthConsumerName,
       GURL(base::StrCat({GetDiscoveryUrl(), "/", access_code})), kGetMethod,
       kContentType, discovery_scopes, kTimeout, kEmptyPostData,
-      kTrafficAnnotation, identity_manager_, signin::ConsentLevel::kSync);
+      kTrafficAnnotation, identity_manager_, consent_level);
 }
 
 void AccessCodeCastDiscoveryInterface::ValidateDiscoveryAccessCode(
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index d59b5e1..276b645a 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -520,7 +520,7 @@
     fake_user_manager_.Reset(
         std::make_unique<user_manager::FakeUserManager>(local_state_.Get()));
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
     network_notifier_ = net::test::MockNetworkChangeNotifier::Create();
 
diff --git a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
index 63f7d566..38178c7 100644
--- a/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/core/ukm_page_load_metrics_observer.cc
@@ -838,25 +838,16 @@
   // When the 1st soft navigation comes in, we record the
   // soft_navigation_interval_responsiveness_metrics_normalization_ as INP
   // before soft nav.
-  if (current_soft_navigation_metrics->count == 0 &&
-      new_soft_navigation_metrics.count == 1) {
+  if (current_soft_navigation_metrics->count == 0) {
     RecordResponsivenessMetricsBeforeSoftNavigationForMainFrame();
     RecordLayoutShiftBeforeSoftNavigationForMainFrame();
+  } else {
+    // Even though a soft-nav arrived, we don't flush until the current one
+    // unloads (i.e. next soft nav arrives).  So the very first skips reporting.
+    RecordSoftNavigationMetrics(
+        GetDelegate().GetPreviousUkmSourceIdForSoftNavigation(),
+        *current_soft_navigation_metrics);
   }
-
-  // Record current soft navigation metrics into Ukm when a new soft navigation
-  // comes in. For example, when 2nd soft navigation with a larger count comes
-  // in, the 1st(current) soft metrics are recorded. The initial soft
-  // navigation metrics that have default values should not reported.
-  if (current_soft_navigation_metrics->count == 0 ||
-      current_soft_navigation_metrics->count >=
-          new_soft_navigation_metrics.count) {
-    return;
-  }
-
-  RecordSoftNavigationMetrics(
-      GetDelegate().GetPreviousUkmSourceIdForSoftNavigation(),
-      *current_soft_navigation_metrics);
 }
 
 const page_load_metrics::ContentfulPaintTimingInfo&
diff --git a/chrome/browser/permissions/permission_manager_browsertest.cc b/chrome/browser/permissions/permission_manager_browsertest.cc
index 7100fec..1f7a0e1 100644
--- a/chrome/browser/permissions/permission_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_manager_browsertest.cc
@@ -57,6 +57,9 @@
       content::PermissionController::SubscriptionId subscription_id) override {
     permissions::PermissionManager::OnPermissionStatusChangeSubscriptionAdded(
         subscription_id);
+    if (callback_.is_null()) {
+      return;
+    }
     std::move(callback_).Run();
   }
 
diff --git a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
index 25aa90a..f4acd6c 100644
--- a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
+++ b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline.cc
@@ -6,6 +6,7 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "chrome/browser/preloading/chrome_preloading.h"
+#include "chrome/browser/preloading/preloading_features.h"
 #include "chrome/browser/preloading/prerender/prerender_utils.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -93,10 +94,9 @@
       /*additional_headers=*/net::HttpRequestHeaders(),
       /*no_vary_search_hint=*/std::nullopt,
       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_BOOKMARK),
-      // Considering the characteristics of triggers (e.g., the duration
-      // from trigger to activation), warm-up is not enabled for now on
-      // this trigger. Please see crbug and its doc for more details.
-      /*should_warm_up_compositor=*/false,
+      /*should_warm_up_compositor=*/
+      base::FeatureList::IsEnabled(
+          features::kPrerender2WarmUpCompositorForBookmarkBar),
       /*should_prepare_paint_tree=*/false,
       content::PreloadingHoldbackStatus::kUnspecified, pipeline_info_,
       preloading_attempt,
diff --git a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline_manager.cc b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline_manager.cc
index 60783954..19beee86 100644
--- a/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline_manager.cc
+++ b/chrome/browser/preloading/bookmarkbar_preload/bookmarkbar_preload_pipeline_manager.cc
@@ -39,10 +39,12 @@
 
 void BookmarkBarPreloadPipelineManager::StartPrerender(const GURL& url) {
   if (pipeline_) {
-    // Prerender is expected to be reset when mouseExit happens or every primary
-    // page changed, so if a pipeline is present, the url is expected to be the
-    // same.
-    CHECK_EQ(url, pipeline_->url());
+    // TODO(https://crbug.com/413259638) Adds back the CHECK which checks `url
+    // == pipeline_->url()` when the investigation is done. Prerender is
+    // expected to be reset when mouseExit happens or every primary page
+    // changed, so if a pipeline is present, the url is expected to be the same.
+    // But the CHECK is causing https://crbug.com/425612820 unexpectedly, the
+    // CHECK is removed at the moment.
     return;
   }
 
diff --git a/chrome/browser/preloading/prefetch/chrome_prefetch_manager.cc b/chrome/browser/preloading/prefetch/chrome_prefetch_manager.cc
index feda4e3..ad4cdb2 100644
--- a/chrome/browser/preloading/prefetch/chrome_prefetch_manager.cc
+++ b/chrome/browser/preloading/prefetch/chrome_prefetch_manager.cc
@@ -57,6 +57,7 @@
           prefetch_url, use_prefetch_proxy, kCCTMetricsSuffix,
           blink::mojom::Referrer(), referring_origin,
           /*no_vary_search_hint=*/std::nullopt,
+          /*priority=*/std::nullopt,
           content::PreloadPipelineInfo::Create(
               /*planned_max_preloading_type=*/content::PreloadingType::
                   kPrefetch),
diff --git a/chrome/browser/preloading/preloading_features.cc b/chrome/browser/preloading/preloading_features.cc
index 12272b9..39f69eb 100644
--- a/chrome/browser/preloading/preloading_features.cc
+++ b/chrome/browser/preloading/preloading_features.cc
@@ -14,4 +14,11 @@
                    "zero_suggest_trigger",
                    false);
 
+BASE_FEATURE(kPrerender2WarmUpCompositorForBookmarkBar,
+             "Prerender2WarmUpCompositorForBookmarkBar",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kPrerender2WarmUpCompositorForNewTabPage,
+             "Prerender2WarmUpCompositorForNewTabPage",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace features
diff --git a/chrome/browser/preloading/preloading_features.h b/chrome/browser/preloading/preloading_features.h
index 27bbec53..936e59b 100644
--- a/chrome/browser/preloading/preloading_features.h
+++ b/chrome/browser/preloading/preloading_features.h
@@ -15,6 +15,11 @@
 BASE_DECLARE_FEATURE_PARAM(std::string, kPrewarmUrl);
 BASE_DECLARE_FEATURE_PARAM(bool, kPrewarmZeroSuggestTrigger);
 
+// If enabled, requests the compositor warm-up (crbug.com/41496019) for
+// each prerender trigger.
+BASE_DECLARE_FEATURE(kPrerender2WarmUpCompositorForBookmarkBar);
+BASE_DECLARE_FEATURE(kPrerender2WarmUpCompositorForNewTabPage);
+
 }  // namespace features
 
 #endif  // CHROME_BROWSER_PRELOADING_PRELOADING_FEATURES_H_
diff --git a/chrome/browser/preloading/prerender/prerender_manager.cc b/chrome/browser/preloading/prerender/prerender_manager.cc
index 4e54db33..eb8e7eb 100644
--- a/chrome/browser/preloading/prerender/prerender_manager.cc
+++ b/chrome/browser/preloading/prerender/prerender_manager.cc
@@ -237,10 +237,9 @@
       /*additional_headers=*/net::HttpRequestHeaders(),
       /*no_vary_search_hint=*/std::nullopt,
       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_AUTO_BOOKMARK),
-      // Considering the characteristics of triggers (e.g., the duration from
-      // trigger to activation), warm-up is not enabled for now on this trigger.
-      // Please see crbug and its doc for more details.
-      /*should_warm_up_compositor=*/false,
+      /*should_warm_up_compositor=*/
+      base::FeatureList::IsEnabled(
+          features::kPrerender2WarmUpCompositorForNewTabPage),
       /*should_prepare_paint_tree=*/false,
       content::PreloadingHoldbackStatus::kUnspecified,
       content::PreloadPipelineInfo::Create(
diff --git a/chrome/browser/preloading/search_preload/search_preload_pipeline.cc b/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
index 7e27979..9f24d1b 100644
--- a/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_pipeline.cc
@@ -87,8 +87,8 @@
       /*use_prefetch_proxy=*/false,
       prerender_utils::kDefaultSearchEngineMetricSuffix,
       blink::mojom::Referrer(),
-      /*referring_origin=*/std::nullopt, no_vary_search_hint, pipeline_info_,
-      attempt->GetWeakPtr(),
+      /*referring_origin=*/std::nullopt, no_vary_search_hint,
+      /*priority=*/std::nullopt, pipeline_info_, attempt->GetWeakPtr(),
       /*holdback_status_override=*/std::nullopt,
       /*ttl=*/features::kDsePreload2PrefetchTtl.Get());
   CHECK(prefetch_handle_);
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
index 6de7b63..7067e364 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
@@ -354,7 +354,7 @@
 
   void CreateDefaultProfile() {
     default_profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(default_profile_manager_->SetUp());
 
     default_profile_ = default_profile_manager_->CreateTestingProfile(
diff --git a/chrome/browser/profiles/profiles_state_unittest.cc b/chrome/browser/profiles/profiles_state_unittest.cc
index 05afa3e..b229bc74 100644
--- a/chrome/browser/profiles/profiles_state_unittest.cc
+++ b/chrome/browser/profiles/profiles_state_unittest.cc
@@ -81,8 +81,7 @@
 class IsGuestModeEnabledTest : public testing::TestWithParam<bool> {
  public:
   IsGuestModeEnabledTest()
-      : profile_manager_(TestingBrowserProcess::GetGlobal(),
-                         &testing_local_state_),
+      : profile_manager_(TestingBrowserProcess::GetGlobal()),
         testing_local_state_(TestingBrowserProcess::GetGlobal()) {
     testing_local_state_.Get()->SetBoolean(prefs::kBrowserGuestModeEnabled,
                                            BrowserGuestModePrefValue());
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index 735be55..d99592d 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -286,7 +286,7 @@
     : public ChromeRenderViewHostTestHarness {
  public:
   ChromePasswordProtectionServiceTest()
-      : profile_manager_(TestingBrowserProcess::GetGlobal(), &local_state_),
+      : profile_manager_(TestingBrowserProcess::GetGlobal()),
         local_state_(TestingBrowserProcess::GetGlobal()) {
     EXPECT_TRUE(profile_manager_.SetUp());
   }
diff --git a/chrome/browser/signin/account_reconcilor_factory.cc b/chrome/browser/signin/account_reconcilor_factory.cc
index 51d6e4b..fd1ecb2 100644
--- a/chrome/browser/signin/account_reconcilor_factory.cc
+++ b/chrome/browser/signin/account_reconcilor_factory.cc
@@ -27,8 +27,8 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/account_manager/account_manager_util.h"
 #include "chrome/browser/lifetime/application_lifetime.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/user_manager/user_manager.h"
@@ -144,7 +144,7 @@
   std::unique_ptr<AccountReconcilor> reconcilor =
       std::make_unique<AccountReconcilor>(
           identity_manager, signin_client,
-          ::GetAccountManagerFacade(profile->GetPath().value()),
+          ash::GetAccountManagerFacade(profile->GetPath().value()),
           CreateAccountReconcilorDelegate(profile));
 #else
   std::unique_ptr<AccountReconcilor> reconcilor =
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc
index 1a6c95c5..ae43534 100644
--- a/chrome/browser/signin/chrome_signin_helper.cc
+++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -52,7 +52,7 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/ui/browser_window.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "components/supervised_user/core/browser/supervised_user_service.h"
 
 #if BUILDFLAG(ENABLE_EXTENSIONS)
@@ -321,14 +321,14 @@
 
   // 3. Displaying an account addition window.
   if (service_type == GAIA_SERVICE_TYPE_ADDSESSION) {
-    ::GetAccountManagerFacade(profile->GetPath().value())
+    ash::GetAccountManagerFacade(profile->GetPath().value())
         ->ShowAddAccountDialog(account_manager::AccountManagerFacade::
                                    AccountAdditionSource::kOgbAddAccount);
     return;
   }
 
   // 4. Displaying the Account Manager for managing accounts.
-  ::GetAccountManagerFacade(profile->GetPath().value())
+  ash::GetAccountManagerFacade(profile->GetPath().value())
       ->ShowManageAccountsSettings();
   return;
 
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc
index b66a7695..5606e56e 100644
--- a/chrome/browser/signin/identity_manager_factory.cc
+++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -40,7 +40,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/browser_process_platform_part.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #endif
 
 #if BUILDFLAG(IS_WIN)
@@ -154,7 +154,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   if (ash::ProfileHelper::IsUserProfile(profile)) {
     params.account_manager_facade =
-        GetAccountManagerFacade(profile->GetPath().value());
+        ash::GetAccountManagerFacade(profile->GetPath().value());
     params.is_regular_profile = true;
   }
 #endif
diff --git a/chrome/browser/signin/identity_test_environment_profile_adaptor.cc b/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
index 9b2b639..77d9caab 100644
--- a/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
+++ b/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
@@ -14,8 +14,8 @@
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/browser_process_platform_part.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #endif
 
 // static
@@ -83,7 +83,7 @@
       ChromeSigninClientFactory::GetForProfile(profile), profile->GetPrefs(),
       profile->GetPath(),
       g_browser_process->platform_part()->GetAccountManagerFactory(),
-      GetAccountManagerFacade(profile->GetPath().value()));
+      ash::GetAccountManagerFacade(profile->GetPath().value()));
 #else
   return signin::IdentityTestEnvironment::BuildIdentityManagerForTests(
       ChromeSigninClientFactory::GetForProfile(profile), profile->GetPrefs(),
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc
index 42057a2..1bfa37f 100644
--- a/chrome/browser/signin/signin_ui_util.cc
+++ b/chrome/browser/signin/signin_ui_util.cc
@@ -55,8 +55,8 @@
 #if BUILDFLAG(IS_CHROMEOS)
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/signin/signin_ui_chromeos_util.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/user_manager/user.h"
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
@@ -187,7 +187,7 @@
                           const std::string& email,
                           signin_metrics::AccessPoint access_point) {
 #if BUILDFLAG(IS_CHROMEOS)
-  ::GetAccountManagerFacade(profile->GetPath().value())
+  ash::GetAccountManagerFacade(profile->GetPath().value())
       ->ShowReauthAccountDialog(
           GetAccountReauthSourceFromAccessPoint(access_point), email,
           base::DoNothing());
diff --git a/chrome/browser/signin/signin_ui_util_unittest.cc b/chrome/browser/signin/signin_ui_util_unittest.cc
index 39043997..7d979694 100644
--- a/chrome/browser/signin/signin_ui_util_unittest.cc
+++ b/chrome/browser/signin/signin_ui_util_unittest.cc
@@ -869,8 +869,7 @@
   content::BrowserTaskEnvironment task_environment(
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   ScopedTestingLocalState local_state(TestingBrowserProcess::GetGlobal());
-  TestingProfileManager profile_manager(TestingBrowserProcess::GetGlobal(),
-                                        &local_state);
+  TestingProfileManager profile_manager(TestingBrowserProcess::GetGlobal());
   ASSERT_TRUE(profile_manager.SetUp());
   std::string name("testing_profile");
   TestingProfile* profile = profile_manager.CreateTestingProfile(
diff --git a/chrome/browser/ui/ash/clipboard/clipboard_image_model_factory_impl.cc b/chrome/browser/ui/ash/clipboard/clipboard_image_model_factory_impl.cc
index 7dc852d..700a11db 100644
--- a/chrome/browser/ui/ash/clipboard/clipboard_image_model_factory_impl.cc
+++ b/chrome/browser/ui/ash/clipboard/clipboard_image_model_factory_impl.cc
@@ -6,41 +6,10 @@
 
 #include <algorithm>
 
-#include "chrome/browser/profiles/profile.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "components/user_manager/user.h"
 #include "components/user_manager/user_manager.h"
 
-namespace {
-
-// Helpers ---------------------------------------------------------------------
-
-// Analogous to `ProfileManager::GetPrimaryUserProfile()` except this method
-// returns `nullptr` in the case where the primary user profile is not ready.
-Profile* GetPrimaryUserProfile() {
-  const auto* const user_manager = user_manager::UserManager::Get();
-  if (!user_manager) {
-    return nullptr;
-  }
-
-  const auto* const user = user_manager->GetPrimaryUser();
-  if (!user) {
-    return nullptr;
-  }
-
-  auto* const helper = ash::BrowserContextHelper::Get();
-  if (!helper) {
-    return nullptr;
-  }
-
-  auto* const browser_context = helper->GetBrowserContextByUser(user);
-  return browser_context ? Profile::FromBrowserContext(browser_context)
-                         : nullptr;
-}
-
-}  // namespace
-
-// ClipboardImageModelFactoryImpl ----------------------------------------------
-
 ClipboardImageModelFactoryImpl::ClipboardImageModelFactoryImpl()
     : idle_timer_(FROM_HERE,
                   base::Minutes(2),
@@ -107,7 +76,7 @@
 }
 
 void ClipboardImageModelFactoryImpl::OnShutdown() {
-  // Reset |request_| to drop its reference to Profile, specifically the
+  // Reset |request_| to drop its reference to BrowserContext, specifically the
   // RenderProcessHost of its WebContents.
   request_.reset();
 }
@@ -123,10 +92,10 @@
   }
 
   if (!request_) {
-    // Use the primary profile instead of the active profile to create the
+    // Use the primary user instead of the active user to create the
     // `content::WebContents` that renders html.
     request_ = std::make_unique<ClipboardImageModelRequest>(
-        GetPrimaryUserProfile(),
+        user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId(),
         base::BindRepeating(&ClipboardImageModelFactoryImpl::StartNextRequest,
                             weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.cc b/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.cc
index 168144f..c3ea320 100644
--- a/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.cc
+++ b/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.cc
@@ -12,7 +12,8 @@
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/sequenced_task_runner.h"
-#include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
+#include "components/account_id/account_id.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
@@ -41,6 +42,15 @@
 
 ClipboardImageModelRequest::TestParams* g_test_params = nullptr;
 
+content::BrowserContext* GetBrowserContextByAccountId(
+    const AccountId& account_id) {
+  auto* browser_context =
+      ash::BrowserContextHelper::Get()->GetBrowserContextByAccountId(
+          account_id);
+  CHECK(browser_context);
+  return browser_context;
+}
+
 }  // namespace
 
 // ClipboardImageModelFactory::Params: -----------------------------------------
@@ -108,14 +118,12 @@
 }
 
 ClipboardImageModelRequest::ClipboardImageModelRequest(
-    Profile* profile,
+    const AccountId& account_id,
     base::RepeatingClosure on_request_finished_callback)
     : widget_(std::make_unique<views::Widget>()),
-      web_view_(new views::WebView(profile)),
+      web_view_(new views::WebView(GetBrowserContextByAccountId(account_id))),
       on_request_finished_callback_(std::move(on_request_finished_callback)),
       request_creation_time_(base::TimeTicks::Now()) {
-  CHECK(profile);
-
   views::Widget::InitParams widget_params(
       views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
diff --git a/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.h b/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.h
index e74228f1..c0afd29 100644
--- a/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.h
+++ b/chrome/browser/ui/ash/clipboard/clipboard_image_model_request.h
@@ -17,6 +17,8 @@
 #include "content/public/browser/web_contents_observer.h"
 #include "ui/base/models/image_model.h"
 
+class AccountId;
+
 namespace ash {
 class ScopedClipboardHistoryPause;
 }  // namespace ash
@@ -30,8 +32,6 @@
 class Widget;
 }  // namespace views
 
-class Profile;
-
 // Renders html in an off-screen WebView, copies the rendered surface, and
 // passes the copy through |deliver_image_model_callback_|. If the request takes
 // takes more than 5s to load, timeout is declared and the callback is not
@@ -105,7 +105,7 @@
   };
 
   ClipboardImageModelRequest(
-      Profile* profile,
+      const AccountId& account_id,
       base::RepeatingClosure on_request_finished_callback);
   ClipboardImageModelRequest(const ClipboardImageModelRequest&) = delete;
   ClipboardImageModelRequest operator=(const ClipboardImageModelRequest&) =
diff --git a/chrome/browser/ui/ash/projector/BUILD.gn b/chrome/browser/ui/ash/projector/BUILD.gn
index 6dbc0221..f63bac8 100644
--- a/chrome/browser/ui/ash/projector/BUILD.gn
+++ b/chrome/browser/ui/ash/projector/BUILD.gn
@@ -45,6 +45,7 @@
     "//chrome/browser/ui/tabs:tab_strip",
     "//chrome/browser/web_applications",
     "//chrome/services/media_gallery_util/public/cpp",
+    "//chromeos/ash/components/account_manager",
     "//chromeos/ash/components/drivefs",
     "//chromeos/ash/components/drivefs/mojom",
     "//chromeos/ash/components/login/login_state",
diff --git a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
index a7ad7be..c625ef1 100644
--- a/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
+++ b/chrome/browser/ui/ash/projector/projector_app_client_impl.cc
@@ -20,8 +20,8 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/ui/ash/projector/projector_soda_installation_controller.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/application_locale_storage/application_locale_storage.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
@@ -179,7 +179,7 @@
 }
 
 void ProjectorAppClientImpl::HandleAccountReauth(const std::string& email) {
-  ::GetAccountManagerFacade(
+  ash::GetAccountManagerFacade(
       ProfileManager::GetActiveUserProfile()->GetPath().value())
       ->ShowReauthAccountDialog(
           account_manager::AccountManagerFacade::AccountAdditionSource::
diff --git a/chrome/browser/ui/ash/session/session_controller_client_impl_unittest.cc b/chrome/browser/ui/ash/session/session_controller_client_impl_unittest.cc
index a0ca43d..f554574 100644
--- a/chrome/browser/ui/ash/session/session_controller_client_impl_unittest.cc
+++ b/chrome/browser/ui/ash/session/session_controller_client_impl_unittest.cc
@@ -80,7 +80,7 @@
     assistant_delegate_ = std::make_unique<AssistantBrowserDelegateImpl>();
 
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
 
     cros_settings_test_helper_ =
diff --git a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
index 28b87e1d59..f5c2ea3e 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
@@ -228,6 +228,16 @@
         /*constraints=*/{},
         content_settings::PartitionKey::GetDefaultForTesting());
   }
+
+  // WINDOW_MANAGEMENT is observed fairly early on, so we need to make sure it's
+  // set to a reasonable value regardless of the |types| passed in.
+  provider->SetWebsiteSetting(
+      ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
+      ContentSettingsType::WINDOW_MANAGEMENT,
+      base::Value(ContentSetting::CONTENT_SETTING_BLOCK),
+      /*constraints=*/{},
+      content_settings::PartitionKey::GetDefaultForTesting());
+
   content_settings::TestUtils::OverrideProvider(map, std::move(provider),
                                                 GetParam());
 }
diff --git a/chrome/browser/ui/webui/ash/account_manager/BUILD.gn b/chrome/browser/ui/webui/ash/account_manager/BUILD.gn
index e270d0d..a0869bb 100644
--- a/chrome/browser/ui/webui/ash/account_manager/BUILD.gn
+++ b/chrome/browser/ui/webui/ash/account_manager/BUILD.gn
@@ -34,6 +34,7 @@
     "//chrome/browser/profiles:profile",
     "//chrome/browser/ui/webui/signin/ash",
     "//chrome/common:constants",
+    "//chromeos/ash/components/account_manager",
     "//components/account_manager_core",
     "//components/prefs",
     "//net",
diff --git a/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.cc b/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.cc
index cd8e0435..5cc380a 100644
--- a/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.cc
+++ b/chrome/browser/ui/webui/ash/account_manager/account_migration_welcome_ui.cc
@@ -17,8 +17,8 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "net/base/url_util.h"
@@ -62,7 +62,7 @@
     const std::string& account_email = args[0].GetString();
 
     Profile* profile = Profile::FromWebUI(web_ui());
-    ::GetAccountManagerFacade(profile->GetPath().value())
+    GetAccountManagerFacade(profile->GetPath().value())
         ->ShowReauthAccountDialog(
             account_manager::AccountManagerFacade::AccountAdditionSource::
                 kAccountManagerMigrationWelcomeScreen,
diff --git a/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc b/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc
index 21705d9..b6203e4 100644
--- a/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc
+++ b/chrome/browser/ui/webui/ash/login/signin_userlist_unittest.cc
@@ -52,7 +52,7 @@
   void SetUp() override {
     testing::Test::SetUp();
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
 
     for (size_t i = 0; i < std::size(kUsersPublic); ++i) {
diff --git a/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler.cc b/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler.cc
index 8af73553..480dba96 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler.cc
@@ -26,9 +26,9 @@
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "chrome/browser/ui/webui/signin/ash/inline_login_dialog.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/tribool.h"
 #include "components/user_manager/user.h"
@@ -362,7 +362,7 @@
 
 void AccountManagerUIHandler::HandleAddAccount(const base::Value::List& args) {
   AllowJavascript();
-  ::GetAccountManagerFacade(profile_->GetPath().value())
+  GetAccountManagerFacade(profile_->GetPath().value())
       ->ShowAddAccountDialog(
           account_manager::AccountManagerFacade::AccountAdditionSource::
               kSettingsAddAccountButton);
@@ -375,7 +375,7 @@
   CHECK(!args.empty());
   const std::string& account_email = args[0].GetString();
 
-  ::GetAccountManagerFacade(profile_->GetPath().value())
+  GetAccountManagerFacade(profile_->GetPath().value())
       ->ShowReauthAccountDialog(
           account_manager::AccountManagerFacade::AccountAdditionSource::
               kSettingsReauthAccountButton,
diff --git a/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler_browsertest.cc b/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler_browsertest.cc
index a6f7c73..15a8a89 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler_browsertest.cc
@@ -21,11 +21,11 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_profile.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "chromeos/ash/components/browser_context_helper/annotated_account_id.h"
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
@@ -151,7 +151,7 @@
     SetUpEnvironment();
 
     auto* account_manager_facade =
-        ::GetAccountManagerFacade(profile_->GetPath().value());
+        GetAccountManagerFacade(profile_->GetPath().value());
     account_apps_availability_ =
         AccountAppsAvailabilityFactory::GetForProfile(profile());
 
@@ -401,7 +401,7 @@
     SetUpEnvironment();
 
     auto* account_manager_facade =
-        ::GetAccountManagerFacade(profile()->GetPath().value());
+        GetAccountManagerFacade(profile()->GetPath().value());
 
     account_apps_availability_ =
         AccountAppsAvailabilityFactory::GetForProfile(profile());
diff --git a/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc b/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc
index 3c1cd45..2d240b7 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/people/people_section.cc
@@ -46,11 +46,11 @@
 #include "chrome/grit/branded_strings.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/dbus/userdataauth/userdataauth_client.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/account_manager_core/pref_names.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/prefs/pref_service.h"
@@ -497,7 +497,7 @@
     account_manager_ = factory->GetAccountManager(profile->GetPath().value());
     DCHECK(account_manager_);
     account_manager_facade_ =
-        ::GetAccountManagerFacade(profile->GetPath().value());
+        GetAccountManagerFacade(profile->GetPath().value());
     DCHECK(account_manager_facade_);
     account_manager_facade_observation_.Observe(account_manager_facade_.get());
     account_apps_availability_ =
diff --git a/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc b/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
index 05ee096..8367fbb7 100644
--- a/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
+++ b/chrome/browser/ui/webui/management/management_ui_handler_unittest.cc
@@ -489,7 +489,7 @@
 #if BUILDFLAG(IS_CHROMEOS)
   void SetUp() override {
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
     fake_user_manager_.Reset(
         std::make_unique<user_manager::FakeUserManager>(local_state_.Get()));
@@ -630,7 +630,7 @@
 #else
   void SetUp() override {
     profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal(), &local_state_);
+        TestingBrowserProcess::GetGlobal());
     ASSERT_TRUE(profile_manager_->SetUp());
   }
   void TearDown() override {
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 198bd8eb..13146851 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -158,13 +158,13 @@
 #include "chrome/browser/ui/webui/ash/settings/pages/people/account_manager_ui_handler.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/grit/browser_resources.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "chromeos/ash/components/login/auth/password_visibility_utils.h"
 #include "chromeos/ash/components/phonehub/phone_hub_manager.h"
 #include "chromeos/ash/experiences/arc/arc_util.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "components/account_manager_core/chromeos/account_manager.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/user_manager/user.h"
 #include "ui/base/ui_base_features.h"
 #else  // !BUILDFLAG(IS_CHROMEOS)
@@ -656,7 +656,7 @@
         factory->GetAccountManager(profile->GetPath().value());
     DCHECK(account_manager);
     auto* account_manager_facade =
-        ::GetAccountManagerFacade(profile->GetPath().value());
+        ash::GetAccountManagerFacade(profile->GetPath().value());
     DCHECK(account_manager_facade);
 
     web_ui()->AddMessageHandler(
diff --git a/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl.cc
index a2ea154..e459194 100644
--- a/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl.cc
@@ -32,11 +32,11 @@
 #include "chrome/browser/ui/webui/signin/ash/signin_helper.h"
 #include "chrome/browser/ui/webui/signin/inline_login_handler.h"
 #include "chrome/common/pref_names.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "chromeos/ash/components/account_manager/account_manager_factory.h"
 #include "chromeos/version/version_loader.h"
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/account_manager_core/chromeos/account_manager_mojo_service.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -337,7 +337,7 @@
     return;
   }
 
-  ::GetAccountManagerFacade(Profile::FromWebUI(web_ui())->GetPath().value())
+  GetAccountManagerFacade(Profile::FromWebUI(web_ui())->GetPath().value())
       ->GetAccounts(
           base::BindOnce(&InlineLoginHandlerImpl::OnGetAccountsToCompleteLogin,
                          weak_factory_.GetWeakPtr(), params));
@@ -428,7 +428,7 @@
     const base::Value::List& args) {
   const std::string& callback_id = args[0].GetString();
   const Profile* profile = Profile::FromWebUI(web_ui());
-  ::GetAccountManagerFacade(profile->GetPath().value())
+  GetAccountManagerFacade(profile->GetPath().value())
       ->GetAccounts(base::BindOnce(&InlineLoginHandlerImpl::OnGetAccounts,
                                    weak_factory_.GetWeakPtr(), callback_id));
 }
@@ -454,7 +454,7 @@
   AllowJavascript();
   CHECK_EQ(1u, args.size());
   const std::string& callback_id = args[0].GetString();
-  ::GetAccountManagerFacade(Profile::FromWebUI(web_ui())->GetPath().value())
+  GetAccountManagerFacade(Profile::FromWebUI(web_ui())->GetPath().value())
       ->GetAccounts(base::BindOnce(
           &InlineLoginHandlerImpl::ContinueGetAccountsNotAvailableInArc,
           weak_factory_.GetWeakPtr(), callback_id));
diff --git a/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc b/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc
index 2101f84..6b2d9cf 100644
--- a/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc
+++ b/chrome/browser/ui/webui/signin/ash/inline_login_handler_impl_browsertest.cc
@@ -31,8 +31,8 @@
 #include "chrome/browser/ui/webui/ash/edu_coexistence/edu_coexistence_login_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 #include "components/account_manager_core/account_manager_facade.h"
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
 #include "components/account_manager_core/mock_account_manager_facade.h"
 #include "components/signin/public/identity_manager/identity_test_utils.h"
 #include "components/signin/public/identity_manager/primary_account_mutator.h"
@@ -366,7 +366,7 @@
   base::ScopedObservation<account_manager::AccountManagerFacade,
                           account_manager::AccountManagerFacade::Observer>
       observation{&observer};
-  observation.Observe(::GetAccountManagerFacade(profile()->GetPath().value()));
+  observation.Observe(GetAccountManagerFacade(profile()->GetPath().value()));
 
   // Call "completeLogin".
   base::Value::List args;
@@ -391,7 +391,7 @@
   base::ScopedObservation<account_manager::AccountManagerFacade,
                           account_manager::AccountManagerFacade::Observer>
       observation{&observer};
-  observation.Observe(::GetAccountManagerFacade(profile()->GetPath().value()));
+  observation.Observe(GetAccountManagerFacade(profile()->GetPath().value()));
 
   // Call "completeLogin".
   base::Value::List args;
diff --git a/chrome/browser/webauthn/chrome_web_authentication_delegate_base.cc b/chrome/browser/webauthn/chrome_web_authentication_delegate_base.cc
index 1e82a8d..f74405af 100644
--- a/chrome/browser/webauthn/chrome_web_authentication_delegate_base.cc
+++ b/chrome/browser/webauthn/chrome_web_authentication_delegate_base.cc
@@ -62,10 +62,6 @@
 bool IsAllowedByPlatformEnterprisePolicy(
     content::BrowserContext* browser_context,
     const url::Origin& caller_origin) {
-  if (!base::FeatureList::IsEnabled(
-          device::kWebAuthnRemoteDesktopAllowedOriginsPolicy)) {
-    return false;
-  }
   const Profile* profile = Profile::FromBrowserContext(browser_context);
   const PrefService* prefs = profile->GetPrefs();
   const base::Value::List& allowed_origins =
diff --git a/chrome/browser/webauthn/chrome_web_authentication_delegate_base_unittest.cc b/chrome/browser/webauthn/chrome_web_authentication_delegate_base_unittest.cc
index 5504d0a..bf84f505 100644
--- a/chrome/browser/webauthn/chrome_web_authentication_delegate_base_unittest.cc
+++ b/chrome/browser/webauthn/chrome_web_authentication_delegate_base_unittest.cc
@@ -5,14 +5,12 @@
 #include "chrome/browser/webauthn/chrome_web_authentication_delegate_base.h"
 
 #include "base/test/scoped_command_line.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/webauthn/webauthn_pref_names.h"
 #include "chrome/browser/webauthn/webauthn_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
-#include "device/fido/features.h"
 
 namespace {
 
@@ -31,8 +29,6 @@
 
   static constexpr char kExampleOrigin[] = "https://example.com";
   static constexpr char kAnotherExampleOrigin[] = "https://another.example.com";
-
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 #if !BUILDFLAG(IS_ANDROID)
@@ -116,7 +112,7 @@
        AdditionalOriginSwitch_WithAllowedOriginsPolicy) {
   // The --webauthn-remote-proxied-requests-allowed-additional-origin switch
   // allows passing an additional origin for testing. This origin will be
-  // allowed if the kWebAuthnRemoteDesktopAllowedOriginsPolicy preference is set
+  // allowed if the WebAuthenticationRemoteDesktopAllowedOrigins policy is set
   // to a non-empty list of origins.  If the policy is set, the command-line
   // origin is treated as another allowed origin in addition to those specified
   // by the policy.
@@ -125,8 +121,6 @@
   scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
       webauthn::switches::kRemoteProxiedRequestsAllowedAdditionalOrigin,
       kExampleOrigin);
-  scoped_feature_list_.InitAndEnableFeature(
-      device::kWebAuthnRemoteDesktopAllowedOriginsPolicy);
 
   // Initially, no origins should be allowed because the allowed origins pref
   // hasn't been set yet.
@@ -170,8 +164,6 @@
   scoped_command_line.GetProcessCommandLine()->AppendSwitchASCII(
       webauthn::switches::kRemoteProxiedRequestsAllowedAdditionalOrigin,
       kExampleOrigin);
-  scoped_feature_list_.InitAndEnableFeature(
-      device::kWebAuthnRemoteDesktopAllowedOriginsPolicy);
 
   PrefService* prefs =
       Profile::FromBrowserContext(GetBrowserContext())->GetPrefs();
@@ -191,8 +183,6 @@
 TEST_F(OriginMayUseRemoteDesktopClientOverrideTest,
        AllowedOriginsPolicy_InvalidURLs) {
   ChromeWebAuthenticationDelegateBase delegate;
-  scoped_feature_list_.InitAndEnableFeature(
-      device::kWebAuthnRemoteDesktopAllowedOriginsPolicy);
 
   PrefService* prefs =
       Profile::FromBrowserContext(GetBrowserContext())->GetPrefs();
@@ -225,26 +215,8 @@
 }
 
 TEST_F(OriginMayUseRemoteDesktopClientOverrideTest,
-       AllowedOriginsPolicy_FeatureDisabled) {
-  ChromeWebAuthenticationDelegateBase delegate;
-  // Feature explicitly disabled.
-  scoped_feature_list_.InitAndDisableFeature(
-      device::kWebAuthnRemoteDesktopAllowedOriginsPolicy);
-
-  PrefService* prefs =
-      Profile::FromBrowserContext(GetBrowserContext())->GetPrefs();
-  prefs->SetList(webauthn::pref_names::kRemoteDesktopAllowedOrigins,
-                 base::Value::List().Append(kExampleOrigin));
-
-  EXPECT_FALSE(delegate.OriginMayUseRemoteDesktopClientOverride(
-      browser_context(), url::Origin::Create(GURL(kExampleOrigin))));
-}
-
-TEST_F(OriginMayUseRemoteDesktopClientOverrideTest,
        AllowedOriginsPolicy_MultipleValidURLs) {
   ChromeWebAuthenticationDelegateBase delegate;
-  scoped_feature_list_.InitAndEnableFeature(
-      device::kWebAuthnRemoteDesktopAllowedOriginsPolicy);
 
   PrefService* prefs =
       Profile::FromBrowserContext(GetBrowserContext())->GetPrefs();
@@ -269,8 +241,6 @@
 TEST_F(OriginMayUseRemoteDesktopClientOverrideTest,
        AllowedOriginsPolicy_SchemePortPathMismatch) {
   ChromeWebAuthenticationDelegateBase delegate;
-  scoped_feature_list_.InitAndEnableFeature(
-      device::kWebAuthnRemoteDesktopAllowedOriginsPolicy);
   PrefService* prefs =
       Profile::FromBrowserContext(GetBrowserContext())->GetPrefs();
 
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index 491e859..45e10c0e 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1750615142-d9184a80be8928f7544a47d00dddd6651104ae64-2fb5013d8b56b7ce1abf4de9f1eb7fd7c30be5cc.profdata
+chrome-android32-main-1750658045-50e3c8677b6f20e00f8866d1d5b29fe70ecf9ba0-7018a9aa0e473ed9715ddf370076c0c7b5139b5c.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 175edf4..0bb0e0bb0e 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1750626127-aaf5d63b9ec02a7dcd95b03caa61597f8b0b83d5-66f202f434b2ca5e0d202fde9fe7a068bec361fe.profdata
+chrome-android64-main-1750659584-7ba5ed25f0bcfd87fe883085f93dc73702922c8b-c8111d5d605d669b1a6616df216dc310d88d1024.profdata
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt
index 47255ec..9d4a396 100644
--- a/chrome/build/android-desktop-x64.pgo.txt
+++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@
-chrome-android-desktop-x64-main-1750399281-0d6a4fab094e091b031248ed5e363c178368e522-8d1dfcf686439ae4b28eaf59cfed8925b167369d.profdata
+chrome-android-desktop-x64-main-1750643961-35e68d17396f6db2bcbc23dd27892cb6240ae0eb-6f8723a37996015adf9f0973cdaed7a179682c44.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 56f24f9..0134b11 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1750615142-3c539fd21f329e5f44652e1d1ef7982b10a308fd-2fb5013d8b56b7ce1abf4de9f1eb7fd7c30be5cc.profdata
+chrome-linux-main-1750658045-4ca2511459b7acd028d0ee68bf1451c8837d91f7-7018a9aa0e473ed9715ddf370076c0c7b5139b5c.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 90fe416..1575933 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1750629070-d4c7f48610c9c00fd6cc18bf1cd42ae5cb299944-21ed3aa07ddaa556707b6d6d2c9dda5cd4b0daff.profdata
+chrome-mac-arm-main-1750665581-b6aba323566cf03ed56fa8929fc1798d64cbc613-8a68f2ecaf5aae6ccbadd7abf27f71817977390d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 14a90af..0d70abd 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1750615142-4116698ee1e56c1b794ba1fe54cdd1c033dcf87d-2fb5013d8b56b7ce1abf4de9f1eb7fd7c30be5cc.profdata
+chrome-mac-main-1750658045-403688b17121d56327474cfea91854fa5376e640-7018a9aa0e473ed9715ddf370076c0c7b5139b5c.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 94910ffc..fcaa5e2 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1750615142-ab6989a2826f4da9428e21f685c9f9e4cf7e287d-2fb5013d8b56b7ce1abf4de9f1eb7fd7c30be5cc.profdata
+chrome-win-arm64-main-1750635802-275ece283fdf3cb06c9b8885c21d3eb616392b4a-a4d9d8a51958ced830089b4a6ba1b4a8b236c7a1.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index e6d7439..24e746f2 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1750582428-b6a70ce89be11feb19742cc53f99c30197d8642a-f2ba84e7a48046c9491af6a9a064864e6ff1ffe3.profdata
+chrome-win32-main-1750625805-eb3eeeb59d884f884116ed13d3ba586ae61010f0-51afbe5ef4c84fa220551fa554021b3c5c6bbfe9.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 96c2312..535ceea 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1750604285-22d0de20cb03f4822cc9db460fe7c02c0eb4ccad-8375b4e94505c046627f98db15a231be538c4de5.profdata
+chrome-win64-main-1750635802-21c841cc9744448c18d7f13635e413c8d76d30a8-a4d9d8a51958ced830089b4a6ba1b4a8b236c7a1.profdata
diff --git a/chrome/test/base/testing_profile_manager.cc b/chrome/test/base/testing_profile_manager.cc
index 6eecb40b..d565c34 100644
--- a/chrome/test/base/testing_profile_manager.cc
+++ b/chrome/test/base/testing_profile_manager.cc
@@ -62,14 +62,6 @@
   local_state_ = owned_local_state_.get();
 }
 
-TestingProfileManager::TestingProfileManager(
-    TestingBrowserProcess* process,
-    ScopedTestingLocalState* local_state)
-    : called_set_up_(false),
-      browser_process_(process),
-      local_state_(local_state),
-      profile_manager_(nullptr) {}
-
 TestingProfileManager::~TestingProfileManager() {
   ProfileDestroyer::DestroyPendingProfilesForShutdown();
 
diff --git a/chrome/test/base/testing_profile_manager.h b/chrome/test/base/testing_profile_manager.h
index 860e4412..ac00541f 100644
--- a/chrome/test/base/testing_profile_manager.h
+++ b/chrome/test/base/testing_profile_manager.h
@@ -40,8 +40,6 @@
 class TestingProfileManager : public ProfileObserver {
  public:
   explicit TestingProfileManager(TestingBrowserProcess* browser_process);
-  TestingProfileManager(TestingBrowserProcess* browser_process,
-                        ScopedTestingLocalState* local_state);
   TestingProfileManager(const TestingProfileManager&) = delete;
   TestingProfileManager& operator=(const TestingProfileManager&) = delete;
   ~TestingProfileManager() override;
diff --git a/chrome/test/v8/OWNERS b/chrome/test/v8/OWNERS
index 67907d6..088ab44 100644
--- a/chrome/test/v8/OWNERS
+++ b/chrome/test/v8/OWNERS
@@ -1 +1,3 @@
+jkummerow@chromium.org
+clemensb@chromium.org
 ahaas@chromium.org
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 6de97e5..73ff525 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16327.0.0-1069769
\ No newline at end of file
+16327.0.0-1069780
\ No newline at end of file
diff --git a/chromeos/ash/components/account_manager/BUILD.gn b/chromeos/ash/components/account_manager/BUILD.gn
index b636dc82..0ceaf5d5 100644
--- a/chromeos/ash/components/account_manager/BUILD.gn
+++ b/chromeos/ash/components/account_manager/BUILD.gn
@@ -6,13 +6,18 @@
 
 component("account_manager") {
   sources = [
+    "account_manager_facade_factory.cc",
+    "account_manager_facade_factory.h",
     "account_manager_factory.cc",
     "account_manager_factory.h",
   ]
 
   public_deps = [ "//components/account_manager_core:account_manager_core" ]
 
-  deps = [ "//base" ]
+  deps = [
+    "//base",
+    "//chromeos/crosapi/mojom",
+  ]
 
   defines = [ "IS_CHROMEOS_ASH_COMPONENTS_ACCOUNT_MANAGER_IMPL" ]
 }
diff --git a/chrome/browser/ash/account_manager/account_manager_facade_factory_ash.cc b/chromeos/ash/components/account_manager/account_manager_facade_factory.cc
similarity index 94%
rename from chrome/browser/ash/account_manager/account_manager_facade_factory_ash.cc
rename to chromeos/ash/components/account_manager/account_manager_facade_factory.cc
index a23e367..f55739e 100644
--- a/chrome/browser/ash/account_manager/account_manager_facade_factory_ash.cc
+++ b/chromeos/ash/components/account_manager/account_manager_facade_factory.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
+#include "chromeos/ash/components/account_manager/account_manager_facade_factory.h"
 
 #include <limits>
 #include <map>
@@ -15,6 +15,7 @@
 #include "components/account_manager_core/chromeos/account_manager.h"
 #include "components/account_manager_core/chromeos/account_manager_mojo_service.h"
 
+namespace ash {
 namespace {
 
 crosapi::AccountManagerMojoService* GetAccountManagerMojoService(
@@ -62,3 +63,5 @@
 
   return it->second.get();
 }
+
+}  // namespace ash
diff --git a/chromeos/ash/components/account_manager/account_manager_facade_factory.h b/chromeos/ash/components/account_manager/account_manager_facade_factory.h
new file mode 100644
index 0000000..0d9196d3
--- /dev/null
+++ b/chromeos/ash/components/account_manager/account_manager_facade_factory.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_COMPONENTS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_FACADE_FACTORY_H_
+#define CHROMEOS_ASH_COMPONENTS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_FACADE_FACTORY_H_
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/component_export.h"
+
+namespace account_manager {
+class AccountManagerFacade;
+}  // namespace account_manager
+
+namespace ash {
+
+// A factory function for getting platform specific implementations of
+// |AccountManagerFacade|.
+// Returns the |AccountManagerFacade| for the given |profile_path|.
+// Note that |AccountManagerFacade| is independent of a |Profile|, and this is
+// needed only because of Multi-Login on Chrome OS, and will be removed soon.
+COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_ACCOUNT_MANAGER)
+account_manager::AccountManagerFacade* GetAccountManagerFacade(
+    const std::string& profile_path);
+
+}  // namespace ash
+
+#endif  // CHROMEOS_ASH_COMPONENTS_ACCOUNT_MANAGER_ACCOUNT_MANAGER_FACADE_FACTORY_H_
diff --git a/chromeos/crosapi/mojom/login.mojom b/chromeos/crosapi/mojom/login.mojom
index e1d9cb1a..a8bf204 100644
--- a/chromeos/crosapi/mojom/login.mojom
+++ b/chromeos/crosapi/mojom/login.mojom
@@ -29,14 +29,9 @@
 // Next MinVersion: 5
 [Uuid="639e9f04-981f-46d1-91da-583c2958265b"]
 interface Login {
-  // Exits the current session. If |data_for_next_login_attempt| is provided,
-  // stores data which can be read by |FetchDataForNextLoginAttempt()|.
-  ExitCurrentSession@1(string? data_for_next_login_attempt) => (string? error);
-
-  // Reads the |data_for_next_login_attempt| set by |ExitCurrentSession()|.
-  // Clears the previously stored data after reading so it can only be read
-  // once.
-  FetchDataForNextLoginAttempt@2() => (string data);
+  // ExitCurrentSession@1 was removed.
+  // FetchDataForNextLoginAttempt@2 was removed.
+  // SetDataForNextLoginAttempt@9 was removed.
 
   // Deprecated. Use |LockCurrentSession()| below.
   LockManagedGuestSession@3() => (string? error);
@@ -47,10 +42,6 @@
   // Fails if there is no existing shared session.
   EndSharedSession@8() => (string? error);
 
-  // Sets data for the next login attempt. The data can be retrieved by
-  // calling |FetchDataForNextLoginAttempt()|.
-  SetDataForNextLoginAttempt@9(string data_for_next_login_attempt) => ();
-
   // Locks the current session. The session has to be either a user session or a
   // Managed Guest Session launched by |LaunchManagedGuestSession()| with a
   // password.
diff --git a/clank b/clank
index 9e7cc1f..37813b0f 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 9e7cc1ffbb07d1afe8f3528f10d621b54993c515
+Subproject commit 37813b0fe2b96138bf8f715b42ac30fba422b9d2
diff --git a/components/account_manager_core/BUILD.gn b/components/account_manager_core/BUILD.gn
index 56105f5..dd3a468 100644
--- a/components/account_manager_core/BUILD.gn
+++ b/components/account_manager_core/BUILD.gn
@@ -24,7 +24,6 @@
     "chromeos/access_token_fetcher.h",
     "chromeos/account_manager.cc",
     "chromeos/account_manager.h",
-    "chromeos/account_manager_facade_factory.h",
     "chromeos/account_manager_mojo_service.cc",
     "chromeos/account_manager_mojo_service.h",
     "chromeos/account_manager_ui.cc",
diff --git a/components/account_manager_core/chromeos/account_manager_facade_factory.h b/components/account_manager_core/chromeos/account_manager_facade_factory.h
deleted file mode 100644
index 05d8dfb..0000000
--- a/components/account_manager_core/chromeos/account_manager_facade_factory.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2020 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_ACCOUNT_MANAGER_CORE_CHROMEOS_ACCOUNT_MANAGER_FACADE_FACTORY_H_
-#define COMPONENTS_ACCOUNT_MANAGER_CORE_CHROMEOS_ACCOUNT_MANAGER_FACADE_FACTORY_H_
-
-#include <memory>
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/component_export.h"
-
-namespace account_manager {
-class AccountManagerFacade;
-}  // namespace account_manager
-
-// A factory function for getting platform specific implementations of
-// |AccountManagerFacade|.
-// Returns the |AccountManagerFacade| for the given |profile_path|.
-// Note that |AccountManagerFacade| is independent of a |Profile|, and this is
-// needed only because of Multi-Login on Chrome OS, and will be removed soon.
-account_manager::AccountManagerFacade* COMPONENT_EXPORT(ACCOUNT_MANAGER_CORE)
-    GetAccountManagerFacade(const std::string& profile_path);
-
-#endif  // COMPONENTS_ACCOUNT_MANAGER_CORE_CHROMEOS_ACCOUNT_MANAGER_FACADE_FACTORY_H_
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc b/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc
index 5dbeb82..65fe1dc0 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_type.cc
@@ -6,14 +6,22 @@
 
 #include <optional>
 
+#include "base/feature_list.h"
 #include "base/types/cxx23_to_underlying.h"
 #include "components/autofill/core/browser/data_model/addresses/contact_info.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/common/autofill_features.h"
 #include "components/strings/grit/components_strings.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace autofill {
 
+FieldType AttributeType::field_type() const {
+  return base::FeatureList::IsEnabled(features::kAutofillAiNoTagTypes)
+             ? field_type_without_tag_types()
+             : field_type_with_tag_types();
+}
+
 FieldTypeSet AttributeType::storable_field_types(
     base::PassKey<EntityTable> pass_key) const {
   if (data_type() == DataType::kName) {
diff --git a/components/autofill/core/browser/data_model/autofill_ai/entity_type.h b/components/autofill/core/browser/data_model/autofill_ai/entity_type.h
index 1b5f8643..6691c5f 100644
--- a/components/autofill/core/browser/data_model/autofill_ai/entity_type.h
+++ b/components/autofill/core/browser/data_model/autofill_ai/entity_type.h
@@ -86,7 +86,9 @@
   //   For name types, `field_subtypes()` includes `NAME_FIRST` etc.
   // - `storable_field_types()` are the ones that may be physically stored in
   //   the database.
-  constexpr FieldType field_type() const;
+  FieldType field_type() const;
+  constexpr FieldType field_type_with_tag_types() const;
+  constexpr FieldType field_type_without_tag_types() const;
   constexpr FieldTypeSet field_subtypes() const;
   FieldTypeSet storable_field_types(base::PassKey<EntityTable> pass_key) const;
 
@@ -147,7 +149,7 @@
   NOTREACHED();
 }
 
-constexpr FieldType AttributeType::field_type() const {
+constexpr FieldType AttributeType::field_type_with_tag_types() const {
   switch (name_) {
     case AttributeTypeName::kPassportName:
       return PASSPORT_NAME_TAG;
@@ -189,11 +191,18 @@
   NOTREACHED();
 }
 
+constexpr FieldType AttributeType::field_type_without_tag_types() const {
+  if (data_type() == DataType::kName) {
+    return NAME_FULL;
+  }
+  return field_type_with_tag_types();
+}
+
 constexpr FieldTypeSet AttributeType::field_subtypes() const {
   if (data_type() == DataType::kName) {
     return FieldTypesOfGroup(FieldTypeGroup::kName);
   }
-  return {field_type()};
+  return {field_type_without_tag_types()};
 }
 
 template <>
diff --git a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.cc b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.cc
index c8ef750..29d3e6b 100644
--- a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.cc
+++ b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.cc
@@ -24,9 +24,13 @@
 
 namespace {
 
-using EntityMap =
-    base::flat_map<EntityType, std::vector<AutofillFieldWithAttributeType>>;
-using SectionMap = base::flat_map<Section, EntityMap>;
+// The furthest distance between two fields so that one field's AttributeType
+// may lead to a dynamic AttributeType assignment of the other.
+constexpr int kMaxPropagationDistance = 5;
+
+bool IsRelevant(const AutofillField& field) {
+  return field.is_focusable() || field.IsSelectElement();
+}
 
 // The set of all FieldTypes that have **more** than one associated
 // AttributeType.
@@ -56,18 +60,37 @@
 static_assert(!kNonInjectiveFieldTypes.contains_any(
     {DRIVERS_LICENSE_EXPIRATION_DATE, PASSPORT_NUMBER, VEHICLE_MODEL}));
 
+// If kAutofillAiNoTagTypes is disabled:
+// AttributeType::field_type() must be injective: distinct AttributeTypes must
+// be mapped to distinct FieldTypes.
+static_assert(
+    std::ranges::all_of(DenseSet<AttributeType>::all(), [](AttributeType a) {
+      return std::ranges::all_of(
+          DenseSet<AttributeType>::all(), [&a](AttributeType b) {
+            return a == b || a.field_type_with_tag_types() !=
+                                 b.field_type_with_tag_types();
+          });
+    }));
+
+// If kAutofillAiNoTagTypes is enabled:
 // AttributeType::field_type() must be mostly injective: distinct AttributeTypes
 // other than `kNonInjectiveFieldTypes` must be mapped to distinct FieldTypes.
 static_assert(
     std::ranges::all_of(DenseSet<AttributeType>::all(), [](AttributeType a) {
       return std::ranges::all_of(
           DenseSet<AttributeType>::all(), [&a](AttributeType b) {
-            return a == b || a.field_type() != b.field_type() ||
-                   kNonInjectiveFieldTypes.contains(a.field_type()) ||
-                   kNonInjectiveFieldTypes.contains(b.field_type());
+            return a == b ||
+                   a.field_type_without_tag_types() !=
+                       b.field_type_without_tag_types() ||
+                   kNonInjectiveFieldTypes.contains(
+                       a.field_type_without_tag_types()) ||
+                   kNonInjectiveFieldTypes.contains(
+                       b.field_type_without_tag_types());
           });
     }));
 
+// A field's static AttributeType is the unique AttributeType whose
+// AttributeType::field_type() is the field's FieldType.
 std::optional<AttributeType> GetStaticAttributeType(
     const AutofillField& field) {
   std::optional<FieldType> ft = field.GetAutofillAiServerTypePredictions();
@@ -79,7 +102,7 @@
     static constexpr auto kTable = []() {
       std::array<std::optional<AttributeType>, MAX_VALID_FIELD_TYPE> arr{};
       for (AttributeType at : DenseSet<AttributeType>::all()) {
-        arr[at.field_type()] = at;
+        arr[at.field_type_with_tag_types()] = at;
       }
       return arr;
     }();
@@ -91,8 +114,9 @@
   static constexpr auto kTable = []() {
     std::array<std::optional<AttributeType>, MAX_VALID_FIELD_TYPE> arr{};
     for (AttributeType at : DenseSet<AttributeType>::all()) {
-      if (!kNonInjectiveFieldTypes.contains(at.field_type())) {
-        arr[at.field_type()] = at;
+      if (!kNonInjectiveFieldTypes.contains(
+              at.field_type_without_tag_types())) {
+        arr[at.field_type_without_tag_types()] = at;
       }
     }
     return arr;
@@ -100,69 +124,110 @@
   return 0 <= *ft && *ft < kTable.size() ? kTable[*ft] : std::nullopt;
 }
 
-// The `i`th element of the returned vector contains the type of `fields[i]`.
-std::vector<std::optional<AttributeType>> GetStaticAttributeTypes(
+// A field is assignable a dynamic AttributeType if there are more than one
+// AttributeTypes whose AttributeType::field_type() is the field's FieldType.
+bool IsAssignableDynamicAttributeType(FieldType ft) {
+  return kNonInjectiveFieldTypes.contains(ft);
+}
+
+std::optional<AttributeType> GetAttributeType(EntityType entity,
+                                              FieldType field_type) {
+  for (AttributeType at : entity.attributes()) {
+    if (at.field_subtypes().contains(field_type)) {
+      return at;
+    }
+  }
+  return std::nullopt;
+}
+
+// Adds to `attributes_by_field[i]` the static types of `fields[i]`.
+void AddStaticAttributeTypes(
     base::span<const std::unique_ptr<AutofillField>> fields,
-    base::optional_ref<const Section> section_of_interest,
-    base::optional_ref<const EntityType> entity_of_interest) {
-  std::vector<std::optional<AttributeType>> field_to_type;
-  field_to_type.resize(fields.size());
-  for (size_t i = 0; i < fields.size(); ++i) {
-    if (section_of_interest && *section_of_interest != fields[i]->section()) {
+    base::span<DenseSet<AttributeType>> attributes_by_field) {
+  DCHECK_EQ(fields.size(), attributes_by_field.size());
+  for (auto [field, attributes] : base::zip(fields, attributes_by_field)) {
+    if (!IsRelevant(*field)) {
       continue;
     }
-    std::optional<AttributeType> at = GetStaticAttributeType(*fields[i]);
+    std::optional<AttributeType> at = GetStaticAttributeType(*field);
     if (!at) {
       continue;
     }
-    if (entity_of_interest && *entity_of_interest != at->entity_type()) {
-      continue;
-    }
-    field_to_type[i] = at;
+    attributes.insert(*at);
   }
-  return field_to_type;
 }
 
-// The `i`th element of the returned vector contains the type of `fields[i]`.
-std::vector<std::vector<AttributeType>> GetDynamicAttributeTypes(
+// Adds to `attributes_by_field[i]` the dynamic types of `fields[i]`.
+void AddDynamicAttributeTypes(
     base::span<const std::unique_ptr<AutofillField>> fields,
-    base::span<const std::optional<AttributeType>> stc_types,
-    base::optional_ref<const Section> section_of_interest,
-    base::optional_ref<const EntityType> entity_of_interest) {
-  std::vector<std::vector<AttributeType>> dyn_types;
-  dyn_types.resize(fields.size());
-
-  if (!base::FeatureList::IsEnabled(features::kAutofillAiNoTagTypes)) {
-    return dyn_types;
+    base::span<DenseSet<AttributeType>> attributes_by_field) {
+  DCHECK_EQ(fields.size(), attributes_by_field.size());
+  if (!base::FeatureList::IsEnabled(features::kAutofillAiNoTagTypes) ||
+      std::ranges::all_of(attributes_by_field,
+                          &DenseSet<AttributeType>::empty)) {
+    return;
   }
 
-  // TODO(crbug.com/422563282): Implement.
-  return dyn_types;
+  // Propagates the applicable EntityTypes in `last_seen` to the `attributes` of
+  // `field`.
+  //
+  // This function is to be called in sequence for a range of AutofillFields.
+  // `offset` counts how many relevant AutofillFields were encountered so far.
+  // `last_seen` maps the EntityTypes and Sections to the maximum offset where
+  // they were seen so far.
+  auto loop_body = [](std::map<std::pair<Section, EntityType>, int>& last_seen,
+                      int& offset, const AutofillField& field,
+                      DenseSet<AttributeType>& attributes) {
+    if (!IsRelevant(field)) {
+      return;
+    }
+    ++offset;
+    const FieldType field_type = field.Type().GetStorableType();
+    if (IsAssignableDynamicAttributeType(field_type)) {
+      for (const auto& [p, entity_offset] : last_seen) {
+        const auto& [entity_section, entity] = p;
+        if (std::abs(entity_offset - offset) > kMaxPropagationDistance ||
+            entity_section != field.section()) {
+          continue;
+        }
+        if (const std::optional<AttributeType> attribute =
+                GetAttributeType(entity, field_type)) {
+          attributes.insert(*attribute);
+        }
+      }
+    }
+    for (AttributeType attribute : attributes) {
+      last_seen[{field.section(), attribute.entity_type()}] = offset;
+    }
+  };
+
+  // Propagate types forward and backward.
+  const int num_fields = static_cast<int>(fields.size());
+  {
+    std::map<std::pair<Section, EntityType>, int> last_seen;
+    int offset = 0;
+    for (int i = 0; i < num_fields; ++i) {
+      loop_body(last_seen, offset, *fields[i], attributes_by_field[i]);
+    }
+  }
+  {
+    std::map<std::pair<Section, EntityType>, int> last_seen;
+    int offset = 0;
+    for (int i = num_fields - 1; i >= 0; --i) {
+      loop_body(last_seen, offset, *fields[i], attributes_by_field[i]);
+    }
+  }
 }
 
-// Merges the static and dynamic AttributeTypes and calls `cb(field, type)` for
-// every possible `type` of `field`.
-template <typename Callback>
-void GetAttributeTypes(base::span<const std::unique_ptr<AutofillField>> fields,
-                       base::optional_ref<const Section> section_of_interest,
-                       base::optional_ref<const EntityType> entity_of_interest,
-                       Callback cb) {
-  std::vector<std::optional<AttributeType>> stc_types_by_field =
-      GetStaticAttributeTypes(fields, section_of_interest, entity_of_interest);
-  std::vector<std::vector<AttributeType>> dyn_types_by_field =
-      GetDynamicAttributeTypes(fields, stc_types_by_field, section_of_interest,
-                               entity_of_interest);
-  for (auto [field, stc_type, dyn_types] :
-       base::zip(fields, stc_types_by_field, dyn_types_by_field)) {
-    if (stc_type) {
-      DCHECK(dyn_types.empty());
-      std::invoke(cb, *field, *stc_type);
-    }
-    for (AttributeType dyn_type : dyn_types) {
-      DCHECK(!stc_type);
-      std::invoke(cb, *field, dyn_type);
-    }
-  }
+// Returns the static and dynamic AttributeTypes.
+// The `i`th value in the returned vector is the dynamic type of `fields[i]`.
+std::vector<DenseSet<AttributeType>> GetAttributeTypes(
+    base::span<const std::unique_ptr<AutofillField>> fields) {
+  std::vector<DenseSet<AttributeType>> attributes_by_field;
+  attributes_by_field.resize(fields.size());
+  AddStaticAttributeTypes(fields, attributes_by_field);
+  AddDynamicAttributeTypes(fields, attributes_by_field);
+  return attributes_by_field;
 }
 
 }  // namespace
@@ -171,36 +236,54 @@
     base::span<const std::unique_ptr<AutofillField>> fields LIFETIME_BOUND,
     const Section& section_of_interest,
     EntityType entity_of_interest) {
-  std::vector<AutofillFieldWithAttributeType> c;
-  GetAttributeTypes(fields, section_of_interest, entity_of_interest,
-                    [&](const AutofillField& field, AttributeType type) {
-                      c.emplace_back(field, type);
-                    });
-  return c;
+  const std::vector<DenseSet<AttributeType>> attributes_by_field =
+      GetAttributeTypes(fields);
+  std::vector<AutofillFieldWithAttributeType> r;
+  for (auto [field, attributes] : base::zip(fields, attributes_by_field)) {
+    if (!section_of_interest || field->section() == section_of_interest) {
+      for (AttributeType attribute : attributes) {
+        if (entity_of_interest == attribute.entity_type()) {
+          r.emplace_back(*field, attribute);
+        }
+      }
+    }
+  }
+  return r;
 }
 
+using EntityMap =
+    base::flat_map<EntityType, std::vector<AutofillFieldWithAttributeType>>;
+
 EntityMap DetermineAttributeTypes(
     base::span<const std::unique_ptr<AutofillField>> fields LIFETIME_BOUND,
     const Section& section_of_interest) {
-  EntityMap c;
-  GetAttributeTypes(fields, section_of_interest,
-                    /*entity_of_interest=*/std::nullopt,
-                    [&](const AutofillField& field, AttributeType type) {
-                      c[type.entity_type()].emplace_back(field, type);
-                    });
-  return c;
+  const std::vector<DenseSet<AttributeType>> attributes_by_field =
+      GetAttributeTypes(fields);
+  EntityMap r;
+  for (auto [field, attributes] : base::zip(fields, attributes_by_field)) {
+    if (!section_of_interest || field->section() == section_of_interest) {
+      for (AttributeType attribute : attributes) {
+        r[attribute.entity_type()].emplace_back(*field, attribute);
+      }
+    }
+  }
+  return r;
 }
 
+using SectionMap = base::flat_map<Section, EntityMap>;
+
 SectionMap DetermineAttributeTypes(
     base::span<const std::unique_ptr<AutofillField>> fields LIFETIME_BOUND) {
-  SectionMap c;
-  GetAttributeTypes(fields, /*section_of_interest=*/std::nullopt,
-                    /*entity_of_interest=*/std::nullopt,
-                    [&](const AutofillField& field, AttributeType type) {
-                      c[field.section()][type.entity_type()].emplace_back(field,
-                                                                          type);
-                    });
-  return c;
+  const std::vector<DenseSet<AttributeType>> attributes_by_field =
+      GetAttributeTypes(fields);
+  SectionMap r;
+  for (auto [field, attributes] : base::zip(fields, attributes_by_field)) {
+    for (AttributeType attribute : attributes) {
+      r[field->section()][attribute.entity_type()].emplace_back(*field,
+                                                                attribute);
+    }
+  }
+  return r;
 }
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.h b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.h
index e918af07..a157809d 100644
--- a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.h
+++ b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types.h
@@ -34,13 +34,20 @@
 // (AutofillField::GetAutofillAiServerTypePredictions()).
 // Every field has at most one static AttributeType.
 //
-// Dynamic types are determined by looking at the surrounding fields.
-// (AutofillField::GetAutofillAiServerTypePredictions()).
-// Every field has at most one static AttributeType.
+// Dynamic types are determined by propagating types to neighboring fields as
+// follows: a target field is assigned an AttributeType if
+// - the source field has been assigned an AttributeType that belongs to the
+//   same EntityType, and
+// - the target field's FieldType is one of the target field's AttributeType's
+//   subtypes (AttributeType::field_subtype()).
+// We only propagate between pairs of fields that are in the same section and
+// whose distance is at most 5.
 //
 // Dynamic types are only determined if `features::kAutofillAiNoTagTypes` is
 // enabled.
 //
+// Invisible non-<select> fields are ignored; they're not assigned any type.
+//
 // The overloads are just specializations of one another for performance
 // reasons. The following expressions are equivalent:
 // - `DetermineAttributeTypes(fields, section, entity)`
diff --git a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc
index 4b78bec..7f55f26 100644
--- a/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc
+++ b/components/autofill/core/browser/form_processing/autofill_ai/determine_attribute_types_unittest.cc
@@ -21,6 +21,10 @@
 
 namespace autofill {
 
+void PrintTo(const AutofillFieldWithAttributeType& f, ::std::ostream* os) {
+  *os << f.field->global_id() << " -> " << f.type.name_as_string();
+}
+
 namespace {
 
 using ::testing::_;
@@ -42,14 +46,20 @@
   std::vector<std::unique_ptr<AutofillField>> fields =
       base::ToVector(field_types, [](const std::vector<FieldType>& fts) {
         auto field = std::make_unique<AutofillField>(
-            test::CreateFieldByRole(!fts.empty() ? fts.front() : EMPTY_TYPE));
+            test::GetFormFieldData(test::FieldDescription(
+                {.role = !fts.empty() ? fts.front() : EMPTY_TYPE})));
         field->set_server_predictions(
             base::ToVector(fts, [](const FieldType ft) {
               return test::CreateFieldPrediction(ft);
             }));
         return field;
       });
-  AssignSections(fields);
+
+  base::flat_map<LocalFrameToken, size_t> frame_tokens;
+  for (auto& field : fields) {
+    field->set_section(
+        Section::FromFieldIdentifier(*fields.front(), frame_tokens));
+  }
   return fields;
 }
 
@@ -64,15 +74,20 @@
       Field(&AutofillFieldWithAttributeType::type, type));
 }
 
+class DetermineAttributeTypesTest : public testing::Test {
+ private:
+  autofill::test::AutofillUnitTestEnvironment autofill_environment_;
+};
+
 // Tests that DetermineAttributeTypes() doesn't crash on empty lists.
-TEST(DetermineAttributeTypesTest, ToleratesEmptyList) {
+TEST_F(DetermineAttributeTypesTest, ToleratesEmptyList) {
   EXPECT_THAT(DetermineAttributeTypes({}), IsEmpty());
   EXPECT_THAT(DetermineAttributeTypes({}, Section()), IsEmpty());
   EXPECT_THAT(DetermineAttributeTypes({}, Section(), kPassport), IsEmpty());
 }
 
 // Tests that DetermineAttributeTypes() is empty on forms that have no entities.
-TEST(DetermineAttributeTypesTest, IsEmptyInUnrelatedForm) {
+TEST_F(DetermineAttributeTypesTest, IsEmptyInUnrelatedForm) {
   std::vector<std::unique_ptr<AutofillField>> fields = CreateFields({
       {NAME_FULL},
       {ADDRESS_HOME_LINE1},
@@ -90,8 +105,7 @@
 }
 
 // Tests that DetermineAttributeTypes() processes `*_TAG` correctly if
-TEST(DetermineAttributeTypesTest, LegacyBehavior) {
-  base::test::ScopedFeatureList features_{features::kAutofillAiNoTagTypes};
+TEST_F(DetermineAttributeTypesTest, LegacyBehavior) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndDisableFeature(features::kAutofillAiNoTagTypes);
 
@@ -114,34 +128,373 @@
       FieldAndType(fields[4], AttributeType(kDriversLicenseName)),
       FieldAndType(fields[5], AttributeType(kDriversLicenseNumber)),
       FieldAndType(fields[6], AttributeType(kDriversLicenseExpirationDate)));
+  const Section section = fields.front()->section();
 
   // DetermineAttributeTypes() overload with Section and AttributeType.
-  EXPECT_THAT(
-      DetermineAttributeTypes(fields, fields.front()->section(), kVehicle),
-      vehicle_matcher);
-  EXPECT_THAT(DetermineAttributeTypes(fields, fields.front()->section(),
-                                      kDriversLicense),
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kVehicle),
+              vehicle_matcher);
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kDriversLicense),
               drivers_license_matcher);
-  EXPECT_THAT(
-      DetermineAttributeTypes(fields, fields.front()->section(), kPassport),
-      IsEmpty());
 
-  // DetermineAttributeTypes() overload with Section, without AttributeType.
+  // DetermineAttributeTypes() overload with Section, without EntityType.
   EXPECT_THAT(
-      DetermineAttributeTypes(fields, fields.front()->section()),
+      DetermineAttributeTypes(fields, section),
       UnorderedElementsAre(Pair(kVehicle, vehicle_matcher),
                            Pair(kDriversLicense, drivers_license_matcher)));
 
   // DetermineAttributeTypes() overload without Section and AttributeType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields),
+      UnorderedElementsAre(
+          Pair(section, UnorderedElementsAre(
+                            Pair(kVehicle, vehicle_matcher),
+                            Pair(kDriversLicense, drivers_license_matcher)))));
+}
+
+// Tests that DetermineAttributeTypes() assigns static types correctly.
+TEST_F(DetermineAttributeTypesTest, AssignsStaticTypes) {
+  base::test::ScopedFeatureList feature_list{features::kAutofillAiNoTagTypes};
+
+  std::vector<std::unique_ptr<AutofillField>> fields = CreateFields({
+      {DRIVERS_LICENSE_NUMBER},
+      {VEHICLE_MAKE},
+      {VEHICLE_MODEL},
+      {DRIVERS_LICENSE_EXPIRATION_DATE},
+      {ADDRESS_HOME_ZIP},
+  });
+
+  using enum AttributeTypeName;
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[1], AttributeType(kVehicleMake)),
+                  FieldAndType(fields[2], AttributeType(kVehicleModel)));
+  auto drivers_license_matcher = ElementsAre(
+      FieldAndType(fields[0], AttributeType(kDriversLicenseNumber)),
+      FieldAndType(fields[3], AttributeType(kDriversLicenseExpirationDate)));
+  const Section section = fields.front()->section();
+
+  // DetermineAttributeTypes() overload with Section and AttributeType.
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kVehicle),
+              vehicle_matcher);
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kDriversLicense),
+              drivers_license_matcher);
+
+  // DetermineAttributeTypes() overload with Section, without EntityType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields, section),
+      UnorderedElementsAre(Pair(kVehicle, vehicle_matcher),
+                           Pair(kDriversLicense, drivers_license_matcher)));
+
+  // DetermineAttributeTypes() overload without Section and AttributeType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields),
+      UnorderedElementsAre(
+          Pair(section, UnorderedElementsAre(
+                            Pair(kVehicle, vehicle_matcher),
+                            Pair(kDriversLicense, drivers_license_matcher)))));
+}
+
+// Tests that DetermineAttributeTypes() assigns dynamic types correctly:
+// - It must look at both the forward and backward vicinity.
+// - Fields can have multiple types simultaneously.
+TEST_F(DetermineAttributeTypesTest, AssignsDynamicTypesToTheVicinity) {
+  base::test::ScopedFeatureList features_list{features::kAutofillAiNoTagTypes};
+
+  // The NAME_{FIRST,MIDDLE,LAST} fields are expected to be assigned to both the
+  // vehicle and the driver's license entities.
+  std::vector<std::unique_ptr<AutofillField>> fields = CreateFields({
+      {VEHICLE_MAKE},
+      {VEHICLE_MODEL},
+      {NAME_FIRST},
+      {NAME_MIDDLE},
+      {NAME_LAST},
+      {DRIVERS_LICENSE_NUMBER},
+      {DRIVERS_LICENSE_EXPIRATION_DATE},
+  });
+
+  using enum AttributeTypeName;
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[0], AttributeType(kVehicleMake)),
+                  FieldAndType(fields[1], AttributeType(kVehicleModel)),
+                  FieldAndType(fields[2], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[3], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[4], AttributeType(kVehicleOwner)));
+  auto drivers_license_matcher = ElementsAre(
+      FieldAndType(fields[2], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[3], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[4], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[5], AttributeType(kDriversLicenseNumber)),
+      FieldAndType(fields[6], AttributeType(kDriversLicenseExpirationDate)));
+  const Section section = fields.front()->section();
+
+  // DetermineAttributeTypes() overload with Section and AttributeType.
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kVehicle),
+              vehicle_matcher);
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kDriversLicense),
+              drivers_license_matcher);
+
+  // DetermineAttributeTypes() overload with Section, without EntityType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields, section),
+      UnorderedElementsAre(Pair(kVehicle, vehicle_matcher),
+                           Pair(kDriversLicense, drivers_license_matcher)));
+
+  // DetermineAttributeTypes() overload without Section and AttributeType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields),
+      UnorderedElementsAre(
+          Pair(section, UnorderedElementsAre(
+                            Pair(kVehicle, vehicle_matcher),
+                            Pair(kDriversLicense, drivers_license_matcher)))));
+}
+
+// Tests that DetermineAttributeTypes() propagates dynamic types forward.
+TEST_F(DetermineAttributeTypesTest, PropagatesDynamicTypesForward) {
+  base::test::ScopedFeatureList features_list{features::kAutofillAiNoTagTypes};
+  using enum AttributeTypeName;
+
+  // The last NAME_FULL field is too far away to be reached by the propagation.
+  std::vector<std::unique_ptr<AutofillField>> fields = CreateFields({
+      {VEHICLE_MAKE}, {UNKNOWN_TYPE}, {NAME_FULL},    {UNKNOWN_TYPE},
+      {UNKNOWN_TYPE}, {NAME_FULL},    {UNKNOWN_TYPE}, {UNKNOWN_TYPE},
+      {UNKNOWN_TYPE}, {NAME_FULL},    {UNKNOWN_TYPE}, {UNKNOWN_TYPE},
+      {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {NAME_FULL},    {UNKNOWN_TYPE},
+      {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {UNKNOWN_TYPE},
+      {NAME_FULL},
+  });
+
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[0], AttributeType(kVehicleMake)),
+                  FieldAndType(fields[2], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[5], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[9], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[14], AttributeType(kVehicleOwner)));
+  const Section section = fields.front()->section();
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kVehicle),
+              vehicle_matcher);
+}
+
+// Tests that DetermineAttributeTypes() propagates dynamic types backward.
+TEST_F(DetermineAttributeTypesTest, PropagatesDynamicTypesBackward) {
+  base::test::ScopedFeatureList features_list{features::kAutofillAiNoTagTypes};
+  using enum AttributeTypeName;
+
+  // The last NAME_FULL field is too far away to be reached by the propagation.
+  std::vector<std::unique_ptr<AutofillField>> fields = CreateFields({
+      {NAME_FULL},    {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {UNKNOWN_TYPE},
+      {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {NAME_FULL},    {UNKNOWN_TYPE},
+      {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {NAME_FULL},
+      {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {NAME_FULL},
+      {UNKNOWN_TYPE}, {UNKNOWN_TYPE}, {NAME_FULL},    {UNKNOWN_TYPE},
+      {VEHICLE_MAKE},
+  });
+
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[6], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[11], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[15], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[18], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[20], AttributeType(kVehicleMake)));
+  const Section section = fields.front()->section();
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kVehicle),
+              vehicle_matcher);
+}
+
+// Tests that DetermineAttributeTypes() propagates dynamic types even if there
+// are other entities between the source and target.
+TEST_F(DetermineAttributeTypesTest,
+       PropagatesDynamicTypesForwardAcrossEntities) {
+  base::test::ScopedFeatureList features_list{features::kAutofillAiNoTagTypes};
+  using enum AttributeTypeName;
+
+  std::vector<std::unique_ptr<AutofillField>> fields =
+      CreateFields({{DRIVERS_LICENSE_NUMBER}, {VEHICLE_VIN}, {NAME_FULL}});
+
+  auto drivers_license_matcher =
+      ElementsAre(FieldAndType(fields[0], AttributeType(kDriversLicenseNumber)),
+                  FieldAndType(fields[2], AttributeType(kDriversLicenseName)));
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[1], AttributeType(kVehicleVin)),
+                  FieldAndType(fields[2], AttributeType(kVehicleOwner)));
+  const Section section = fields.front()->section();
+
+  // DetermineAttributeTypes() overload with Section and AttributeType.
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kVehicle),
+              vehicle_matcher);
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kDriversLicense),
+              drivers_license_matcher);
+
+  // DetermineAttributeTypes() overload with Section, without EntityType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields, section),
+      UnorderedElementsAre(Pair(kVehicle, vehicle_matcher),
+                           Pair(kDriversLicense, drivers_license_matcher)));
+
+  // DetermineAttributeTypes() overload without Section and AttributeType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields),
+      UnorderedElementsAre(
+          Pair(section, UnorderedElementsAre(
+                            Pair(kVehicle, vehicle_matcher),
+                            Pair(kDriversLicense, drivers_license_matcher)))));
+}
+
+// Tests that DetermineAttributeTypes() propagates dynamic types even if there
+// are other entities between the source and target.
+TEST_F(DetermineAttributeTypesTest,
+       PropagatesDynamicTypesBackwardAcrossEntities) {
+  base::test::ScopedFeatureList features_list{features::kAutofillAiNoTagTypes};
+  using enum AttributeTypeName;
+
+  std::vector<std::unique_ptr<AutofillField>> fields =
+      CreateFields({{NAME_FULL}, {DRIVERS_LICENSE_NUMBER}, {VEHICLE_VIN}});
+
+  auto drivers_license_matcher = ElementsAre(
+      FieldAndType(fields[0], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[1], AttributeType(kDriversLicenseNumber)));
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[0], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[2], AttributeType(kVehicleVin)));
+  const Section section = fields.front()->section();
+
+  // DetermineAttributeTypes() overload with Section and AttributeType.
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kVehicle),
+              vehicle_matcher);
+  EXPECT_THAT(DetermineAttributeTypes(fields, section, kDriversLicense),
+              drivers_license_matcher);
+
+  // DetermineAttributeTypes() overload with Section, without EntityType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields, section),
+      UnorderedElementsAre(Pair(kVehicle, vehicle_matcher),
+                           Pair(kDriversLicense, drivers_license_matcher)));
+
+  // DetermineAttributeTypes() overload without Section and AttributeType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields),
+      UnorderedElementsAre(
+          Pair(section, UnorderedElementsAre(
+                            Pair(kVehicle, vehicle_matcher),
+                            Pair(kDriversLicense, drivers_license_matcher)))));
+}
+
+// Tests that DetermineAttributeTypes() isolates fields from different sections
+// from another.
+TEST_F(DetermineAttributeTypesTest, DistinguishesBetweenSections) {
+  base::test::ScopedFeatureList features_list{features::kAutofillAiNoTagTypes};
+
+  std::vector<std::unique_ptr<AutofillField>> fields = CreateFields({
+      {NAME_FIRST},
+      {NAME_MIDDLE},
+      {NAME_LAST},
+      {VEHICLE_MAKE},
+      {VEHICLE_MODEL},
+      {NAME_FIRST},
+      {NAME_MIDDLE},
+      {NAME_LAST},
+      {DRIVERS_LICENSE_NUMBER},
+      {DRIVERS_LICENSE_EXPIRATION_DATE},
+  });
+  AssignSections(fields);
+
+  using enum AttributeTypeName;
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[0], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[1], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[2], AttributeType(kVehicleOwner)),
+                  FieldAndType(fields[3], AttributeType(kVehicleMake)),
+                  FieldAndType(fields[4], AttributeType(kVehicleModel)));
+  auto drivers_license_matcher = ElementsAre(
+      FieldAndType(fields[5], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[6], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[7], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[8], AttributeType(kDriversLicenseNumber)),
+      FieldAndType(fields[9], AttributeType(kDriversLicenseExpirationDate)));
+  const Section vehicle_section = fields[0]->section();
+  const Section drivers_license_section = fields[5]->section();
+  ASSERT_NE(vehicle_section, drivers_license_section);
+
+  // DetermineAttributeTypes() overload with Section and AttributeType.
+  EXPECT_THAT(DetermineAttributeTypes(fields, vehicle_section, kVehicle),
+              vehicle_matcher);
+  EXPECT_THAT(DetermineAttributeTypes(fields, vehicle_section, kDriversLicense),
+              IsEmpty());
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields, drivers_license_section, kDriversLicense),
+      drivers_license_matcher);
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields, drivers_license_section, kVehicle),
+      IsEmpty());
+
+  // DetermineAttributeTypes() overload with Section, without EntityType.
+  EXPECT_THAT(DetermineAttributeTypes(fields, vehicle_section),
+              UnorderedElementsAre(Pair(kVehicle, vehicle_matcher)));
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields, drivers_license_section),
+      UnorderedElementsAre(Pair(kDriversLicense, drivers_license_matcher)));
+
+  // DetermineAttributeTypes() overload without Section and AttributeType.
+  EXPECT_THAT(
+      DetermineAttributeTypes(fields),
+      UnorderedElementsAre(
+          Pair(vehicle_section, ElementsAre(Pair(kVehicle, vehicle_matcher))),
+          Pair(drivers_license_section,
+               ElementsAre(Pair(kDriversLicense, drivers_license_matcher)))));
+}
+
+// Tests for that the overloads behave equivalently:
+// - `DetermineAttributeTypes(fields, section, entity)`
+// - `DetermineAttributeTypes(fields, section)[entity]`
+// - `DetermineAttributeTypes(fields)[section][entity]`
+TEST_F(DetermineAttributeTypesTest, OverloadEquivalence) {
+  base::test::ScopedFeatureList features_list{features::kAutofillAiNoTagTypes};
+
+  // We create four fields such that
+  // - `field[0]` propagates to `field[2]` and
+  // - `field[3]` propagates to `field[1]`.
+  // In particular, one propagation should not block the other.
+  std::vector<std::unique_ptr<AutofillField>> fields = CreateFields({
+      {VEHICLE_MAKE},
+      {NAME_FULL},
+      {NAME_FULL},
+      {DRIVERS_LICENSE_NUMBER},
+  });
+  base::flat_map<LocalFrameToken, size_t> frame_tokens;
+  Section section1 = Section::FromFieldIdentifier(*fields[0], frame_tokens);
+  Section section2 = Section::FromFieldIdentifier(*fields[2], frame_tokens);
+  ASSERT_NE(section1, section2);
+  fields[0]->set_section(section1);
+  fields[1]->set_section(section2);
+  fields[2]->set_section(section1);
+  fields[3]->set_section(section2);
+
+  using enum AttributeTypeName;
+  auto vehicle_matcher =
+      ElementsAre(FieldAndType(fields[0], AttributeType(kVehicleMake)),
+                  FieldAndType(fields[2], AttributeType(kVehicleOwner)));
+  auto drivers_license_matcher = ElementsAre(
+      FieldAndType(fields[1], AttributeType(kDriversLicenseName)),
+      FieldAndType(fields[3], AttributeType(kDriversLicenseNumber)));
+
+  EXPECT_THAT(DetermineAttributeTypes(fields, section1, kVehicle),
+              vehicle_matcher);
+  EXPECT_THAT(DetermineAttributeTypes(fields, section2, kVehicle), IsEmpty());
+  EXPECT_THAT(DetermineAttributeTypes(fields, section1, kDriversLicense),
+              IsEmpty());
+  EXPECT_THAT(DetermineAttributeTypes(fields, section2, kDriversLicense),
+              drivers_license_matcher);
+
+  // DetermineAttributeTypes() overload with Section, without EntityType.
+  EXPECT_THAT(DetermineAttributeTypes(fields, section1),
+              ElementsAre(Pair(kVehicle, vehicle_matcher)));
+  EXPECT_THAT(DetermineAttributeTypes(fields, section2),
+              ElementsAre(Pair(kDriversLicense, drivers_license_matcher)));
+
+  // DetermineAttributeTypes() overload without Section and AttributeType.
   EXPECT_THAT(DetermineAttributeTypes(fields),
               UnorderedElementsAre(
-                  Pair(fields.front()->section(),
-                       UnorderedElementsAre(
-                           Pair(kVehicle, vehicle_matcher),
-                           Pair(kDriversLicense, drivers_license_matcher)))));
-  EXPECT_THAT(DetermineAttributeTypes({}, Section(),
-                                      EntityType(EntityTypeName::kPassport)),
-              IsEmpty());
+                  Pair(section1, ElementsAre(Pair(kVehicle, vehicle_matcher))),
+                  Pair(section2, ElementsAre(Pair(kDriversLicense,
+                                                  drivers_license_matcher)))));
 }
 
 }  // namespace
diff --git a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager.cc b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager.cc
index e8b4308..e74deef 100644
--- a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager.cc
+++ b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_manager.cc
@@ -372,8 +372,7 @@
 
   const AutofillField* autofill_field =
       form.GetFieldById(trigger_field.global_id());
-  if (!autofill_field ||
-      !autofill_field->GetAutofillAiServerTypePredictions()) {
+  if (!autofill_field) {
     return {};
   }
 
diff --git a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_suggestions_unittest.cc b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_suggestions_unittest.cc
index c93bb92d..b5311c7b 100644
--- a/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_suggestions_unittest.cc
+++ b/components/autofill/core/browser/integrators/autofill_ai/autofill_ai_suggestions_unittest.cc
@@ -4,25 +4,24 @@
 
 #include "components/autofill/core/browser/integrators/autofill_ai/autofill_ai_suggestions.h"
 
+#include <optional>
 #include <ranges>
 #include <string>
-#include <variant>
 
 #include "base/check.h"
-#include "base/containers/contains.h"
 #include "base/containers/span.h"
 #include "base/containers/to_vector.h"
-#include "base/strings/utf_string_conversions.h"
+#include "base/types/optional_ref.h"
 #include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_instance.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_type.h"
 #include "components/autofill/core/browser/data_model/autofill_ai/entity_type_names.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_structure.h"
+#include "components/autofill/core/browser/suggestions/suggestion_test_helpers.h"
 #include "components/autofill/core/browser/suggestions/suggestion_type.h"
 #include "components/autofill/core/browser/test_utils/autofill_form_test_utils.h"
 #include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
-#include "components/autofill/core/common/unique_ids.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace autofill {
@@ -31,10 +30,13 @@
 
 using FieldPrediction =
     AutofillQueryResponse::FormSuggestion::FieldSuggestion::FieldPrediction;
-using ::testing::Ge;
+using ::testing::Contains;
+using ::testing::ElementsAre;
+using ::testing::Field;
 using ::testing::IsEmpty;
+using ::testing::Matcher;
 using ::testing::Not;
-using ::testing::SizeIs;
+using ::testing::ResultOf;
 
 constexpr char kAppLocaleUS[] = "en-US";
 
@@ -51,352 +53,286 @@
   return test::GetVehicleEntityInstance(options);
 }
 
+Matcher<const Suggestion&> HasMainText(const std::u16string& text) {
+  return ResultOf(
+      "Suggestion::main_text.value",
+      [](const Suggestion& s) { return s.main_text.value; }, text);
+}
+
+Matcher<const Suggestion&> HasLabel(const std::u16string& label) {
+  return Field(
+      &Suggestion::labels,
+      ElementsAre(ElementsAre(Field(&Suggestion::Text::value, label))));
+}
+
+Matcher<const Suggestion&> HasType(SuggestionType type) {
+  return Field("Suggestion::type", &Suggestion::type, type);
+}
+
+auto SuggestionsAre(auto&&... matchers) {
+  return ElementsAre(std::forward<decltype(matchers)>(matchers)...,
+                     HasType(SuggestionType::kSeparator),
+                     HasType(SuggestionType::kManageAutofillAi));
+}
+
 class AutofillAiSuggestionsTest : public testing::Test {
+ public:
+  void SetEntities(std::vector<EntityInstance> entities) {
+    entities_ = std::move(entities);
+  }
+
+  // Sets the form to one whose `i`th field has types `multiple_field_types[i]`.
+  void SetForm(
+      const std::vector<std::vector<FieldType>>& multiple_field_types) {
+    test::FormDescription form_description;
+    for (std::vector<FieldType> field_types : multiple_field_types) {
+      FieldType type = field_types.empty() ? UNKNOWN_TYPE : field_types[0];
+      form_description.fields.emplace_back(
+          test::FieldDescription({.role = type}));
+    }
+    form_structure_.emplace(test::GetFormData(form_description));
+    CHECK_EQ(multiple_field_types.size(), form_structure_->field_count());
+    for (size_t i = 0; i < form_structure_->field_count(); i++) {
+      form_structure_->field(i)->set_server_predictions(
+          base::ToVector(multiple_field_types[i], [](FieldType type) {
+            FieldPrediction prediction;
+            prediction.set_type(type);
+            return prediction;
+          }));
+    }
+  }
+
+  // Sets the form to one whose `i`th field has type `field_types[i]`.
+  void SetForm(const std::vector<FieldType>& field_types) {
+    SetForm(base::ToVector(field_types, [](FieldType type) {
+      return std::vector<FieldType>({type});
+    }));
+  }
+
+  AutofillField& field(size_t i) { return *form_structure_->fields()[i]; }
+
+  std::optional<std::u16string> GetFillValueForField(
+      const Suggestion::AutofillAiPayload& payload,
+      const AutofillField& field) {
+    auto entity_it =
+        std::ranges::find(entities_, payload.guid, &EntityInstance::guid);
+    if (entity_it == entities_.end()) {
+      return std::nullopt;
+    }
+    auto attribute_it = std::ranges::find_if(
+        entity_it->attributes(), [&field](const AttributeInstance& attribute) {
+          return attribute.type().field_type() ==
+                 field.GetAutofillAiServerTypePredictions();
+        });
+    if (attribute_it == entity_it->attributes().end()) {
+      return std::nullopt;
+    }
+    return attribute_it->GetInfo(field.Type().GetStorableType(), kAppLocaleUS,
+                                 field.format_string());
+  }
+
+  std::vector<Suggestion> CreateFillingSuggestions(const AutofillField& field) {
+    return autofill::CreateFillingSuggestions(*form_structure_, field,
+                                              entities_, kAppLocaleUS);
+  }
+
  private:
   test::AutofillUnitTestEnvironment autofill_test_environment_;
+  std::vector<EntityInstance> entities_;
+  std::optional<FormStructure> form_structure_;
 };
 
-size_t CountFillingSuggestions(base::span<const Suggestion> suggestions) {
-  return std::ranges::count_if(suggestions, [](const Suggestion& suggestion) {
-    return suggestion.type == SuggestionType::kFillAutofillAi;
-  });
+std::u16string GetPassportName(const EntityInstance& entity) {
+  return entity.attribute(AttributeType(AttributeTypeName::kPassportName))
+      ->GetCompleteInfo(kAppLocaleUS);
 }
 
-std::u16string GetEntityInstanceValue(
-    const EntityInstance entity,
-    AttributeType attribute,
-    const std::string& app_locale = kAppLocaleUS) {
-  return entity.attribute(attribute)->GetCompleteInfo(app_locale);
+std::u16string GetPassportNumber(const EntityInstance& entity) {
+  return entity.attribute(AttributeType(AttributeTypeName::kPassportNumber))
+      ->GetCompleteInfo(kAppLocaleUS);
 }
 
-std::optional<std::u16string> GetFillValueForField(
-    base::span<const EntityInstance> entities,
-    const Suggestion::AutofillAiPayload& payload,
-    const AutofillField& field,
-    const std::string& app_locale = kAppLocaleUS) {
-  auto entity_it =
-      std::ranges::find(entities, payload.guid, &EntityInstance::guid);
-  if (entity_it == entities.end()) {
-    return std::nullopt;
-  }
-  auto attribute_it = std::ranges::find_if(
-      entity_it->attributes(), [&field](const AttributeInstance& attribute) {
-        return attribute.type().field_type() ==
-               field.GetAutofillAiServerTypePredictions();
-      });
-  if (attribute_it == entity_it->attributes().end()) {
-    return std::nullopt;
-  }
-  return attribute_it->GetInfo(field.Type().GetStorableType(), app_locale,
-                               field.format_string());
-}
-
-std::unique_ptr<FormStructure> CreateFormStructureWithMultiplePredictions(
-    const std::vector<std::vector<FieldType>>& multiple_field_types) {
-  test::FormDescription form_description;
-  for (std::vector<FieldType> field_types : multiple_field_types) {
-    FieldType type = field_types.empty() ? UNKNOWN_TYPE : field_types[0];
-    form_description.fields.emplace_back(
-        test::FieldDescription({.role = type}));
-  }
-  auto form_structure =
-      std::make_unique<FormStructure>(test::GetFormData(form_description));
-  CHECK_EQ(multiple_field_types.size(), form_structure->field_count());
-  for (size_t i = 0; i < form_structure->field_count(); i++) {
-    form_structure->field(i)->set_server_predictions(
-        base::ToVector(multiple_field_types[i], [](FieldType type) {
-          FieldPrediction prediction;
-          prediction.set_type(type);
-          return prediction;
-        }));
-  }
-  return form_structure;
-}
-
-std::unique_ptr<FormStructure> CreateFormStructure(
-    const std::vector<FieldType>& field_types) {
-  return CreateFormStructureWithMultiplePredictions(base::ToVector(
-      field_types,
-      [](FieldType type) { return std::vector<FieldType>({type}); }));
+// Tests that no suggestions are generated when the field has a non-Autofill AI
+// type.
+TEST_F(AutofillAiSuggestionsTest, NoSuggestionsOnNonAiField) {
+  SetEntities({MakePassportWithRandomGuid()});
+  SetForm({ADDRESS_HOME_ZIP, PASSPORT_NUMBER, PHONE_HOME_WHOLE_NUMBER});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)), IsEmpty());
 }
 
 TEST_F(AutofillAiSuggestionsTest, GetFillingSuggestion_PassportEntity) {
   EntityInstance passport_entity = MakePassportWithRandomGuid();
-  std::vector<EntityInstance> entities = {passport_entity};
+  SetEntities({passport_entity});
+  SetForm({PASSPORT_NAME_TAG, PASSPORT_NUMBER, PHONE_HOME_WHOLE_NUMBER});
 
-  FieldType triggering_field_type = PASSPORT_NAME_TAG;
-  AttributeType triggering_attribute_type =
-      AttributeType(AttributeTypeName::kPassportName);
-  std::unique_ptr<FormStructure> form = CreateFormStructure(
-      {triggering_field_type, PASSPORT_NUMBER, PHONE_HOME_WHOLE_NUMBER});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], entities, kAppLocaleUS);
+  std::vector<Suggestion> suggestions = CreateFillingSuggestions(field(0));
 
   // There should be only one suggestion whose main text matches the entity
-  // value for the `triggering_field_type`.
-  EXPECT_EQ(suggestions.size(), 3u);
-  EXPECT_EQ(suggestions[0].main_text.value,
-            GetEntityInstanceValue(passport_entity, triggering_attribute_type));
-  EXPECT_EQ(suggestions[1].type, SuggestionType::kSeparator);
-  EXPECT_EQ(suggestions[2].type, SuggestionType::kManageAutofillAi);
+  // value for the PASSPORT_NAME_TAG.
+  EXPECT_THAT(suggestions,
+              SuggestionsAre(HasMainText(GetPassportName(passport_entity))));
 
   const Suggestion::AutofillAiPayload* payload =
       std::get_if<Suggestion::AutofillAiPayload>(&suggestions[0].payload);
   ASSERT_TRUE(payload);
-  EXPECT_EQ(suggestions[0].icon, Suggestion::Icon::kIdCard);
+  EXPECT_THAT(suggestions[0], HasIcon(Suggestion::Icon::kIdCard));
 
-  // The triggering/first field is of AutofillAi Type.
-  EXPECT_EQ(GetFillValueForField(entities, *payload, *form->fields()[0]),
-            GetEntityInstanceValue(passport_entity, triggering_attribute_type));
-  // The second field in the form is also of AutofillAi.
-  EXPECT_EQ(
-      GetFillValueForField(entities, *payload, *form->fields()[1]),
-      GetEntityInstanceValue(
-          passport_entity, AttributeType(AttributeTypeName::kPassportNumber)));
-  // The third field is not of AutofillAi type.
-  EXPECT_EQ(GetFillValueForField(entities, *payload, *form->fields()[2]),
-            std::nullopt);
+  // The triggering/first field is of Autofill AI type.
+  EXPECT_EQ(GetFillValueForField(*payload, field(0)),
+            GetPassportName(passport_entity));
+  // The second field in the form is also of Autofill AI type.
+  EXPECT_EQ(GetFillValueForField(*payload, field(1)),
+            GetPassportNumber(passport_entity));
+  // The third field is not of Autofill AI type.
+  EXPECT_EQ(GetFillValueForField(*payload, field(2)), std::nullopt);
 }
 
 TEST_F(AutofillAiSuggestionsTest, GetFillingSuggestion_PrefixMatching) {
   EntityInstance passport_prefix_matches =
       MakePassportWithRandomGuid({.name = u"Jon Doe"});
-
   EntityInstance passport_prefix_does_not_match =
       MakePassportWithRandomGuid({.name = u"Harry Potter"});
 
-  FieldType triggering_field_type = PASSPORT_NAME_TAG;
-  AttributeType triggering_attribute_type =
-      AttributeType(AttributeTypeName::kPassportName);
-  std::unique_ptr<FormStructure> form = CreateFormStructure(
-      {triggering_field_type, PASSPORT_NUMBER, PHONE_HOME_WHOLE_NUMBER});
-
-  form->field(0)->set_value(u"J");
-
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0],
-      {passport_prefix_matches, passport_prefix_does_not_match}, kAppLocaleUS);
+  SetEntities({passport_prefix_matches, passport_prefix_does_not_match});
+  SetForm({PASSPORT_NAME_TAG, PASSPORT_NUMBER, PHONE_HOME_WHOLE_NUMBER});
+  field(0).set_value(u"J");
 
   // There should be only one suggestion whose main text matches is a prefix of
   // the value already existing in the triggering field.
   // Note that there is one separator and one footer suggestion as well.
-  EXPECT_EQ(suggestions.size(), 3u);
-  EXPECT_EQ(suggestions[0].main_text.value,
-            GetEntityInstanceValue(passport_prefix_matches,
-                                   triggering_attribute_type));
+  EXPECT_THAT(
+      CreateFillingSuggestions(field(0)),
+      SuggestionsAre(HasMainText(GetPassportName(passport_prefix_matches))));
 }
 
 // Tests that no prefix matching is performed if the attribute that would be
 // filled into the triggering field is obfuscated.
 TEST_F(AutofillAiSuggestionsTest,
        GetFillingSuggestionNoPrefixMatchingForObfuscatedAttributes) {
-  EntityInstance passport = MakePassportWithRandomGuid({.number = u"12345"});
-
-  FieldType triggering_field_type = PASSPORT_NUMBER;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_ISSUING_COUNTRY});
-
-  form->field(0)->set_value(u"12");
-
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {passport}, kAppLocaleUS);
-  EXPECT_FALSE(suggestions.empty());
+  SetEntities({MakePassportWithRandomGuid({.number = u"12345"})});
+  SetForm({PASSPORT_NUMBER, PASSPORT_ISSUING_COUNTRY});
+  field(0).set_value(u"12");
+  EXPECT_THAT(CreateFillingSuggestions(field(0)), Not(IsEmpty()));
 }
 
 TEST_F(AutofillAiSuggestionsTest,
        GetFillingSuggestion_SkipFieldsThatDoNotMatchTheTriggeringFieldSection) {
   EntityInstance passport_entity = MakePassportWithRandomGuid();
-  std::vector<EntityInstance> entities = {passport_entity};
+  SetEntities({passport_entity});
+  SetForm({PASSPORT_NAME_TAG, PASSPORT_NUMBER});
 
-  FieldType triggering_field_type = PASSPORT_NAME_TAG;
-  AttributeType triggering_attribute_type =
-      AttributeType(AttributeTypeName::kPassportName);
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_NUMBER});
-  // Assign different sections to the fields.
-  base::flat_map<LocalFrameToken, size_t> frame_token_ids;
-  for (const std::unique_ptr<AutofillField>& field : form->fields()) {
-    field->set_section(Section::FromFieldIdentifier(*field, frame_token_ids));
-  }
+  field(0).set_section(Section::FromAutocomplete(Section::Autocomplete("foo")));
+  field(1).set_section(Section::FromAutocomplete(Section::Autocomplete("bar")));
+  ASSERT_NE(field(0).section(), field(1).section());
 
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], entities, kAppLocaleUS);
-
-  // There should be only one suggestion whose main text matches the entity
-  // value for the `triggering_field_type`.
-  EXPECT_THAT(suggestions, SizeIs(Ge(1)));
-  EXPECT_EQ(suggestions[0].main_text.value,
-            GetEntityInstanceValue(passport_entity, triggering_attribute_type));
+  std::vector<Suggestion> suggestions = CreateFillingSuggestions(field(0));
+  EXPECT_THAT(suggestions,
+              SuggestionsAre(HasMainText(GetPassportName(passport_entity))));
 
   const Suggestion::AutofillAiPayload* payload =
       std::get_if<Suggestion::AutofillAiPayload>(&suggestions[0].payload);
   ASSERT_TRUE(payload);
-  // The triggering/first field is of AutofillAi Type.
-  EXPECT_EQ(GetFillValueForField(entities, *payload, *form->fields()[0]),
-            GetEntityInstanceValue(passport_entity, triggering_attribute_type));
+  // The triggering/first field is of Autofill AI type.
+  EXPECT_EQ(GetFillValueForField(*payload, field(0)),
+            GetPassportName(passport_entity));
 }
 
+// Tests that there are no suggestions if the existing entities don't match the
+// triggering field.
 TEST_F(AutofillAiSuggestionsTest, NonMatchingEntity_DoNoReturnSuggestions) {
   EntityInstance drivers_license_entity =
       test::GetDriversLicenseEntityInstance();
-  std::vector<EntityInstance> entities = {drivers_license_entity};
-
-  FieldType triggering_field_type = PASSPORT_NAME_TAG;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], entities, kAppLocaleUS);
-
-  // There should be no suggestion since the triggering is a passport field and
-  // the only available entity is for loyalty cards.
-  EXPECT_EQ(suggestions.size(), 0u);
+  SetEntities({drivers_license_entity});
+  SetForm({PASSPORT_NAME_TAG});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)), IsEmpty());
 }
 
 // Tests that suggestions whose structured attribute would have empty text for
 // the value to fill into the triggering field are not shown.
 TEST_F(AutofillAiSuggestionsTest, EmptyMainTextForStructuredAttribute) {
   EntityInstance passport = MakePassportWithRandomGuid({.name = u"Miller"});
+  SetEntities({passport});
 
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructureWithMultiplePredictions(
-          {{NAME_FIRST, PASSPORT_NAME_TAG},
+  base::optional_ref<const AttributeInstance> name =
+      passport.attribute(AttributeType(AttributeTypeName::kPassportName));
+  ASSERT_TRUE(name);
+  ASSERT_EQ(name->GetInfo(NAME_FIRST, kAppLocaleUS, std::nullopt), u"");
+  ASSERT_EQ(name->GetInfo(NAME_LAST, kAppLocaleUS, std::nullopt), u"Miller");
+
+  SetForm({{NAME_FIRST, PASSPORT_NAME_TAG},
            {NAME_LAST, PASSPORT_NAME_TAG},
            {PASSPORT_NUMBER}});
 
-  base::optional_ref<const AttributeInstance> name_attribute =
-      passport.attribute(AttributeType(AttributeTypeName::kPassportName));
-  ASSERT_TRUE(name_attribute);
-  ASSERT_THAT(name_attribute->GetInfo(NAME_FIRST, kAppLocaleUS, std::nullopt),
-              u"");
-  ASSERT_THAT(name_attribute->GetInfo(NAME_LAST, kAppLocaleUS, std::nullopt),
-              u"Miller");
-
-  EXPECT_THAT(CreateFillingSuggestions(*form, *form->fields()[0], {passport},
-                                       kAppLocaleUS),
-              IsEmpty());
-  EXPECT_THAT(CreateFillingSuggestions(*form, *form->fields()[1], {passport},
-                                       kAppLocaleUS),
-              Not(IsEmpty()));
+  EXPECT_THAT(CreateFillingSuggestions(field(0)), IsEmpty());
+  EXPECT_THAT(CreateFillingSuggestions(field(1)), Not(IsEmpty()));
 }
 
 TEST_F(AutofillAiSuggestionsTest, GetFillingSuggestion_DedupeSuggestions) {
-  EntityInstance passport = MakePassportWithRandomGuid();
-
-  EntityInstance passport_a_with_different_expiry_date =
+  EntityInstance passport1 = MakePassportWithRandomGuid();
+  EntityInstance passport2 = MakePassportWithRandomGuid(
+      {.name = u"Jon Doe", .number = u"927908CYGAS1"});
+  EntityInstance passport3 =
       MakePassportWithRandomGuid({.expiry_date = u"2001-12-01"});
-
-  EntityInstance passport_a_without_an_expiry_date =
+  EntityInstance passport4 =
       MakePassportWithRandomGuid({.expiry_date = nullptr});
+  SetEntities({passport1, passport2, passport3, passport4});
+  SetForm({PASSPORT_NAME_TAG, PASSPORT_NUMBER, PASSPORT_ISSUING_COUNTRY});
 
-  EntityInstance another_persons_passport = MakePassportWithRandomGuid(
-      {.name = u"Jon doe", .number = u"927908CYGAS1"});
-
-  std::vector<EntityInstance> entities = {passport, another_persons_passport,
-                                          passport_a_with_different_expiry_date,
-                                          passport_a_without_an_expiry_date};
-
-  FieldType triggering_field_type = PASSPORT_NAME_TAG;
-  AttributeType triggering_attribute_type =
-      AttributeType(AttributeTypeName::kPassportName);
-  std::unique_ptr<FormStructure> form = CreateFormStructure(
-      {triggering_field_type, PASSPORT_NUMBER, PASSPORT_ISSUING_COUNTRY});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], entities, kAppLocaleUS);
-
-  // The passport with passport_a_with_different_expiry_date should be
-  // deduped because while it has an unique attribute (expiry date), the form
-  // does not contain a field with PASSPORT_ISSUE_DATE, which
-  // makes it identical to `passport`. The passport with
-  // passport_a_without_an_expiry_date should be deduped because it is a
-  // proper subset of `passport`.
-  ASSERT_THAT(suggestions, SizeIs(Ge(2)));
-  EXPECT_EQ(suggestions[0].main_text.value,
-            GetEntityInstanceValue(another_persons_passport,
-                                   triggering_attribute_type));
-  EXPECT_EQ(suggestions[1].main_text.value,
-            GetEntityInstanceValue(passport, triggering_attribute_type));
+  // `passport3` is deduped because there is no expiry date in the form and its
+  // remaining attributes are a subset of `passport1`.
+  // `passport4` is deduped because it is a proper subset of `passport1`.
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasMainText(GetPassportName(passport2)),
+                             HasMainText(GetPassportName(passport1))));
 }
 
 // Tests that an "Undo Autofill" suggestion is appended if the trigger field
 // is autofilled.
 TEST_F(AutofillAiSuggestionsTest, GetFillingSuggestions_Undo) {
-  EntityInstance passport_entity = MakePassportWithRandomGuid();
+  SetEntities({MakePassportWithRandomGuid()});
+  SetForm({PASSPORT_NUMBER});
 
-  std::unique_ptr<FormStructure> form = CreateFormStructure({PASSPORT_NUMBER});
-
-  EXPECT_FALSE(
-      base::Contains(CreateFillingSuggestions(*form, *form->fields()[0],
-                                              {passport_entity}, kAppLocaleUS),
-                     SuggestionType::kUndoOrClear, &Suggestion::type));
-
-  form->field(0)->set_is_autofilled(true);
-  EXPECT_TRUE(
-      base::Contains(CreateFillingSuggestions(*form, *form->fields()[0],
-                                              {passport_entity}, kAppLocaleUS),
-                     SuggestionType::kUndoOrClear, &Suggestion::type));
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              Not(Contains(HasType(SuggestionType::kUndoOrClear))));
+  field(0).set_is_autofilled(true);
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              Contains(HasType(SuggestionType::kUndoOrClear)));
 }
 
 TEST_F(AutofillAiSuggestionsTest, LabelGeneration_SingleEntity_NoLabelAdded) {
-  EntityInstance passport_entity = MakePassportWithRandomGuid();
-
-  FieldType triggering_field_type = PASSPORT_NUMBER;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_NAME_TAG});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {passport_entity}, kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 1u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Passport");
+  SetEntities({MakePassportWithRandomGuid()});
+  SetForm({PASSPORT_NUMBER, PASSPORT_NAME_TAG});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Passport")));
 }
 
-// Check that the existence of an entity (in this case `vehicle_entity`) that
-// does not fill the triggering field, still affects label generation.
+// Tests that the existence of an entity that does not fill the triggering field
+// still affects label generation.
 TEST_F(
     AutofillAiSuggestionsTest,
     LabelGeneration_SingleSuggestion_OtherEntitiesFillOtherFieldsInForm_LabelAdded) {
-  EntityInstance vehicle_entity = MakeVehicleWithRandomGuid(
-      {.plate = nullptr, .make = nullptr, .model = nullptr, .year = nullptr});
-  EntityInstance vehicle_entity_b =
-      MakeVehicleWithRandomGuid({.name = nullptr, .number = nullptr});
-
-  FieldType triggering_field_type = VEHICLE_LICENSE_PLATE;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, VEHICLE_VIN});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {vehicle_entity, vehicle_entity_b},
-      kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 1u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Vehicle · BMW · Series 2");
+  SetEntities(
+      {MakeVehicleWithRandomGuid({.plate = nullptr,
+                                  .make = nullptr,
+                                  .model = nullptr,
+                                  .year = nullptr}),
+       MakeVehicleWithRandomGuid({.name = nullptr, .number = nullptr})});
+  SetForm({VEHICLE_LICENSE_PLATE, VEHICLE_VIN});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Vehicle · BMW · Series 2")));
 }
 
-// In this test, the main test is the passport number, which is not the top
-// differentiating attribute (passport name), therefore, we add a label.
+// Test that if focused field (here: passport number) is not the highest-ranking
+// disambiguating label (passport name), we the latter as a label.
 TEST_F(AutofillAiSuggestionsTest,
        LabelGeneration_TwoSuggestions_SameMainText_AddTopDifferentiatingLabel) {
-  EntityInstance passport_entity = MakePassportWithRandomGuid();
-  EntityInstance passport_entity_b = MakePassportWithRandomGuid(
-      {.name = u"Machado de Assis", .number = u"123"});
-
-  FieldType triggering_field_type = PASSPORT_NUMBER;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_NAME_TAG});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {passport_entity, passport_entity_b},
-      kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 2u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Passport · Pippi Långstrump");
-
-  EXPECT_EQ(suggestions[1].labels.size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0][0].value, u"Passport · Machado de Assis");
+  SetEntities({MakePassportWithRandomGuid(),
+               MakePassportWithRandomGuid(
+                   {.name = u"Machado de Assis", .number = u"123"})});
+  SetForm({PASSPORT_NUMBER, PASSPORT_NAME_TAG});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Passport · Pippi Långstrump"),
+                             HasLabel(u"Passport · Machado de Assis")));
 }
 
 // Note that because the main text is the top disambiguating field (and is
@@ -404,26 +340,14 @@
 TEST_F(
     AutofillAiSuggestionsTest,
     LabelGeneration_TwoSuggestions_MainTextIsDisambiguating_DifferentMainText_DoNotAddDifferentiatingLabel) {
-  EntityInstance passport_entity = MakePassportWithRandomGuid();
-  EntityInstance passport_entity_b = MakePassportWithRandomGuid(
-      {.name = u"Machado de Assis", .country = u"Brazil"});
+  SetEntities({MakePassportWithRandomGuid(),
+               MakePassportWithRandomGuid(
+                   {.name = u"Machado de Assis", .country = u"Brazil"})});
 
   // Note that passport name is the first at the rank of disambiguating texts.
-  FieldType triggering_field_type = PASSPORT_NAME_TAG;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_ISSUING_COUNTRY});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {passport_entity, passport_entity_b},
-      kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 2u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Passport");
-
-  EXPECT_EQ(suggestions[1].labels.size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0][0].value, u"Passport");
+  SetForm({PASSPORT_NAME_TAG, PASSPORT_ISSUING_COUNTRY});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Passport"), HasLabel(u"Passport")));
 }
 
 // Note that while the main text is the top disambiguating field, we need
@@ -431,26 +355,14 @@
 TEST_F(
     AutofillAiSuggestionsTest,
     LabelGeneration_TwoSuggestions_MainTextIsDisambiguating_SameMainText_AddDifferentiatingLabel) {
-  EntityInstance passport_entity = MakePassportWithRandomGuid();
-  EntityInstance passport_entity_b =
-      MakePassportWithRandomGuid({.country = u"Brazil"});
+  SetEntities({MakePassportWithRandomGuid(),
+               MakePassportWithRandomGuid({.country = u"Brazil"})});
 
   // Note that passport name is the first at the rank of disambiguating texts.
-  FieldType triggering_field_type = PASSPORT_NAME_TAG;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_ISSUING_COUNTRY});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {passport_entity, passport_entity_b},
-      kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 2u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Passport · Sweden");
-
-  EXPECT_EQ(suggestions[1].labels.size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0][0].value, u"Passport · Brazil");
+  SetForm({PASSPORT_NAME_TAG, PASSPORT_ISSUING_COUNTRY});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Passport · Sweden"),
+                             HasLabel(u"Passport · Brazil")));
 }
 
 // Note that because the main text is not the top disambiguating field, we do
@@ -459,124 +371,61 @@
 TEST_F(
     AutofillAiSuggestionsTest,
     LabelGeneration_TwoSuggestions_MainTextIsNotTopDisambiguatingType_addDifferentiatingLabel) {
-  EntityInstance passport_entity = MakePassportWithRandomGuid();
-  EntityInstance passport_entity_b = MakePassportWithRandomGuid(
-      {.name = u"Machado de Assis", .country = u"Brazil"});
+  SetEntities({MakePassportWithRandomGuid(),
+               MakePassportWithRandomGuid(
+                   {.name = u"Machado de Assis", .country = u"Brazil"})});
 
   // Passport country is a disambiguating text, meaning it can be used to
   // further differentiate passport labels when the top type (passport name) is
   // the same. However, we still add the top differentiating label as a label,
   // as we always prioritize having it.
-  FieldType triggering_field_type = PASSPORT_ISSUING_COUNTRY;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_NUMBER});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {passport_entity, passport_entity_b},
-      kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 2u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Passport · Pippi Långstrump");
-
-  EXPECT_EQ(suggestions[1].labels.size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0][0].value, u"Passport · Machado de Assis");
+  SetForm({PASSPORT_ISSUING_COUNTRY, PASSPORT_NUMBER});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Passport · Pippi Långstrump"),
+                             HasLabel(u"Passport · Machado de Assis")));
 }
 
 // Note that in this case all entities have the same maker, so it is removed
 // from the possible list of labels.
 TEST_F(AutofillAiSuggestionsTest,
        LabelGeneration_ThreeSuggestions_AddDifferentiatingLabel) {
-  EntityInstance vehicle_entity = MakeVehicleWithRandomGuid();
-  EntityInstance vehicle_entity_b =
-      MakeVehicleWithRandomGuid({.model = u"Series 3"});
-  EntityInstance vehicle_entity_c =
-      MakeVehicleWithRandomGuid({.name = u"Diego Maradona"});
-
-  std::unique_ptr<FormStructure> form = CreateFormStructure(
-      {VEHICLE_LICENSE_PLATE, VEHICLE_MODEL, VEHICLE_OWNER_TAG});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0],
-      {vehicle_entity, vehicle_entity_b, vehicle_entity_c}, kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 3u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value,
-            u"Vehicle · Series 2 · Knecht Ruprecht");
-
-  EXPECT_EQ(suggestions[1].labels.size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0][0].value,
-            u"Vehicle · Series 3 · Knecht Ruprecht");
-
-  EXPECT_EQ(suggestions[2].labels.size(), 1u);
-  EXPECT_EQ(suggestions[2].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[2].labels[0][0].value,
-            u"Vehicle · Series 2 · Diego Maradona");
+  SetEntities({MakeVehicleWithRandomGuid(),
+               MakeVehicleWithRandomGuid({.model = u"Series 3"}),
+               MakeVehicleWithRandomGuid({.name = u"Diego Maradona"})});
+  SetForm({VEHICLE_LICENSE_PLATE, VEHICLE_MODEL, VEHICLE_OWNER_TAG});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Vehicle · Series 2 · Knecht Ruprecht"),
+                             HasLabel(u"Vehicle · Series 3 · Knecht Ruprecht"),
+                             HasLabel(u"Vehicle · Series 2 · Diego Maradona")));
 }
 
 TEST_F(
     AutofillAiSuggestionsTest,
     LabelGeneration_ThreeSuggestions_WithMissingValues_AddDifferentiatingLabel) {
-  EntityInstance passport_entity_a =
-      MakePassportWithRandomGuid({.country = u"Brazil"});
-
-  // Note that passport b can only fill the triggering name field and has no
-  // country data label to add.
-  EntityInstance passport_entity_b =
-      MakePassportWithRandomGuid({.number = u"9876", .country = nullptr});
-
-  EntityInstance passport_entity_c = MakePassportWithRandomGuid();
-
-  FieldType triggering_field_type = PASSPORT_NUMBER;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_ISSUING_COUNTRY});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0],
-      {passport_entity_a, passport_entity_b, passport_entity_c}, kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 3u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Passport · Brazil");
-
-  EXPECT_EQ(suggestions[1].labels.size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0][0].value, u"Passport");
-
-  EXPECT_EQ(suggestions[2].labels.size(), 1u);
-  EXPECT_EQ(suggestions[2].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[2].labels[0][0].value, u"Passport · Sweden");
+  SetEntities(
+      {MakePassportWithRandomGuid({.country = u"Brazil"}),
+       // This passport can only fill the triggering name field and has no
+       // country data label to add.
+       MakePassportWithRandomGuid({.number = u"9876", .country = nullptr}),
+       MakePassportWithRandomGuid()});
+  SetForm({PASSPORT_NUMBER, PASSPORT_ISSUING_COUNTRY});
+  EXPECT_THAT(
+      CreateFillingSuggestions(field(0)),
+      SuggestionsAre(HasLabel(u"Passport · Brazil"), HasLabel(u"Passport"),
+                     HasLabel(u"Passport · Sweden")));
 }
 
-// In this test we see that while the passports have different expiry dates,
-// they are not added as labels since they are not part of the entity
-// disambiguating attributes.
+// Test that the non-disambiguating attributes (here: the expiry dates) do not
+// occur in the labels.
 TEST_F(
     AutofillAiSuggestionsTest,
     LabelGeneration_TwoSuggestions_PassportsWithDifferentExpiryDates_DoNotAddDifferentiatingLabel) {
-  EntityInstance passport_entity = MakePassportWithRandomGuid();
-  EntityInstance passport_entity_b =
-      MakePassportWithRandomGuid({.expiry_date = u"2018-12-29"});
-
-  FieldType triggering_field_type = PASSPORT_NUMBER;
-  std::unique_ptr<FormStructure> form =
-      CreateFormStructure({triggering_field_type, PASSPORT_ISSUING_COUNTRY,
-                           PASSPORT_NAME_TAG, PASSPORT_EXPIRATION_DATE});
-  std::vector<Suggestion> suggestions = CreateFillingSuggestions(
-      *form, *form->fields()[0], {passport_entity, passport_entity_b},
-      kAppLocaleUS);
-
-  ASSERT_EQ(CountFillingSuggestions(suggestions), 2u);
-  EXPECT_EQ(suggestions[0].labels.size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[0].labels[0][0].value, u"Passport");
-
-  EXPECT_EQ(suggestions[1].labels.size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0].size(), 1u);
-  EXPECT_EQ(suggestions[1].labels[0][0].value, u"Passport");
+  SetEntities({MakePassportWithRandomGuid(),
+               MakePassportWithRandomGuid({.expiry_date = u"2018-12-29"})});
+  SetForm({PASSPORT_NUMBER, PASSPORT_ISSUING_COUNTRY, PASSPORT_NAME_TAG,
+           PASSPORT_EXPIRATION_DATE});
+  EXPECT_THAT(CreateFillingSuggestions(field(0)),
+              SuggestionsAre(HasLabel(u"Passport"), HasLabel(u"Passport")));
 }
 
 }  // namespace
diff --git a/components/autofill/core/browser/payments/payments_service_url.cc b/components/autofill/core/browser/payments/payments_service_url.cc
index 4419938..935f0e99 100644
--- a/components/autofill/core/browser/payments/payments_service_url.cc
+++ b/components/autofill/core/browser/payments/payments_service_url.cc
@@ -36,19 +36,18 @@
 // URLs used when opening the Payment methods management page from
 // chrome://settings/payments.
 const char kProdPaymentsManageCardsUrl[] =
-    "https://pay.google.com/"
-    "pay?p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign="
-    "payment_methods";
+    "https://wallet.google.com/wallet?"
+    "p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign="
+    "paymentmethods";
 const char kSandboxPaymentsManageCardsUrl[] =
     "https://pay.sandbox.google.com/"
     "pay?p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign="
     "payment_methods";
 
 // URL used when opening the Loyalty cards page from chrome://settings/payments.
-// TODO(crbug.com/416662510): Update the UTM params to be the correct one.
 const char kManageLoyaltyCardsUrl[] =
-    "https://pay.google.com/"
-    "pay?p=passes&utm_source=chrome&utm_medium=settings";
+    "https://wallet.google.com/wallet?"
+    "p=passes&utm_source=chrome&utm_medium=settings&utm_campaign=loyalty";
 
 // LINT.IfChange
 const char kVirtualCardEnrollmentSupportUrl[] =
diff --git a/components/autofill/core/browser/payments/payments_service_url_unittest.cc b/components/autofill/core/browser/payments/payments_service_url_unittest.cc
index 4f0e5961..18ff048 100644
--- a/components/autofill/core/browser/payments/payments_service_url_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_service_url_unittest.cc
@@ -35,9 +35,9 @@
       switches::kWalletServiceUseSandbox, "0");
 
   const char kExpectedURL[] =
-      "https://pay.google.com/"
-      "pay?p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign="
-      "payment_methods";
+      "https://wallet.google.com/wallet?"
+      "p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign="
+      "paymentmethods";
 
   EXPECT_EQ(kExpectedURL, GetManageInstrumentsUrl().spec());
   EXPECT_EQ(kExpectedURL, GetManageAddressesUrl().spec());
@@ -45,9 +45,9 @@
 
 TEST(PaymentsServiceUrl, UrlWithInstrumentId) {
   const char kExpectedURL[] =
-      "https://pay.google.com/"
-      "pay?p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign="
-      "payment_methods&id=123";
+      "https://wallet.google.com/wallet?"
+      "p=paymentmethods&utm_source=chrome&utm_medium=settings&utm_campaign="
+      "paymentmethods&id=123";
 
   EXPECT_EQ(kExpectedURL, GetManageInstrumentUrl(/*instrument_id=*/123).spec());
 }
diff --git a/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc b/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc
index a5da5cb..50ead8d0 100644
--- a/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc
+++ b/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc
@@ -22,6 +22,7 @@
   return result;
 }
 
+// Returns the form field relevant to the `role`.
 FormFieldData CreateFieldByRole(FieldType role) {
   FormFieldData field;
   // TODO(crbug.com/406073718): Add the missing roles and/or fail loudly.
diff --git a/components/autofill/core/browser/test_utils/autofill_form_test_utils.h b/components/autofill/core/browser/test_utils/autofill_form_test_utils.h
index 6e170ca..94204f9 100644
--- a/components/autofill/core/browser/test_utils/autofill_form_test_utils.h
+++ b/components/autofill/core/browser/test_utils/autofill_form_test_utils.h
@@ -121,9 +121,6 @@
 // messages might refer to the form.
 testing::Message DescribeFormData(const FormData& form_data);
 
-// Returns the form field relevant to the |role|.
-FormFieldData CreateFieldByRole(FieldType role);
-
 // Creates a FormFieldData to be fed to the parser.
 FormFieldData GetFormFieldData(const FieldDescription& fd);
 
diff --git a/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc b/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
index c6edc81..a9b55960 100644
--- a/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
+++ b/components/autofill/core/browser/webdata/autofill_ai/entity_table.cc
@@ -372,10 +372,10 @@
     }
     std::underlying_type_t<VerificationStatus> underlying_verification_status =
         s.ColumnInt(4);
-    attribute_records[entity_guid][attribute_type_name].push_back(
-        {.field_type = underlying_field_type,
-         .value = decrypted_value,
-         .verification_status = underlying_verification_status});
+    attribute_records[std::move(entity_guid)][std::move(attribute_type_name)]
+        .push_back({.field_type = underlying_field_type,
+                    .value = std::move(decrypted_value),
+                    .verification_status = underlying_verification_status});
   }
   if (!s.Succeeded()) {
     return {};
diff --git a/components/collaboration_strings.grdp b/components/collaboration_strings.grdp
index 3030c42..20f0f45 100644
--- a/components/collaboration_strings.grdp
+++ b/components/collaboration_strings.grdp
@@ -56,6 +56,12 @@
   <message name="IDS_COLLABORATION_SHARE_BUTTON_CHROME_OUT_OF_DATE_ERROR_DIALOG_BODY" desc="The body for the chrome out of date dialog, shown when user tries to share a tab group.">
     To share tab groups, update Chrome
   </message>
+  <message name="IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_UPDATE_BUTTON" desc="The text of the button to update the app from the chrome out of date dialog, shown when user tries to share or join a tab group.">
+    Update Chrome
+  </message>
+  <message name="IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_NOT_NOW_BUTTON" desc="The text of the button to skip updating the app from the chrome out of date dialog, shown when user tries to share or join a tab group.">
+    Not now
+  </message>
   <message name="IDS_COLLABORATION_SIGNED_OUT_HEADER" desc="The title for the signed out dialog." formatter_data="android_java">
     You're signed out
   </message>
diff --git a/components/collaboration_strings_grdp/IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_NOT_NOW_BUTTON.png.sha1 b/components/collaboration_strings_grdp/IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_NOT_NOW_BUTTON.png.sha1
new file mode 100644
index 0000000..d720ca41
--- /dev/null
+++ b/components/collaboration_strings_grdp/IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_NOT_NOW_BUTTON.png.sha1
@@ -0,0 +1 @@
+a9440a89daeb5a5953bc8f1db0e90820ff392a5b
\ No newline at end of file
diff --git a/components/collaboration_strings_grdp/IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_UPDATE_BUTTON.png.sha1 b/components/collaboration_strings_grdp/IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_UPDATE_BUTTON.png.sha1
new file mode 100644
index 0000000..d720ca41
--- /dev/null
+++ b/components/collaboration_strings_grdp/IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_UPDATE_BUTTON.png.sha1
@@ -0,0 +1 @@
+a9440a89daeb5a5953bc8f1db0e90820ff392a5b
\ No newline at end of file
diff --git a/components/feature_engagement/internal/tracker_impl.cc b/components/feature_engagement/internal/tracker_impl.cc
index ecd38562..5889ed8 100644
--- a/components/feature_engagement/internal/tracker_impl.cc
+++ b/components/feature_engagement/internal/tracker_impl.cc
@@ -34,6 +34,8 @@
 #include "components/feature_engagement/internal/feature_config_event_storage_validator.h"
 #include "components/feature_engagement/internal/in_memory_event_store.h"
 #include "components/feature_engagement/internal/init_aware_event_model.h"
+#include "components/feature_engagement/internal/multiple_event_model_provider.h"
+#include "components/feature_engagement/internal/multiple_event_model_writer.h"
 #include "components/feature_engagement/internal/never_availability_model.h"
 #include "components/feature_engagement/internal/never_event_storage_validator.h"
 #include "components/feature_engagement/internal/noop_display_lock_controller.h"
@@ -143,6 +145,7 @@
 // static
 std::unique_ptr<Tracker> Tracker::Create(
     const base::FilePath& storage_dir,
+    const base::FilePath& device_storage_dir,
     const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
     leveldb_proto::ProtoDatabaseProvider* db_provider,
     std::unique_ptr<TrackerEventExporter> event_exporter,
@@ -184,8 +187,7 @@
 
   auto event_model =
       std::make_unique<InitAwareEventModel>(std::move(raw_event_model));
-  auto event_model_provider =
-      std::make_unique<SingleEventModelProvider>(std::move(event_model));
+
   auto condition_validator =
       std::make_unique<FeatureConfigConditionValidator>();
   auto time_provider = std::make_unique<SystemTimeProvider>();
@@ -202,6 +204,35 @@
   auto availability_model = std::make_unique<AvailabilityModelImpl>(
       std::move(availability_store_loader));
 
+  std::unique_ptr<EventModelProvider> event_model_provider;
+  if (IsOnDeviceStorageEnabled()) {
+    base::FilePath device_event_storage_dir =
+        device_storage_dir.AppendASCII(std::string(kEventDBName));
+    auto device_event_db = db_provider->GetUniqueDB<Event>(
+        leveldb_proto::ProtoDbType::FEATURE_ENGAGEMENT_EVENT,
+        device_event_storage_dir, background_task_runner);
+
+    auto device_event_store =
+        std::make_unique<PersistentEventStore>(std::move(device_event_db));
+
+    auto device_event_storage_validator =
+        std::make_unique<FeatureConfigEventStorageValidator>();
+    device_event_storage_validator->InitializeFeatures(
+        GetAllFeatures(), GetAllGroups(), *configuration);
+
+    auto device_raw_event_model = std::make_unique<EventModelImpl>(
+        std::move(device_event_store),
+        std::move(device_event_storage_validator));
+
+    auto device_event_model = std::make_unique<InitAwareEventModel>(
+        std::move(device_raw_event_model));
+    event_model_provider = std::make_unique<MultipleEventModelProvider>(
+        std::move(event_model), std::move(device_event_model));
+  } else {
+    event_model_provider =
+        std::make_unique<SingleEventModelProvider>(std::move(event_model));
+  }
+
   return std::make_unique<TrackerImpl>(
       std::move(event_model_provider), std::move(availability_model),
       std::move(configuration), std::make_unique<DisplayLockControllerImpl>(),
diff --git a/components/feature_engagement/internal/tracker_impl_unittest.cc b/components/feature_engagement/internal/tracker_impl_unittest.cc
index ed564a4..6c4e9ac 100644
--- a/components/feature_engagement/internal/tracker_impl_unittest.cc
+++ b/components/feature_engagement/internal/tracker_impl_unittest.cc
@@ -28,6 +28,7 @@
 #include "components/feature_engagement/internal/event_model_impl.h"
 #include "components/feature_engagement/internal/feature_config_condition_validator.h"
 #include "components/feature_engagement/internal/in_memory_event_store.h"
+#include "components/feature_engagement/internal/multiple_event_model_provider.h"
 #include "components/feature_engagement/internal/never_availability_model.h"
 #include "components/feature_engagement/internal/never_event_storage_validator.h"
 #include "components/feature_engagement/internal/once_condition_validator.h"
@@ -64,6 +65,9 @@
 BASE_FEATURE(kTrackerTestFeatureSnooze,
              "test_snooze",
              base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kTrackerTestFeatureDeviceStorage,
+             "test_device_storage",
+             base::FEATURE_DISABLED_BY_DEFAULT);
 BASE_FEATURE(kTrackerTestGroupOne,
              "test_group_one",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -73,7 +77,8 @@
                            bool valid,
                            bool tracking_only,
                            bool snooze_params,
-                           const char* additional_event_name = nullptr) {
+                           const char* additional_event_name = nullptr,
+                           StorageType storage_type = StorageType::PROFILE) {
   FeatureConfig config;
   config.valid = valid;
   config.used.name = feature.name + std::string("_used");
@@ -94,6 +99,7 @@
     event_config.storage = 7U;
     config.event_configs.emplace(std::move(event_config));
   }
+  config.storage_type = storage_type;
   configuration->SetConfiguration(&feature, config);
 }
 
@@ -1503,6 +1509,295 @@
                                            1);
 }
 
+class MultipleEventModelTrackerImplTest : public TrackerImplTest {
+ public:
+  MultipleEventModelTrackerImplTest() = default;
+
+  MultipleEventModelTrackerImplTest(const MultipleEventModelTrackerImplTest&) =
+      delete;
+  MultipleEventModelTrackerImplTest& operator=(
+      const MultipleEventModelTrackerImplTest&) = delete;
+
+  void SetUp() override {
+    std::unique_ptr<EditableConfiguration> configuration =
+        std::make_unique<EditableConfiguration>();
+    configuration_ = configuration.get();
+
+    RegisterFeatureConfig(configuration.get(), kTrackerTestFeatureFoo,
+                          true /* is_valid */, false /* tracking_only */,
+                          false /* snooze_params */);
+    RegisterFeatureConfig(configuration.get(), kTrackerTestFeatureBar,
+                          true /* is_valid */, false /* tracking_only */,
+                          false /* snooze_params */);
+    RegisterFeatureConfig(configuration.get(), kTrackerTestFeatureBaz,
+                          true /* is_valid */, true /* tracking_only */,
+                          false /* snooze_params */);
+    RegisterFeatureConfig(configuration.get(), kTrackerTestFeatureQux,
+                          false /* is_valid */, false /* tracking_only */,
+                          false /* snooze_params */);
+    RegisterFeatureConfig(configuration.get(), kTrackerTestFeatureEvent,
+                          /*valid=*/true, /*tracking_only=*/false,
+                          /*snooze_params=*/false, "test_event_event");
+    RegisterFeatureConfig(configuration.get(), kTrackerTestFeatureSnooze,
+                          true /* is_valid */, false /* tracking_only */,
+                          true /* snooze_params */);
+    RegisterFeatureConfig(configuration.get(), kTrackerTestFeatureDeviceStorage,
+                          true /* is_valid */, false /* tracking_only */,
+                          true /* snooze_params */,
+                          nullptr /* additional_event_name */,
+                          StorageType::DEVICE);
+    RegisterGroupConfig(configuration.get(), kTrackerTestGroupOne,
+                        true /* is_valid */);
+
+    std::unique_ptr<TestTrackerInMemoryEventStore> profile_event_store =
+        CreateEventStore();
+    profile_event_store_ = profile_event_store.get();
+
+    std::unique_ptr<TestTrackerInMemoryEventStore> device_event_store =
+        CreateEventStore();
+    device_event_store_ = device_event_store.get();
+
+    auto profile_event_model = std::make_unique<EventModelImpl>(
+        std::move(profile_event_store),
+        std::make_unique<StoreEverythingEventStorageValidator>());
+    profile_event_model_ = profile_event_model.get();
+
+    auto device_event_model = std::make_unique<EventModelImpl>(
+        std::move(device_event_store),
+        std::make_unique<StoreEverythingEventStorageValidator>());
+    device_event_model_ = device_event_model.get();
+
+    auto multiple_event_model_provider =
+        std::make_unique<MultipleEventModelProvider>(
+            std::move(profile_event_model), std::move(device_event_model));
+
+    auto availability_model = std::make_unique<TestTrackerAvailabilityModel>();
+    availability_model_ = availability_model.get();
+    availability_model_->SetIsReady(ShouldAvailabilityStoreBeReady());
+
+    auto display_lock_controller =
+        std::make_unique<TestTrackerDisplayLockController>();
+    display_lock_controller_ = display_lock_controller.get();
+
+    auto condition_validator = CreateConditionValidator();
+    condition_validator_ = condition_validator.get();
+
+    auto time_provider = std::make_unique<TestTimeProvider>();
+    time_provider_ = time_provider.get();
+    time_provider->SetCurrentDay(1u);
+
+    auto event_exporter = std::make_unique<TestTrackerEventExporter>();
+    event_exporter_ = event_exporter.get();
+
+    auto session_controller = std::make_unique<TestSessionController>();
+    session_controller_ = session_controller.get();
+
+    tracker_ = std::make_unique<TrackerImpl>(
+        std::move(multiple_event_model_provider), std::move(availability_model),
+        std::move(configuration), std::move(display_lock_controller),
+        std::move(condition_validator), std::move(time_provider),
+        std::move(event_exporter), std::move(session_controller));
+  }
+
+ protected:
+  void VerifyEventTriggerForStore(TestTrackerInMemoryEventStore* store,
+                                  const std::string& event_name,
+                                  uint32_t count) {
+    Event trigger_event = store->GetEvent(event_name);
+    if (count == 0) {
+      EXPECT_EQ(0, trigger_event.events_size());
+      return;
+    }
+
+    EXPECT_EQ(1, trigger_event.events_size());
+    EXPECT_EQ(1u, trigger_event.events(0).day());
+    EXPECT_EQ(count, trigger_event.events(0).count());
+  }
+
+  void VerifyEventTrigger(TestTrackerInMemoryEventStore* store,
+                          std::string event_name,
+                          uint32_t count) {
+    VerifyEventTriggerForStore(store, event_name, count);
+  }
+
+  void VerifyEventTriggerEvents(TestTrackerInMemoryEventStore* store,
+                                const base::Feature& feature,
+                                uint32_t count) {
+    VerifyEventTrigger(
+        store, configuration_->GetFeatureConfig(feature).trigger.name, count);
+  }
+
+  void VerifyGroupEventTriggerEvents(TestTrackerInMemoryEventStore* store,
+                                     const base::Feature& group,
+                                     uint32_t count) {
+    VerifyEventTrigger(
+        store, configuration_->GetGroupConfig(group).trigger.name, count);
+  }
+
+  raw_ptr<EventModel> profile_event_model_;
+  raw_ptr<EventModel> device_event_model_;
+  raw_ptr<TestTrackerInMemoryEventStore> device_event_store_;
+  raw_ptr<TestTrackerInMemoryEventStore> profile_event_store_;
+};
+
+TEST_F(MultipleEventModelTrackerImplTest,
+       TestInitializationWithMultipleStorage) {
+  EXPECT_FALSE(tracker_->IsInitialized());
+
+  StoringInitializedCallback callback;
+  tracker_->AddOnInitializedCallback(base::BindOnce(
+      &StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
+  EXPECT_FALSE(callback.invoked());
+
+  // Ensure all initialization is finished.
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(tracker_->IsInitialized());
+  EXPECT_TRUE(callback.invoked());
+  EXPECT_TRUE(callback.success());
+}
+
+TEST_F(MultipleEventModelTrackerImplTest, TestWriteEventToMultipleStores) {
+  // Ensure all initialization is finished.
+  StoringInitializedCallback callback;
+  tracker_->AddOnInitializedCallback(base::BindOnce(
+      &StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
+  base::RunLoop().RunUntilIdle();
+  base::UserActionTester user_action_tester;
+
+  // The first time a feature triggers it should be shown.
+  EXPECT_TRUE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureFoo));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 1u);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureFoo));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 1u);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureQux));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyUserActionsTriggerChecks(user_action_tester, 2, 0, 0, 1);
+  VerifyUserActionsTriggered(user_action_tester, 1, 0, 0, 0);
+  VerifyUserActionsNotTriggered(user_action_tester, 1, 0, 0, 1);
+  VerifyUserActionsWouldHaveTriggered(user_action_tester, 0, 0, 0, 0);
+  VerifyUserActionsDismissed(user_action_tester, 0);
+  VerifyHistograms(true, 1, 1, 0, false, 0, 0, 0, false, 0, 0, 0, true, 0, 1,
+                   0);
+
+  // While in-product help is currently showing, no other features should be
+  // shown.
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureBar));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureBar, 0);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureBar, 0);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 1u);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureQux));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyUserActionsTriggerChecks(user_action_tester, 2, 1, 0, 2);
+  VerifyUserActionsTriggered(user_action_tester, 1, 0, 0, 0);
+  VerifyUserActionsNotTriggered(user_action_tester, 1, 1, 0, 2);
+  VerifyUserActionsWouldHaveTriggered(user_action_tester, 0, 0, 0, 0);
+  VerifyUserActionsDismissed(user_action_tester, 0);
+  VerifyHistograms(true, 1, 1, 0, true, 0, 1, 0, false, 0, 0, 0, true, 0, 2, 0);
+
+  // After dismissing the current in-product help, that feature can not be shown
+  // again, but a different feature should.
+  tracker_->Dismissed(kTrackerTestFeatureFoo);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureFoo));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 1u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 1u);
+  EXPECT_TRUE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureBar));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureBar, 1u);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureBar, 1u);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 2u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 2u);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureQux));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 2u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 2u);
+  VerifyUserActionsTriggerChecks(user_action_tester, 3, 2, 0, 3);
+  VerifyUserActionsTriggered(user_action_tester, 1, 1, 0, 0);
+  VerifyUserActionsNotTriggered(user_action_tester, 2, 1, 0, 3);
+  VerifyUserActionsWouldHaveTriggered(user_action_tester, 0, 0, 0, 0);
+  VerifyUserActionsDismissed(user_action_tester, 1);
+  VerifyHistograms(true, 1, 2, 0, true, 1, 1, 0, false, 0, 0, 0, true, 0, 3, 0);
+
+  // After dismissing the second registered feature, no more in-product help
+  // should be shown, since kTrackerTestFeatureQux is invalid.
+  tracker_->Dismissed(kTrackerTestFeatureBar);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureFoo));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureFoo, 1u);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 2u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 2u);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureBar));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureBar, 1u);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureBar, 1u);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 2u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 2u);
+  EXPECT_FALSE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureQux));
+  VerifyEventTriggerEvents(profile_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyEventTriggerEvents(device_event_store_, kTrackerTestFeatureQux, 0);
+  VerifyGroupEventTriggerEvents(profile_event_store_, kTrackerTestGroupOne, 2u);
+  VerifyGroupEventTriggerEvents(device_event_store_, kTrackerTestGroupOne, 2u);
+  VerifyUserActionsTriggerChecks(user_action_tester, 4, 3, 0, 4);
+  VerifyUserActionsTriggered(user_action_tester, 1, 1, 0, 0);
+  VerifyUserActionsNotTriggered(user_action_tester, 3, 2, 0, 4);
+  VerifyUserActionsWouldHaveTriggered(user_action_tester, 0, 0, 0, 0);
+  VerifyUserActionsDismissed(user_action_tester, 2);
+  VerifyHistograms(true, 1, 3, 0, true, 1, 2, 0, false, 0, 0, 0, true, 0, 4, 0);
+}
+
+TEST_F(MultipleEventModelTrackerImplTest, TestReadEventFromSingleStorage) {
+  // Ensure all initialization is finished.
+  StoringInitializedCallback callback;
+  tracker_->AddOnInitializedCallback(base::BindOnce(
+      &StoringInitializedCallback::OnInitialized, base::Unretained(&callback)));
+  base::RunLoop().RunUntilIdle();
+  base::UserActionTester user_action_tester;
+
+  // All the features should not be triggered yet.
+  EXPECT_FALSE(
+      tracker_->HasEverTriggered(kTrackerTestFeatureDeviceStorage, false));
+
+  // For triggered features, has ever triggered from storage should returns
+  // true, as the storage is set to 1.
+  EXPECT_TRUE(tracker_->ShouldTriggerHelpUI(kTrackerTestFeatureDeviceStorage));
+  tracker_->Dismissed(kTrackerTestFeatureDeviceStorage);
+  VerifyEventTriggerEvents(profile_event_store_,
+                           kTrackerTestFeatureDeviceStorage, 1u);
+  VerifyEventTriggerEvents(device_event_store_,
+                           kTrackerTestFeatureDeviceStorage, 1u);
+
+  const std::string& event_trigger_name =
+      kTrackerTestFeatureDeviceStorage.name + std::string("_trigger");
+
+  // Clear the trigger event from the profile model. `HasEverTriggered` should
+  // still return true because the event persists in the device model, which is
+  // the designated storage for this feature.
+  profile_event_model_->ClearEvent(event_trigger_name);
+  EXPECT_TRUE(
+      tracker_->HasEverTriggered(kTrackerTestFeatureDeviceStorage, false));
+
+  // Clear the trigger event from the device model. Now that the event is
+  // cleared from its designated storage, `HasEverTriggered` should return
+  // false.
+  device_event_model_->ClearEvent(event_trigger_name);
+  EXPECT_FALSE(
+      tracker_->HasEverTriggered(kTrackerTestFeatureDeviceStorage, false));
+}
+
 namespace test {
 
 class ScopedIphFeatureListTest : public TrackerImplTest {
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index 1e3f0a3..1830578b 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -17,6 +17,7 @@
 namespace feature_engagement {
 
 // Returns true if adding on-device storage is enabled.
+COMPONENT_EXPORT(FEATURE_ENGAGEMENT_FEATURE_CONSTANTS)
 bool IsOnDeviceStorageEnabled();
 
 #define FEATURE_CONSTANTS_DECLARE_FEATURE(feature_name)  \
diff --git a/components/feature_engagement/public/tracker.h b/components/feature_engagement/public/tracker.h
index a6e04e8..48fe65ea 100644
--- a/components/feature_engagement/public/tracker.h
+++ b/components/feature_engagement/public/tracker.h
@@ -160,6 +160,7 @@
   // will be provided.
   static std::unique_ptr<Tracker> Create(
       const base::FilePath& storage_dir,
+      const base::FilePath& device_storage_dir,
       const scoped_refptr<base::SequencedTaskRunner>& background_task_runner,
       leveldb_proto::ProtoDatabaseProvider* db_provider,
       std::unique_ptr<TrackerEventExporter> event_exporter,
diff --git a/components/page_load_metrics/renderer/page_timing_metrics_sender.cc b/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
index 8b081d0..e727b60 100644
--- a/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
+++ b/components/page_load_metrics/renderer/page_timing_metrics_sender.cc
@@ -132,7 +132,7 @@
   // increase by one, and the navigation_id to update, however, we have no
   // expectations about start_time values.  This is because soft-navs start_time
   // might not be monotonically increasing. See: crbug.com/418449366#comment3
-  CHECK(new_metrics.count > soft_navigation_metrics_->count);
+  CHECK(new_metrics.count >= soft_navigation_metrics_->count);
   CHECK(!new_metrics.start_time.is_zero());
   CHECK(new_metrics.navigation_id != soft_navigation_metrics_->navigation_id);
 
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc
index 323144d..ea66837a 100644
--- a/components/signin/public/base/signin_switches.cc
+++ b/components/signin/public/base/signin_switches.cc
@@ -226,7 +226,12 @@
 
 BASE_FEATURE(kEnableExtensionsExplicitBrowserSignin,
              "EnableExtensionsExplicitBrowserSignin",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
+             base::FEATURE_ENABLED_BY_DEFAULT
+#else
+             base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+);
 
 bool IsExtensionsExplicitBrowserSigninEnabled() {
   return base::FeatureList::IsEnabled(kEnableExtensionsExplicitBrowserSignin);
diff --git a/components/viz/service/layers/layer_context_impl_base_unittest.cc b/components/viz/service/layers/layer_context_impl_base_unittest.cc
index b16a626..056144f 100644
--- a/components/viz/service/layers/layer_context_impl_base_unittest.cc
+++ b/components/viz/service/layers/layer_context_impl_base_unittest.cc
@@ -99,6 +99,12 @@
       compositor_frame_sink_support_.get(), /*draw_mode_is_gpu=*/true);
 }
 
+void LayerContextImplTest::RecreateLayerContextImplWithParams(
+    bool draw_mode_is_gpu) {
+  layer_context_impl_ = LayerContextImpl::CreateForTesting(
+      compositor_frame_sink_support_.get(), draw_mode_is_gpu);
+}
+
 void LayerContextImplTest::ResetTestState() {
   // Property tree node IDs and layers are reinitialized in
   // CreateDefaultUpdate if first_update_ is true.
diff --git a/components/viz/service/layers/layer_context_impl_base_unittest.h b/components/viz/service/layers/layer_context_impl_base_unittest.h
index 701be2f..a694981 100644
--- a/components/viz/service/layers/layer_context_impl_base_unittest.h
+++ b/components/viz/service/layers/layer_context_impl_base_unittest.h
@@ -189,6 +189,7 @@
 
  protected:
   cc::LayerImpl* GetLayerFromActiveTree(int layer_id);
+  void RecreateLayerContextImplWithParams(bool draw_mode_is_gpu);
 
   FakeCompositorFrameSinkClient dummy_client_;
   FrameSinkManagerImpl frame_sink_manager_;
diff --git a/components/viz/service/layers/layer_context_impl_unittest.cc b/components/viz/service/layers/layer_context_impl_unittest.cc
index 79dde09f2..d5cbc738 100644
--- a/components/viz/service/layers/layer_context_impl_unittest.cc
+++ b/components/viz/service/layers/layer_context_impl_unittest.cc
@@ -180,6 +180,16 @@
       return name.str();
     });
 
+TEST_F(LayerContextImplTest, DrawModeIsGpuForwardedViaSettings) {
+  RecreateLayerContextImplWithParams(/*draw_mode_is_gpu=*/true);
+  cc::LayerTreeHostImpl* host_impl = layer_context_impl_->host_impl();
+  EXPECT_TRUE(host_impl->settings().display_tree_draw_mode_is_gpu);
+
+  RecreateLayerContextImplWithParams(/*draw_mode_is_gpu=*/false);
+  host_impl = layer_context_impl_->host_impl();
+  EXPECT_FALSE(host_impl->settings().display_tree_draw_mode_is_gpu);
+}
+
 TEST_F(LayerContextImplTest, TransferableUIResourceLifecycleAndEdgeCases) {
   cc::LayerTreeHostImpl* host_impl = layer_context_impl_->host_impl();
 
diff --git a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
index 0dba893..92fc16e 100644
--- a/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
+++ b/components/webauthn/android/java/src/org/chromium/components/webauthn/Fido2CredentialRequest.java
@@ -229,8 +229,7 @@
         mErrorCallback = errorCallback;
         mRecordingCallback = recordingCallback;
         @Nullable Origin remoteDesktopOrigin = null;
-        if (DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS)
-                && options.remoteDesktopClientOverride != null
+        if (options.remoteDesktopClientOverride != null
                 && isChrome(mAuthenticationContextProvider.getWebContents())) {
             // SECURITY: remoteDesktopClientOverride comes from the renderer process and is
             // untrusted. We only use the override origin if the "caller origin" is explicitly
@@ -281,10 +280,8 @@
             String effectiveOriginString = convertOriginToString(origin);
             // Handle remote desktop client override for ClientDataJSON.
             // The origin from remoteDesktopClientOverride is only used after validation in
-            // ValidateDomainAndRelyingPartyID() confirmed that "caller origin" is allowlisted.
-            if (DeviceFeatureMap.isEnabled(
-                            DeviceFeatureList.WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS)
-                    && options.remoteDesktopClientOverride != null) {
+            // ValidateDomainAndRelyingPartyID() confirmed that the "caller origin" is allowlisted.
+            if (options.remoteDesktopClientOverride != null) {
                 effectiveOriginString =
                         convertOriginToString(
                                 new Origin(options.remoteDesktopClientOverride.origin));
@@ -462,8 +459,7 @@
         }
 
         @Nullable Origin remoteDesktopOrigin = null;
-        if (DeviceFeatureMap.isEnabled(DeviceFeatureList.WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS)
-                && options.extensions.remoteDesktopClientOverride != null
+        if (options.extensions.remoteDesktopClientOverride != null
                 && isChrome(mAuthenticationContextProvider.getWebContents())) {
             // SECURITY: remoteDesktopClientOverride comes from the renderer process and is
             // untrusted. We only use the override origin if the "caller origin" is explicitly
@@ -525,10 +521,8 @@
             String effectiveOriginString = callerOriginString;
             // Handle remote desktop client override for ClientDataJSON.
             // The origin from remoteDesktopClientOverride is only used after validation in
-            // ValidateDomainAndRelyingPartyID() confirmed that "caller origin" is allowlisted.
-            if (DeviceFeatureMap.isEnabled(
-                            DeviceFeatureList.WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS)
-                    && options.extensions.remoteDesktopClientOverride != null) {
+            // ValidateDomainAndRelyingPartyID() confirmed that the "caller origin" is allowlisted.
+            if (options.extensions.remoteDesktopClientOverride != null) {
                 effectiveOriginString =
                         convertOriginToString(
                                 new Origin(options.extensions.remoteDesktopClientOverride.origin));
diff --git a/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java b/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java
index 4c50629..5ae7b9e 100644
--- a/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java
+++ b/components/webauthn/android/junit/src/org/chromium/components/webauthn/Fido2CredentialRequestRobolectricTest.java
@@ -104,7 +104,6 @@
     public void setUp() throws Exception {
         FeatureOverrides.newBuilder()
                 .enable(DeviceFeatureList.WEBAUTHN_ANDROID_USE_PASSKEY_CACHE)
-                .enable(DeviceFeatureList.WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS)
                 .disable(BlinkFeatures.SECURE_PAYMENT_CONFIRMATION_BROWSER_BOUND_KEYS)
                 .apply();
 
diff --git a/content/browser/browser_context.cc b/content/browser/browser_context.cc
index 80919b9..60f267e 100644
--- a/content/browser/browser_context.cc
+++ b/content/browser/browser_context.cc
@@ -198,6 +198,7 @@
     const std::string& embedder_histogram_suffix,
     bool javascript_enabled,
     std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+    std::optional<PrefetchPriority> priority,
     const net::HttpRequestHeaders& additional_headers,
     std::unique_ptr<PrefetchRequestStatusListener> request_status_listener,
     base::TimeDelta ttl,
@@ -221,6 +222,7 @@
       this, url, prefetch_type, embedder_histogram_suffix,
       blink::mojom::Referrer(), javascript_enabled,
       /*referring_origin=*/std::nullopt, std::move(no_vary_search_hint),
+      std::move(priority),
       /*attempt=*/nullptr, additional_headers,
       std::move(request_status_listener), ttl, should_append_variations_header,
       should_disable_block_until_head_timeout);
diff --git a/content/browser/permissions/permission_controller_impl.cc b/content/browser/permissions/permission_controller_impl.cc
index bc4dd4c..8fed0c9 100644
--- a/content/browser/permissions/permission_controller_impl.cc
+++ b/content/browser/permissions/permission_controller_impl.cc
@@ -701,12 +701,7 @@
 PermissionStatus PermissionControllerImpl::GetCombinedPermissionAndDeviceStatus(
     const blink::mojom::PermissionDescriptorPtr& permission,
     RenderFrameHost* render_frame_host) {
-  auto permission_type =
-      blink::PermissionDescriptorToPermissionType(permission);
-
-  CHECK(permission_type == blink::PermissionType::VIDEO_CAPTURE ||
-        permission_type == blink::PermissionType::AUDIO_CAPTURE ||
-        permission_type == blink::PermissionType::GEOLOCATION);
+  CHECK(PermissionUtil::IsDevicePermission(permission));
   return GetPermissionStatusForCurrentDocumentInternal(
       permission, render_frame_host, /*should_include_device_status=*/true);
 }
diff --git a/content/browser/permissions/permission_service_impl.cc b/content/browser/permissions/permission_service_impl.cc
index 096c7dc7..88789e5 100644
--- a/content/browser/permissions/permission_service_impl.cc
+++ b/content/browser/permissions/permission_service_impl.cc
@@ -376,21 +376,20 @@
     PermissionDescriptorPtr permission,
     PermissionStatus last_known_status,
     mojo::PendingRemote<blink::mojom::PermissionObserver> observer) {
-  if (!base::FeatureList::IsEnabled(blink::features::kPermissionElement)) {
-    bad_message::ReceivedBadMessage(
-        context_->render_frame_host()->GetProcess(),
-        bad_message::PSI_ADD_PAGE_EMBEDDED_PERMISSION_OBSERVER_WITHOUT_FEATURE);
-    return;
-  }
   auto type = blink::MaybePermissionDescriptorToPermissionType(permission);
   if (!type) {
     ReceivedBadMessage();
     return;
   }
-  context_->CreateSubscription(
-      permission, origin_, GetCombinedPermissionAndDeviceStatus(permission),
-      last_known_status, /*should_include_device_status*/ true,
-      std::move(observer));
+  bool should_include_device_status =
+      PermissionUtil::IsDevicePermission(permission);
+  PermissionStatus current_status =
+      should_include_device_status
+          ? GetCombinedPermissionAndDeviceStatus(permission)
+          : GetPermissionStatusForCurrentContext(permission);
+  context_->CreateSubscription(permission, origin_, current_status,
+                               last_known_status, should_include_device_status,
+                               std::move(observer));
 }
 
 void PermissionServiceImpl::NotifyEventListener(
diff --git a/content/browser/permissions/permission_util.cc b/content/browser/permissions/permission_util.cc
index 2ff4b3b..4530dcf4 100644
--- a/content/browser/permissions/permission_util.cc
+++ b/content/browser/permissions/permission_util.cc
@@ -97,4 +97,11 @@
   return true;
 }
 
+bool PermissionUtil::IsDevicePermission(
+    const blink::mojom::PermissionDescriptorPtr& descriptor) {
+  return descriptor->name == blink::mojom::PermissionName::VIDEO_CAPTURE ||
+         descriptor->name == blink::mojom::PermissionName::AUDIO_CAPTURE ||
+         descriptor->name == blink::mojom::PermissionName::GEOLOCATION;
+}
+
 }  // namespace content
diff --git a/content/browser/permissions/permission_util.h b/content/browser/permissions/permission_util.h
index b0cafa85..56576c3 100644
--- a/content/browser/permissions/permission_util.h
+++ b/content/browser/permissions/permission_util.h
@@ -47,6 +47,12 @@
       const std::vector<blink::mojom::PermissionDescriptorPtr>& types,
       RenderFrameHost* rfh,
       const blink::mojom::PermissionDescriptorPtr& descriptor);
+
+  // Returns true if the given descriptor is a capability that combines the
+  // browser's permission status with a device-level status (and, therefore,
+  // can be retrieved through `GetCombinedPermissionAndDeviceStatus(...)`).
+  CONTENT_EXPORT static bool IsDevicePermission(
+      const blink::mojom::PermissionDescriptorPtr&);
 };
 
 }  // namespace content
diff --git a/content/browser/preloading/prefetch/contamination_delay_browsertest.cc b/content/browser/preloading/prefetch/contamination_delay_browsertest.cc
index 0cfa515..c25afe3 100644
--- a/content/browser/preloading/prefetch/contamination_delay_browsertest.cc
+++ b/content/browser/preloading/prefetch/contamination_delay_browsertest.cc
@@ -256,6 +256,7 @@
       test::kPreloadingEmbedderHistgramSuffixForTesting,
       blink::mojom::Referrer(), referring_origin,
       /*no_vary_search_hint=*/std::nullopt,
+      /*priority=*/std::nullopt,
       PreloadPipelineInfo::Create(
           /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
       /*attempt=*/nullptr, /*holdback_status_override=*/std::nullopt,
diff --git a/content/browser/preloading/prefetch/no_vary_search_helper_unittest.cc b/content/browser/preloading/prefetch/no_vary_search_helper_unittest.cc
index 967bea6..ceff8f1 100644
--- a/content/browser/preloading/prefetch/no_vary_search_helper_unittest.cc
+++ b/content/browser/preloading/prefetch/no_vary_search_helper_unittest.cc
@@ -109,6 +109,7 @@
             blink::mojom::Referrer(),
             std::make_optional(SpeculationRulesTags()),
             /*no_vary_search_hint=*/std::nullopt,
+            /*priority=*/std::nullopt,
             /*prefetch_document_manager=*/nullptr,
             PreloadPipelineInfo::Create(/*planned_max_preloading_type=*/
                                         PreloadingType::kPrefetch));
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index 020fb66d..9defc88 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -329,6 +329,7 @@
     const blink::mojom::Referrer& referrer,
     std::optional<SpeculationRulesTags> speculation_rules_tags,
     std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+    std::optional<PrefetchPriority> priority,
     base::WeakPtr<PrefetchDocumentManager> prefetch_document_manager,
     scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
     base::WeakPtr<PreloadingAttempt> attempt)
@@ -357,7 +358,8 @@
               .javascript_enabled,
           PrefetchContainerDefaultTtlInPrefetchService(),
           /*should_append_variations_header=*/true,
-          /*should_disable_block_until_head_timeout=*/false) {
+          /*should_disable_block_until_head_timeout=*/false,
+          priority) {
   CHECK(prefetch_type_.IsRendererInitiated());
 }
 
@@ -369,6 +371,7 @@
     const blink::mojom::Referrer& referrer,
     const std::optional<url::Origin>& referring_origin,
     std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+    std::optional<PrefetchPriority> priority,
     scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
     base::WeakPtr<PreloadingAttempt> attempt,
     std::optional<PreloadingHoldbackStatus> holdback_status_override,
@@ -398,7 +401,8 @@
           ttl.has_value() ? ttl.value()
                           : PrefetchContainerDefaultTtlInPrefetchService(),
           /*should_append_variations_header=*/true,
-          /*should_disable_block_until_head_timeout=*/false) {
+          /*should_disable_block_until_head_timeout=*/false,
+          priority) {
   CHECK(!prefetch_type_.IsRendererInitiated());
   CHECK(PrefetchBrowserInitiatedTriggersEnabled());
   CHECK(!embedder_histogram_suffix_.value().empty());
@@ -413,6 +417,7 @@
     bool javascript_enabled,
     const std::optional<url::Origin>& referring_origin,
     std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+    std::optional<PrefetchPriority> priority,
     base::WeakPtr<PreloadingAttempt> attempt,
     const net::HttpRequestHeaders& additional_headers,
     std::unique_ptr<PrefetchRequestStatusListener> request_status_listener,
@@ -444,7 +449,8 @@
           javascript_enabled,
           ttl,
           should_append_variations_header,
-          should_disable_block_until_head_timeout) {
+          should_disable_block_until_head_timeout,
+          priority) {
   CHECK(!prefetch_type_.IsRendererInitiated());
   CHECK(PrefetchBrowserInitiatedTriggersEnabled());
   CHECK(!embedder_histogram_suffix_.value().empty());
@@ -472,7 +478,8 @@
     bool is_javascript_enabled,
     base::TimeDelta ttl,
     bool should_append_variations_header,
-    bool should_disable_block_until_head_timeout)
+    bool should_disable_block_until_head_timeout,
+    std::optional<PrefetchPriority> priority)
     : referring_render_frame_host_id_(referring_render_frame_host_id),
       referring_origin_(referring_origin),
       referring_url_hash_(referring_url_hash),
@@ -498,7 +505,8 @@
       ttl_(ttl),
       should_append_variations_header_(should_append_variations_header),
       should_disable_block_until_head_timeout_(
-          should_disable_block_until_head_timeout) {
+          should_disable_block_until_head_timeout),
+      priority_(priority) {
   is_likely_ahead_of_prerender_ =
       CalculateIsLikelyAheadOfPrerender(*preload_pipeline_info_);
 
@@ -1739,6 +1747,20 @@
       net::SiteForCookies::FromOrigin(origin));
 
   auto priority = [&] {
+    if (GetPrefetchPriority().has_value()) {
+      switch (GetPrefetchPriority().value()) {
+        case PrefetchPriority::kLow:
+          return net::RequestPriority::IDLE;
+        case PrefetchPriority::kMedium:
+          return net::RequestPriority::LOW;
+        case PrefetchPriority::kHigh:
+          return net::RequestPriority::MEDIUM;
+        case PrefetchPriority::kHighest:
+          return net::RequestPriority::HIGHEST;
+      }
+    }
+
+    // TODO(crbug.com/426404355): Migrate to use `PrefetchPriority`.
     if (IsSpeculationRuleType(prefetch_type_.trigger_type())) {
       // This may seem inverted (surely immediate prefetches would be higher
       // priority), but the fact that we're doing this at all for more
diff --git a/content/browser/preloading/prefetch/prefetch_container.h b/content/browser/preloading/prefetch/prefetch_container.h
index 3b97186..37227a8 100644
--- a/content/browser/preloading/prefetch/prefetch_container.h
+++ b/content/browser/preloading/prefetch/prefetch_container.h
@@ -24,6 +24,7 @@
 #include "content/browser/preloading/speculation_rules/speculation_rules_tags.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/global_routing_id.h"
+#include "content/public/browser/prefetch_priority.h"
 #include "content/public/browser/prefetch_request_status_listener.h"
 #include "content/public/browser/preload_pipeline_info.h"
 #include "content/public/browser/preloading.h"
@@ -112,6 +113,7 @@
       const blink::mojom::Referrer& referrer,
       std::optional<SpeculationRulesTags> speculation_rules_tags,
       std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+      std::optional<PrefetchPriority> priority,
       base::WeakPtr<PrefetchDocumentManager> prefetch_document_manager,
       scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
       base::WeakPtr<PreloadingAttempt> attempt = nullptr);
@@ -127,6 +129,7 @@
       const blink::mojom::Referrer& referrer,
       const std::optional<url::Origin>& referring_origin,
       std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+      std::optional<PrefetchPriority> priority,
       scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
       base::WeakPtr<PreloadingAttempt> attempt = nullptr,
       std::optional<PreloadingHoldbackStatus> holdback_status_override =
@@ -145,6 +148,7 @@
       bool javascript_enabled,
       const std::optional<url::Origin>& referring_origin,
       std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+      std::optional<PrefetchPriority> priority,
       base::WeakPtr<PreloadingAttempt> attempt = nullptr,
       const net::HttpRequestHeaders& additional_headers = {},
       std::unique_ptr<PrefetchRequestStatusListener> request_status_listener =
@@ -806,6 +810,10 @@
     return should_disable_block_until_head_timeout_;
   }
 
+  std::optional<PrefetchPriority> GetPrefetchPriority() const {
+    return priority_;
+  }
+
  protected:
   friend class PrefetchContainerTestBase;
 
@@ -838,7 +846,8 @@
       bool is_javascript_enabled,
       base::TimeDelta ttl,
       bool should_append_variations_header,
-      bool should_disable_block_until_head_timeout);
+      bool should_disable_block_until_head_timeout,
+      std::optional<PrefetchPriority> priority);
 
   // Update |prefetch_status_| and report prefetch status to
   // DevTools without updating TriggeringOutcome.
@@ -1118,6 +1127,10 @@
   // `PrefetchBlockUntilHeadTimeout()` as a `prefetch_params`.
   const bool should_disable_block_until_head_timeout_ = false;
 
+  // An optimization hint indicating how quickly this prefetch should be
+  // available.
+  const std::optional<PrefetchPriority> priority_ = std::nullopt;
+
   // Timing information for metrics
   //
   // Constraint: That earlier one is null implies that later one is null.
diff --git a/content/browser/preloading/prefetch/prefetch_container_unittest.cc b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
index f4020c6d..1406290 100644
--- a/content/browser/preloading/prefetch/prefetch_container_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_container_unittest.cc
@@ -83,7 +83,8 @@
                      /*use_prefetch_proxy=*/true, options.eagerness),
         blink::mojom::Referrer(),
         std::make_optional(std::move(options.speculation_rules_tags)),
-        /*no_vary_search_hint=*/std::nullopt, options.prefetch_document_manager,
+        /*no_vary_search_hint=*/std::nullopt, /*priority=*/std::nullopt,
+        options.prefetch_document_manager,
         PreloadPipelineInfo::Create(
             /*planned_max_preloading_type=*/PreloadingType::kPrefetch));
   }
@@ -97,7 +98,7 @@
                      /*use_prefetch_proxy=*/true),
         test::kPreloadingEmbedderHistgramSuffixForTesting,
         blink::mojom::Referrer(), std::move(referring_origin),
-        /*no_vary_search_hint=*/std::nullopt,
+        /*no_vary_search_hint=*/std::nullopt, /*priority=*/std::nullopt,
         PreloadPipelineInfo::Create(
             /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
         /*attempt=*/nullptr);
@@ -116,6 +117,7 @@
         /*javascript_enabled=*/true,
         /*referring_origin=*/std::nullopt,
         /*no_vary_search_hint=*/std::nullopt,
+        /*priority=*/std::nullopt,
         /*attempt=*/nullptr, additional_headers,
         /*request_status_listener=*/nullptr, base::Minutes(10),
         should_append_additional_headers);
@@ -335,6 +337,7 @@
       blink::mojom::Referrer(),
       std::make_optional(SpeculationRulesTags({"example"})),
       /*no_vary_search_hint=*/std::nullopt,
+      /*priority=*/std::nullopt,
       /*prefetch_document_manager=*/nullptr,
       PreloadPipelineInfo::Create(
           /*planned_max_preloading_type=*/PreloadingType::kPrefetch));
@@ -365,6 +368,7 @@
       blink::mojom::Referrer(),
       /*referring_origin=*/std::nullopt,
       /*no_vary_search_hint=*/std::nullopt,
+      /*priority=*/std::nullopt,
       PreloadPipelineInfo::Create(
           /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
       /*attempt=*/nullptr);
diff --git a/content/browser/preloading/prefetch/prefetch_document_manager.cc b/content/browser/preloading/prefetch/prefetch_document_manager.cc
index 59c40d6..96e4490 100644
--- a/content/browser/preloading/prefetch/prefetch_document_manager.cc
+++ b/content/browser/preloading/prefetch/prefetch_document_manager.cc
@@ -271,8 +271,9 @@
   auto container = std::make_unique<PrefetchContainer>(
       static_cast<RenderFrameHostImpl&>(render_frame_host()), document_token_,
       url, prefetch_type, referrer, std::move(speculation_rules_tags),
-      std::move(no_vary_search_hint), weak_method_factory_.GetWeakPtr(),
-      std::move(preload_pipeline_info), attempt->GetWeakPtr());
+      std::move(no_vary_search_hint), /*priority=*/std::nullopt,
+      weak_method_factory_.GetWeakPtr(), std::move(preload_pipeline_info),
+      attempt->GetWeakPtr());
   DVLOG(1) << *container << ": created";
 
   referring_page_metrics_.prefetch_attempted_count++;
diff --git a/content/browser/preloading/prefetch/prefetch_scheduler.cc b/content/browser/preloading/prefetch/prefetch_scheduler.cc
index 03f44ee..7dbbbc6 100644
--- a/content/browser/preloading/prefetch/prefetch_scheduler.cc
+++ b/content/browser/preloading/prefetch/prefetch_scheduler.cc
@@ -49,21 +49,33 @@
   return GetActiveSetSizeLimitForBase();
 }
 
-PrefetchPriority CalculatePriorityImpl(
+PrefetchSchedulerPriority CalculatePriorityImpl(
     const PrefetchContainer& prefetch_container) {
+  if (prefetch_container.GetPrefetchPriority().has_value()) {
+    switch (prefetch_container.GetPrefetchPriority().value()) {
+      case PrefetchPriority::kLow:
+      case PrefetchPriority::kMedium:
+      case PrefetchPriority::kHigh:
+        return PrefetchSchedulerPriority::kBase;
+      case PrefetchPriority::kHighest:
+        return PrefetchSchedulerPriority::kBurstForPrefetchPriority;
+    }
+  }
+
   // Burst/prioritize if ahead of prerender.
+  // TODO(crbug.com/426404355): Migrate to use `PrefetchPriority`.
   if (prefetch_container.IsLikelyAheadOfPrerender()) {
     switch (features::kPrerender2FallbackPrefetchSchedulerPolicy.Get()) {
       case features::Prerender2FallbackPrefetchSchedulerPolicy::kNotUse:
         break;
       case features::Prerender2FallbackPrefetchSchedulerPolicy::kPrioritize:
-        return PrefetchPriority::kHighAheadOfPrerender;
+        return PrefetchSchedulerPriority::kHighAheadOfPrerender;
       case features::Prerender2FallbackPrefetchSchedulerPolicy::kBurst:
-        return PrefetchPriority::kBurstAheadOfPrerender;
+        return PrefetchSchedulerPriority::kBurstAheadOfPrerender;
     }
   }
 
-  return PrefetchPriority::kBase;
+  return PrefetchSchedulerPriority::kBase;
 }
 
 bool IsReadyToStartPrefetch(const PrefetchQueue::Item& item) {
@@ -105,7 +117,7 @@
 }  // namespace
 
 PrefetchQueue::Item::Item(base::WeakPtr<PrefetchContainer> prefetch_container,
-                          PrefetchPriority priority)
+                          PrefetchSchedulerPriority priority)
     : prefetch_container(std::move(prefetch_container)), priority(priority) {}
 
 PrefetchQueue::Item::Item(const PrefetchQueue::Item&& other)
@@ -127,7 +139,7 @@
 PrefetchQueue::~PrefetchQueue() = default;
 
 void PrefetchQueue::Push(base::WeakPtr<PrefetchContainer> prefetch_container,
-                         PrefetchPriority priority) {
+                         PrefetchSchedulerPriority priority) {
   CHECK(prefetch_container);
   // Postcondition: Pushing registered one is not allowed.
   CHECK(!Remove(prefetch_container));
@@ -153,7 +165,7 @@
 }
 
 bool PrefetchQueue::MaybeUpdatePriority(PrefetchContainer& prefetch_container,
-                                        PrefetchPriority priority) {
+                                        PrefetchSchedulerPriority priority) {
   for (auto it = queue_.cbegin(); it != queue_.cend(); ++it) {
     if (it->prefetch_container.get() == &prefetch_container) {
       if (it->priority != priority) {
@@ -185,7 +197,7 @@
   return false;
 }
 
-PrefetchPriority PrefetchScheduler::CalculatePriority(
+PrefetchSchedulerPriority PrefetchScheduler::CalculatePriority(
     const PrefetchContainer& prefetch_container) {
   if (calculate_priority_for_test_) {
     return calculate_priority_for_test_.Run(prefetch_container);
@@ -202,7 +214,7 @@
     }
   }
 
-  PrefetchPriority priority = CalculatePriority(prefetch_container);
+  PrefetchSchedulerPriority priority = CalculatePriority(prefetch_container);
   queue_.Push(prefetch_container.GetWeakPtr(), priority);
 
   Progress();
@@ -217,7 +229,7 @@
     }
   }
 
-  PrefetchPriority priority = CalculatePriority(prefetch_container);
+  PrefetchSchedulerPriority priority = CalculatePriority(prefetch_container);
   queue_.Push(prefetch_container.GetWeakPtr(), priority);
 
   ProgressAsync();
@@ -306,7 +318,7 @@
   // priority. See
   // https://chromium-review.googlesource.com/c/chromium/src/+/6402914/comment/8b5c845f_0b7f6f7e/
 
-  auto internal = [&](PrefetchPriority threshold_priority,
+  auto internal = [&](PrefetchSchedulerPriority threshold_priority,
                       size_t active_limit) {
     // Invariant: `active_set_.size() == 0 && there is a ready prefetch` is
     // false. I.e. doesn't stuck.
@@ -334,8 +346,9 @@
     }
   };
 
-  internal(PrefetchPriority::kBurstThreshold, GetActiveSetSizeLimitForBurst());
-  internal(PrefetchPriority::kBase, GetActiveSetSizeLimitForBase());
+  internal(PrefetchSchedulerPriority::kBurstThreshold,
+           GetActiveSetSizeLimitForBurst());
+  internal(PrefetchSchedulerPriority::kBase, GetActiveSetSizeLimitForBase());
 }
 
 void PrefetchScheduler::ProgressOne(
@@ -371,7 +384,7 @@
 }
 
 void PrefetchScheduler::SetCalculatePriorityForTesting(
-    base::RepeatingCallback<PrefetchPriority(const PrefetchContainer&)>
+    base::RepeatingCallback<PrefetchSchedulerPriority(const PrefetchContainer&)>
         callback) {
   calculate_priority_for_test_ = std::move(callback);
 }
diff --git a/content/browser/preloading/prefetch/prefetch_scheduler.h b/content/browser/preloading/prefetch/prefetch_scheduler.h
index 93ea5f7..b8d06bf 100644
--- a/content/browser/preloading/prefetch/prefetch_scheduler.h
+++ b/content/browser/preloading/prefetch/prefetch_scheduler.h
@@ -33,10 +33,11 @@
 //
 // TODO(crbug.com/406403063): Rethink about granularity. We don't stick on the
 // above policy. More rough priorities e.g. kBase/kHigh/kBurst might work well.
+// This will eventually be done by `PrefetchPriority`(crbug.com/426404355).
 //
 // See also `PrefetchScheduler::NotifyAttributeMightChangedAndProgressAsync()`
 // when you add a new one.
-enum class PrefetchPriority {
+enum class PrefetchSchedulerPriority {
   // Default.
   kBase = 0,
   // For tests. Do not use outside tests.
@@ -49,8 +50,10 @@
   kBurstThreshold = 10,
   // For tests. Do not use outside tests.
   kBurstTest = 11,
+  // Priority derived from `PrefetchPriority`.
+  kBurstForPrefetchPriority = 12,
   // Burst priority for prefetch ahead of prerender.
-  kBurstAheadOfPrerender = 12,
+  kBurstAheadOfPrerender = 13,
 };
 
 // Priority queue for prefetches
@@ -61,7 +64,7 @@
  public:
   struct Item {
     Item(base::WeakPtr<PrefetchContainer> prefetch_container,
-         PrefetchPriority priority);
+         PrefetchSchedulerPriority priority);
     ~Item();
 
     // Movable but not copyable.
@@ -71,7 +74,7 @@
     Item& operator=(const Item&) = delete;
 
     base::WeakPtr<PrefetchContainer> prefetch_container;
-    PrefetchPriority priority;
+    PrefetchSchedulerPriority priority;
   };
 
   PrefetchQueue();
@@ -86,7 +89,7 @@
   size_t size() const { return queue_.size(); }
 
   void Push(base::WeakPtr<PrefetchContainer> prefetch_container,
-            PrefetchPriority priority);
+            PrefetchSchedulerPriority priority);
 
   // Pops `PrefetchContainer`
   //
@@ -95,8 +98,9 @@
   // - `pred`
   // - Priority is larger or equal to `threshold_priority`.
   template <class Predicate>
-  std::optional<PrefetchQueue::Item> Pop(Predicate pred,
-                                         PrefetchPriority threshold_priority) {
+  std::optional<PrefetchQueue::Item> Pop(
+      Predicate pred,
+      PrefetchSchedulerPriority threshold_priority) {
     for (auto it = queue_.cbegin(); it != queue_.cend(); ++it) {
       if (it->priority < threshold_priority) {
         break;
@@ -120,7 +124,7 @@
   //
   // Returns true iff priority is updated.
   bool MaybeUpdatePriority(PrefetchContainer& prefetch_container,
-                           PrefetchPriority priority);
+                           PrefetchSchedulerPriority priority);
 
  private:
   std::vector<PrefetchQueue::Item> queue_;
@@ -189,11 +193,11 @@
   void Progress();
 
   void SetCalculatePriorityForTesting(
-      base::RepeatingCallback<PrefetchPriority(const PrefetchContainer&)>
-          callback);
+      base::RepeatingCallback<
+          PrefetchSchedulerPriority(const PrefetchContainer&)> callback);
 
  private:
-  PrefetchPriority CalculatePriority(
+  PrefetchSchedulerPriority CalculatePriority(
       const PrefetchContainer& prefetch_container);
 
   void ProgressAsync();
@@ -218,7 +222,7 @@
   // `PrefetchScheduler::ProgressAsync()`.
   bool in_eviction_ = false;
 
-  base::RepeatingCallback<PrefetchPriority(const PrefetchContainer&)>
+  base::RepeatingCallback<PrefetchSchedulerPriority(const PrefetchContainer&)>
       calculate_priority_for_test_;
 
 #if DCHECK_IS_ON()
diff --git a/content/browser/preloading/prefetch/prefetch_service.cc b/content/browser/preloading/prefetch/prefetch_service.cc
index 094f6543..1959097 100644
--- a/content/browser/preloading/prefetch/prefetch_service.cc
+++ b/content/browser/preloading/prefetch/prefetch_service.cc
@@ -2103,17 +2103,20 @@
 void PrefetchService::EvictPrefetchesForBrowsingDataRemoval(
     const StoragePartition::StorageKeyMatcherFunction& storage_key_filter,
     PrefetchStatus status) {
-  // TODO(crbug.com/40262310): Handle for prefetches from non-SpeculationRules
   std::vector<base::WeakPtr<PrefetchContainer>> prefetches_to_reset;
   for (const auto& prefetch_iter : owned_prefetches_) {
     base::WeakPtr<PrefetchContainer> prefetch_container =
         prefetch_iter.second->GetWeakPtr();
     CHECK(prefetch_container);
-    std::optional<url::Origin> referring_origin =
-        prefetch_container->GetReferringOrigin();
-    if (referring_origin.has_value() &&
-        storage_key_filter.Run(
-            blink::StorageKey::CreateFirstParty(referring_origin.value()))) {
+
+    // If `referring_origin` is std::nullopt (e.g some browser-initiated
+    // prefetch), use the origin of the prefetch URL itself, since we generally
+    // handle no referring origin prefetches as a same-origin prefetch fashion.
+    const url::Origin target_origin =
+        prefetch_container->GetReferringOrigin().value_or(
+            url::Origin::Create(prefetch_container->GetURL()));
+    if (storage_key_filter.Run(
+            blink::StorageKey::CreateFirstParty(target_origin))) {
       prefetch_container->SetPrefetchStatus(status);
       prefetches_to_reset.push_back(prefetch_container);
     }
diff --git a/content/browser/preloading/prefetch/prefetch_service_unittest.cc b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
index 6688eb0..d6d6dfc9 100644
--- a/content/browser/preloading/prefetch/prefetch_service_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_service_unittest.cc
@@ -537,6 +537,7 @@
         test::kPreloadingEmbedderHistgramSuffixForTesting, referrer,
         std::move(referring_origin),
         /*no_vary_search_hint=*/std::nullopt,
+        /*priority=*/std::nullopt,
         PreloadPipelineInfo::Create(
             /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
         /*attempt=*/nullptr);
@@ -555,7 +556,7 @@
       bool should_disable_block_until_head_timeout = false) {
     return browser_context()->StartBrowserPrefetchRequest(
         url, test::kPreloadingEmbedderHistgramSuffixForTesting, true,
-        no_vary_search_data, additional_headers,
+        no_vary_search_data, /*priority=*/std::nullopt, additional_headers,
         std::move(request_status_listener), ttl,
         /*should_append_variations_header=*/true,
         should_disable_block_until_head_timeout);
@@ -5589,6 +5590,74 @@
       0);
 }
 
+// Tests that browsing data removal for prefetch is performed per 1) its
+// `referring_origin` 2) if that is std::nullopt, then prefetch url.
+TEST_P(PrefetchServiceTest, PrefetchEviction) {
+  base::HistogramTester histogram_tester;
+
+  struct TestCase {
+    const std::optional<url::Origin> referring_origin;
+    const GURL prefetch_url;
+  };
+  const std::vector<TestCase> test_cases = {
+      {url::Origin::Create(GURL("https://a.test")), GURL("https://a.test/0")},
+      {url::Origin::Create(GURL("https://a.test")), GURL("https://b.test/1")},
+      {url::Origin::Create(GURL("https://b.test")), GURL("https://a.test/2")},
+      {url::Origin::Create(GURL("https://b.test")), GURL("https://b.test/3")},
+      {std::nullopt, GURL("https://a.test/4")},
+      {std::nullopt, GURL("https://b.test/5")},
+  };
+
+  MakePrefetchService(
+      std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
+          /*num_on_prefetch_likely_calls=*/std::nullopt));
+  PrefetchService* prefetch_service =
+      BrowserContextImpl::From(browser_context())->GetPrefetchService();
+
+  std::vector<std::unique_ptr<PrefetchHandle>> handles;
+  for (const auto& test_case : test_cases) {
+    handles.push_back(
+        MakePrefetchFromEmbedder(test_case.prefetch_url,
+                                 PrefetchType(PreloadingTriggerType::kEmbedder,
+                                              /*use_prefetch_proxy=*/false),
+                                 /*referrer=*/{}, test_case.referring_origin));
+  }
+  task_environment()->RunUntilIdle();
+
+  // Evict prefetches from "a.test". The prefetch for "a.test" with no
+  // `referring_origin` should also be removed.
+  auto filter_builder = BrowsingDataFilterBuilder::Create(
+      BrowsingDataFilterBuilder::Mode::kDelete);
+  filter_builder->AddOrigin(url::Origin::Create(GURL("https://a.test")));
+  auto filter = filter_builder->BuildStorageKeyFilter();
+  prefetch_service->EvictPrefetchesForBrowsingDataRemoval(
+      filter, PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved);
+  task_environment()->RunUntilIdle();
+  EXPECT_FALSE(handles[0]->IsAlive());
+  EXPECT_FALSE(handles[1]->IsAlive());
+  EXPECT_FALSE(handles[4]->IsAlive());
+  EXPECT_TRUE(handles[2]->IsAlive());
+  EXPECT_TRUE(handles[3]->IsAlive());
+  EXPECT_TRUE(handles[5]->IsAlive());
+  histogram_tester.ExpectUniqueSample(
+      "Preloading.Prefetch.PrefetchStatus",
+      PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 3);
+
+  // Attempt to clear all the cache. The remaining prefetches are also removed.
+  prefetch_service->EvictPrefetchesForBrowsingDataRemoval(
+      BrowsingDataFilterBuilder::Create(
+          BrowsingDataFilterBuilder::Mode::kPreserve)
+          ->BuildStorageKeyFilter(),
+      PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved);
+  task_environment()->RunUntilIdle();
+  EXPECT_FALSE(handles[2]->IsAlive());
+  EXPECT_FALSE(handles[3]->IsAlive());
+  EXPECT_FALSE(handles[5]->IsAlive());
+  histogram_tester.ExpectUniqueSample(
+      "Preloading.Prefetch.PrefetchStatus",
+      PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 6);
+}
+
 // Tests that the prefetch eviction for eligible but not started triggers (i.e.
 // `PreloadingAttempt`'s `PreloadingHoldbackStatus` is `kUnspecified`) causes no
 // crash. This is a regression test of crbug.com/404703517.
@@ -7646,6 +7715,7 @@
         prefetch_url, std::move(prefetch_type), blink::mojom::Referrer(),
         std::make_optional(SpeculationRulesTags()),
         /*no_vary_search_hint=*/std::nullopt,
+        /*priority=*/std::nullopt,
         /*prefetch_document_manager=*/nullptr,
         PreloadPipelineInfo::Create(planned_max_preloading_type),
         attempt->GetWeakPtr());
@@ -7920,10 +7990,10 @@
           base::BindRepeating([](const PrefetchContainer& prefetch_container) {
             if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
                     "?prioritize=1")) {
-              return PrefetchPriority::kHighTest;
+              return PrefetchSchedulerPriority::kHighTest;
             }
 
-            return PrefetchPriority::kBase;
+            return PrefetchSchedulerPriority::kBase;
           }));
 
   const auto url_1 = GURL("https://example.com/one");
@@ -8000,10 +8070,10 @@
           base::BindRepeating([](const PrefetchContainer& prefetch_container) {
             if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
                     "?prioritize=1")) {
-              return PrefetchPriority::kHighTest;
+              return PrefetchSchedulerPriority::kHighTest;
             }
 
-            return PrefetchPriority::kBase;
+            return PrefetchSchedulerPriority::kBase;
           }));
 
   const auto url_1 = GURL("https://example.com/one");
@@ -8064,10 +8134,10 @@
           base::BindRepeating([](const PrefetchContainer& prefetch_container) {
             if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
                     "?burst=1")) {
-              return PrefetchPriority::kBurstTest;
+              return PrefetchSchedulerPriority::kBurstTest;
             }
 
-            return PrefetchPriority::kBase;
+            return PrefetchSchedulerPriority::kBase;
           }));
 
   const auto url_1 = GURL("https://example.com/one");
@@ -8169,10 +8239,10 @@
           base::BindRepeating([](const PrefetchContainer& prefetch_container) {
             if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
                     "?burst=1")) {
-              return PrefetchPriority::kBurstTest;
+              return PrefetchSchedulerPriority::kBurstTest;
             }
 
-            return PrefetchPriority::kBase;
+            return PrefetchSchedulerPriority::kBase;
           }));
 
   const auto url_1 = GURL("https://example.com/one");
@@ -8274,10 +8344,10 @@
           base::BindRepeating([](const PrefetchContainer& prefetch_container) {
             if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
                     "?burst=1")) {
-              return PrefetchPriority::kBurstTest;
+              return PrefetchSchedulerPriority::kBurstTest;
             }
 
-            return PrefetchPriority::kBase;
+            return PrefetchSchedulerPriority::kBase;
           }));
 
   const auto url_1 = GURL("https://example.com/one");
diff --git a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor_unittest.cc b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor_unittest.cc
index e6ee973..0b076d0f9 100644
--- a/content/browser/preloading/prefetch/prefetch_url_loader_interceptor_unittest.cc
+++ b/content/browser/preloading/prefetch/prefetch_url_loader_interceptor_unittest.cc
@@ -389,6 +389,7 @@
         std::move(prefetch_type), blink::mojom::Referrer(),
         std::make_optional(SpeculationRulesTags()),
         /*no_vary_search_hint=*/std::nullopt,
+        /*priority=*/std::nullopt,
         /*prefetch_document_manager=*/nullptr,
         PreloadPipelineInfo::Create(
             /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
@@ -411,6 +412,7 @@
         test::kPreloadingEmbedderHistgramSuffixForTesting,
         blink::mojom::Referrer(), std::move(referring_origin),
         /*no_vary_search_hint=*/std::nullopt,
+        /*priority=*/std::nullopt,
         PreloadPipelineInfo::Create(
             /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
 
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index 146afc55..f51ebe9 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -2825,6 +2825,61 @@
 
   void TestActivateOnWindowOpen(std::string_view window_features);
 
+  std::string SpeculationRulesInsertionScriptWithBothTargetHint(GURL url) {
+    constexpr char add_speculationrules_with_both_target_hints[] = R"({
+      var script = document.createElement('script');
+      script.type = 'speculationrules';
+      script.text = `{"prerender": [
+          {"target_hint": "_self", "urls": ["$1"]},
+          {"target_hint": "_blank", "urls": ["$1"]}
+        ]
+      }`;
+      document.head.appendChild(script);
+    })";
+
+    return base::ReplaceStringPlaceholders(
+        add_speculationrules_with_both_target_hints, {url.spec()}, nullptr);
+  }
+
+  std::string SpeculationRulesInsertionScriptWithOneSelfAndTwoBlankTargetHint(
+      GURL url) {
+    constexpr char add_speculationrules_with_both_target_hints[] = R"({
+      var script = document.createElement('script');
+      script.type = 'speculationrules';
+      script.text = `{"prerender": [
+          {"target_hint": "_self", "urls": ["$1"]},
+          {"target_hint": "_blank", "urls": ["$1"]},
+          {"target_hint": "_blank", "urls": ["$1"]}
+        ]
+      }`;
+      document.head.appendChild(script);
+    })";
+
+    return base::ReplaceStringPlaceholders(
+        add_speculationrules_with_both_target_hints, {url.spec()}, nullptr);
+  }
+
+  std::string SpeculationRulesWithIdAndTargetHint(GURL url,
+                                                  std::string id,
+                                                  std::string target_hint) {
+    constexpr char add_speculationrules_with_id_and_target_hint[] = R"({
+      var script = document.createElement('script');
+      script.type = 'speculationrules';
+      script.id = '$1';
+      script.text = `{"prerender":
+                      [{
+                        "target_hint": "$2",
+                        "urls": ["$3"]
+                      }]
+      }`;
+      document.head.appendChild(script);
+    })";
+
+    return base::ReplaceStringPlaceholders(
+        add_speculationrules_with_id_and_target_hint,
+        {id, target_hint, url.spec()}, nullptr);
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -3413,6 +3468,275 @@
   EXPECT_EQ(web_contents()->GetLastCommittedURL(), initial_url);
 }
 
+// Tests that adding speculation rules with both target_hint and removes
+// target_hint=_self won't affect prerender activation on target_hint=_blank.
+IN_PROC_BROWSER_TEST_F(PrerenderTargetHintEnabledBrowserTest,
+                       PrerenderBothTargetHintButRemovesTargetHintSelf) {
+  const GURL initial_url = GetUrl("/simple_links.html");
+  const GURL prerendering_url = GetUrl("/title2.html");
+
+  const std::string add_speculation_rules_target_hint_self_script =
+      SpeculationRulesWithIdAndTargetHint(prerendering_url, "self_specrules",
+                                          "_self");
+  const std::string add_speculation_rules_target_hint_blank_script =
+      SpeculationRulesWithIdAndTargetHint(prerendering_url, "blank_specrules",
+                                          "_blank");
+
+  // Navigate to an initial page which has a link to `prerendering_url`.
+  ASSERT_TRUE(NavigateToURL(shell(), initial_url));
+
+  // Adding speculation rules with target_hint=_self.
+  EXPECT_TRUE(
+      ExecJs(web_contents(), add_speculation_rules_target_hint_self_script));
+  prerender_helper()->WaitForPrerenderLoadCompletion(*web_contents(),
+                                                     prerendering_url);
+
+  // Adding speculation rules with target_hint=_blank.
+  WebContents* new_tab_prerender_web_contents = nullptr;
+  base::RunLoop run_loop;
+
+  auto creation_subscription = RegisterWebContentsCreationCallback(
+      base::BindLambdaForTesting([&](content::WebContents* web_contents) {
+        new_tab_prerender_web_contents = web_contents;
+        run_loop.QuitClosure().Run();
+      }));
+
+  EXPECT_TRUE(
+      ExecJs(web_contents(), add_speculation_rules_target_hint_blank_script));
+  // Wait for the new tab prerender.
+  run_loop.Run();
+
+  prerender_helper()->WaitForPrerenderLoadCompletion(
+      *new_tab_prerender_web_contents, prerendering_url);
+
+  ASSERT_NE(new_tab_prerender_web_contents, web_contents_impl());
+  ExpectWebContentsIsForNewTabPrerendering(*new_tab_prerender_web_contents);
+
+  ASSERT_TRUE(
+      ExecJs(web_contents_impl()->GetPrimaryMainFrame(),
+             "document.querySelector('script[id=self_specrules]').remove()"));
+
+  // Speculationrules removal for the initiator tab shouldn't cancel the
+  // prerender in the new WebContents.
+  EXPECT_FALSE(HasHostForUrl(*web_contents(), prerendering_url));
+  EXPECT_TRUE(HasHostForUrl(*new_tab_prerender_web_contents, prerendering_url));
+
+  // Open a new window with "_blank" and `noopener`. This should activate
+  // the prerendered page.
+  test::PrerenderHostObserver new_tab_prerender_observer(
+      *new_tab_prerender_web_contents, prerendering_url);
+
+  {
+    const std::string new_tab_opener_script =
+        "window.open(\"title2.html\", \"_blank\", \"noopener\")";
+    EXPECT_TRUE(ExecJs(web_contents(), new_tab_opener_script));
+    new_tab_prerender_observer.WaitForActivation();
+    EXPECT_EQ(new_tab_prerender_web_contents->GetLastCommittedURL(),
+              prerendering_url);
+    EXPECT_FALSE(
+        HasHostForUrl(*new_tab_prerender_web_contents, prerendering_url));
+  }
+}
+
+// Tests that adding speculation rules with both target_hint and removes
+// target_hint=_blank won't affect prerender activation on target_hint=_self.
+IN_PROC_BROWSER_TEST_F(PrerenderTargetHintEnabledBrowserTest,
+                       PrerenderBothTargetHintButRemovesTargetHintBlank) {
+  const GURL initial_url = GetUrl("/simple_links.html");
+  const GURL prerendering_url = GetUrl("/title2.html");
+
+  const std::string add_speculation_rules_target_hint_self_script =
+      SpeculationRulesWithIdAndTargetHint(prerendering_url, "self_specrules",
+                                          "_self");
+  const std::string add_speculation_rules_target_hint_blank_script =
+      SpeculationRulesWithIdAndTargetHint(prerendering_url, "blank_specrules",
+                                          "_blank");
+
+  // Navigate to an initial page which has a link to `prerendering_url`.
+  ASSERT_TRUE(NavigateToURL(shell(), initial_url));
+
+  // Adding speculation rules with target_hint=_self.
+  EXPECT_TRUE(
+      ExecJs(web_contents(), add_speculation_rules_target_hint_self_script));
+  prerender_helper()->WaitForPrerenderLoadCompletion(*web_contents(),
+                                                     prerendering_url);
+  // Adding speculation rules with target_hint=_blank.
+  WebContents* new_tab_prerender_web_contents = nullptr;
+  base::RunLoop run_loop;
+
+  auto creation_subscription = RegisterWebContentsCreationCallback(
+      base::BindLambdaForTesting([&](content::WebContents* web_contents) {
+        new_tab_prerender_web_contents = web_contents;
+        run_loop.QuitClosure().Run();
+      }));
+
+  EXPECT_TRUE(
+      ExecJs(web_contents(), add_speculation_rules_target_hint_blank_script));
+  // Wait for the new tab prerender.
+  run_loop.Run();
+
+  prerender_helper()->WaitForPrerenderLoadCompletion(
+      *new_tab_prerender_web_contents, prerendering_url);
+
+  ASSERT_NE(new_tab_prerender_web_contents, web_contents_impl());
+  ExpectWebContentsIsForNewTabPrerendering(*new_tab_prerender_web_contents);
+  FrameTreeNodeId new_tab_host_id = test::PrerenderTestHelper::GetHostForUrl(
+      *new_tab_prerender_web_contents, prerendering_url);
+
+  test::PrerenderHostObserver new_tab_prerender_observer(
+      *new_tab_prerender_web_contents, new_tab_host_id);
+
+  ASSERT_TRUE(
+      ExecJs(web_contents_impl()->GetPrimaryMainFrame(),
+             "document.querySelector('script[id=blank_specrules]').remove()"));
+
+  // Speculationrules removal for the new tab shouldn't cancel the prerender in
+  // the initial WebContents.
+  EXPECT_TRUE(HasHostForUrl(*web_contents(), prerendering_url));
+  new_tab_prerender_observer.WaitForDestroyed();
+
+  test::PrerenderHostObserver prerender_observer(*web_contents(),
+                                                 prerendering_url);
+
+  // The prerender in the initial WebContents should be able to be activated.
+  {
+    ASSERT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
+                       JsReplace("location = $1", prerendering_url)));
+    prerender_observer.WaitForActivation();
+  }
+}
+
+// Tests that speculation rules with both target_hint in one script section can
+// be prerendered and activated correctly.
+IN_PROC_BROWSER_TEST_F(PrerenderTargetHintEnabledBrowserTest,
+                       ActivateOnBothTargetHint) {
+  const GURL initial_url = GetUrl("/simple_links.html");
+  const GURL prerendering_url = GetUrl("/title2.html");
+
+  const std::string add_speculation_rules_script =
+      SpeculationRulesInsertionScriptWithBothTargetHint(prerendering_url);
+
+  // Navigate to an initial page which has a link to `prerendering_url`.
+  ASSERT_TRUE(NavigateToURL(shell(), initial_url));
+
+  // Start prerendering `prerendering_url`.
+  WebContents* new_tab_prerender_web_contents = nullptr;
+  base::RunLoop run_loop;
+
+  auto creation_subscription = RegisterWebContentsCreationCallback(
+      base::BindLambdaForTesting([&](content::WebContents* web_contents) {
+        new_tab_prerender_web_contents = web_contents;
+        run_loop.QuitClosure().Run();
+      }));
+
+  EXPECT_TRUE(ExecJs(web_contents(), add_speculation_rules_script));
+  // Wait for the new tab prerender.
+  run_loop.Run();
+
+  prerender_helper()->WaitForPrerenderLoadCompletion(*web_contents(),
+                                                     prerendering_url);
+  prerender_helper()->WaitForPrerenderLoadCompletion(
+      *new_tab_prerender_web_contents, prerendering_url);
+
+  ASSERT_NE(new_tab_prerender_web_contents, web_contents_impl());
+  ExpectWebContentsIsForNewTabPrerendering(*new_tab_prerender_web_contents);
+
+  // Open a new window with "_blank" and `noopener`. This should activate
+  // the prerendered page.
+  test::PrerenderHostObserver new_tab_prerender_observer(
+      *new_tab_prerender_web_contents, prerendering_url);
+  test::PrerenderHostObserver prerender_observer(*web_contents(),
+                                                 prerendering_url);
+
+  {
+    const std::string new_tab_opener_script =
+        "window.open(\"title2.html\", \"_blank\", \"noopener\")";
+    EXPECT_TRUE(ExecJs(web_contents(), new_tab_opener_script));
+    new_tab_prerender_observer.WaitForActivation();
+    EXPECT_EQ(new_tab_prerender_web_contents->GetLastCommittedURL(),
+              prerendering_url);
+
+    // Prerender activation in the new tab shouldn't cancel the prerender in
+    // the initial WebContents.
+    EXPECT_TRUE(HasHostForUrl(*web_contents(), prerendering_url));
+    EXPECT_FALSE(
+        HasHostForUrl(*new_tab_prerender_web_contents, prerendering_url));
+  }
+
+  // The prerender in the initial WebContents should be able to be activated.
+  {
+    ASSERT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
+                       JsReplace("location = $1", prerendering_url)));
+    prerender_observer.WaitForActivation();
+  }
+}
+
+// Tests that speculation rules with 1 _self and 2 _blank target_hints in one
+// script section can be prerendered and activated correctly.
+IN_PROC_BROWSER_TEST_F(PrerenderTargetHintEnabledBrowserTest,
+                       ActivateOnOneSelfAndTwoBlankTargetHint) {
+  const GURL initial_url = GetUrl("/simple_links.html");
+  const GURL prerendering_url = GetUrl("/title2.html");
+
+  const std::string add_speculation_rules_script =
+      SpeculationRulesInsertionScriptWithOneSelfAndTwoBlankTargetHint(
+          prerendering_url);
+
+  // Navigate to an initial page which has a link to `prerendering_url`.
+  ASSERT_TRUE(NavigateToURL(shell(), initial_url));
+
+  // Start prerendering `prerendering_url`.
+  WebContents* new_tab_prerender_web_contents = nullptr;
+  base::RunLoop run_loop;
+
+  auto creation_subscription = RegisterWebContentsCreationCallback(
+      base::BindLambdaForTesting([&](content::WebContents* web_contents) {
+        new_tab_prerender_web_contents = web_contents;
+        run_loop.QuitClosure().Run();
+      }));
+
+  EXPECT_TRUE(ExecJs(web_contents(), add_speculation_rules_script));
+  // Wait for the new tab prerender.
+  run_loop.Run();
+
+  prerender_helper()->WaitForPrerenderLoadCompletion(*web_contents(),
+                                                     prerendering_url);
+  prerender_helper()->WaitForPrerenderLoadCompletion(
+      *new_tab_prerender_web_contents, prerendering_url);
+
+  ASSERT_NE(new_tab_prerender_web_contents, web_contents_impl());
+  ExpectWebContentsIsForNewTabPrerendering(*new_tab_prerender_web_contents);
+
+  // Open a new window with "_blank" and `noopener`. This should activate
+  // the prerendered page.
+  test::PrerenderHostObserver new_tab_prerender_observer(
+      *new_tab_prerender_web_contents, prerendering_url);
+  test::PrerenderHostObserver prerender_observer(*web_contents(),
+                                                 prerendering_url);
+
+  {
+    const std::string new_tab_opener_script =
+        "window.open(\"title2.html\", \"_blank\", \"noopener\")";
+    EXPECT_TRUE(ExecJs(web_contents(), new_tab_opener_script));
+    new_tab_prerender_observer.WaitForActivation();
+    EXPECT_EQ(new_tab_prerender_web_contents->GetLastCommittedURL(),
+              prerendering_url);
+
+    // Prerender activation in the new tab shouldn't cancel the prerender in
+    // the initial WebContents.
+    EXPECT_TRUE(HasHostForUrl(*web_contents(), prerendering_url));
+    EXPECT_FALSE(
+        HasHostForUrl(*new_tab_prerender_web_contents, prerendering_url));
+  }
+
+  // The prerender in the initial WebContents should be able to be activated.
+  {
+    ASSERT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
+                       JsReplace("location = $1", prerendering_url)));
+    prerender_observer.WaitForActivation();
+  }
+}
+
 // Tests that window.open() annotated with "_blank" and "noopener" can activate
 // a prerender whose target_hint is "_blank".
 IN_PROC_BROWSER_TEST_F(PrerenderTargetHintEnabledBrowserTest,
diff --git a/content/browser/preloading/prerender/prerender_features.cc b/content/browser/preloading/prerender/prerender_features.cc
index 624bd79..51d4e4c 100644
--- a/content/browser/preloading/prerender/prerender_features.cc
+++ b/content/browser/preloading/prerender/prerender_features.cc
@@ -108,6 +108,13 @@
              "Prerender2DisallowNonTrustworthyHttp",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kPrerender2WarmUpCompositorForImmediate,
+             "Prerender2WarmUpCompositorForImmediate",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kPrerender2WarmUpCompositorForNonImmediate,
+             "Prerender2WarmUpCompositorForNonImmediate",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 bool UsePrefetchPrerenderIntegration() {
   return base::FeatureList::IsEnabled(
              features::kPrerender2FallbackPrefetchSpecRules) ||
diff --git a/content/browser/preloading/prerender/prerender_features.h b/content/browser/preloading/prerender/prerender_features.h
index ac07742b..8b32549 100644
--- a/content/browser/preloading/prerender/prerender_features.h
+++ b/content/browser/preloading/prerender/prerender_features.h
@@ -84,6 +84,11 @@
 
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kPrerender2DisallowNonTrustworthyHttp);
 
+// If enabled, requests the compositor warm-up (crbug.com/41496019) for
+// Immediate/non-Immediate Speculation Rules prerenders.
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kPrerender2WarmUpCompositorForImmediate);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kPrerender2WarmUpCompositorForNonImmediate);
+
 CONTENT_EXPORT bool UsePrefetchPrerenderIntegration();
 
 }  // namespace features
diff --git a/content/browser/preloading/prerenderer_impl.cc b/content/browser/preloading/prerenderer_impl.cc
index 0a2bc98a..c578e9b 100644
--- a/content/browser/preloading/prerenderer_impl.cc
+++ b/content/browser/preloading/prerenderer_impl.cc
@@ -9,6 +9,7 @@
 
 #include "content/browser/preloading/prerenderer_impl.h"
 
+#include <algorithm>
 #include <vector>
 
 #include "base/feature_list.h"
@@ -36,10 +37,48 @@
 struct PrerendererImpl::PrerenderInfo {
   blink::mojom::SpeculationInjectionType injection_type;
   blink::mojom::SpeculationEagerness eagerness;
+  bool is_target_blank;
   FrameTreeNodeId prerender_host_id;
   GURL url;
+
+  PrerenderInfo() = default;
+  explicit PrerenderInfo(
+      const blink::mojom::SpeculationCandidatePtr& candidate);
+
+  static bool PrerenderInfoComparator(const PrerenderInfo& p1,
+                                      const PrerenderInfo& p2);
+
+  bool operator<(const PrerenderInfo& p) const {
+    return PrerenderInfoComparator(*this, p);
+  }
+
+  bool operator==(const PrerenderInfo& p) const {
+    return !PrerenderInfoComparator(*this, p) &&
+           !PrerenderInfoComparator(p, *this);
+  }
 };
 
+bool PrerendererImpl::PrerenderInfo::PrerenderInfoComparator(
+    const PrerenderInfo& p1,
+    const PrerenderInfo& p2) {
+  if (p1.url != p2.url) {
+    return p1.url < p2.url;
+  }
+
+  return p1.is_target_blank < p2.is_target_blank;
+}
+
+// `prerender_host_id` is not provided by `SpeculationCandidatePtr`, so
+// FrameTreeNodeId() is assigned instead. The value should be updated once it is
+// available.
+PrerendererImpl::PrerenderInfo::PrerenderInfo(
+    const blink::mojom::SpeculationCandidatePtr& candidate)
+    : injection_type(candidate->injection_type),
+      eagerness(candidate->eagerness),
+      is_target_blank(candidate->target_browsing_context_name_hint ==
+                      blink::mojom::SpeculationTargetHint::kBlank),
+      url(candidate->url) {}
+
 PrerendererImpl::PrerendererImpl(RenderFrameHost& render_frame_host)
     : WebContentsObserver(WebContents::FromRenderFrameHost(&render_frame_host)),
       render_frame_host_(render_frame_host) {
@@ -97,8 +136,9 @@
     }
   }
 
-  std::ranges::stable_sort(prerender_candidates, std::less<>(),
-                           [](const auto& p) { return p.second->url; });
+  std::ranges::stable_sort(
+      prerender_candidates, std::less<>(),
+      [](const auto& p) { return PrerenderInfo(p.second); });
   std::vector<std::pair<size_t, blink::mojom::SpeculationCandidatePtr>>
       candidates_to_start;
 
@@ -108,36 +148,39 @@
 
   // Compare the sorted candidate and started prerender lists to one another.
   // Since they are sorted, we process the lexicographically earlier of the two
-  // URLs pointed at by the iterators, and compare the range of entries in each
-  // that match that URL.
+  // PrerenderInfos pointed at by the iterators, and compare the range of
+  // entries in each that match that PrerenderInfo.
   //
-  // URLs which are present in the prerender list but not the candidate list can
-  // no longer proceed and are cancelled.
+  // PrerenderInfos which are present in the prerender list but not the
+  // candidate list can no longer proceed and are cancelled.
   //
-  // URLs which are present in the candidate list but not the prerender list
-  // could be started and are gathered in `candidates_to_start`.
+  // PrerenderInfos which are present in the candidate list but not the
+  // prerender list could be started and are gathered in `candidates_to_start`.
   auto candidate_it = prerender_candidates.begin();
   auto started_it = started_prerenders_.begin();
   while (candidate_it != prerender_candidates.end() ||
          started_it != started_prerenders_.end()) {
-    // Select the lesser of the two URLs to diff.
-    GURL url;
-    if (started_it == started_prerenders_.end())
-      url = candidate_it->second->url;
-    else if (candidate_it == prerender_candidates.end())
-      url = started_it->url;
-    else
-      url = std::min(candidate_it->second->url, started_it->url);
+    // Select the lesser of the two PrerenderInfos to diff.
+    PrerenderInfo prerender_info;
+    if (started_it == started_prerenders_.end()) {
+      prerender_info = PrerenderInfo(candidate_it->second);
+    } else if (candidate_it == prerender_candidates.end()) {
+      prerender_info = *started_it;
+    } else {
+      prerender_info =
+          std::min(PrerenderInfo(candidate_it->second), *started_it);
+    }
 
-    // Select the ranges from both that match the URL in question.
+    // Select the ranges from both that match the PrerenderInfo in question.
     auto equal_prerender_end = std::ranges::find_if(
         started_it, started_prerenders_.end(),
-        [&](const auto& started) { return started.url != url; });
+        [&](const auto& started) { return started != prerender_info; });
     base::span<PrerenderInfo> matching_prerenders(started_it,
                                                   equal_prerender_end);
     auto equal_candidate_end = std::ranges::find_if(
-        candidate_it, prerender_candidates.end(),
-        [&](const auto& candidate) { return candidate.second->url != url; });
+        candidate_it, prerender_candidates.end(), [&](const auto& candidate) {
+          return PrerenderInfo(candidate.second) != prerender_info;
+        });
     base::span<std::pair<size_t, blink::mojom::SpeculationCandidatePtr>>
         matching_candidates(candidate_it, equal_candidate_end);
 
@@ -148,19 +191,30 @@
       }
       // TODO(jbroman): This doesn't currently care about other aspects, like
       // the referrer. This doesn't presently matter, but in the future we might
-      // want to cancel if there are candidates which match by URL but none of
-      // which permit this prerender.
+      // want to cancel if there are candidates which match by PrerenderInfo but
+      // none of which permit this prerender.
       if (matching_candidates.empty()) {
         removed_prerender_rules.push_back(prerender.prerender_host_id);
       }
     }
 
     // Decide what new candidates to start.
-    // For now, start the first candidate for a URL only if there are no
-    // matching prerenders. We could be cleverer in the future.
+    // For now, start one candidate per target hint for a URL only if there are
+    // no matching prerenders. We could be cleverer in the future.
     if (matching_prerenders.empty()) {
       CHECK(!matching_candidates.empty());
-      candidates_to_start.push_back(std::move(matching_candidates[0]));
+
+      std::set<PrerenderInfo> processed_prerender_info;
+
+      for (auto& matching_candidate : matching_candidates) {
+        PrerenderInfo matching_candidate_prerender_info =
+            PrerenderInfo(matching_candidate.second);
+        if (processed_prerender_info
+                .insert(std::move(matching_candidate_prerender_info))
+                .second) {
+          candidates_to_start.push_back(std::move(matching_candidate));
+        }
+      }
     }
 
     // Advance the iterators past all matching entries.
@@ -258,10 +312,13 @@
 
   auto& rfhi = static_cast<RenderFrameHostImpl&>(render_frame_host_.get());
 
+  // `prerender_host_id` is not available yet.
+  PrerenderInfo prerender_info(candidate);
+
   auto [begin, end] = std::ranges::equal_range(
-      started_prerenders_.begin(), started_prerenders_.end(), candidate->url,
-      std::less<>(), &PrerenderInfo::url);
-  // cannot currently start a second prerender with the same URL
+      started_prerenders_.begin(), started_prerenders_.end(), prerender_info,
+      PrerenderInfo::PrerenderInfoComparator);
+  // cannot currently start a second prerender with the same URL and target_hint
   if (begin != end) {
     return false;
   }
@@ -294,6 +351,18 @@
         candidate->no_vary_search_hint);
   }
 
+  const bool should_warm_up_compositor = [&] {
+    switch (candidate->eagerness) {
+      case blink::mojom::SpeculationEagerness::kImmediate:
+        return base::FeatureList::IsEnabled(
+            features::kPrerender2WarmUpCompositorForImmediate);
+      case blink::mojom::SpeculationEagerness::kModerate:
+      case blink::mojom::SpeculationEagerness::kConservative:
+        return base::FeatureList::IsEnabled(
+            features::kPrerender2WarmUpCompositorForNonImmediate);
+    }
+  }();
+
   PrerenderAttributes attributes(
       candidate->url,
       PreloadingTriggerTypeFromSpeculationInjectionType(
@@ -304,7 +373,7 @@
                              SpeculationRulesTags(candidate->tags)),
       Referrer{*candidate->referrer}, no_vary_search_hint, &rfhi,
       web_contents->GetWeakPtr(), ui::PAGE_TRANSITION_LINK,
-      /*should_warm_up_compositor=*/false,
+      should_warm_up_compositor,
       /*should_prepare_paint_tree=*/false,
       /*url_match_predicate=*/{},
       /*prerender_navigation_handle_callback=*/{},
@@ -316,7 +385,7 @@
           candidate->injection_type);
   PreloadingPredictor creating_predictor =
       GetPredictorForPreloadingTriggerType(trigger_type);
-  FrameTreeNodeId prerender_host_id = [&] {
+  prerender_info.prerender_host_id = [&] {
     // TODO(crbug.com/40235424): Handle the case where multiple speculation
     // rules have the same URL but its `target_browsing_context_name_hint` is
     // different. In the current implementation, only the first rule is
@@ -372,18 +441,20 @@
   // it is needed to re-calculate the right place here on `started_prerenders_`
   // for new candidates.
   end = std::ranges::upper_bound(started_prerenders_.begin(),
-                                 started_prerenders_.end(), candidate->url,
-                                 std::less<>(), &PrerenderInfo::url);
+                                 started_prerenders_.end(), prerender_info,
+                                 PrerenderInfo::PrerenderInfoComparator);
 
-  started_prerenders_.insert(end, {.injection_type = candidate->injection_type,
-                                   .eagerness = candidate->eagerness,
-                                   .prerender_host_id = prerender_host_id,
-                                   .url = candidate->url});
+  started_prerenders_.insert(end, std::move(prerender_info));
 
   return true;
 }
 
 bool PrerendererImpl::ShouldWaitForPrerenderResult(const GURL& url) {
+  // This function is used to check whetehr a prerender is started to avoid
+  // starting prefetch in OnPointerDown, OnPointerHover or other heuristic
+  // methods which don't take target_hint into consideration. So unlike other
+  // functions in this file, this part uses `url` only instead of
+  // `PrerenderInfo` which consists of target_hint information.
   auto [begin, end] = std::ranges::equal_range(
       started_prerenders_.begin(), started_prerenders_.end(), url,
       std::less<>(), &PrerenderInfo::url);
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index a424de715..2474afa 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -113,6 +113,7 @@
 #include "content/browser/network/cross_origin_embedder_policy_reporter.h"
 #include "content/browser/permissions/permission_controller_impl.h"
 #include "content/browser/permissions/permission_service_context.h"
+#include "content/browser/permissions/permission_util.h"
 #include "content/browser/preloading/preloading_decider.h"
 #include "content/browser/preloading/prerender/prerender_final_status.h"
 #include "content/browser/preloading/prerender/prerender_host_registry.h"
@@ -18650,7 +18651,9 @@
       std::to_array<std::pair<PermissionName, PermissionType>>(
           {{PermissionName::VIDEO_CAPTURE, PermissionType::VIDEO_CAPTURE},
            {PermissionName::AUDIO_CAPTURE, PermissionType::AUDIO_CAPTURE},
-           {PermissionName::GEOLOCATION, PermissionType::GEOLOCATION}});
+           {PermissionName::GEOLOCATION, PermissionType::GEOLOCATION},
+           {PermissionName::WINDOW_MANAGEMENT,
+            PermissionType::WINDOW_MANAGEMENT}});
 
   base::flat_map<PermissionName, PermissionStatus> permission_map;
   for (const auto& permission : kPermissions) {
@@ -18667,12 +18670,17 @@
 
 blink::mojom::PermissionStatus RenderFrameHostImpl::GetCombinedPermissionStatus(
     blink::PermissionType permission_type) {
+  auto descriptor = content::PermissionDescriptorUtil::
+      CreatePermissionDescriptorForPermissionType(permission_type);
+  if (PermissionUtil::IsDevicePermission(descriptor)) {
+    return GetBrowserContext()
+        ->GetPermissionController()
+        ->GetCombinedPermissionAndDeviceStatus(descriptor, this);
+  }
   return GetBrowserContext()
       ->GetPermissionController()
-      ->GetCombinedPermissionAndDeviceStatus(
-          content::PermissionDescriptorUtil::
-              CreatePermissionDescriptorForPermissionType(permission_type),
-          this);
+      ->GetPermissionResultForCurrentDocument(descriptor, this)
+      .status;
 }
 
 media::PictureInPictureEventsInfo::AutoPipReasonCallback
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index b4ecd1aa..53e8cbf 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -566,9 +566,10 @@
     kRefusedForPdfContent = 6,
     kRefusedForJitMismatch = 7,
     kRefusedForV8OptimizationMismatch = 8,
-    kMaxValue = kRefusedForV8OptimizationMismatch
+    kRefusedNonNavigation = 9,
+    kMaxValue = kRefusedNonNavigation
   };
-  // LINT.ThenChange(tools/metrics/histograms/metadata/browser/histograms.xml:SpareProcessMaybeTakeAction)
+  // LINT.ThenChange(//tools/metrics/histograms/metadata/browser/histograms.xml:SpareProcessMaybeTakeAction)
 
   // Please keep in sync with "RenderProcessHostDelayShutdownReason" in
   // tools/metrics/histograms/metadata/browser/enums.xml. These values should
diff --git a/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc b/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc
index 67a1250..eafc051 100644
--- a/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc
+++ b/content/browser/renderer_host/spare_render_process_host_manager_browsertest.cc
@@ -929,6 +929,7 @@
         features::kAndroidWarmUpSpareRendererWithTimeout,
         {
             {features::kAndroidSpareRendererKillWhenBackgrounded.name, "true"},
+            {features::kAndroidSpareRendererOnlyForNavigation.name, "true"},
         });
   }
 
@@ -958,6 +959,46 @@
   process_watcher.Wait();
   EXPECT_TRUE(spare_manager.GetSpares().empty());
 }
+
+IN_PROC_BROWSER_TEST_F(AndroidSpareRendererProcessHostManagerTest,
+                       OnlyForNavigation) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto& spare_manager = SpareRenderProcessHostManagerImpl::Get();
+  BrowserContext* browser_context =
+      ShellContentBrowserClient::Get()->browser_context();
+  spare_manager.WarmupSpare(browser_context);
+  EXPECT_EQ(spare_manager.GetSpares().size(), 1u);
+
+  GURL test_url = embedded_test_server()->GetURL("/simple_page.html");
+  scoped_refptr<SiteInstance> test_site_instance =
+      SiteInstance::CreateForURL(browser_context, test_url);
+  base::HistogramTester histogram_tester;
+
+  // Emulate a non-navigation process allocation. The
+  // kServiceWorkerProcessManager source is only used for testing.
+  // Since the feature AndroidSpareRendererOnlyForNavigation is enabled,
+  // the allocation will not get a spare renderer.
+  EXPECT_FALSE(spare_manager.MaybeTakeSpare(
+      browser_context, static_cast<SiteInstanceImpl*>(test_site_instance.get()),
+      ProcessAllocationContext{
+          ProcessAllocationSource::kServiceWorkerProcessManager}));
+  // Also verify that the SpareProcessMaybeTakeAction UMA correctly records the
+  // reason.
+  histogram_tester.ExpectUniqueSample(
+      "BrowserRenderProcessHost.SpareProcessMaybeTakeAction",
+      content::RenderProcessHostImpl::SpareProcessMaybeTakeAction::
+          kRefusedNonNavigation,
+      1);
+  // Navigation request can still allocate a spare renderer.
+  EXPECT_TRUE(spare_manager.MaybeTakeSpare(
+      browser_context, static_cast<SiteInstanceImpl*>(test_site_instance.get()),
+      ProcessAllocationContext{
+          ProcessAllocationSource::kNavigationRequest,
+          NavigationProcessAllocationContext{
+              ProcessAllocationNavigationStage::kBeforeNetworkRequest, 0,
+              false}}));
+}
 #endif
 
 class ExtraSpareRenderProcessHostManagerTest
diff --git a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
index 69682bb..81ee93a 100644
--- a/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
+++ b/content/browser/renderer_host/spare_render_process_host_manager_impl.cc
@@ -134,6 +134,9 @@
     case SpareProcessMaybeTakeAction::kRefusedForV8OptimizationMismatch:
       action_name = "RefusedForV8OptimizationMismatch";
       break;
+    case SpareProcessMaybeTakeAction::kRefusedNonNavigation:
+      action_name = "RefusedNonNavigation";
+      break;
   }
   return base::StrCat(
       {"BrowserRenderProcessHost.SpareProcessMaybeTakeTime.", action_name});
@@ -600,7 +603,17 @@
   } else if (next_spare_rph->AreV8OptimizationsDisabled() !=
              site_instance->GetSiteInfo().are_v8_optimizations_disabled()) {
     action = SpareProcessMaybeTakeAction::kRefusedForV8OptimizationMismatch;
-  } else {
+  }
+#if BUILDFLAG(IS_ANDROID)
+  else if (features::kAndroidSpareRendererOnlyForNavigation.Get() &&
+           !allocation_context.IsForNavigation() &&
+           // Always allow test to allocate a spare renderer so as
+           // not to break existing tests.
+           allocation_context.source != ProcessAllocationSource::kTest) {
+    action = SpareProcessMaybeTakeAction::kRefusedNonNavigation;
+  }
+#endif
+  else {
     action = SpareProcessMaybeTakeAction::kSpareTaken;
   }
   LogSpareProcessTakeActionUMAs(next_spare_rph, action, allocation_context);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 9d30d71..234b46c 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -12018,6 +12018,7 @@
     const blink::mojom::Referrer& referrer,
     const std::optional<url::Origin>& referring_origin,
     std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+    std::optional<PrefetchPriority> priority,
     scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
     base::WeakPtr<PreloadingAttempt> attempt,
     std::optional<PreloadingHoldbackStatus> holdback_status_override,
@@ -12037,7 +12038,7 @@
                              use_prefetch_proxy);
   auto container = std::make_unique<PrefetchContainer>(
       *this, prefetch_url, prefetch_type, embedder_histogram_suffix, referrer,
-      referring_origin, std::move(no_vary_search_hint),
+      referring_origin, std::move(no_vary_search_hint), std::move(priority),
       std::move(preload_pipeline_info), std::move(attempt),
       holdback_status_override, std::move(ttl));
 
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 64afb1a..81de3790 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -1013,6 +1013,7 @@
       const blink::mojom::Referrer& referrer,
       const std::optional<url::Origin>& referring_origin,
       std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+      std::optional<PrefetchPriority> priority,
       scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
       base::WeakPtr<PreloadingAttempt> attempt,
       std::optional<PreloadingHoldbackStatus> holdback_status_override,
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java b/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java
index 44bbdb5..70b4882 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/media/capture/ScreenCapture.java
@@ -22,6 +22,7 @@
 import android.view.WindowManager;
 
 import androidx.activity.result.ActivityResult;
+import androidx.annotation.GuardedBy;
 
 import org.jni_zero.CalledByNative;
 import org.jni_zero.JNINamespace;
@@ -61,19 +62,28 @@
     // until that foreground service is running.
     private static final ConditionVariable sLatch = new ConditionVariable(false);
 
-    // Since we run processing in a background thread, we need to prevent the native side from
-    // being destructed sometimes. See comments on `DesktopCapturerAndroid` for more information.
-    private final Object mNativeDestructionLock = new Object();
+    // Lock to protect access to fields that are modified mainly on the background thread. This is
+    // also used to prevent destruction of the native side while JNI methods are running. See
+    // comments on `DesktopCapturerAndroid` for more information.
+    private final Object mBackgroundLock = new Object();
     private long mNativeDesktopCapturerAndroid;
 
     private final HandlerThread mBackgroundThread = new HandlerThread("ScreenCapture");
     private @Nullable Handler mBackgroundHandler;
-    private @Nullable WebContents mWebContents;
+
+    @GuardedBy("mBackgroundLock")
     private @Nullable MediaProjection mMediaProjection;
 
-    // While capture is running these references should only be modified on the background thread.
+    @GuardedBy("mBackgroundLock")
     private @Nullable VirtualDisplay mVirtualDisplay;
+
+    @GuardedBy("mBackgroundLock")
     private @Nullable ImageReader mImageReader;
+
+    @GuardedBy("mBackgroundLock")
+    private @Nullable WebContents mWebContents;
+
+    @GuardedBy("mBackgroundLock")
     private int mAcquiredImageCount;
 
     private ScreenCapture(long nativeDesktopCapturerAndroid) {
@@ -102,6 +112,7 @@
         assert oldPickState == null;
     }
 
+    @GuardedBy("mBackgroundLock")
     private @Nullable Context maybeGetContext() {
         final WindowAndroid window = assumeNonNull(mWebContents).getTopLevelNativeWindow();
         if (window == null) return null;
@@ -117,7 +128,6 @@
     boolean startCapture() {
         final PickState pickState = sNextPickState.getAndSet(null);
         assert pickState != null;
-        mWebContents = pickState.mWebContents;
 
         final ActivityResult activityResult = pickState.mActivityResult;
         assert activityResult.getData() != null;
@@ -126,46 +136,50 @@
         // MediaProjection API. It's okay to block here since we are on the desktop capturer thread.
         sLatch.block();
 
-        // TODO(crbug.com/352187279): Update the context if the WebContents is reparented.
-        final Context context = maybeGetContext();
-        if (context == null) return false;
+        synchronized (mBackgroundLock) {
+            mWebContents = pickState.mWebContents;
+            // TODO(crbug.com/352187279): Update the context if the WebContents is reparented.
+            final Context context = maybeGetContext();
+            if (context == null) return false;
 
-        var manager =
-                (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
-        if (manager == null) return false;
+            var manager =
+                    (MediaProjectionManager)
+                            context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+            if (manager == null) return false;
 
-        mMediaProjection =
-                manager.getMediaProjection(
-                        activityResult.getResultCode(), activityResult.getData());
-        if (mMediaProjection == null) return false;
+            mMediaProjection =
+                    manager.getMediaProjection(
+                            activityResult.getResultCode(), activityResult.getData());
+            if (mMediaProjection == null) return false;
 
-        mBackgroundThread.start();
-        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+            mBackgroundThread.start();
+            mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
 
-        // Take the MediaProjection callbacks on the background thread as we may call JNI methods or
-        // update references accessed by the ImageReader handling code.
-        mMediaProjection.registerCallback(new MediaProjectionCallback(), mBackgroundHandler);
+            // We must use a background thread and `Handler` here since the current thread
+            // (DesktopCapturer thread) does not have a `Looper` set up.
+            mMediaProjection.registerCallback(new MediaProjectionCallback(), mBackgroundHandler);
 
-        var windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        var windowMetrics = windowManager.getMaximumWindowMetrics();
-        final Rect bounds = windowMetrics.getBounds();
-        int width = bounds.width();
-        int height = bounds.height();
-        int dpi = context.getResources().getConfiguration().densityDpi;
+            var windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+            var windowMetrics = windowManager.getMaximumWindowMetrics();
+            final Rect bounds = windowMetrics.getBounds();
+            int width = bounds.width();
+            int height = bounds.height();
+            int dpi = context.getResources().getConfiguration().densityDpi;
 
-        recreateListener(width, height, PixelFormat.RGBA_8888, dpi);
+            recreateListener(width, height, PixelFormat.RGBA_8888, dpi);
+        }
         return true;
     }
 
     @CalledByNative
     void destroy() {
         if (mBackgroundThread != null) {
-            // End the background thread before taking `mNativeDestructionLock` since messages run
-            // on this thread may need to take `mNativeDestructionLock` and could deadlock
+            // End the background thread before taking `mBackgroundLock` since messages run
+            // on this thread may need to take `mBackgroundLock` and could deadlock
             // otherwise.
             mBackgroundThread.quit();
         }
-        synchronized (mNativeDestructionLock) {
+        synchronized (mBackgroundLock) {
             if (mMediaProjection != null) {
                 mMediaProjection.stop();
                 mMediaProjection = null;
@@ -176,6 +190,7 @@
     }
 
     private class ImageListener implements ImageReader.OnImageAvailableListener {
+        @GuardedBy("mBackgroundLock")
         private @Nullable Image maybeAcquireImage(ImageReader reader) {
             assert mBackgroundThread.getLooper().isCurrentThread();
             // If we have acquired the maximum number of images `acquireLatestImage`
@@ -200,32 +215,35 @@
 
         private void releaseImage(ImageReader reader, Image image) {
             assert mBackgroundThread.getLooper().isCurrentThread();
-            // If we recreate the ImageReader, we may get an old release here. The image will
-            // already have been closed since the ImageReader is closed, but it's safe to call close
-            // again here.
-            image.close();
 
-            // `mAcquiredImageCount` is only for the current ImageReader, so don't incorrectly
-            // decrement it for an old ImageReader.
-            if (reader == mImageReader) mAcquiredImageCount--;
+            synchronized (mBackgroundLock) {
+                // If we recreate the ImageReader, we may get an old release here. The image will
+                // already have been closed since the ImageReader is closed, but it's safe to call
+                // close
+                // again here.
+                image.close();
 
-            // Now that we closed an image, we may be able to acquire a new image.
-            onImageAvailable(reader);
+                // `mAcquiredImageCount` is only for the current ImageReader, so don't incorrectly
+                // decrement it for an old ImageReader.
+                if (reader == mImageReader) mAcquiredImageCount--;
+
+                // Now that we closed an image, we may be able to acquire a new image.
+                onImageAvailable(reader);
+            }
         }
 
         @Override
         public void onImageAvailable(ImageReader reader) {
             assert mBackgroundThread.getLooper().isCurrentThread();
 
-            // If we recreate the ImageReader we may get a call with the old reader here. Skip this
-            // case.
-            if (reader != mImageReader) return;
-
-            // Prevent native destruction until JNI methods are done.
-            synchronized (mNativeDestructionLock) {
+            synchronized (mBackgroundLock) {
                 // If the native side was destroyed, then exit without calling JNI methods.
                 if (mNativeDesktopCapturerAndroid == 0) return;
 
+                // If we recreate the ImageReader we may get a call with the old reader here. Skip
+                // this case.
+                if (reader != mImageReader) return;
+
                 // Note that we can't use `acquireLatestImage` here because we can't close older
                 // images until the C++ side is finished using them.
                 final Image image = maybeAcquireImage(reader);
@@ -280,7 +298,7 @@
         @Override
         public void onStop() {
             assert mBackgroundThread.getLooper().isCurrentThread();
-            synchronized (mNativeDestructionLock) {
+            synchronized (mBackgroundLock) {
                 if (mNativeDesktopCapturerAndroid == 0) return;
                 mMediaProjection = null;
                 ScreenCaptureJni.get().onStop(mNativeDesktopCapturerAndroid);
@@ -288,6 +306,7 @@
         }
     }
 
+    @GuardedBy("mBackgroundLock")
     private void destroyListener() {
         if (mImageReader != null) {
             mImageReader.close();
@@ -300,6 +319,7 @@
         }
     }
 
+    @GuardedBy("mBackgroundLock")
     private void recreateListener(int width, int height, int format, int dpi) {
         destroyListener();
         mImageReader = ImageReader.newInstance(width, height, format, /* maxImages= */ 2);
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 96e882e..01f516f 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -333,6 +333,7 @@
     "popup_menu.h",
     "prefetch_handle.h",
     "prefetch_metrics.h",
+    "prefetch_priority.h",
     "prefetch_request_status_listener.h",
     "prefetch_service_delegate.cc",
     "prefetch_service_delegate.h",
diff --git a/content/public/browser/browser_context.h b/content/public/browser/browser_context.h
index d9d30c0..4c67e4d4 100644
--- a/content/public/browser/browser_context.h
+++ b/content/public/browser/browser_context.h
@@ -22,6 +22,7 @@
 #include "content/common/content_export.h"
 #include "content/public/browser/k_anonymity_service_delegate.h"
 #include "content/public/browser/prefetch_handle.h"
+#include "content/public/browser/prefetch_priority.h"
 #include "content/public/browser/prefetch_request_status_listener.h"
 #include "content/public/browser/zoom_level_delegate.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -202,7 +203,8 @@
 
   // Starts a prefetch network request for the given `url`.
   // `embedder_histogram_suffix` is used for generating internal histogram names
-  // recorded per trigger. `ttl` (Time-To-Live) specifies how long
+  // recorded per trigger. `priority` is an optimization hint of how quickly
+  // this prefetch should be available. `ttl` (Time-To-Live) specifies how long
   // prefetched data remains valid in the cache. After this period, the data is
   // reset. `should_disable_block_until_head_timeout` specifies whether we
   // should have a timeout when this prefetch blocks the navigation until its
@@ -214,6 +216,7 @@
       const std::string& embedder_histogram_suffix,
       bool javascript_enabled,
       std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+      std::optional<PrefetchPriority> priority,
       const net::HttpRequestHeaders& additional_headers,
       std::unique_ptr<PrefetchRequestStatusListener> request_status_listener,
       base::TimeDelta ttl,
diff --git a/content/public/browser/permission_result.cc b/content/public/browser/permission_result.cc
index 77cbcdbb..9d71662 100644
--- a/content/public/browser/permission_result.cc
+++ b/content/public/browser/permission_result.cc
@@ -6,6 +6,10 @@
 
 namespace content {
 
+PermissionResult::PermissionResult()
+    : status(PermissionStatus::ASK),
+      source(PermissionStatusSource::UNSPECIFIED) {}
+
 PermissionResult::PermissionResult(
     PermissionStatus permission_status,
     PermissionStatusSource permission_status_source,
diff --git a/content/public/browser/permission_result.h b/content/public/browser/permission_result.h
index d00c92a4..7510876 100644
--- a/content/public/browser/permission_result.h
+++ b/content/public/browser/permission_result.h
@@ -52,6 +52,7 @@
 };
 
 struct CONTENT_EXPORT PermissionResult {
+  PermissionResult();
   PermissionResult(
       PermissionStatus permission_status,
       PermissionStatusSource permission_status_source,
diff --git a/content/public/browser/prefetch_priority.h b/content/public/browser/prefetch_priority.h
new file mode 100644
index 0000000..6d8f190
--- /dev/null
+++ b/content/public/browser/prefetch_priority.h
@@ -0,0 +1,23 @@
+// 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 CONTENT_PUBLIC_BROWSER_PREFETCH_PRIORITY_H_
+#define CONTENT_PUBLIC_BROWSER_PREFETCH_PRIORITY_H_
+
+namespace content {
+
+// An optimization hint that indicates the relative priority of a prefetch
+// request. A higher priority suggests that the prefetch caller expects prefetch
+// resources to be available sooner.
+// TODO(crbug.com/426404355): Consider revisitting the name.
+enum class PrefetchPriority {
+  kLow = 0,
+  kMedium = 1,
+  kHigh = 2,
+  kHighest = 3,
+};
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_PREFETCH_PRIORITY_H_
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h
index ff12574..c5daa64 100644
--- a/content/public/browser/web_contents.h
+++ b/content/public/browser/web_contents.h
@@ -34,6 +34,7 @@
 #include "content/public/browser/page.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/prefetch_handle.h"
+#include "content/public/browser/prefetch_priority.h"
 #include "content/public/browser/preload_pipeline_info.h"
 #include "content/public/browser/preloading.h"
 #include "content/public/browser/preloading_trigger_type.h"
@@ -1629,6 +1630,8 @@
   //   perspectives. Normally it should be nullopt and then the opaque origin is
   //   used internally, but if necessary, custom value from trusted surfaces can
   //   be embedded into it here.
+  // - `priority is an optimization hint of how quickly this prefetch should be
+  //    available. Performs no relevant optimization if passing `std::nullopt`
   // - `preload_pipeline_info` is used to designate what pipeline this prefetch
   //   belongs to.
   // - `attempt` is used to record some metrics associated with this prefetch
@@ -1647,6 +1650,7 @@
       const blink::mojom::Referrer& referrer,
       const std::optional<url::Origin>& referring_origin,
       std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
+      std::optional<PrefetchPriority> priority,
       scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
       base::WeakPtr<PreloadingAttempt> attempt,
       std::optional<PreloadingHoldbackStatus> holdback_status_override,
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 028d483c..3a7b1c5 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -72,6 +72,10 @@
 const base::FeatureParam<bool> kAndroidSpareRendererKillWhenBackgrounded{
     &kAndroidWarmUpSpareRendererWithTimeout, "kill_when_backgrounded", false};
 
+// Only allow the navigation related allocation to use the spare renderer.
+const base::FeatureParam<bool> kAndroidSpareRendererOnlyForNavigation{
+    &kAndroidWarmUpSpareRendererWithTimeout, "only_for_navigation", false};
+
 // Whether to allow attaching an inner WebContents not owned by the outer
 // WebContents. This is for prototyping purposes and should not be enabled in
 // production.
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 17a2aff..803ba25 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -45,6 +45,8 @@
     kAndroidSpareRendererMemoryThreshold;
 CONTENT_EXPORT extern const base::FeatureParam<bool>
     kAndroidSpareRendererKillWhenBackgrounded;
+CONTENT_EXPORT extern const base::FeatureParam<bool>
+    kAndroidSpareRendererOnlyForNavigation;
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAttachUnownedInnerWebContents);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAudioServiceLaunchOnStartup);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kAudioServiceOutOfProcess);
diff --git a/device/fido/features.cc b/device/fido/features.cc
index d5589f5..a0e94f1 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -157,11 +157,6 @@
              "kWebAuthenticationSyncSecurityDomainBeforePINRenewal",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Default enabled in M136. Remove in or after M139.
-BASE_FEATURE(kWebAuthnRemoteDesktopAllowedOriginsPolicy,
-             "WebAuthenticationRemoteDesktopAllowedOriginsPolicy",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Default enabled in M135. Remove in or after M138.
 BASE_FEATURE(kWebAuthnMicrosoftSoftwareUnexportableKeyProvider,
              "WebAuthenticationMicrosoftSoftwareUnexportableKeyProvider",
diff --git a/device/fido/features.h b/device/fido/features.h
index 2dd0766e..949eb498 100644
--- a/device/fido/features.h
+++ b/device/fido/features.h
@@ -111,11 +111,6 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 BASE_DECLARE_FEATURE(kSyncSecurityDomainBeforePINRenewal);
 
-// Feature flag for the
-// `WebAuthenticationRemoteDesktopAllowedOrigins` enterprise policy.
-COMPONENT_EXPORT(DEVICE_FIDO)
-BASE_DECLARE_FEATURE(kWebAuthnRemoteDesktopAllowedOriginsPolicy);
-
 // Enables using the Microsoft Software Key Storage Provider to store
 // unexportable keys when a TPM is not available.
 COMPONENT_EXPORT(DEVICE_FIDO)
diff --git a/gpu/command_buffer/client/client_shared_image.h b/gpu/command_buffer/client/client_shared_image.h
index 93057b6..e658c887 100644
--- a/gpu/command_buffer/client/client_shared_image.h
+++ b/gpu/command_buffer/client/client_shared_image.h
@@ -26,12 +26,10 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/gpu_memory_buffer_handle.h"
 
-namespace base {
-namespace trace_event {
+namespace base::trace_event {
 class ProcessMemoryDump;
 class MemoryAllocatorDumpGuid;
-}  // namespace trace_event
-}  // namespace base
+}  // namespace base::trace_event
 
 namespace gfx {
 class GpuMemoryBuffer;
diff --git a/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
index f2a14e5..8611fdb 100644
--- a/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
@@ -944,7 +944,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 4
         },
         "test": "components_browsertests",
         "test_id_prefix": "ninja://components:components_browsertests/"
diff --git a/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
index f2a14e5..8611fdb 100644
--- a/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
@@ -944,7 +944,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 4
         },
         "test": "components_browsertests",
         "test_id_prefix": "ninja://components:components_browsertests/"
diff --git a/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
index 529d9ceb..69e8589 100644
--- a/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
@@ -1001,7 +1001,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
+          "shards": 4
         },
         "test": "components_browsertests",
         "test_id_prefix": "ninja://components:components_browsertests/"
diff --git a/infra/config/targets/autoshard_exceptions.json b/infra/config/targets/autoshard_exceptions.json
index 14280c6..b97cd65 100644
--- a/infra/config/targets/autoshard_exceptions.json
+++ b/infra/config/targets/autoshard_exceptions.json
@@ -16,7 +16,7 @@
                 "try_builder": "android-x64-rel"
             },
             "components_browsertests": {
-                "shards": 5,
+                "shards": 4,
                 "try_builder": "android-x64-rel"
             }
         },
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index 87b7a1c..e775faf 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -240,6 +240,7 @@
     "//ios/chrome/common/app_group",
     "//ios/chrome/common/app_group:main_app",
     "//ios/chrome/common/credential_provider",
+    "//ios/components/ui_util",
     "//ios/public/provider/chrome/browser/app_distribution:app_distribution_api",
     "//ios/web/public",
     "//ios/web/public/thread",
diff --git a/ios/chrome/app/application_delegate/DEPS b/ios/chrome/app/application_delegate/DEPS
new file mode 100644
index 0000000..04b4c18
--- /dev/null
+++ b/ios/chrome/app/application_delegate/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+ios/components/ui_util/dynamic_type_util.h"
+]
\ No newline at end of file
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.mm b/ios/chrome/app/application_delegate/metrics_mediator.mm
index 6b555992..ef1f34cd 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator.mm
@@ -52,6 +52,7 @@
 #import "ios/chrome/common/app_group/app_group_metrics.h"
 #import "ios/chrome/common/app_group/app_group_metrics_mainapp.h"
 #import "ios/chrome/common/credential_provider/constants.h"
+#import "ios/components/ui_util/dynamic_type_util.h"
 #import "ios/public/provider/chrome/browser/app_distribution/app_distribution_api.h"
 #import "ios/web/public/thread/web_task_traits.h"
 #import "ios/web/public/thread/web_thread.h"
@@ -532,6 +533,7 @@
     [self recordStartupDuplicatedTabCount:duplicatedTabCount];
     [self recordTabsAgeAtStartup:timesSinceCreation];
     [self recordAndResetWarmStartCount];
+    ui_util::RecordSystemFontSizeMetrics();
   } else {
     [[PreviousSessionInfo sharedInstance] incrementWarmStartCount];
     [self recordResumeTabCount:tabCount];
diff --git a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
index f8315c4..e256796 100644
--- a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
+++ b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
@@ -167,9 +167,12 @@
   void DidUnshareGroup(std::optional<tab_groups::LocalTabGroupID> local_id,
                        NSError* error);
 
-  // Callback called when the user acknowledge the error.
+  // Callback called when the user acknowledges the error.
   void ErrorAccepted(ResultCallback result);
 
+  // Callback called when the user accepts to update the app.
+  void Update(ResultCallback result);
+
   // Returns the local tab group that matches `either_id`.
   const TabGroup* GetLocalGroup(const tab_groups::EitherGroupID& either_id);
 
diff --git a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
index 5627e8c..1f94baa6 100644
--- a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
+++ b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
@@ -170,10 +170,7 @@
   NSString* title = base::SysUTF8ToNSString(error.error_header);
   NSString* message = base::SysUTF8ToNSString(error.error_body);
 
-  auto alert_action = base::CallbackToBlock(
-      base::BindOnce(&IOSCollaborationControllerDelegate::ErrorAccepted,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(result)));
-  // Make sure to present it on top of any visible view.
+  // Make sure to present the alert on top of any visible view.
   UIViewController* top_view_controller =
       top_view_controller::TopPresentedViewControllerFrom(
           base_view_controller_);
@@ -183,10 +180,37 @@
                                                    browser:browser_
                                                      title:title
                                                    message:message];
-  [alert_coordinator_
-      addItemWithTitle:l10n_util::GetNSString(IDS_IOS_SHARED_GROUP_ERROR_GOT_IT)
-                action:alert_action
-                 style:UIAlertActionStyleDefault];
+
+  if (error.type() == ErrorInfo::Type::kUpdateChromeUiForVersionOutOfDate) {
+    auto update_action = base::CallbackToBlock(
+        base::BindOnce(&IOSCollaborationControllerDelegate::Update,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(result)));
+    [alert_coordinator_
+        addItemWithTitle:
+            l10n_util::GetNSString(
+                IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_UPDATE_BUTTON)
+                  action:update_action
+                   style:UIAlertActionStyleDefault];
+
+    auto dismiss_action = base::CallbackToBlock(
+        base::BindOnce(&IOSCollaborationControllerDelegate::ErrorAccepted,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(result)));
+    [alert_coordinator_
+        addItemWithTitle:
+            l10n_util::GetNSString(
+                IDS_COLLABORATION_CHROME_OUT_OF_DATE_ERROR_DIALOG_NOT_NOW_BUTTON)
+                  action:dismiss_action
+                   style:UIAlertActionStyleCancel];
+  } else {
+    auto alert_action = base::CallbackToBlock(
+        base::BindOnce(&IOSCollaborationControllerDelegate::ErrorAccepted,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(result)));
+    [alert_coordinator_ addItemWithTitle:l10n_util::GetNSString(
+                                             IDS_IOS_SHARED_GROUP_ERROR_GOT_IT)
+                                  action:alert_action
+                                   style:UIAlertActionStyleDefault];
+  }
+
   [alert_coordinator_ start];
 }
 
@@ -576,6 +600,14 @@
   std::move(result).Run(CollaborationControllerDelegate::Outcome::kSuccess);
 }
 
+void IOSCollaborationControllerDelegate::Update(ResultCallback result) {
+  CommandDispatcher* dispatcher = browser_->GetCommandDispatcher();
+  id<ApplicationCommands> application_handler =
+      HandlerForProtocol(dispatcher, ApplicationCommands);
+  [application_handler showAppStorePage];
+  std::move(result).Run(CollaborationControllerDelegate::Outcome::kSuccess);
+}
+
 const TabGroup* IOSCollaborationControllerDelegate::GetLocalGroup(
     const tab_groups::EitherGroupID& either_id) {
   if (!tab_group_sync_service_) {
diff --git a/ios/chrome/browser/feature_engagement/model/BUILD.gn b/ios/chrome/browser/feature_engagement/model/BUILD.gn
index e0cd907..6939830 100644
--- a/ios/chrome/browser/feature_engagement/model/BUILD.gn
+++ b/ios/chrome/browser/feature_engagement/model/BUILD.gn
@@ -20,6 +20,7 @@
     "//components/feature_engagement",
     "//components/leveldb_proto",
     "//ios/chrome/app:tests_hook",
+    "//ios/chrome/browser/shared/model/paths",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile:profile_keyed_service_factory",
     "//ios/chrome/browser/shared/public/commands",
diff --git a/ios/chrome/browser/feature_engagement/model/DEPS b/ios/chrome/browser/feature_engagement/model/DEPS
index f2a09a14..daf1b2f 100644
--- a/ios/chrome/browser/feature_engagement/model/DEPS
+++ b/ios/chrome/browser/feature_engagement/model/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   "+ios/chrome/browser/default_browser/model",
   "+ios/chrome/browser/authentication/ui_bundled/signin",
+  "+ios/chrome/browser/shared/model/paths",
 ]
diff --git a/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm b/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm
index 626b1c9..8ce4b607 100644
--- a/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm
+++ b/ios/chrome/browser/feature_engagement/model/tracker_factory_util.mm
@@ -6,6 +6,7 @@
 
 #import "base/memory/ptr_util.h"
 #import "base/memory/ref_counted.h"
+#import "base/path_service.h"
 #import "base/task/sequenced_task_runner.h"
 #import "base/task/thread_pool.h"
 #import "components/feature_engagement/public/feature_activation.h"
@@ -13,6 +14,7 @@
 #import "ios/chrome/app/tests_hook.h"
 #import "ios/chrome/browser/feature_engagement/model/event_exporter.h"
 #import "ios/chrome/browser/feature_engagement/model/ios_tracker_session_controller.h"
+#import "ios/chrome/browser/shared/model/paths/paths.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 
 namespace {
@@ -44,6 +46,11 @@
   base::FilePath storage_dir = profile->GetStatePath().Append(
       kIOSFeatureEngagementTrackerStorageDirname);
 
+  base::FilePath device_storage_dir;
+  base::PathService::Get(ios::DIR_USER_DATA, &device_storage_dir);
+  device_storage_dir =
+      device_storage_dir.Append(kIOSFeatureEngagementTrackerStorageDirname);
+
   leveldb_proto::ProtoDatabaseProvider* db_provider =
       profile->GetProtoDatabaseProvider();
 
@@ -52,7 +59,7 @@
   auto session_controller = std::make_unique<IOSTrackerSessionController>();
 
   return feature_engagement::Tracker::Create(
-      storage_dir, background_task_runner, db_provider,
+      storage_dir, device_storage_dir, background_task_runner, db_provider,
       std::move(event_exporter),
       feature_engagement::Tracker::GetDefaultConfigurationProviders(),
       std::move(session_controller));
diff --git a/ios/chrome/browser/saved_tab_groups/ui/face_pile_view.mm b/ios/chrome/browser/saved_tab_groups/ui/face_pile_view.mm
index a3d8294..95b7b75 100644
--- a/ios/chrome/browser/saved_tab_groups/ui/face_pile_view.mm
+++ b/ios/chrome/browser/saved_tab_groups/ui/face_pile_view.mm
@@ -135,7 +135,9 @@
     // This is needed to avoid anti-aliasing artifacts around the border itself.
     UIView* plusXContainerView =
         [self createCircularContainerWithSize:containerSize];
-    plusXContainerView.layer.cornerRadius = containerSize / 2.0;
+    [UIView performWithoutAnimation:^{
+      plusXContainerView.layer.cornerRadius = containerSize / 2.0;
+    }];
     [plusXContainerView addSubview:plusXLabel];
 
     [_facesStackView addArrangedSubview:plusXContainerView];
@@ -174,7 +176,7 @@
         [isDarkMode ? [UIColor colorNamed:kSolidWhiteColor]
                     : [UIColor colorNamed:kSolidBlackColor]
             colorWithAlphaComponent:kPlusXlabelContainerBackgroundAlpha];
-    _plusXLabel.textColor = [UIColor colorNamed:kTextPrimaryColor];
+    _plusXLabel.textColor = [UIColor colorNamed:kSolidWhiteColor];
     return;
   }
   if (isDarkMode) {
@@ -231,12 +233,19 @@
   plusXLabel.textAlignment = NSTextAlignmentCenter;
   plusXLabel.font = [UIFont systemFontOfSize:kPlusXlabelFontSize
                                       weight:UIFontWeightMedium];
+  [plusXLabel setContentHuggingPriority:UILayoutPriorityRequired
+                                forAxis:UILayoutConstraintAxisHorizontal];
+  [plusXLabel
+      setContentCompressionResistancePriority:UILayoutPriorityRequired
+                                      forAxis:UILayoutConstraintAxisHorizontal];
 
   // Configure a container in order to add an inner horizontal margin around the
   // label.
   UIView* plusXLabelContainer = [[UIView alloc] init];
   plusXLabelContainer.translatesAutoresizingMaskIntoConstraints = NO;
-  plusXLabelContainer.layer.cornerRadius = _avatarSize / 2.0;
+  [UIView performWithoutAnimation:^{
+    plusXLabelContainer.layer.cornerRadius = _avatarSize / 2.0;
+  }];
   plusXLabelContainer.layer.masksToBounds = YES;
   [plusXLabelContainer addSubview:plusXLabel];
 
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
index 2087397d..875f7d4 100644
--- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
+++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -259,6 +259,9 @@
 // received.
 const char kContextsToOpen[] = "IOS.NumberOfContextsToOpen";
 
+// The App Store page for Google Chrome.
+NSString* const kChromeAppStoreURL = @"https://apps.apple.com/app/id535886823";
+
 // Enum for IOS.NumberOfContextsToOpen histogram.
 // Keep in sync with "ContextsToOpen" in tools/metrics/histograms/enums.xml.
 enum class ContextsToOpen {
@@ -2397,6 +2400,13 @@
   _safariImportCoordinator = safariDataImportCoordinator;
 }
 
+- (void)showAppStorePage {
+  [[UIApplication sharedApplication]
+                openURL:[NSURL URLWithString:kChromeAppStoreURL]
+                options:@{}
+      completionHandler:nil];
+}
+
 #pragma mark - SettingsCommands
 
 // TODO(crbug.com/41352590) : Remove show settings from MainController.
diff --git a/ios/chrome/browser/shared/public/commands/application_commands.h b/ios/chrome/browser/shared/public/commands/application_commands.h
index a9027f73..46ff3b3 100644
--- a/ios/chrome/browser/shared/public/commands/application_commands.h
+++ b/ios/chrome/browser/shared/public/commands/application_commands.h
@@ -177,6 +177,9 @@
 - (void)displaySafariDataImportEntryPointWithUIHandler:
     (id<SafariDataImportUIHandler>)UIHandler;
 
+// Shows the application App Store page, if any.
+- (void)showAppStorePage;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_APPLICATION_COMMANDS_H_
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm
index 7ab3aaf0..a4c2da0 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm
@@ -959,8 +959,19 @@
     [topToolbar.topAnchor
         constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
     [topToolbar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
-    [topToolbar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor]
+    [topToolbar.trailingAnchor
+        constraintEqualToAnchor:self.view.trailingAnchor],
   ]];
+
+#if defined(__IPHONE_26_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_26_0
+  if (@available(iOS 26, *)) {
+    UIScrollEdgeElementContainerInteraction* edgeEffect =
+        [[UIScrollEdgeElementContainerInteraction alloc] init];
+    edgeEffect.edge = UIRectEdgeTop;
+    edgeEffect.scrollView = self.scrollView;
+    [topToolbar addInteraction:edgeEffect];
+  }
+#endif
 }
 
 // Adds the bottom toolbar and sets constraints.
@@ -980,6 +991,16 @@
 
   [self.layoutGuideCenter referenceView:bottomToolbar
                               underName:kTabGridBottomToolbarGuide];
+
+#if defined(__IPHONE_26_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_26_0
+  if (@available(iOS 26, *)) {
+    UIScrollEdgeElementContainerInteraction* edgeEffect =
+        [[UIScrollEdgeElementContainerInteraction alloc] init];
+    edgeEffect.edge = UIRectEdgeBottom;
+    edgeEffect.scrollView = self.scrollView;
+    [bottomToolbar addInteraction:edgeEffect];
+  }
+#endif
 }
 
 // Adds the PinnedTabsViewController and sets constraints.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm
index 12c2adaa..bd792b2 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_group_view_controller.mm
@@ -949,8 +949,7 @@
     [bottomToolbar
         setScrollViewScrolledToEdge:self.gridViewController.scrolledToBottom];
   }
-  [bottomToolbar setEditButtonHidden:YES];
-  [bottomToolbar setDoneButtonHidden:YES];
+  bottomToolbar.isInTabGroupView = YES;
 
   [_container addSubview:bottomToolbar];
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h
index afadcec..a7cd135c 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h
@@ -35,6 +35,8 @@
 // shown in toolbar and its background color. Setting this property will also
 // set it on `newTabButton`.
 @property(nonatomic, assign) TabGridPage page;
+// Whether the toolbar is in a tab group view or not.
+@property(nonatomic, assign) BOOL isInTabGroupView;
 // This property together with `page` and self.traitCollection control the
 // items shown in toolbar and its background color.
 @property(nonatomic, assign) TabGridMode mode;
@@ -51,8 +53,6 @@
 - (void)setNewTabButtonEnabled:(BOOL)enabled;
 // Sets `enabled` on the done button.
 - (void)setDoneButtonEnabled:(BOOL)enabled;
-// Sets the visibility of the Done button.
-- (void)setDoneButtonHidden:(BOOL)hidden;
 // Sets `enabled` on the closeAll button.
 - (void)setCloseAllButtonEnabled:(BOOL)enabled;
 // Uses undo or closeAll text on the close all button based on `useUndo` value.
@@ -73,8 +73,6 @@
 - (void)setEditButtonMenu:(UIMenu*)menu;
 // Sets `enabled` on the Edit button.
 - (void)setEditButtonEnabled:(BOOL)enabled;
-// Sets the visibility of the Edit button.
-- (void)setEditButtonHidden:(BOOL)hidden;
 
 // Hides components and uses a black background color for tab grid transition
 // animation.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
index baee6a4..ea680583 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
@@ -125,6 +125,14 @@
 
 #pragma mark - Public
 
+- (void)setIsInTabGroupView:(BOOL)isInTabGroupView {
+  if (_isInTabGroupView == isInTabGroupView) {
+    return;
+  }
+  _isInTabGroupView = isInTabGroupView;
+  [self updateLayout];
+}
+
 - (void)setPage:(TabGridPage)page {
   if (_page == page) {
     return;
@@ -161,10 +169,6 @@
   _doneButton.enabled = enabled;
 }
 
-- (void)setDoneButtonHidden:(BOOL)hidden {
-  _doneButton.hidden = hidden;
-}
-
 - (void)setCloseAllButtonEnabled:(BOOL)enabled {
   _closeAllOrUndoButton.enabled = enabled;
 }
@@ -265,10 +269,6 @@
   _editButton.enabled = enabled;
 }
 
-- (void)setEditButtonHidden:(BOOL)hidden {
-  _editButton.hidden = hidden;
-}
-
 #pragma mark - Private
 
 - (void)setupViews {
@@ -436,10 +436,13 @@
     [_largeNewTabButton removeFromSuperview];
 
     // For incognito/regular pages, display all 3 buttons;
-    // For Tab Groups and remote tabs page, only display trailing button.
+    // For Remote tabs page/TabGroup panel, only display trailing button.
+    // For Tab Group view only display the new tab button
     if (self.page == TabGridPageRemoteTabs ||
         self.page == TabGridPageTabGroups) {
       [_toolbar setItems:@[ _spaceItem, trailingButton ]];
+    } else if (self.isInTabGroupView) {
+      [_toolbar setItems:@[ _spaceItem, _newTabButtonItem, _spaceItem ]];
     } else {
       [_toolbar setItems:@[
         leadingButton, _spaceItem, _newTabButtonItem, _spaceItem, trailingButton
@@ -452,7 +455,7 @@
   } else {
     [NSLayoutConstraint deactivateConstraints:_compactConstraints];
     [_toolbar removeFromSuperview];
-    // Do not display new tab button for Tab Groups and remote tabs page.
+    // Do not display new tab button for remote tabs page/TabGroup panel.
     if (self.page == TabGridPageRemoteTabs ||
         self.page == TabGridPageTabGroups) {
       [NSLayoutConstraint deactivateConstraints:_floatingConstraints];
@@ -485,28 +488,31 @@
 // middle/scrolled to the top states.
 - (void)createScrolledBackgrounds {
   _scrolledToEdge = YES;
-  if (IsIOSSoftLockEnabled()) {
-    _scrollBackgroundView = [[TabGridToolbarScrollingBackground alloc] init];
-    _scrollBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
-    [self addSubview:_scrollBackgroundView];
-    AddSameConstraintsToSides(
-        self, _scrollBackgroundView,
-        LayoutSides::kLeading | LayoutSides::kTop | LayoutSides::kTrailing);
+  if (@available(iOS 26, *)) {
   } else {
-    _backgroundView =
-        [[TabGridToolbarBackground alloc] initWithFrame:self.frame];
-    _backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
-    [self addSubview:_backgroundView];
-    AddSameConstraintsToSides(
-        self, _backgroundView,
-        LayoutSides::kLeading | LayoutSides::kTop | LayoutSides::kTrailing);
-  }
+    if (IsIOSSoftLockEnabled()) {
+      _scrollBackgroundView = [[TabGridToolbarScrollingBackground alloc] init];
+      _scrollBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
+      [self addSubview:_scrollBackgroundView];
+      AddSameConstraintsToSides(
+          self, _scrollBackgroundView,
+          LayoutSides::kLeading | LayoutSides::kTop | LayoutSides::kTrailing);
+    } else {
+      _backgroundView =
+          [[TabGridToolbarBackground alloc] initWithFrame:self.frame];
+      _backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
+      [self addSubview:_backgroundView];
+      AddSameConstraintsToSides(
+          self, _backgroundView,
+          LayoutSides::kLeading | LayoutSides::kTop | LayoutSides::kTrailing);
+    }
 
-  // A non-nil UIImage has to be added in the background of the toolbar to avoid
-  // having an additional blur effect.
-  [_toolbar setBackgroundImage:[[UIImage alloc] init]
-            forToolbarPosition:UIBarPositionAny
-                    barMetrics:UIBarMetricsDefault];
+    // A non-nil UIImage has to be added in the background of the toolbar to
+    // avoid having an additional blur effect.
+    [_toolbar setBackgroundImage:[[UIImage alloc] init]
+              forToolbarPosition:UIBarPositionAny
+                      barMetrics:UIBarMetricsDefault];
+  }
 }
 
 // Updates the visibility of the backgrounds based on the state of the TabGrid.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm
index 5243da9..bad16c6 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_page_control.mm
@@ -79,8 +79,9 @@
 const CGFloat kSliderMargin = 2.0;
 
 // Vertical margin between the slider and the segment on each side.
-const CGFloat kSliderVerticalMargin =
+const CGFloat kLegacySliderVerticalMargin =
     std::max((kSegmentHeight - kSliderHeight) / 2.0, 0.0);
+const CGFloat kSliderVerticalMargin = -1.5;
 
 // Width and height of the separator bars between segments.
 const CGFloat kSeparatorWidth = 1.0;
@@ -638,7 +639,11 @@
     }
   }
 
-  iconNotSelected.tintColor = [UIColor colorNamed:kStaticGrey300Color];
+  if (@available(iOS 26, *)) {
+    iconNotSelected.tintColor = UIColor.whiteColor;
+  } else {
+    iconNotSelected.tintColor = [UIColor colorNamed:kStaticGrey300Color];
+  }
   iconSelected.tintColor = UIColor.blackColor;
 
   [self insertSubview:iconNotSelected belowSubview:self.sliderView];
@@ -650,17 +655,20 @@
 - (void)setupViews {
   self.scrolledToEdge = YES;
 
-  UIView* backgroundView = [[UIView alloc]
-      initWithFrame:CGRectMake(0, 0, kOverallWidth, kSegmentHeight)];
-  backgroundView.backgroundColor =
-      [UIColor colorWithWhite:1 alpha:kScrolledToTopBackgroundAlpha];
-  backgroundView.userInteractionEnabled = NO;
-  backgroundView.layer.cornerRadius = kBackgroundCornerRadius;
-  backgroundView.layer.masksToBounds = YES;
-  [self addSubview:backgroundView];
-  backgroundView.center =
-      CGPointMake(kOverallWidth / 2.0, kOverallHeight / 2.0);
-  self.background = backgroundView;
+  if (@available(iOS 26, *)) {
+  } else {
+    UIView* backgroundView = [[UIView alloc]
+        initWithFrame:CGRectMake(0, 0, kOverallWidth, kSegmentHeight)];
+    backgroundView.backgroundColor =
+        [UIColor colorWithWhite:1 alpha:kScrolledToTopBackgroundAlpha];
+    backgroundView.userInteractionEnabled = NO;
+    backgroundView.layer.cornerRadius = kBackgroundCornerRadius;
+    backgroundView.layer.masksToBounds = YES;
+    [self addSubview:backgroundView];
+    backgroundView.center =
+        CGPointMake(kOverallWidth / 2.0, kOverallHeight / 2.0);
+    self.background = backgroundView;
+  }
 
   // Set up the layout guides for the segments.
   UILayoutGuide* incognitoGuide = [[UILayoutGuide alloc] init];
@@ -709,10 +717,20 @@
   ]];
 
   // Add the slider above the section images and labels.
+  CGFloat verticalMargin;
+  if (@available(iOS 26, *)) {
+    verticalMargin = kSliderVerticalMargin;
+  } else {
+    verticalMargin = kLegacySliderVerticalMargin;
+  }
   CGRect sliderFrame =
-      CGRectMake(0, kSliderVerticalMargin, kSliderWidth, kSliderHeight);
+      CGRectMake(0, verticalMargin, kSliderWidth, kSliderHeight);
   UIView* slider = [[UIView alloc] initWithFrame:sliderFrame];
-  slider.layer.cornerRadius = kSliderCornerRadius;
+  if (@available(iOS 26, *)) {
+    slider.layer.cornerRadius = kSliderHeight / 2.0;
+  } else {
+    slider.layer.cornerRadius = kSliderCornerRadius;
+  }
   slider.layer.masksToBounds = YES;
   slider.backgroundColor = UIColor.whiteColor;
   if (ios::provider::IsRaccoonEnabled()) {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
index 9b5b9b6c..ca1cc7aa 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
@@ -222,12 +222,18 @@
 }
 
 - (void)hide {
-  self.backgroundColor = UIColor.blackColor;
+  if (@available(iOS 26, *)) {
+  } else {
+    self.backgroundColor = UIColor.blackColor;
+  }
   self.pageControl.alpha = 0.0;
 }
 
 - (void)show {
-  self.backgroundColor = UIColor.clearColor;
+  if (@available(iOS 26, *)) {
+  } else {
+    self.backgroundColor = UIColor.clearColor;
+  }
   self.pageControl.alpha = 1.0;
 }
 
@@ -548,28 +554,31 @@
 - (void)createScrolledBackgrounds {
   _scrolledToEdge = YES;
 
-  if (IsIOSSoftLockEnabled()) {
-    _scrollBackgroundView = [[TabGridToolbarScrollingBackground alloc] init];
-    _scrollBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
-    [self insertSubview:_scrollBackgroundView atIndex:0];
-    AddSameConstraintsToSides(
-        self, _scrollBackgroundView,
-        LayoutSides::kLeading | LayoutSides::kBottom | LayoutSides::kTrailing);
+  if (@available(iOS 26, *)) {
   } else {
-    _backgroundView =
-        [[TabGridToolbarBackground alloc] initWithFrame:self.frame];
-    _backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
-    [self addSubview:_backgroundView];
-    AddSameConstraintsToSides(
-        self, _backgroundView,
-        LayoutSides::kLeading | LayoutSides::kBottom | LayoutSides::kTrailing);
-  }
+    if (IsIOSSoftLockEnabled()) {
+      _scrollBackgroundView = [[TabGridToolbarScrollingBackground alloc] init];
+      _scrollBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
+      [self insertSubview:_scrollBackgroundView atIndex:0];
+      AddSameConstraintsToSides(self, _scrollBackgroundView,
+                                LayoutSides::kLeading | LayoutSides::kBottom |
+                                    LayoutSides::kTrailing);
+    } else {
+      _backgroundView =
+          [[TabGridToolbarBackground alloc] initWithFrame:self.frame];
+      _backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
+      [self addSubview:_backgroundView];
+      AddSameConstraintsToSides(self, _backgroundView,
+                                LayoutSides::kLeading | LayoutSides::kBottom |
+                                    LayoutSides::kTrailing);
+    }
 
-  // A non-nil UIImage has to be added in the background of the toolbar to
-  // avoid having an additional blur effect.
-  [self setBackgroundImage:[[UIImage alloc] init]
-        forToolbarPosition:UIBarPositionAny
-                barMetrics:UIBarMetricsDefault];
+    // A non-nil UIImage has to be added in the background of the toolbar to
+    // avoid having an additional blur effect.
+    [self setBackgroundImage:[[UIImage alloc] init]
+          forToolbarPosition:UIBarPositionAny
+                  barMetrics:UIBarMetricsDefault];
+  }
 }
 
 // Returns YES if should use compact bottom toolbar layout.
diff --git a/ios/chrome/browser/web/model/font_size/font_size_tab_helper.mm b/ios/chrome/browser/web/model/font_size/font_size_tab_helper.mm
index 3b8e708..363f3a6 100644
--- a/ios/chrome/browser/web/model/font_size/font_size_tab_helper.mm
+++ b/ios/chrome/browser/web/model/font_size/font_size_tab_helper.mm
@@ -26,78 +26,6 @@
 #import "ios/public/provider/chrome/browser/text_zoom/text_zoom_api.h"
 #import "services/metrics/public/cpp/ukm_builders.h"
 
-namespace {
-
-// Content size category to report UMA metrics.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class IOSContentSizeCategory {
-  kUnspecified = 0,
-  kExtraSmall = 1,
-  kSmall = 2,
-  kMedium = 3,
-  kLarge = 4,
-  kExtraLarge = 5,
-  kExtraExtraLarge = 6,
-  kExtraExtraExtraLarge = 7,
-  kAccessibilityMedium = 8,
-  kAccessibilityLarge = 9,
-  kAccessibilityExtraLarge = 10,
-  kAccessibilityExtraExtraLarge = 11,
-  kAccessibilityExtraExtraExtraLarge = 12,
-  kMaxValue = kAccessibilityExtraExtraExtraLarge,
-};
-
-// Converts a UIKit content size category to a content size category for
-// reporting.
-IOSContentSizeCategory IOSContentSizeCategoryForCurrentUIContentSizeCategory() {
-  UIContentSizeCategory size =
-      UIApplication.sharedApplication.preferredContentSizeCategory;
-  if ([size isEqual:UIContentSizeCategoryUnspecified]) {
-    return IOSContentSizeCategory::kUnspecified;
-  }
-  if ([size isEqual:UIContentSizeCategoryExtraSmall]) {
-    return IOSContentSizeCategory::kExtraSmall;
-  }
-  if ([size isEqual:UIContentSizeCategorySmall]) {
-    return IOSContentSizeCategory::kSmall;
-  }
-  if ([size isEqual:UIContentSizeCategoryMedium]) {
-    return IOSContentSizeCategory::kMedium;
-  }
-  if ([size isEqual:UIContentSizeCategoryLarge]) {
-    return IOSContentSizeCategory::kLarge;
-  }
-  if ([size isEqual:UIContentSizeCategoryExtraLarge]) {
-    return IOSContentSizeCategory::kExtraLarge;
-  }
-  if ([size isEqual:UIContentSizeCategoryExtraExtraLarge]) {
-    return IOSContentSizeCategory::kExtraExtraLarge;
-  }
-  if ([size isEqual:UIContentSizeCategoryExtraExtraExtraLarge]) {
-    return IOSContentSizeCategory::kExtraExtraExtraLarge;
-  }
-  if ([size isEqual:UIContentSizeCategoryAccessibilityMedium]) {
-    return IOSContentSizeCategory::kAccessibilityMedium;
-  }
-  if ([size isEqual:UIContentSizeCategoryAccessibilityLarge]) {
-    return IOSContentSizeCategory::kAccessibilityLarge;
-  }
-  if ([size isEqual:UIContentSizeCategoryAccessibilityExtraLarge]) {
-    return IOSContentSizeCategory::kAccessibilityExtraLarge;
-  }
-  if ([size isEqual:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
-    return IOSContentSizeCategory::kAccessibilityExtraExtraLarge;
-  }
-  if ([size isEqual:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
-    return IOSContentSizeCategory::kAccessibilityExtraExtraExtraLarge;
-  }
-
-  return IOSContentSizeCategory::kUnspecified;
-}
-
-}  // namespace
-
 FontSizeTabHelper::FontSizeTabHelper(web::WebState* web_state)
     : web_state_(web_state), weak_factory_(this) {
   DCHECK(ios::provider::IsTextZoomEnabled());
@@ -142,8 +70,8 @@
 void FontSizeTabHelper::LogZoomEvent(Zoom zoom) const {
   // Log when the user zooms to see if there are certain websites that are
   // broken when zooming.
-  IOSContentSizeCategory content_size_category =
-      IOSContentSizeCategoryForCurrentUIContentSizeCategory();
+  ui_util::IOSContentSizeCategory content_size_category =
+      ui_util::GetPreferredContentSizeCategory();
   ukm::UkmRecorder* ukm_recorder = GetApplicationContext()->GetUkmRecorder();
   ukm::SourceId source_id = ukm::GetSourceIdForWebStateDocument(web_state_);
   ukm::builders::IOS_PageZoomChanged(source_id)
diff --git a/ios/components/ui_util/BUILD.gn b/ios/components/ui_util/BUILD.gn
index 66618d83..8141f71 100644
--- a/ios/components/ui_util/BUILD.gn
+++ b/ios/components/ui_util/BUILD.gn
@@ -1,5 +1,7 @@
 source_set("ui_util") {
   sources = [
+    "content_size_category_description.h",
+    "content_size_category_description.mm",
     "dynamic_type_util.h",
     "dynamic_type_util.mm",
   ]
diff --git a/ios/components/ui_util/content_size_category_description.h b/ios/components/ui_util/content_size_category_description.h
new file mode 100644
index 0000000..fcce6f5
--- /dev/null
+++ b/ios/components/ui_util/content_size_category_description.h
@@ -0,0 +1,26 @@
+// 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 IOS_COMPONENTS_UI_UTIL_CONTENT_SIZE_CATEGORY_DESCRIPTION_H_
+#define IOS_COMPONENTS_UI_UTIL_CONTENT_SIZE_CATEGORY_DESCRIPTION_H_
+
+#import <UIKit/UIKit.h>
+
+namespace ui_util {
+enum class IOSContentSizeCategory;
+}  // namespace ui_util
+
+// Description related for UIContentSizeCategory.
+@interface ContentSizeCategoryDescription : NSObject
+
+- (instancetype)init NS_UNAVAILABLE;
+- (instancetype)initWithCategory:(ui_util::IOSContentSizeCategory)category
+                      multiplier:(float)multiplier NS_DESIGNATED_INITIALIZER;
+
+@property(nonatomic, assign) ui_util::IOSContentSizeCategory category;
+@property(nonatomic, assign) float multiplier;
+
+@end
+
+#endif  // IOS_COMPONENTS_UI_UTIL_CONTENT_SIZE_CATEGORY_DESCRIPTION_H_
diff --git a/ios/components/ui_util/content_size_category_description.mm b/ios/components/ui_util/content_size_category_description.mm
new file mode 100644
index 0000000..b3d50907
--- /dev/null
+++ b/ios/components/ui_util/content_size_category_description.mm
@@ -0,0 +1,19 @@
+// 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.
+
+#import "ios/components/ui_util/content_size_category_description.h"
+
+@implementation ContentSizeCategoryDescription
+
+- (instancetype)initWithCategory:(ui_util::IOSContentSizeCategory)category
+                      multiplier:(float)multiplier {
+  self = [super init];
+  if (self) {
+    _category = category;
+    _multiplier = multiplier;
+  }
+  return self;
+}
+
+@end
diff --git a/ios/components/ui_util/dynamic_type_util.h b/ios/components/ui_util/dynamic_type_util.h
index 463eb01..96894d2 100644
--- a/ios/components/ui_util/dynamic_type_util.h
+++ b/ios/components/ui_util/dynamic_type_util.h
@@ -9,6 +9,35 @@
 
 namespace ui_util {
 
+// Content size category to report UMA metrics.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+// LINT.IfChange(IOSContentSizeCategory)
+enum class IOSContentSizeCategory {
+  kUnspecified = 0,
+  kExtraSmall = 1,
+  kSmall = 2,
+  kMedium = 3,
+  kLarge = 4,  // System default.
+  kExtraLarge = 5,
+  kExtraExtraLarge = 6,
+  kExtraExtraExtraLarge = 7,
+  kAccessibilityMedium = 8,
+  kAccessibilityLarge = 9,
+  kAccessibilityExtraLarge = 10,
+  kAccessibilityExtraExtraLarge = 11,
+  kAccessibilityExtraExtraExtraLarge = 12,
+  kMaxValue = kAccessibilityExtraExtraExtraLarge,
+};
+// LINT.ThenChange(//tools/metrics/histograms/enums.xml:IOSContentSizeCategory)
+
+// Returns the `IOSContentSizeCategory` value for
+// `UIApplication.sharedApplication.preferredContentSizeCategory`.
+ui_util::IOSContentSizeCategory GetPreferredContentSizeCategory();
+
+// Records metrics related to the system fonts.
+void RecordSystemFontSizeMetrics();
+
 // Returns system suggested font size multiplier (e.g. 1.5 if the font size
 // should be 50% bigger) for the actual system preferred content size category.
 float SystemSuggestedFontSizeMultiplier();
diff --git a/ios/components/ui_util/dynamic_type_util.mm b/ios/components/ui_util/dynamic_type_util.mm
index c823d51..4209bbc 100644
--- a/ios/components/ui_util/dynamic_type_util.mm
+++ b/ios/components/ui_util/dynamic_type_util.mm
@@ -4,10 +4,88 @@
 
 #import "ios/components/ui_util/dynamic_type_util.h"
 
-#import "base/metrics/histogram_macros.h"
+#import "base/metrics/histogram_functions.h"
+#import "ios/components/ui_util/content_size_category_description.h"
+
+namespace {
+
+NSDictionary* const kFontDescriptionDictionary = @{
+  UIContentSizeCategoryUnspecified : [[ContentSizeCategoryDescription alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kUnspecified
+            multiplier:1],
+  UIContentSizeCategoryExtraSmall : [[ContentSizeCategoryDescription alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kExtraSmall
+            multiplier:0.82],
+  UIContentSizeCategorySmall : [[ContentSizeCategoryDescription alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kSmall
+            multiplier:0.88],
+  UIContentSizeCategoryMedium : [[ContentSizeCategoryDescription alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kMedium
+            multiplier:0.94],
+  UIContentSizeCategoryLarge : [[ContentSizeCategoryDescription alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kLarge
+            multiplier:1],
+  UIContentSizeCategoryExtraLarge : [[ContentSizeCategoryDescription alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kExtraLarge
+            multiplier:1.12],
+  UIContentSizeCategoryExtraExtraLarge : [[ContentSizeCategoryDescription alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kExtraExtraLarge
+            multiplier:1.24],
+  UIContentSizeCategoryExtraExtraExtraLarge : [[ContentSizeCategoryDescription
+      alloc]
+      initWithCategory:ui_util::IOSContentSizeCategory::kExtraExtraExtraLarge
+            multiplier:1.35],
+  UIContentSizeCategoryAccessibilityMedium :
+      [[ContentSizeCategoryDescription alloc]
+          initWithCategory:ui_util::IOSContentSizeCategory::kAccessibilityMedium
+                multiplier:1.65],
+  UIContentSizeCategoryAccessibilityLarge :
+      [[ContentSizeCategoryDescription alloc]
+          initWithCategory:ui_util::IOSContentSizeCategory::kAccessibilityLarge
+                multiplier:1.94],
+  UIContentSizeCategoryAccessibilityExtraLarge :
+      [[ContentSizeCategoryDescription alloc]
+          initWithCategory:ui_util::IOSContentSizeCategory::
+                               kAccessibilityExtraLarge
+                multiplier:2.35],
+  UIContentSizeCategoryAccessibilityExtraExtraLarge :
+      [[ContentSizeCategoryDescription alloc]
+          initWithCategory:ui_util::IOSContentSizeCategory::
+                               kAccessibilityExtraExtraLarge
+                multiplier:2.76],
+  UIContentSizeCategoryAccessibilityExtraExtraExtraLarge :
+      [[ContentSizeCategoryDescription alloc]
+          initWithCategory:ui_util::IOSContentSizeCategory::
+                               kAccessibilityExtraExtraExtraLarge
+                multiplier:3.12],
+};
+
+}  // namespace
 
 namespace ui_util {
 
+ui_util::IOSContentSizeCategory GetPreferredContentSizeCategory() {
+  ContentSizeCategoryDescription* preferred_system_font_decription =
+      kFontDescriptionDictionary[UIApplication.sharedApplication
+                                     .preferredContentSizeCategory];
+  return preferred_system_font_decription
+             ? preferred_system_font_decription.category
+             : IOSContentSizeCategory::kUnspecified;
+}
+
+void RecordSystemFontSizeMetrics() {
+  ContentSizeCategoryDescription* preferred_system_font_decription =
+      kFontDescriptionDictionary[UIApplication.sharedApplication
+                                     .preferredContentSizeCategory];
+  // In case there is a new accessibility value, log if there is a value we
+  // are missing. Use the sharedApplication value as this method can be called
+  // with an explicit value for the first time.
+  base::UmaHistogramBoolean("Accessibility.iOS.NewLargerTextCategory",
+                            !preferred_system_font_decription);
+  base::UmaHistogramEnumeration("IOS.System.PreferredSystemFontSize",
+                                preferred_system_font_decription.category);
+}
+
 float SystemSuggestedFontSizeMultiplier() {
   return SystemSuggestedFontSizeMultiplier(
       UIApplication.sharedApplication.preferredContentSizeCategory);
@@ -17,32 +95,9 @@
   // Scaling numbers are calculated by [UIFont
   // preferredFontForTextStyle:UIFontTextStyleBody].pointSize, which are [14,
   // 15, 16, 17(default), 19, 21, 23, 28, 33, 40, 47, 53].
-  static NSDictionary* font_size_map = @{
-    UIContentSizeCategoryUnspecified : @1,
-    UIContentSizeCategoryExtraSmall : @0.82,
-    UIContentSizeCategorySmall : @0.88,
-    UIContentSizeCategoryMedium : @0.94,
-    UIContentSizeCategoryLarge : @1,  // system default
-    UIContentSizeCategoryExtraLarge : @1.12,
-    UIContentSizeCategoryExtraExtraLarge : @1.24,
-    UIContentSizeCategoryExtraExtraExtraLarge : @1.35,
-    UIContentSizeCategoryAccessibilityMedium : @1.65,
-    UIContentSizeCategoryAccessibilityLarge : @1.94,
-    UIContentSizeCategoryAccessibilityExtraLarge : @2.35,
-    UIContentSizeCategoryAccessibilityExtraExtraLarge : @2.76,
-    UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @3.12,
-  };
-  NSNumber* font_size = font_size_map[category];
-  static dispatch_once_t once_token;
-  dispatch_once(&once_token, ^{
-    // In case there is a new accessibility value, log if there is a value we
-    // are missing. Use the sharedApplication value as this method can be called
-    // with an explicit value for the first time.
-    UMA_HISTOGRAM_BOOLEAN("Accessibility.iOS.NewLargerTextCategory",
-                          !font_size_map[UIApplication.sharedApplication
-                                             .preferredContentSizeCategory]);
-  });
-  return font_size ? font_size.floatValue : 1;
+  ContentSizeCategoryDescription* system_font_description =
+      kFontDescriptionDictionary[category];
+  return system_font_description ? system_font_description.multiplier : 1;
 }
 
 float SystemSuggestedFontSizeMultiplier(UIContentSizeCategory category,
diff --git a/media/gpu/vaapi/test_utils.cc b/media/gpu/vaapi/test_utils.cc
index d59b329c..9fadc3f 100644
--- a/media/gpu/vaapi/test_utils.cc
+++ b/media/gpu/vaapi/test_utils.cc
@@ -19,11 +19,7 @@
 #include "third_party/libyuv/include/libyuv.h"
 #include "ui/gfx/buffer_format_util.h"
 
-// TODO(crbug.com/421698315): Remove.
-#include "ui/gfx/gpu_memory_buffer.h"
-
 #if BUILDFLAG(IS_CHROMEOS)
-// TODO(crbug.com/421698315): Remove.
 #include "media/gpu/test/local_gpu_memory_buffer_manager.h"
 #endif
 
diff --git a/net/disk_cache/sql/sql_persistent_store.cc b/net/disk_cache/sql/sql_persistent_store.cc
index 9484866..752bf634 100644
--- a/net/disk_cache/sql/sql_persistent_store.cc
+++ b/net/disk_cache/sql/sql_persistent_store.cc
@@ -250,7 +250,11 @@
   EntryInfoOrError OpenOrCreateEntry(const CacheEntryKey& key);
   OptionalEntryInfoOrError OpenEntry(const CacheEntryKey& key);
   EntryInfoOrError CreateEntry(const CacheEntryKey& key);
-
+  Error DoomEntry(const CacheEntryKey& key,
+                  const base::UnguessableToken& token);
+  Error DeleteDoomedEntry(const CacheEntryKey& key,
+                          const base::UnguessableToken& token);
+  Error DeleteLiveEntry(const CacheEntryKey& key);
   Error DeleteAllEntries();
 
  private:
@@ -261,6 +265,13 @@
   OptionalEntryInfoOrError OpenEntryInternal(const CacheEntryKey& key);
   EntryInfoOrError CreateEntryInternal(const CacheEntryKey& key,
                                        bool run_existance_check);
+  Error DoomEntryInternal(const CacheEntryKey& key,
+                          const base::UnguessableToken& token,
+                          bool& corruption_detected);
+  Error DeleteDoomedEntryInternal(const CacheEntryKey& key,
+                                  const base::UnguessableToken& token);
+  Error DeleteLiveEntryInternal(const CacheEntryKey& key,
+                                bool& corruption_detected);
   Error DeleteAllEntriesInternal();
 
   // Updates the in-memory `store_status_` by `entry_count_delta` and
@@ -273,9 +284,22 @@
                                              int64_t entry_count_delta,
                                              int64_t total_size_delta);
 
+  // Recalculates the store's status (entry count and total size) directly from
+  // the database. This is a recovery mechanism used when metadata might be
+  // inconsistent, e.g., after a numerical overflow.
+  bool RecalculateStoreStatusAndCommitTransaction(
+      sql::Transaction& transaction);
+
   int64_t CalculateResourceEntryCount();
   int64_t CalculateTotalSize();
 
+  // A helper method for checking that the database initialization was
+  // successful before proceeding with any database operations.
+  void CheckDatabaseInitStatus() {
+    CHECK(db_init_status_.has_value());
+    CHECK_EQ(*db_init_status_, Error::kOk);
+  }
+
   const base::FilePath path_;
   const int64_t max_bytes_;
   sql::Database db_;
@@ -443,9 +467,7 @@
 }
 
 OptionalEntryInfoOrError Backend::OpenEntryInternal(const CacheEntryKey& key) {
-  CHECK(db_init_status_.has_value());
-  CHECK_EQ(*db_init_status_, Error::kOk);
-
+  CheckDatabaseInitStatus();
   constexpr char kSqlSelectResources[] =
       // clang-format off
       "SELECT "
@@ -520,9 +542,7 @@
 
 EntryInfoOrError Backend::CreateEntryInternal(const CacheEntryKey& key,
                                               bool run_existance_check) {
-  CHECK(db_init_status_.has_value());
-  CHECK_EQ(*db_init_status_, Error::kOk);
-
+  CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
     return base::unexpected(Error::kFailedToStartTransaction);
@@ -591,6 +611,284 @@
   return entry_info;
 }
 
+Error Backend::DoomEntry(const CacheEntryKey& key,
+                         const base::UnguessableToken& token) {
+  TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.DoomEntry", "data",
+                     [&](perfetto::TracedValue trace_context) {
+                       auto dict = std::move(trace_context).WriteDictionary();
+                       dict.Add("key", key.string());
+                       dict.Add("token", token.ToString());
+                       PopulateTraceDetails(store_status_, dict);
+                     });
+  base::ElapsedTimer timer;
+  bool corruption_detected = false;
+  auto result = DoomEntryInternal(key, token, corruption_detected);
+  RecordTimeAndErrorResultHistogram(
+      "DoomEntry", timer.Elapsed(),
+      corruption_detected ? Error::kInvalidData : result);
+  TRACE_EVENT_END1("disk_cache", "SqlBackend.DoomEntry", "result",
+                   [&](perfetto::TracedValue trace_context) {
+                     auto dict = std::move(trace_context).WriteDictionary();
+                     PopulateTraceDetails(result, store_status_, dict);
+                     dict.Add("corruption_detected", corruption_detected);
+                   });
+  return result;
+}
+
+Error Backend::DoomEntryInternal(const CacheEntryKey& key,
+                                 const base::UnguessableToken& token,
+                                 bool& corruption_detected) {
+  CheckDatabaseInitStatus();
+  sql::Transaction transaction(&db_);
+  if (!transaction.Begin()) {
+    return Error::kFailedToStartTransaction;
+  }
+
+  int64_t doomed_count = 0;
+  // Use checked numerics to safely calculate the change in total size and
+  // detect potential metadata corruption from overflows.
+  base::CheckedNumeric<int64_t> total_size_delta = 0;
+  {
+    constexpr char kSqlMarkDoomedResources[] =
+        // clang-format off
+        "UPDATE resources "
+        "SET "
+          "doomed=? "          // 0
+        "WHERE "
+          "cache_key=? AND "   // 1
+          "token_high=? AND "  // 2
+          "token_low=? AND "   // 3
+          "doomed=? "          // 4
+        "RETURNING "
+          "bytes_usage";       // 0
+    // clang-format on
+
+    // Intentionally DCHECK() for performance
+    DCHECK(db_.IsSQLValid(kSqlMarkDoomedResources));
+    sql::Statement statement(
+        db_.GetCachedStatement(SQL_FROM_HERE, kSqlMarkDoomedResources));
+    // Set the new value: doomed = true.
+    statement.BindBool(0, true);
+    statement.BindString(1, key.string());
+    statement.BindInt64(2, TokenHigh(token));
+    statement.BindInt64(3, TokenLow(token));
+    // Set the current value to match: doomed = false.
+    statement.BindBool(4, false);
+    // Iterate through the rows returned by the RETURNING clause.
+    while (statement.Step()) {
+      // Since we're dooming an entry, its size is subtracted from the total.
+      total_size_delta -= statement.ColumnInt64(0);
+      // Count how many entries were actually updated.
+      ++doomed_count;
+    }
+  }
+
+  if (doomed_count > 1) {
+    // TODO(crbug.com/422065015): Add histograms to track how often this
+    // unexpected case is reached. A cache_key and token combination should
+    // uniquely identify a single non-doomed entry.
+  }
+
+  // If no rows were updated, it means the entry was not found (or the token
+  // was wrong), so we report kNotFound.
+  if (doomed_count == 0) {
+    return transaction.Commit() ? Error::kNotFound
+                                : Error::kFailedToCommitTransaction;
+  }
+
+  // If the `total_size_delta` calculation resulted in an overflow, it suggests
+  // that the `bytes_usage` value in the database was corrupt. In this case, we
+  // trigger a full recalculation of the store's status to recover to a
+  // consistent state.
+  if (!total_size_delta.IsValid()) {
+    corruption_detected = true;
+    return RecalculateStoreStatusAndCommitTransaction(transaction)
+               ? Error::kOk
+               : Error::kFailedToCommitTransaction;
+  }
+
+  if (!UpdateStoreStatusAndCommitTransaction(
+          transaction,
+          /*entry_count_delta=*/-doomed_count,
+          /*total_size_delta=*/total_size_delta.ValueOrDie())) {
+    return Error::kFailedToCommitTransaction;
+  }
+
+  return Error::kOk;
+}
+
+Error Backend::DeleteDoomedEntry(const CacheEntryKey& key,
+                                 const base::UnguessableToken& token) {
+  TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.DeleteDoomedEntry", "data",
+                     [&](perfetto::TracedValue trace_context) {
+                       auto dict = std::move(trace_context).WriteDictionary();
+                       dict.Add("key", key.string());
+                       dict.Add("token", token.ToString());
+                       PopulateTraceDetails(store_status_, dict);
+                     });
+  base::ElapsedTimer timer;
+  auto result = DeleteDoomedEntryInternal(key, token);
+  RecordTimeAndErrorResultHistogram("DeleteDoomedEntry", timer.Elapsed(),
+                                    result);
+  TRACE_EVENT_END1("disk_cache", "SqlBackend.DeleteDoomedEntry", "result",
+                   [&](perfetto::TracedValue trace_context) {
+                     auto dict = std::move(trace_context).WriteDictionary();
+                     PopulateTraceDetails(result, store_status_, dict);
+                   });
+  return result;
+}
+
+Error Backend::DeleteDoomedEntryInternal(const CacheEntryKey& key,
+                                         const base::UnguessableToken& token) {
+  CheckDatabaseInitStatus();
+  sql::Transaction transaction(&db_);
+  if (!transaction.Begin()) {
+    return Error::kFailedToStartTransaction;
+  }
+
+  int64_t deleted_count = 0;
+  {
+    constexpr char kSqlDeleteFromResources[] =
+        // clang-format off
+        "DELETE FROM resources "
+        "WHERE "
+          "cache_key=? AND "   // 0
+          "token_high=? AND "  // 1
+          "token_low=? AND "   // 2
+          "doomed=?";          // 3
+    // clang-format on
+
+    // Intentionally DCHECK() for performance
+    DCHECK(db_.IsSQLValid(kSqlDeleteFromResources));
+    sql::Statement statement(
+        db_.GetCachedStatement(SQL_FROM_HERE, kSqlDeleteFromResources));
+    statement.BindString(0, key.string());
+    statement.BindInt64(1, TokenHigh(token));
+    statement.BindInt64(2, TokenLow(token));
+    // Target rows where doomed = true.
+    statement.BindBool(3, true);
+    if (!statement.Run()) {
+      return Error::kFailedToExecute;
+    }
+    deleted_count = db_.GetLastChangeCount();
+  }
+
+  if (deleted_count > 1) {
+    // TODO(crbug.com/422065015): Add histograms to track how often this
+    // unexpected case is reached. A cache_key and token combination should
+    // uniquely identify a single doomed entry.
+  }
+
+  // If we didn't find any doomed entry matching the key and token, report it.
+  if (deleted_count == 0) {
+    return transaction.Commit() ? Error::kNotFound
+                                : Error::kFailedToCommitTransaction;
+  }
+
+  // TODO(crbug.com/422065015): delete body data from the `blobs` table.
+
+  return transaction.Commit() ? Error::kOk : Error::kFailedToExecute;
+}
+
+Error Backend::DeleteLiveEntry(const CacheEntryKey& key) {
+  TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.DeleteLiveEntry", "data",
+                     [&](perfetto::TracedValue trace_context) {
+                       auto dict = std::move(trace_context).WriteDictionary();
+                       dict.Add("key", key.string());
+                       PopulateTraceDetails(store_status_, dict);
+                     });
+  base::ElapsedTimer timer;
+  bool corruption_detected = false;
+  auto result = DeleteLiveEntryInternal(key, corruption_detected);
+  RecordTimeAndErrorResultHistogram(
+      "DeleteLiveEntry", timer.Elapsed(),
+      corruption_detected ? Error::kInvalidData : result);
+  TRACE_EVENT_END1("disk_cache", "SqlBackend.DeleteLiveEntry", "result",
+                   [&](perfetto::TracedValue trace_context) {
+                     auto dict = std::move(trace_context).WriteDictionary();
+                     PopulateTraceDetails(result, store_status_, dict);
+                     dict.Add("corruption_detected", corruption_detected);
+                   });
+  return result;
+}
+
+Error Backend::DeleteLiveEntryInternal(const CacheEntryKey& key,
+                                       bool& corruption_detected) {
+  CheckDatabaseInitStatus();
+  sql::Transaction transaction(&db_);
+  if (!transaction.Begin()) {
+    return Error::kFailedToStartTransaction;
+  }
+
+  // We need to collect the tokens of deleted entries to later remove their
+  // corresponding data from the `blobs` table.
+  std::vector<base::UnguessableToken> tokens_to_be_deleted;
+  // Use checked numerics to safely update the total cache size.
+  base::CheckedNumeric<int64_t> total_size_delta = 0;
+  int64_t deleted_count = 0;
+  {
+    constexpr char kSqlDeleteFromResources[] =
+        // clang-format off
+        "DELETE FROM resources "
+        "WHERE "
+          "cache_key=? AND "  // 0
+          "doomed=? "         // 1
+        "RETURNING "
+          "token_high,"       // 0
+          "token_low,"        // 1
+          "bytes_usage";      // 2
+    // clang-format on
+    // Intentionally DCHECK() for performance
+    DCHECK(db_.IsSQLValid(kSqlDeleteFromResources));
+    sql::Statement statement(
+        db_.GetCachedStatement(SQL_FROM_HERE, kSqlDeleteFromResources));
+    statement.BindString(0, key.string());
+    // Target rows where doomed = false.
+    statement.BindBool(1, false);
+    while (statement.Step()) {
+      ++deleted_count;
+      auto maybe_token = ToUnguessableToken(statement.ColumnInt64(0),
+                                            statement.ColumnInt64(1));
+      // If deserializing the token fails, it's a sign of data corruption.
+      if (!maybe_token) {
+        corruption_detected = true;
+        continue;
+      }
+      // The size of the deleted entry is subtracted from the total.
+      total_size_delta -= statement.ColumnInt64(2);
+      tokens_to_be_deleted.emplace_back(*maybe_token);
+    }
+  }
+
+  // If no entries were deleted, the key wasn't found.
+  if (deleted_count == 0) {
+    return transaction.Commit() ? Error::kNotFound
+                                : Error::kFailedToCommitTransaction;
+  }
+
+  // TODO(crbug.com/422065015): delete body data from the `blobs` table.
+
+  // If we detected corruption, or if the size update calculation overflowed,
+  // our metadata is suspect. We recover by recalculating everything from
+  // scratch.
+  if (corruption_detected || !total_size_delta.IsValid()) {
+    corruption_detected = true;
+    return RecalculateStoreStatusAndCommitTransaction(transaction)
+               ? Error::kOk
+               : Error::kFailedToCommitTransaction;
+  }
+
+  if (!UpdateStoreStatusAndCommitTransaction(
+          transaction,
+          /*entry_count_delta=*/
+          -static_cast<int64_t>(tokens_to_be_deleted.size()),
+          /*total_size_delta=*/total_size_delta.ValueOrDie())) {
+    return Error::kFailedToCommitTransaction;
+  }
+
+  return Error::kOk;
+}
+
 Error Backend::DeleteAllEntries() {
   TRACE_EVENT_BEGIN1("disk_cache", "SqlBackend.DeleteAllEntries", "data",
                      [&](perfetto::TracedValue trace_context) {
@@ -610,8 +908,7 @@
 }
 
 Error Backend::DeleteAllEntriesInternal() {
-  CHECK(db_init_status_.has_value());
-  CHECK_EQ(*db_init_status_, Error::kOk);
+  CheckDatabaseInitStatus();
   sql::Transaction transaction(&db_);
   if (!transaction.Begin()) {
     return Error::kFailedToStartTransaction;
@@ -701,6 +998,17 @@
   return true;
 }
 
+bool Backend::RecalculateStoreStatusAndCommitTransaction(
+    sql::Transaction& transaction) {
+  store_status_.entry_count = CalculateResourceEntryCount();
+  store_status_.total_size = CalculateTotalSize();
+  meta_table_.SetValue(kSqlBackendMetaTableKeyEntryCount,
+                       store_status_.entry_count);
+  meta_table_.SetValue(kSqlBackendMetaTableKeyTotalSize,
+                       store_status_.total_size);
+  return transaction.Commit();
+}
+
 // Recalculates the number of non-doomed entries in the `resources` table.
 int64_t Backend::CalculateResourceEntryCount() {
   constexpr char kSqlSelectCountFromResources[] =
@@ -782,7 +1090,26 @@
         .WithArgs(key)
         .Then(WrapCallback(std::move(callback)));
   }
-
+  void DoomEntry(const CacheEntryKey& key,
+                 const base::UnguessableToken& token,
+                 ErrorCallback callback) override {
+    backend_.AsyncCall(&Backend::DoomEntry)
+        .WithArgs(key, token)
+        .Then(WrapCallback(std::move(callback)));
+  }
+  void DeleteDoomedEntry(const CacheEntryKey& key,
+                         const base::UnguessableToken& token,
+                         ErrorCallback callback) override {
+    backend_.AsyncCall(&Backend::DeleteDoomedEntry)
+        .WithArgs(key, token)
+        .Then(WrapCallback(std::move(callback)));
+  }
+  void DeleteLiveEntry(const CacheEntryKey& key,
+                       ErrorCallback callback) override {
+    backend_.AsyncCall(&Backend::DeleteLiveEntry)
+        .WithArgs(key)
+        .Then(WrapCallback(std::move(callback)));
+  }
   void DeleteAllEntries(ErrorCallback callback) override {
     backend_.AsyncCall(&Backend::DeleteAllEntries)
         .Then(WrapCallback(std::move(callback)));
diff --git a/net/disk_cache/sql/sql_persistent_store.h b/net/disk_cache/sql/sql_persistent_store.h
index 974a2f4..6928c01 100644
--- a/net/disk_cache/sql/sql_persistent_store.h
+++ b/net/disk_cache/sql/sql_persistent_store.h
@@ -56,7 +56,8 @@
     kFailedToExecute = 10,
     kInvalidData = 11,
     kAlreadyExists = 12,
-    kMaxValue = kAlreadyExists
+    kNotFound = 13,
+    kMaxValue = kNotFound
   };
   // LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:SqlDiskCacheStoreError)
 
@@ -125,6 +126,29 @@
   virtual void CreateEntry(const CacheEntryKey& key,
                            EntryInfoOrErrorCallback callback) = 0;
 
+  // Marks an entry for future deletion. When an entry is "doomed", it is
+  // immediately removed from the cache's entry count and total size, but its
+  // data remains on disk until `DeleteDoomedEntry()` is called. The `token`
+  // ensures that only the correct instance of an entry is doomed.
+  virtual void DoomEntry(const CacheEntryKey& key,
+                         const base::UnguessableToken& token,
+                         ErrorCallback callback) = 0;
+
+  // Physically deletes an entry that has been previously marked as doomed. This
+  // operation completes the deletion process by removing the entry's data from
+  // the database. The `token` ensures that only a specific, doomed instance of
+  // the entry is deleted.
+  virtual void DeleteDoomedEntry(const CacheEntryKey& key,
+                                 const base::UnguessableToken& token,
+                                 ErrorCallback callback) = 0;
+
+  // Deletes a "live" entry, i.e., an entry whose `doomed` flag is not set.
+  // This is for use for entries which are not open; open entries should have
+  // `DoomEntry()` called, and then `DeleteDoomedEntry()` once they're no longer
+  // in used.
+  virtual void DeleteLiveEntry(const CacheEntryKey& key,
+                               ErrorCallback callback) = 0;
+
   // Deletes all entries from the cache. `callback` is invoked on completion.
   virtual void DeleteAllEntries(ErrorCallback callback) = 0;
 
diff --git a/net/disk_cache/sql/sql_persistent_store_unittest.cc b/net/disk_cache/sql/sql_persistent_store_unittest.cc
index bcc8172..a4b674a 100644
--- a/net/disk_cache/sql/sql_persistent_store_unittest.cc
+++ b/net/disk_cache/sql/sql_persistent_store_unittest.cc
@@ -214,6 +214,53 @@
     return std::move(*maybe_result);
   }
 
+  // Synchronous wrapper for DoomEntry.
+  SqlPersistentStore::Error DoomEntry(const CacheEntryKey& key,
+                                      const base::UnguessableToken& token) {
+    base::RunLoop run_loop;
+    std::optional<SqlPersistentStore::Error> maybe_result;
+    store_->DoomEntry(
+        key, token,
+        base::BindLambdaForTesting([&](SqlPersistentStore::Error result) {
+          maybe_result = result;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    CHECK(maybe_result.has_value());
+    return *maybe_result;
+  }
+
+  // Synchronous wrapper for DeleteDoomedEntry.
+  SqlPersistentStore::Error DeleteDoomedEntry(
+      const CacheEntryKey& key,
+      const base::UnguessableToken& token) {
+    base::RunLoop run_loop;
+    std::optional<SqlPersistentStore::Error> maybe_result;
+    store_->DeleteDoomedEntry(
+        key, token,
+        base::BindLambdaForTesting([&](SqlPersistentStore::Error result) {
+          maybe_result = result;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    CHECK(maybe_result.has_value());
+    return *maybe_result;
+  }
+
+  // Synchronous wrapper for DeleteLiveEntry.
+  SqlPersistentStore::Error DeleteLiveEntry(const CacheEntryKey& key) {
+    base::RunLoop run_loop;
+    std::optional<SqlPersistentStore::Error> maybe_result;
+    store_->DeleteLiveEntry(
+        key, base::BindLambdaForTesting([&](SqlPersistentStore::Error result) {
+          maybe_result = result;
+          run_loop.Quit();
+        }));
+    run_loop.Run();
+    CHECK(maybe_result.has_value());
+    return *maybe_result;
+  }
+
   // Synchronous wrapper for DeleteAllEntries.
   SqlPersistentStore::Error DeleteAllEntries() {
     base::RunLoop run_loop;
@@ -236,6 +283,17 @@
     return s.ColumnInt(0);
   }
 
+  // Helper to count doomed rows in the resource table.
+  int64_t CountDoomedResourcesTable(const CacheEntryKey& key) {
+    auto db = ManuallyOpenDatabase();
+    sql::Statement s(db->GetUniqueStatement(
+        "SELECT COUNT(*) FROM resources WHERE cache_key=? AND doomed=?"));
+    s.BindString(0, key.string());
+    s.BindBool(1, true);  // doomed = true
+    CHECK(s.Step());
+    return s.ColumnInt64(0);
+  }
+
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::DEFAULT};
   base::ScopedTempDir temp_dir_;
@@ -655,6 +713,368 @@
             SqlPersistentStore::Error::kInvalidData);
 }
 
+TEST_F(SqlPersistentStoreTest, DoomEntrySuccess) {
+  CreateAndInitStore();
+  const CacheEntryKey kKeyToDoom("key-to-doom");
+  const CacheEntryKey kKeyToKeep("key-to-keep");
+  const int64_t size_to_doom =
+      kSqlBackendStaticResourceSize + kKeyToDoom.string().size();
+  const int64_t size_to_keep =
+      kSqlBackendStaticResourceSize + kKeyToKeep.string().size();
+
+  // Create two entries.
+  auto create_result_to_doom = CreateEntry(kKeyToDoom);
+  ASSERT_TRUE(create_result_to_doom.has_value());
+  auto create_result_to_keep = CreateEntry(kKeyToKeep);
+  ASSERT_TRUE(create_result_to_keep.has_value());
+
+  const auto token_to_doom = create_result_to_doom->token;
+  ASSERT_EQ(GetEntryCount(), 2);
+  ASSERT_EQ(GetSizeOfAllEntries(), size_to_doom + size_to_keep);
+
+  // Doom one of the entries.
+  ASSERT_EQ(DoomEntry(kKeyToDoom, token_to_doom),
+            SqlPersistentStore::Error::kOk);
+
+  // Verify that the entry count and size are updated, reflecting that one entry
+  // was logically removed.
+  EXPECT_EQ(GetEntryCount(), 1);
+  EXPECT_EQ(GetSizeOfAllEntries(), size_to_keep);
+
+  // Verify the doomed entry can no longer be opened.
+  auto open_doomed_result = OpenEntry(kKeyToDoom);
+  ASSERT_TRUE(open_doomed_result.has_value());
+  EXPECT_FALSE(open_doomed_result->has_value());
+
+  // Verify the other entry can still be opened.
+  auto open_kept_result = OpenEntry(kKeyToKeep);
+  ASSERT_TRUE(open_kept_result.has_value());
+  ASSERT_TRUE(open_kept_result->has_value());
+  EXPECT_EQ((*open_kept_result)->token, create_result_to_keep->token);
+
+  // Verify the doomed entry still exists in the table but is marked as doomed,
+  // and the other entry is unaffected.
+  ClearStore();
+  EXPECT_EQ(CountResourcesTable(), 2);
+  EXPECT_EQ(CountDoomedResourcesTable(kKeyToDoom), 1);
+  EXPECT_EQ(CountDoomedResourcesTable(kKeyToKeep), 0);
+}
+
+TEST_F(SqlPersistentStoreTest, DoomEntryFailsNotFound) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey("non-existent-key");
+  ASSERT_EQ(GetEntryCount(), 0);
+
+  // Attempt to doom an entry that doesn't exist.
+  auto result = DoomEntry(kKey, base::UnguessableToken::Create());
+  ASSERT_EQ(result, SqlPersistentStore::Error::kNotFound);
+
+  // Verify that the counts remain unchanged.
+  EXPECT_EQ(GetEntryCount(), 0);
+  EXPECT_EQ(GetSizeOfAllEntries(), 0);
+}
+
+TEST_F(SqlPersistentStoreTest, DoomEntryFailsWrongToken) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey1("key1");
+  const CacheEntryKey kKey2("key2");
+  const int64_t size1 = kSqlBackendStaticResourceSize + kKey1.string().size();
+  const int64_t size2 = kSqlBackendStaticResourceSize + kKey2.string().size();
+
+  // Create two entries.
+  auto create_result1 = CreateEntry(kKey1);
+  ASSERT_TRUE(create_result1.has_value());
+  auto create_result2 = CreateEntry(kKey2);
+  ASSERT_TRUE(create_result2.has_value());
+  ASSERT_EQ(GetEntryCount(), 2);
+
+  // Attempt to doom key1 with an incorrect token.
+  auto result = DoomEntry(kKey1, base::UnguessableToken::Create());
+  ASSERT_EQ(result, SqlPersistentStore::Error::kNotFound);
+
+  // Verify that the counts remain unchanged and both entries can still be
+  // opened.
+  EXPECT_EQ(GetEntryCount(), 2);
+  EXPECT_EQ(GetSizeOfAllEntries(), size1 + size2);
+
+  auto open_result1 = OpenEntry(kKey1);
+  ASSERT_TRUE(open_result1.has_value());
+  ASSERT_TRUE(open_result1->has_value());
+  EXPECT_EQ((*open_result1)->token, create_result1->token);
+
+  auto open_result2 = OpenEntry(kKey2);
+  ASSERT_TRUE(open_result2.has_value());
+  ASSERT_TRUE(open_result2->has_value());
+  EXPECT_EQ((*open_result2)->token, create_result2->token);
+}
+
+TEST_F(SqlPersistentStoreTest, DoomEntryWithCorruptSizeRecovers) {
+  CreateAndInitStore();
+  const CacheEntryKey kKeyToCorrupt("key-to-corrupt");
+  const CacheEntryKey kKeyToKeep("key-to-keep");
+  const int64_t keep_key_size = kKeyToKeep.string().size();
+  const int64_t expected_size_after_recovery =
+      kSqlBackendStaticResourceSize + keep_key_size;
+
+  // Create one entry to keep, and one to corrupt and doom.
+  auto create_corrupt_result = CreateEntry(kKeyToCorrupt);
+  ASSERT_TRUE(create_corrupt_result.has_value());
+  ASSERT_TRUE(CreateEntry(kKeyToKeep).has_value());
+  ASSERT_EQ(GetEntryCount(), 2);
+  const auto token_to_doom = create_corrupt_result->token;
+  ClearStore();
+
+  // Manually open the database and corrupt the `bytes_usage` for one entry
+  // to an extreme value that will cause an overflow during calculation.
+  {
+    auto db = ManuallyOpenDatabase();
+    sql::Statement statement(db->GetUniqueStatement(
+        "UPDATE resources SET bytes_usage = ? WHERE cache_key = ?"));
+    statement.BindInt64(0, std::numeric_limits<int64_t>::min());
+    statement.BindString(1, kKeyToCorrupt.string());
+    ASSERT_TRUE(statement.Run());
+  }
+
+  // Re-initialize the store with the corrupted database.
+  CreateAndInitStore();
+
+  // Doom the entry with the corrupted size. This will trigger an overflow in
+  // `total_size_delta`, causing `!total_size_delta.IsValid()` to be true.
+  // The store should recover by recalculating its state from the database.
+  ASSERT_EQ(DoomEntry(kKeyToCorrupt, token_to_doom),
+            SqlPersistentStore::Error::kOk);
+
+  // Verify that recovery was successful. The entry count should be 1 (for the
+  // entry we kept), and the total size should be correctly calculated for
+  // that single remaining entry, ignoring the corrupted value.
+  EXPECT_EQ(GetEntryCount(), 1);
+  EXPECT_EQ(GetSizeOfAllEntries(), expected_size_after_recovery);
+
+  // Verify the state on disk.
+  ClearStore();
+  // Both entries should still exist in the table.
+  EXPECT_EQ(CountResourcesTable(), 2);
+  // The corrupted entry should be marked as doomed.
+  EXPECT_EQ(CountDoomedResourcesTable(kKeyToCorrupt), 1);
+  // The other entry should be unaffected.
+  EXPECT_EQ(CountDoomedResourcesTable(kKeyToKeep), 0);
+}
+
+TEST_F(SqlPersistentStoreTest, DeleteDoomedEntrySuccess) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey("my-key");
+
+  // Create and doom an entry.
+  auto create_result = CreateEntry(kKey);
+  ASSERT_TRUE(create_result.has_value());
+  const auto token = create_result->token;
+  ASSERT_EQ(DoomEntry(kKey, token), SqlPersistentStore::Error::kOk);
+  ASSERT_EQ(GetEntryCount(), 0);
+  ClearStore();
+  ASSERT_EQ(CountResourcesTable(), 1);
+  CreateAndInitStore();
+
+  // Delete the doomed entry.
+  ASSERT_EQ(DeleteDoomedEntry(kKey, token), SqlPersistentStore::Error::kOk);
+
+  // Verify the entry is now physically gone from the database.
+  ClearStore();
+  EXPECT_EQ(CountResourcesTable(), 0);
+}
+
+TEST_F(SqlPersistentStoreTest, DeleteDoomedEntryFailsOnLiveEntry) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey("my-key");
+
+  // Create a live entry.
+  auto create_result = CreateEntry(kKey);
+  ASSERT_TRUE(create_result.has_value());
+  const auto token = create_result->token;
+  ASSERT_EQ(GetEntryCount(), 1);
+
+  // Attempt to delete it with DeleteDoomedEntry. This should fail because the
+  // entry is not marked as doomed.
+  auto result = DeleteDoomedEntry(kKey, token);
+  ASSERT_EQ(result, SqlPersistentStore::Error::kNotFound);
+
+  // Verify the entry still exists.
+  EXPECT_EQ(GetEntryCount(), 1);
+  ClearStore();
+  EXPECT_EQ(CountResourcesTable(), 1);
+}
+
+TEST_F(SqlPersistentStoreTest, DeleteLiveEntrySuccess) {
+  CreateAndInitStore();
+  const CacheEntryKey kKeyToDelete("key-to-delete");
+  const CacheEntryKey kKeyToKeep("key-to-keep");
+  const int64_t size_to_delete =
+      kSqlBackendStaticResourceSize + kKeyToDelete.string().size();
+  const int64_t size_to_keep =
+      kSqlBackendStaticResourceSize + kKeyToKeep.string().size();
+
+  // Create two entries.
+  ASSERT_TRUE(CreateEntry(kKeyToDelete).has_value());
+  auto create_result_to_keep = CreateEntry(kKeyToKeep);
+  ASSERT_TRUE(create_result_to_keep.has_value());
+  ASSERT_EQ(GetEntryCount(), 2);
+  ASSERT_EQ(GetSizeOfAllEntries(), size_to_delete + size_to_keep);
+
+  // Delete one of the live entries.
+  ASSERT_EQ(DeleteLiveEntry(kKeyToDelete), SqlPersistentStore::Error::kOk);
+
+  // Verify the cache is updated correctly.
+  EXPECT_EQ(GetEntryCount(), 1);
+  EXPECT_EQ(GetSizeOfAllEntries(), size_to_keep);
+
+  // Verify the deleted entry cannot be opened.
+  auto open_deleted_result = OpenEntry(kKeyToDelete);
+  ASSERT_TRUE(open_deleted_result.has_value());
+  EXPECT_FALSE(open_deleted_result->has_value());
+
+  // Verify the other entry can still be opened.
+  auto open_kept_result = OpenEntry(kKeyToKeep);
+  ASSERT_TRUE(open_kept_result.has_value());
+  ASSERT_TRUE(open_kept_result->has_value());
+  EXPECT_EQ((*open_kept_result)->token, create_result_to_keep->token);
+
+  // Verify the entry is physically gone from the database.
+  ClearStore();
+  EXPECT_EQ(CountResourcesTable(), 1);
+}
+
+TEST_F(SqlPersistentStoreTest, DeleteLiveEntryFailsNotFound) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey("non-existent-key");
+  ASSERT_EQ(GetEntryCount(), 0);
+
+  // Attempt to delete an entry that doesn't exist.
+  auto result = DeleteLiveEntry(kKey);
+  ASSERT_EQ(result, SqlPersistentStore::Error::kNotFound);
+}
+
+TEST_F(SqlPersistentStoreTest, DeleteLiveEntryFailsOnDoomedEntry) {
+  CreateAndInitStore();
+  const CacheEntryKey kDoomedKey("doomed-key");
+  const CacheEntryKey kLiveKey("live-key");
+  const int64_t live_key_size =
+      kSqlBackendStaticResourceSize + kLiveKey.string().size();
+
+  // Create one live entry and one entry that will be doomed.
+  auto create_doomed_result = CreateEntry(kDoomedKey);
+  ASSERT_TRUE(create_doomed_result.has_value());
+  ASSERT_TRUE(CreateEntry(kLiveKey).has_value());
+
+  // Doom one of the entries.
+  ASSERT_EQ(DoomEntry(kDoomedKey, create_doomed_result->token),
+            SqlPersistentStore::Error::kOk);
+  // After dooming, one entry is live, one is doomed (logically removed).
+  ASSERT_EQ(GetEntryCount(), 1);
+  ASSERT_EQ(GetSizeOfAllEntries(), live_key_size);
+
+  // Attempt to delete the doomed entry with DeleteLiveEntry. This should fail
+  // because it's not "live".
+  auto result = DeleteLiveEntry(kDoomedKey);
+  ASSERT_EQ(result, SqlPersistentStore::Error::kNotFound);
+
+  // Verify that the live entry was not affected.
+  EXPECT_EQ(GetEntryCount(), 1);
+  auto open_live_result = OpenEntry(kLiveKey);
+  ASSERT_TRUE(open_live_result.has_value());
+  ASSERT_TRUE(open_live_result->has_value());
+
+  // Verify the doomed entry still exists in the table (as doomed), and the
+  // live entry is also present.
+  ClearStore();
+  EXPECT_EQ(CountResourcesTable(), 2);
+  EXPECT_EQ(CountDoomedResourcesTable(kDoomedKey), 1);
+  EXPECT_EQ(CountDoomedResourcesTable(kLiveKey), 0);
+}
+
+TEST_F(SqlPersistentStoreTest, DeleteLiveEntryWithCorruptTokenRecovers) {
+  CreateAndInitStore();
+  const CacheEntryKey kKeyToCorrupt("key-to-corrupt-token");
+  const CacheEntryKey kKeyToKeep("key-to-keep");
+  const int64_t keep_key_size = kKeyToKeep.string().size();
+  const int64_t expected_size_after_recovery =
+      kSqlBackendStaticResourceSize + keep_key_size;
+
+  // Create one entry to keep, and one to corrupt and delete.
+  ASSERT_TRUE(CreateEntry(kKeyToCorrupt).has_value());
+  ASSERT_TRUE(CreateEntry(kKeyToKeep).has_value());
+  ASSERT_EQ(GetEntryCount(), 2);
+  ClearStore();
+
+  // Manually open the database and corrupt the token for one entry so that
+  // it becomes invalid.
+  {
+    auto db = ManuallyOpenDatabase();
+    sql::Statement statement(db->GetUniqueStatement(
+        "UPDATE resources SET token_high = 0, token_low = 0 WHERE cache_key = "
+        "?"));
+    statement.BindString(0, kKeyToCorrupt.string());
+    ASSERT_TRUE(statement.Run());
+  }
+
+  // Re-initialize the store with the corrupted database.
+  CreateAndInitStore();
+
+  // Delete the entry with the corrupted token. This will trigger the
+  // `corruption_detected` path, forcing a full recalculation.
+  ASSERT_EQ(DeleteLiveEntry(kKeyToCorrupt), SqlPersistentStore::Error::kOk);
+
+  // Verify that recovery was successful. The entry count and total size
+  // should now reflect only the entry that was kept.
+  EXPECT_EQ(GetEntryCount(), 1);
+  EXPECT_EQ(GetSizeOfAllEntries(), expected_size_after_recovery);
+
+  // Verify the state on disk. Only the un-corrupted entry should remain.
+  ClearStore();
+  EXPECT_EQ(CountResourcesTable(), 1);
+}
+
+TEST_F(SqlPersistentStoreTest, DeleteLiveEntryWithCorruptSizeRecovers) {
+  CreateAndInitStore();
+  const CacheEntryKey kKeyToCorrupt("key-to-corrupt-size");
+  const CacheEntryKey kKeyToKeep("key-to-keep");
+  const int64_t keep_key_size = kKeyToKeep.string().size();
+  const int64_t expected_size_after_recovery =
+      kSqlBackendStaticResourceSize + keep_key_size;
+
+  // Create one entry to keep, and one to corrupt and delete.
+  ASSERT_TRUE(CreateEntry(kKeyToCorrupt).has_value());
+  ASSERT_TRUE(CreateEntry(kKeyToKeep).has_value());
+  ASSERT_EQ(GetEntryCount(), 2);
+  ClearStore();
+
+  // Manually open the database and corrupt the `bytes_usage` for one entry
+  // to an extreme value that will cause an underflow during calculation.
+  {
+    auto db = ManuallyOpenDatabase();
+    sql::Statement statement(db->GetUniqueStatement(
+        "UPDATE resources SET bytes_usage = ? WHERE cache_key = ?"));
+    statement.BindInt64(0, std::numeric_limits<int64_t>::max());
+    statement.BindString(1, kKeyToCorrupt.string());
+    ASSERT_TRUE(statement.Run());
+  }
+
+  // Re-initialize the store with the corrupted database.
+  CreateAndInitStore();
+
+  // Delete the entry with the corrupted size. This will trigger an underflow
+  // in `total_size_delta`, causing `!total_size_delta.IsValid()` to be true.
+  // The store should recover by recalculating its state from the database.
+  ASSERT_EQ(DeleteLiveEntry(kKeyToCorrupt), SqlPersistentStore::Error::kOk);
+
+  // Verify that recovery was successful. The entry count and total size
+  // should now reflect only the entry that was kept.
+  EXPECT_EQ(GetEntryCount(), 1);
+  EXPECT_EQ(GetSizeOfAllEntries(), expected_size_after_recovery);
+
+  // Verify the state on disk. Only the un-corrupted entry should remain.
+  ClearStore();
+  EXPECT_EQ(CountResourcesTable(), 1);
+}
+
 TEST_F(SqlPersistentStoreTest, DeleteAllEntriesNonEmpty) {
   CreateAndInitStore();
   const CacheEntryKey kKey1("key1");
@@ -908,6 +1328,59 @@
   EXPECT_FALSE(callback_run);
 }
 
+TEST_F(SqlPersistentStoreTest, DoomEntryCallbackNotRunOnStoreDestruction) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey("my-key");
+  auto create_result = CreateEntry(kKey);
+  ASSERT_TRUE(create_result.has_value());
+
+  bool callback_run = false;
+  store_->DoomEntry(kKey, create_result->token,
+                    base::BindLambdaForTesting([&](SqlPersistentStore::Error) {
+                      callback_run = true;
+                    }));
+  store_.reset();
+  FlushPendingTask();
+
+  EXPECT_FALSE(callback_run);
+}
+
+TEST_F(SqlPersistentStoreTest,
+       DeleteDoomedEntryCallbackNotRunOnStoreDestruction) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey("my-key");
+  auto create_result = CreateEntry(kKey);
+  ASSERT_TRUE(create_result.has_value());
+  ASSERT_EQ(DoomEntry(kKey, create_result->token),
+            SqlPersistentStore::Error::kOk);
+
+  bool callback_run = false;
+  store_->DeleteDoomedEntry(
+      kKey, create_result->token,
+      base::BindLambdaForTesting(
+          [&](SqlPersistentStore::Error) { callback_run = true; }));
+  store_.reset();
+  FlushPendingTask();
+
+  EXPECT_FALSE(callback_run);
+}
+
+TEST_F(SqlPersistentStoreTest,
+       DeleteLiveEntryCallbackNotRunOnStoreDestruction) {
+  CreateAndInitStore();
+  const CacheEntryKey kKey("my-key");
+  ASSERT_TRUE(CreateEntry(kKey).has_value());
+
+  bool callback_run = false;
+  store_->DeleteLiveEntry(
+      kKey, base::BindLambdaForTesting(
+                [&](SqlPersistentStore::Error) { callback_run = true; }));
+  store_.reset();
+  FlushPendingTask();
+
+  EXPECT_FALSE(callback_run);
+}
+
 TEST_F(SqlPersistentStoreTest,
        DeleteAllEntriesCallbackNotRunOnStoreDestruction) {
   CreateAndInitStore();
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index a1084ca..fcad1f5e 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -1197,6 +1197,11 @@
     return nullptr;
   }
 
+  if (pool()->RequiresHTTP11(stream_key().destination(),
+                             stream_key().network_anonymization_key())) {
+    return nullptr;
+  }
+
   if (HasAvailableSpdySession()) {
     base::WeakPtr<SpdySession> spdy_session = pool()->FindAvailableSpdySession(
         stream_key(), spdy_session_key(), IsIpBasedPoolingEnabled(), net_log());
diff --git a/net/http/http_stream_pool_attempt_manager_unittest.cc b/net/http/http_stream_pool_attempt_manager_unittest.cc
index 0679355..c73e06ff 100644
--- a/net/http/http_stream_pool_attempt_manager_unittest.cc
+++ b/net/http/http_stream_pool_attempt_manager_unittest.cc
@@ -3145,6 +3145,52 @@
   ASSERT_EQ(pool().TotalActiveStreamCount(), 2u);
 }
 
+// Test that an IP pooled SPDY session is not used if the destination requires
+// HTTP/1.1.
+TEST_F(HttpStreamPoolAttemptManagerTest, SpdyMatchingIpSessionRequiresHttp11) {
+  const IPEndPoint kCommonEndPoint = MakeIPEndPoint("192.0.2.1", 443);
+
+  // Create a SPDY session for www.example.org.
+  StreamRequester requester_a;
+  requester_a.set_destination("https://www.example.org");
+  CreateFakeSpdySession(requester_a.GetStreamKey(), kCommonEndPoint);
+  requester_a.RequestStream(pool());
+  requester_a.WaitForResult();
+  EXPECT_THAT(requester_a.result(), Optional(IsOk()));
+  EXPECT_EQ(requester_a.negotiated_protocol(), NextProto::kProtoHTTP2);
+  ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
+
+  // Mark example.test as requiring HTTP/1.1.
+  const HttpStreamKey stream_key_b =
+      StreamKeyBuilder().set_destination("https://example.test").Build();
+  http_server_properties()->SetHTTP11Required(
+      stream_key_b.destination(), stream_key_b.network_anonymization_key());
+
+  // Set up DNS resolution for example.test to resolve to the same IP.
+  resolver()
+      ->AddFakeRequest()
+      ->add_endpoint(
+          ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
+      .CompleteStartSynchronously(OK);
+
+  // Set up socket data for a new TCP connection for example.test.
+  SequencedSocketData data;
+  socket_factory()->AddSocketDataProvider(&data);
+  SSLSocketDataProvider ssl(ASYNC, OK);
+  ssl.next_protos_expected_in_ssl_config = {NextProto::kProtoHTTP11};
+  socket_factory()->AddSSLSocketDataProvider(&ssl);
+
+  // Request a stream for example.test.
+  StreamRequester requester_b(stream_key_b);
+  requester_b.RequestStream(pool());
+  requester_b.WaitForResult();
+
+  // The request should succeed by creating a new HTTP/1.1 connection.
+  EXPECT_THAT(requester_b.result(), Optional(IsOk()));
+  EXPECT_NE(requester_b.negotiated_protocol(), NextProto::kProtoHTTP2);
+  ASSERT_EQ(pool().TotalActiveStreamCount(), 2u);
+}
+
 // Regression test for crbug.com/385296757.
 // If an IP matching SPDY session is created during the stream attempt delay,
 // use that session instead of attempting a new connection after the delay.
diff --git a/services/device/public/cpp/device_feature_map.cc b/services/device/public/cpp/device_feature_map.cc
index a4f075e..d9be9e13 100644
--- a/services/device/public/cpp/device_feature_map.cc
+++ b/services/device/public/cpp/device_feature_map.cc
@@ -25,7 +25,6 @@
 const base::Feature* const kFeaturesExposedToJava[] = {
     &device::kWebAuthnAndroidUsePasskeyCache,
     &device::kWebAuthnPasskeyUpgrade,
-    &device::kWebAuthnRemoteDesktopAllowedOriginsPolicy,
     &kGenericSensorExtraClasses,
     &kBatteryStatusManagerBroadcastReceiverInBackground,
     &device::features::kBluetoothRfcommAndroid,
diff --git a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
index 4d50c528..9731425 100644
--- a/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
+++ b/services/device/public/java/src/org/chromium/device/DeviceFeatureList.java
@@ -27,8 +27,6 @@
     public static final String BATTERY_STATUS_MANAGER_BROADCAST_RECEIVER_IN_BACKGROUND =
             "BatteryStatusManagerBroadcastReceiverInBackground";
     public static final String WEBAUTHN_PASSKEY_UPGRADE = "WebAuthenticationPasskeyUpgrade";
-    public static final String WEBAUTHN_REMOTE_DESKTOP_ALLOWED_ORIGINS =
-            "WebAuthenticationRemoteDesktopAllowedOriginsPolicy";
     public static final String BLUETOOTH_RFCOMM_ANDROID = "BluetoothRfcommAndroid";
 
     public static final MutableFlagWithSafeDefault sGmsCoreLocationRequestParamOverride =
diff --git a/services/network/network_service_network_delegate.cc b/services/network/network_service_network_delegate.cc
index 5f3a7934..7f39e32 100644
--- a/services/network/network_service_network_delegate.cc
+++ b/services/network/network_service_network_delegate.cc
@@ -108,13 +108,6 @@
         request->traffic_annotation());
   }
 
-  if (!loader)
-    return net::OK;
-
-  if (network_service) {
-    loader->SetEnableReportingRawHeaders(network_service->HasRawHeadersAccess(
-        loader->GetProcessId(), *effective_url));
-  }
   return net::OK;
 }
 
diff --git a/services/network/scheduler/network_service_task_queues.cc b/services/network/scheduler/network_service_task_queues.cc
index 813478e..634ebba 100644
--- a/services/network/scheduler/network_service_task_queues.cc
+++ b/services/network/scheduler/network_service_task_queues.cc
@@ -4,6 +4,7 @@
 
 #include "services/network/scheduler/network_service_task_queues.h"
 
+#include "base/memory/raw_ptr.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/rand_util.h"
@@ -14,15 +15,23 @@
 namespace network {
 
 // Observes task execution on a specific network service task queue and records
-// queuing time metrics.
+// metrics.
 class NetworkServiceTaskObserver : public base::TaskObserver {
  public:
-  explicit NetworkServiceTaskObserver(std::string queue_name)
-      : queue_name_(std::move(queue_name)) {}
+  explicit NetworkServiceTaskObserver(
+      std::string queue_name,
+      base::sequence_manager::TaskQueue::Handle* queue)
+      : queue_name_(std::move(queue_name)), queue_(queue) {}
+
   void WillProcessTask(const base::PendingTask& pending_task,
                        bool was_blocked_or_low_priority) override {
-    // Sample queuing time with a 0.001 probability to reduce metrics overhead.
+    // Sample with a 0.001 probability to reduce metrics overhead.
     if (sampler_.ShouldSample(0.001)) {
+      base::UmaHistogramCounts100(
+          base::StrCat(
+              {"NetworkService.Scheduler.IOThread.NumberOfPendingTasks.",
+               queue_name_, "Queue"}),
+          (*queue_)->GetNumberOfPendingTasks());
       base::UmaHistogramTimes(
           base::StrCat({"NetworkService.Scheduler.IOThread.QueuingTime.",
                         queue_name_, "Queue"}),
@@ -33,6 +42,8 @@
 
  private:
   const std::string queue_name_;
+  // `queue_` outlives this task observer.
+  raw_ptr<base::sequence_manager::TaskQueue::Handle> queue_;
   const base::MetricsSubSampler sampler_;
 };
 
@@ -81,7 +92,7 @@
         base::sequence_manager::TaskQueue::Spec(
             GetTaskQueueName(static_cast<QueueType>(i))));
     task_observers_[i] = std::make_unique<NetworkServiceTaskObserver>(
-        QueueTypeToString(static_cast<QueueType>(i)));
+        QueueTypeToString(static_cast<QueueType>(i)), &task_queues_[i]);
     task_queues_[i]->AddTaskObserver(task_observers_[i].get());
   }
 
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index d436300..3761ae66 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -1799,10 +1799,6 @@
   return factory_params_->process_id;
 }
 
-void URLLoader::SetEnableReportingRawHeaders(bool allow) {
-  enable_reporting_raw_headers_ = allow;
-}
-
 uint32_t URLLoader::GetResourceType() const {
   return resource_type_;
 }
@@ -2190,19 +2186,9 @@
     return;
   }
 
-  // This is gated by enable_reporting_raw_headers_ to be backwards compatible
-  // with the old report_raw_headers behavior, where we wouldn't even send
-  // raw_response_headers_ to the trusted browser process based devtools
-  // instrumentation. This is observed in the case of HSTS redirects, where
-  // url_request_->response_headers has the HSTS redirect headers, like
-  // Non-Authoritative-Reason, but raw_response_headers_ has something else
-  // which doesn't include HSTS information. This is tested by
-  // DevToolsTest.TestRawHeadersWithRedirectAndHSTS.
-  // TODO(crbug.com/40781698): Remove enable_reporting_raw_headers_
   const net::HttpResponseHeaders* response_headers =
-      raw_response_headers_ && enable_reporting_raw_headers_
-          ? raw_response_headers_.get()
-          : url_request_->response_headers();
+      raw_response_headers_ ? raw_response_headers_.get()
+                            : url_request_->response_headers();
   std::vector<network::mojom::HttpRawHeaderPairPtr> header_array =
       ResponseHeaderToRawHeaderPairs(*response_headers);
 
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index 99ac2f19..9975031 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -289,8 +289,6 @@
     return shared_storage_request_helper_.get();
   }
 
-  void SetEnableReportingRawHeaders(bool enable);
-
   void set_partial_decoder_decoding_buffer_size_for_testing(
       int partial_decoder_decoding_buffer_size) {
     partial_decoder_decoding_buffer_size_ =
diff --git a/services/webnn/ort/context_impl_ort.cc b/services/webnn/ort/context_impl_ort.cc
index 331fc0c..ef86273 100644
--- a/services/webnn/ort/context_impl_ort.cc
+++ b/services/webnn/ort/context_impl_ort.cc
@@ -114,7 +114,8 @@
        /*sqrt_input=*/{DataTypeConstraint::kFloat16To32, kMaxRank},
        /*tan_input=*/{DataTypeConstraint::kFloat16To32, kMaxRank},
        /*elu_input=*/{},
-       /*expand_input=*/{},
+       /*expand_input=*/
+       {DataTypeConstraint::kAllDataTypesAtLeast8bits, kMaxRank},
        /*gather_input=*/{},
        /*gather_indices=*/{},
        /*gather_elements_input=*/{},
@@ -135,7 +136,7 @@
        /*instance_normalization_input=*/{},
        /*instance_normalization_scale=*/{},
        /*layer_normalization_input=*/{},
-       /*leaky_relu_input=*/{},
+       /*leaky_relu_input=*/{DataTypeConstraint::kFloat16To32, kMaxRank},
        /*linear_input=*/{},
        /*lstm_input=*/{},
        /*lstm_bias=*/{},
@@ -149,7 +150,7 @@
        {DataTypeConstraint::kFloat16To32, {3, 8}},
        /*max_pool2d_input=*/
        {kInts8Float16To32, {3, 8}},
-       /*prelu_input=*/{},
+       /*prelu_input=*/{DataTypeConstraint::kFloat16To32Ints32To64, kMaxRank},
        /*quantize_linear_input=*/{},
        /*quantize_linear_zero_point=*/{},
        /*reduce_l1_input=*/{},
diff --git a/services/webnn/ort/graph_builder_ort.cc b/services/webnn/ort/graph_builder_ort.cc
index e35ba42..80a86bd 100644
--- a/services/webnn/ort/graph_builder_ort.cc
+++ b/services/webnn/ort/graph_builder_ort.cc
@@ -50,9 +50,12 @@
 constexpr base::cstring_view kOpTypeClamp = "Clip";
 constexpr base::cstring_view kOpTypeConv2d = "Conv";
 constexpr base::cstring_view kOpTypeConvTranspose2d = "ConvTranspose";
+constexpr base::cstring_view kOpTypeExpand = "Expand";
 constexpr base::cstring_view kOpTypeGelu = "Gelu";
 constexpr base::cstring_view kOpTypeGemm = "Gemm";
+constexpr base::cstring_view kOpTypeLeakyRelu = "LeakyRelu";
 constexpr base::cstring_view kOpTypeHardSwish = "HardSwish";
+constexpr base::cstring_view kOpTypePRelu = "PRelu";
 constexpr base::cstring_view kOpTypeRelu = "Relu";
 constexpr base::cstring_view kOpTypeReshape = "Reshape";
 constexpr base::cstring_view kOpTypeSigmoid = "Sigmoid";
@@ -231,6 +234,39 @@
       /*shape=*/{}, base::span_from_ref(value));
 }
 
+std::string GraphBuilderOrt::CreateInitializerForShape(
+    base::span<const uint32_t> shape) {
+  std::array<int64_t, 1> new_shape_dims = {
+      base::checked_cast<int64_t>(shape.size())};
+  std::vector<int64_t> new_shape_value(shape.begin(), shape.end());
+  return CreateInitializer<int64_t>(new_shape_dims, new_shape_value);
+}
+
+void GraphBuilderOrt::AddExpandNode(base::cstring_view node_name,
+                                    base::cstring_view input,
+                                    base::cstring_view output,
+                                    base::span<const uint32_t> shape) {
+  // `new_shape` should be the name of an int64 tensor that specifies the
+  // output's shape.
+  const std::string new_shape = CreateInitializerForShape(shape);
+
+  std::array<const char*, 2> inputs = {input.c_str(), new_shape.c_str()};
+  std::array<const char*, 1> outputs = {output.c_str()};
+
+  model_editor_.AddNode(kOpTypeExpand, node_name, inputs, outputs);
+}
+
+std::string GraphBuilderOrt::CreateExpandNode(
+    base::cstring_view input,
+    base::span<const uint32_t> shape) {
+  const std::string node_name = GenerateNodeName(
+      base::JoinString({kInserted, kOpTypeExpand}, kUnderscore));
+  const std::string output = GenerateOperandName();
+
+  AddExpandNode(node_name, input, output, shape);
+  return output;
+}
+
 template <typename T>
 void GraphBuilderOrt::AddBinaryOperation(const T& operation,
                                          base::cstring_view op_type) {
@@ -585,6 +621,20 @@
   model_editor_.AddNode(kOpTypeClamp, node_name, inputs, outputs);
 }
 
+void GraphBuilderOrt::AddExpandOperation(const mojom::Expand& expand) {
+  const std::string node_name = GenerateNodeName(expand.label);
+  const std::string input = GetOperandNameById(expand.input_operand_id);
+  const std::string output = GetOperandNameById(expand.output_operand_id);
+
+  CHECK(context_properties_.data_type_limits.expand_input.Supports(
+      GetOperand(expand.input_operand_id).descriptor));
+
+  const std::vector<uint32_t>& output_shape =
+      GetOperand(expand.output_operand_id).descriptor.shape();
+
+  AddExpandNode(node_name, input, output, output_shape);
+}
+
 void GraphBuilderOrt::AddGemmOperation(const mojom::Gemm& gemm) {
   const std::string node_name = GenerateNodeName(gemm.label);
   const std::string input_a = GetOperandNameById(gemm.a_operand_id);
@@ -628,6 +678,25 @@
   model_editor_.AddNode(kOpTypeGemm, node_name, inputs, outputs, attributes);
 }
 
+void GraphBuilderOrt::AddLeakyReluOperation(
+    const mojom::LeakyRelu& leaky_relu) {
+  const std::string node_name = GenerateNodeName(leaky_relu.label);
+  const std::string input = GetOperandNameById(leaky_relu.input_operand_id);
+  const std::string output = GetOperandNameById(leaky_relu.output_operand_id);
+
+  CHECK(context_properties_.data_type_limits.leaky_relu_input.Supports(
+      GetOperand(leaky_relu.input_operand_id).descriptor));
+
+  std::array<const char*, 1> inputs = {input.c_str()};
+  std::array<const char*, 1> outputs = {output.c_str()};
+
+  constexpr base::cstring_view kAttrAlpha = "alpha";
+  std::array<ScopedOrtOpAttr, 1> attributes = {
+      model_editor_.CreateAttribute(kAttrAlpha, leaky_relu.alpha)};
+  model_editor_.AddNode(kOpTypeLeakyRelu, node_name, inputs, outputs,
+                        attributes);
+}
+
 void GraphBuilderOrt::AddPool2dOperation(const mojom::Pool2d& pool2d) {
   std::vector<ScopedOrtOpAttr> attributes;
   constexpr base::cstring_view kAttrDilations = "dilations";
@@ -721,15 +790,11 @@
   CHECK(context_properties_.data_type_limits.reshape_input.Supports(
       GetOperand(reshape.input_operand_id).descriptor));
 
-  // `new_shape` should be an int64 tensor that specifies the output's shape.
   const std::vector<uint32_t>& output_shape =
       GetOperand(reshape.output_operand_id).descriptor.shape();
-  std::array<int64_t, 1> new_shape_dims = {
-      base::checked_cast<int64_t>(output_shape.size())};
-  std::vector<int64_t> new_shape_value(output_shape.begin(),
-                                       output_shape.end());
-  const std::string new_shape =
-      CreateInitializer<int64_t>(new_shape_dims, new_shape_value);
+  // `new_shape` should be the name of an int64 tensor that specifies the
+  // output's shape.
+  const std::string new_shape = CreateInitializerForShape(output_shape);
 
   std::array<const char*, 2> inputs = {input.c_str(), new_shape.c_str()};
   std::array<const char*, 1> outputs = {output.c_str()};
@@ -755,6 +820,34 @@
   model_editor_.AddNode(kOpTypeSoftmax, node_name, inputs, outputs, attributes);
 }
 
+void GraphBuilderOrt::AddPreluOperation(const mojom::Prelu& prelu) {
+  const std::string node_name = GenerateNodeName(prelu.label);
+  std::string input = GetOperandNameById(prelu.input_operand_id);
+  const std::string slope = GetOperandNameById(prelu.slope_operand_id);
+  const std::string output = GetOperandNameById(prelu.output_operand_id);
+
+  const DataTypeLimits& data_type_limits = context_properties_.data_type_limits;
+  const OperandDescriptor& input_descriptor =
+      GetOperand(prelu.input_operand_id).descriptor;
+  CHECK(data_type_limits.prelu_input.Supports(input_descriptor));
+  const OperandDescriptor& slope_descriptor =
+      GetOperand(prelu.slope_operand_id).descriptor;
+  CHECK(data_type_limits.prelu_input.Supports(slope_descriptor));
+
+  const std::vector<uint32_t>& input_shape = input_descriptor.shape();
+  const std::vector<uint32_t>& slope_shape = slope_descriptor.shape();
+  // ONNX Prelu requires slope's shape to be unidirectionally broadcastable to
+  // input when the shape of slope is smaller than the input. While WebNN allows
+  // input and slope to be bidirectionally broadcastable.
+  if (!BroadcastShapes(slope_shape, input_shape, /*bidirectional=*/false)) {
+    input = CreateExpandNode(input, slope_shape);
+  }
+  std::array<const char*, 2> inputs = {input.c_str(), slope.c_str()};
+  std::array<const char*, 1> outputs = {output.c_str()};
+
+  model_editor_.AddNode(kOpTypePRelu, node_name, inputs, outputs);
+}
+
 void GraphBuilderOrt::AddTransposeOperation(const mojom::Transpose& transpose) {
   const std::string node_name = GenerateNodeName(transpose.label);
   const std::string input = GetOperandNameById(transpose.input_operand_id);
@@ -809,6 +902,10 @@
         AddElementWiseUnaryOperation(*operation->get_element_wise_unary());
         break;
       }
+      case mojom::Operation::Tag::kExpand: {
+        AddExpandOperation(*operation->get_expand());
+        break;
+      }
       case mojom::Operation::Tag::kGelu: {
         CHECK(data_type_limits.gelu_input.Supports(
             GetOperand(operation->get_gelu()->input_operand_id).descriptor));
@@ -830,6 +927,14 @@
         AddPool2dOperation(*operation->get_pool2d());
         break;
       }
+      case mojom::Operation::Tag::kLeakyRelu: {
+        AddLeakyReluOperation(*operation->get_leaky_relu());
+        break;
+      }
+      case mojom::Operation::Tag::kPrelu: {
+        AddPreluOperation(*operation->get_prelu());
+        break;
+      }
       case mojom::Operation::Tag::kRelu: {
         CHECK(data_type_limits.relu_input.Supports(
             GetOperand(operation->get_relu()->input_operand_id).descriptor));
@@ -873,7 +978,6 @@
       case mojom::Operation::Tag::kCumulativeSum:
       case mojom::Operation::Tag::kDequantizeLinear:
       case mojom::Operation::Tag::kElu:
-      case mojom::Operation::Tag::kExpand:
       case mojom::Operation::Tag::kGather:
       case mojom::Operation::Tag::kGatherElements:
       case mojom::Operation::Tag::kGatherNd:
@@ -886,9 +990,7 @@
       case mojom::Operation::Tag::kLstm:
       case mojom::Operation::Tag::kLstmCell:
       case mojom::Operation::Tag::kMatmul:
-      case mojom::Operation::Tag::kLeakyRelu:
       case mojom::Operation::Tag::kPad:
-      case mojom::Operation::Tag::kPrelu:
       case mojom::Operation::Tag::kQuantizeLinear:
       case mojom::Operation::Tag::kReduce:
       case mojom::Operation::Tag::kResample2d:
diff --git a/services/webnn/ort/graph_builder_ort.h b/services/webnn/ort/graph_builder_ort.h
index 5963693b..dc7e69fc 100644
--- a/services/webnn/ort/graph_builder_ort.h
+++ b/services/webnn/ort/graph_builder_ort.h
@@ -107,6 +107,18 @@
     requires internal::IsSupportedTensorType<DataType>
   std::string CreateScalarInitializer(const DataType& value);
 
+  // A helper method creating an int64 tensor with the given shape value.
+  // It can be used by `reshape` and `expand` to create an initializer that
+  // specifies the output's shape.
+  std::string CreateInitializerForShape(base::span<const uint32_t> shape);
+
+  void AddExpandNode(base::cstring_view node_name,
+                     base::cstring_view input,
+                     base::cstring_view output,
+                     base::span<const uint32_t> shape);
+
+  std::string CreateExpandNode(base::cstring_view input,
+                               base::span<const uint32_t> shape);
   template <typename T>
   void AddBinaryOperation(const T& operation, base::cstring_view op_type);
   template <typename T>
@@ -120,8 +132,11 @@
       const mojom::ElementWiseBinary& element_wise_binary);
   void AddElementWiseUnaryOperation(
       const mojom::ElementWiseUnary& element_wise_unary);
+  void AddExpandOperation(const mojom::Expand& expand);
   void AddGemmOperation(const mojom::Gemm& gemm);
+  void AddLeakyReluOperation(const mojom::LeakyRelu& leaky_relu);
   void AddPool2dOperation(const mojom::Pool2d& pool2d);
+  void AddPreluOperation(const mojom::Prelu& prelu);
   void AddReshapeOperation(const mojom::Reshape& reshape);
   void AddSoftmaxOperation(const mojom::Softmax& softmax);
   void AddTransposeOperation(const mojom::Transpose& transpose);
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d5b3b56..dfe1d49 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -25178,6 +25178,21 @@
             ]
         }
     ],
+    "UpdateStateBeforeUnbinding": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "UpdateStateBeforeUnbinding"
+                    ]
+                }
+            ]
+        }
+    ],
     "UploadOfficeToCloud": [
         {
             "platforms": [
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index 59b9429..ed1cedbe 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -307,7 +307,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/13678680/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/13679868/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index b51577c3..7c35b011 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -2675,6 +2675,10 @@
              "WebRtcAudioSinkUseTimestampAligner",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kWebRtcPqcForDtls,
+             "WebRtcPqcForDtls",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enable borderless mode for desktop PWAs. go/borderless-mode
 BASE_FEATURE(kWebAppBorderless,
              "WebAppBorderless",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index bfd82a5c..6d291f0a 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -1788,6 +1788,10 @@
 // capture timestamps. This is disabled by default.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebRtcAudioSinkUseTimestampAligner);
 
+// This feature enables using Post-Quantum Crypto(PQC) for DTLS to improve
+// WebRTC's security.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebRtcPqcForDtls);
+
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAppBorderless);
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAppEnableScopeExtensions);
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebAppEnableScopeExtensionsBySite);
diff --git a/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
index 173bd8b6..909bd56 100644
--- a/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
+++ b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
@@ -56,8 +56,8 @@
     Event* event) {
   DCHECK(execution_context_of_event_target);
   DCHECK(event);
-  DCHECK(event->target());
-  DCHECK(event->currentTarget());
+  DCHECK(event->RawTarget());
+  DCHECK(event->RawCurrentTarget());
 
   v8::Isolate* isolate = GetIsolate();
 
@@ -79,7 +79,8 @@
     // difference but the advantage that we can use listener's |ScriptState|
     // after it get compiled.
     // https://html.spec.whatwg.org/C/#event-handler-value
-    v8::Local<v8::Value> listener = GetListenerObject(*event->currentTarget());
+    v8::Local<v8::Value> listener =
+        GetListenerObject(*event->RawCurrentTarget());
 
     if (listener.IsEmpty() || !listener->IsObject())
       return;
@@ -142,7 +143,7 @@
     // Step 8-2: If |struct|’s invocation-target-in-shadow-tree is false (i.e.,
     // event's target is in a shadow tree), then set |global|’s current
     // event to event.
-    Node* target_node = event->target()->ToNode();
+    Node* target_node = event->RawTarget()->ToNode();
     if (!(target_node && target_node->IsInShadowTree()))
       window->SetCurrentEvent(event);
   }
@@ -155,7 +156,7 @@
 
     // Step 10: Call a listener with event's currentTarget as receiver and event
     // and handle errors if thrown.
-    InvokeInternal(*event->currentTarget(), *event, js_event);
+    InvokeInternal(*event->RawCurrentTarget(), *event, js_event);
 
     if (try_catch.HasCaught()) {
       // Step 10-2: Set legacyOutputDidListenersThrowFlag if given.
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
index 5f73c85..56204a3 100644
--- a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
@@ -61,7 +61,7 @@
   //         event handler given eventTarget and name.
   // Step 2. If callback is null, then return.
   v8::Local<v8::Value> listener_value =
-      GetListenerObject(*event.currentTarget());
+      GetListenerObject(*event.RawCurrentTarget());
   if (listener_value.IsEmpty() || listener_value->IsNull())
     return;
   DCHECK(HasCompiledHandler());
@@ -72,7 +72,7 @@
   // handling be false.
   const bool special_error_event_handling =
       IsA<ErrorEvent>(event) && event.type() == event_type_names::kError &&
-      event.currentTarget()->IsWindowOrWorkerGlobalScope();
+      event.RawCurrentTarget()->IsWindowOrWorkerGlobalScope();
 
   // Step 4. Process the Event object event as follows:
   //   If special error event handling is true
@@ -102,7 +102,8 @@
     // https://html.spec.whatwg.org/C/#runtime-script-errors-2
     ScriptValue error_attribute = error_event->error(script_state_of_listener);
     if (error_attribute.IsEmpty() ||
-        error_event->target()->InterfaceName() == event_target_names::kWorker) {
+        error_event->RawTarget()->InterfaceName() ==
+            event_target_names::kWorker) {
       error_attribute = ScriptValue::CreateNull(isolate);
     }
     arguments = {
@@ -131,10 +132,11 @@
   }
   ScriptValue result;
   if (!event_handler_
-           ->InvokeWithoutRunnabilityCheck(event.currentTarget(), arguments)
+           ->InvokeWithoutRunnabilityCheck(event.RawCurrentTarget(), arguments)
            .To(&result) ||
-      isolate->IsExecutionTerminating())
+      isolate->IsExecutionTerminating()) {
     return;
+  }
   v8::Local<v8::Value> v8_return_value = result.V8Value();
 
   // There is nothing to do if |v8_return_value| is null or undefined.
diff --git a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
index d58a07f..e6159b2 100644
--- a/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
+++ b/third_party/blink/renderer/core/css/css_to_length_conversion_data.h
@@ -190,6 +190,14 @@
     double DynamicWidth() const { return dynamic_width_; }
     double DynamicHeight() const { return dynamic_height_; }
 
+    String ToString() const {
+      return String::Format(
+          "large_width: %f, large_height: %f, small_width: %f, small_height: "
+          "%f, dynamic_width: %f, dynamic_height: %f",
+          large_width_, large_height_, small_width_, small_height_,
+          dynamic_width_, dynamic_height_);
+    }
+
    private:
     // v*, lv*
     double large_width_ = 0;
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.cc b/third_party/blink/renderer/core/css/element_rule_collector.cc
index 7b85268..ca215b4b 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.cc
+++ b/third_party/blink/renderer/core/css/element_rule_collector.cc
@@ -643,13 +643,23 @@
       if (!match) {
         continue;
       }
-      // If matching was for pseudo-element with ancestors vector,
-      // check that we really reached the end of it.
-      // E.g. for div::column::scroll-marker, matching for column pseudo,
-      // vector would be just [column], index would be 1 (meaning matching
-      // found pseudo style ::scroll-marker), and for rule div::column, index
-      // would be 0 (meaning matching found actual style).
-      // Anything else would mean no match.
+
+      // If matching was for a pseudo-element with a vector of ancestors,
+      // check that we really reached the end of it. E.g., when matching
+      // the selector div::column::scroll-marker against a ::column
+      // pseudo-element, the vector would be just {::column}, and the
+      // index would be 1 (meaning that the matcher found the ::column,
+      // but also went further and found the pseudo-element selector
+      // ::scroll-marker; this is fine, as we'd get dynamic_pseudo).
+      //
+      // Likewise, for the selector div::column, the index would be 0
+      // (meaning that the entire selector matched, and nothing more),
+      // which is also a match.
+      //
+      // But for the opposite, namely the selector div::column against
+      // the pseudo-element ::column::scroll-marker (with the vector
+      // {::column, ::scroll-marker}), we'd get index 0, which isn't
+      // a match.
       if (context.pseudo_element &&
           (result.pseudo_ancestor_index == kNotFound ||
            result.pseudo_ancestor_index <
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 41badb7..990dc869 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -781,8 +781,8 @@
   // See comment on viewport_size_.
   void UpdateViewportSize();
   const CSSToLengthConversionData::ViewportSize& GetViewportSize() const {
-    DCHECK(viewport_size_ == CSSToLengthConversionData::ViewportSize(
-                                 GetDocument().GetLayoutView()));
+    DCHECK_EQ(viewport_size_, CSSToLengthConversionData::ViewportSize(
+                                  GetDocument().GetLayoutView()));
     return viewport_size_;
   }
 
diff --git a/third_party/blink/renderer/core/dom/create_element_flags.h b/third_party/blink/renderer/core/dom/create_element_flags.h
index e430c75..c4b6ab08 100644
--- a/third_party/blink/renderer/core/dom/create_element_flags.h
+++ b/third_party/blink/renderer/core/dom/create_element_flags.h
@@ -34,11 +34,6 @@
   // https://dom.spec.whatwg.org/#dom-document-createelement
   static CreateElementFlags ByCreateElement() { return CreateElementFlags(); }
 
-  // https://wicg.github.io/webcomponents/proposals/Scoped-Custom-Element-Registries
-  static CreateElementFlags ByShadowRootCreateElement() {
-    return CreateElementFlags().SetAsyncCustomElements();
-  }
-
   // https://html.spec.whatwg.org/C/#create-an-element-for-the-token
   static CreateElementFlags ByFragmentParser(Document* document) {
     return CreateElementFlags()
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 59dbd9c..8d34bb4b 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -1272,39 +1272,34 @@
 }
 
 // https://dom.spec.whatwg.org/#dom-document-createelement
-// TODO(crbug.com/1304439): Move it to `tree_scope.cc` if the feature
-// `ScopedCustomElementRegistry` can stabilize.
-Element* TreeScope::CreateElementForBinding(const AtomicString& name,
-                                            ExceptionState& exception_state) {
-  Document& document = GetDocument();
-  if (!IsValidElementName(&document, name)) {
+Element* Document::CreateElementForBinding(const AtomicString& name,
+                                           ExceptionState& exception_state) {
+  if (!IsValidElementName(this, name)) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidCharacterError,
         StrCat({"The tag name provided ('", name, "') is not a valid name."}));
     return nullptr;
   }
 
-  if (document.IsXHTMLDocument() || IsA<HTMLDocument>(document)) {
+  if (IsXHTMLDocument() || IsA<HTMLDocument>(this)) {
     // 2. If the context object is an HTML document, let localName be
     // converted to ASCII lowercase.
-    AtomicString local_name = document.ConvertLocalName(name);
+    AtomicString local_name = ConvertLocalName(name);
     if (CustomElement::ShouldCreateCustomElement(local_name)) {
       return CustomElement::CreateCustomElement(
           *this,
           QualifiedName(g_null_atom, local_name, html_names::xhtmlNamespaceURI),
-          IsA<ShadowRoot>(this)
-              ? CreateElementFlags::ByShadowRootCreateElement()
-              : CreateElementFlags::ByCreateElement());
+          CreateElementFlags::ByCreateElement());
     }
     if (auto* element = HTMLElementFactory::Create(
-            local_name, document, CreateElementFlags::ByCreateElement())) {
+            local_name, *this, CreateElementFlags::ByCreateElement())) {
       return element;
     }
     QualifiedName q_name(g_null_atom, local_name,
                          html_names::xhtmlNamespaceURI);
-    return MakeGarbageCollected<HTMLUnknownElement>(q_name, document);
+    return MakeGarbageCollected<HTMLUnknownElement>(q_name, *this);
   }
-  return MakeGarbageCollected<Element>(QualifiedName(name), &document);
+  return MakeGarbageCollected<Element>(QualifiedName(name), this);
 }
 
 AtomicString GetTypeExtension(
@@ -1330,9 +1325,7 @@
 }
 
 // https://dom.spec.whatwg.org/#dom-document-createelement
-// TODO(crbug.com/1304439): Move it to `tree_scope.cc` if the feature
-// `ScopedCustomElementRegistry` can stabilize.
-Element* TreeScope::CreateElementForBinding(
+Element* Document::CreateElementForBinding(
     const AtomicString& local_name,
     const V8UnionElementCreationOptionsOrString* string_or_options,
     ExceptionState& exception_state) {
@@ -1340,10 +1333,8 @@
     return CreateElementForBinding(local_name, exception_state);
   }
 
-  Document& document = GetDocument();
-
   // 1. If localName does not match Name production, throw InvalidCharacterError
-  if (!IsValidElementName(&document, local_name)) {
+  if (!IsValidElementName(this, local_name)) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidCharacterError,
         StrCat({"The tag name provided ('", local_name,
@@ -1352,15 +1343,14 @@
   }
 
   // 2. localName converted to ASCII lowercase
-  const AtomicString& converted_local_name =
-      document.ConvertLocalName(local_name);
+  const AtomicString& converted_local_name = ConvertLocalName(local_name);
   QualifiedName q_name(g_null_atom, converted_local_name,
-                       document.IsXHTMLDocument() || IsA<HTMLDocument>(document)
+                       IsXHTMLDocument() || IsA<HTMLDocument>(this)
                            ? html_names::xhtmlNamespaceURI
                            : g_null_atom);
 
   // 3.
-  const AtomicString& is = GetTypeExtension(&document, string_or_options);
+  const AtomicString& is = GetTypeExtension(this, string_or_options);
 
   // 5. Let element be the result of creating an element given ...
   Element* element =
@@ -1393,11 +1383,9 @@
   return q_name;
 }
 
-// TODO(crbug.com/1304439): Move it to `tree_scope.cc` if the feature
-// `ScopedCustomElementRegistry` can stabilize.
-Element* TreeScope::createElementNS(const AtomicString& namespace_uri,
-                                    const AtomicString& qualified_name,
-                                    ExceptionState& exception_state) {
+Element* Document::createElementNS(const AtomicString& namespace_uri,
+                                   const AtomicString& qualified_name,
+                                   ExceptionState& exception_state) {
   QualifiedName q_name(
       CreateQualifiedName(namespace_uri, qualified_name, exception_state,
                           Document::QualifiedNameParsingMode::kParsingElement));
@@ -1406,18 +1394,13 @@
 
   CreateElementFlags flags = CreateElementFlags::ByCreateElement();
   if (CustomElement::ShouldCreateCustomElement(q_name)) {
-    return CustomElement::CreateCustomElement(
-        *this, q_name,
-        IsA<ShadowRoot>(this) ? CreateElementFlags::ByShadowRootCreateElement()
-                              : CreateElementFlags::ByCreateElement());
+    return CustomElement::CreateCustomElement(*this, q_name, flags);
   }
-  return GetDocument().CreateRawElement(q_name, flags);
+  return CreateRawElement(q_name, flags);
 }
 
 // https://dom.spec.whatwg.org/#internal-createelementns-steps
-// TODO(crbug.com/1304439): Move it to `tree_scope.cc` if the feature
-// `ScopedCustomElementRegistry` can stabilize.
-Element* TreeScope::createElementNS(
+Element* Document::createElementNS(
     const AtomicString& namespace_uri,
     const AtomicString& qualified_name,
     const V8UnionElementCreationOptionsOrString* string_or_options,
@@ -1431,12 +1414,10 @@
   if (q_name == QualifiedName::Null())
     return nullptr;
 
-  Document& document = GetDocument();
-
   // 2.
-  const AtomicString& is = GetTypeExtension(&document, string_or_options);
+  const AtomicString& is = GetTypeExtension(this, string_or_options);
 
-  if (!IsValidElementName(&document, qualified_name)) {
+  if (!IsValidElementName(this, qualified_name)) {
     exception_state.ThrowDOMException(
         DOMExceptionCode::kInvalidCharacterError,
         StrCat({"The tag name provided ('", qualified_name,
@@ -1453,11 +1434,9 @@
 
 // Entry point of "create an element".
 // https://dom.spec.whatwg.org/#concept-create-element
-// TODO(crbug.com/1304439): Move it to `tree_scope.cc` if the feature
-// `ScopedCustomElementRegistry` can stabilize.
-Element* TreeScope::CreateElement(const QualifiedName& q_name,
-                                  const CreateElementFlags flags,
-                                  const AtomicString& is) {
+Element* Document::CreateElement(const QualifiedName& q_name,
+                                 const CreateElementFlags flags,
+                                 const AtomicString& is) {
   CustomElementDefinition* definition = nullptr;
   if (flags.IsCustomElements() &&
       q_name.NamespaceURI() == html_names::xhtmlNamespaceURI) {
@@ -1468,10 +1447,10 @@
   }
 
   if (definition)
-    return definition->CreateElement(GetDocument(), q_name, flags);
+    return definition->CreateElement(*this, q_name, flags);
 
-  return CustomElement::CreateUncustomizedOrUndefinedElement(GetDocument(),
-                                                             q_name, flags, is);
+  return CustomElement::CreateUncustomizedOrUndefinedElement(*this, q_name,
+                                                             flags, is);
 }
 
 DocumentFragment* Document::createDocumentFragment() {
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 8693ebe..9e55499 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -263,6 +263,7 @@
 class TrustedHTML;
 class V8DocumentReadyState;
 class V8NodeFilter;
+class V8UnionElementCreationOptionsOrString;
 class V8UnionStringOrTrustedHTML;
 class ViewportData;
 class VisitedLinkState;
@@ -489,6 +490,28 @@
                    CustomElementRegistry*,
                    ExceptionState&);
 
+  Element* CreateElementForBinding(const AtomicString& local_name,
+                                   ExceptionState& = ASSERT_NO_EXCEPTION);
+  Element* CreateElementForBinding(
+      const AtomicString& local_name,
+      const V8UnionElementCreationOptionsOrString* string_or_options,
+      ExceptionState& exception_state);
+
+  // "create an element" defined in DOM standard. This supports both of
+  // autonomous custom elements and customized built-in elements.
+  Element* CreateElement(const QualifiedName&,
+                         const CreateElementFlags,
+                         const AtomicString& is);
+
+  Element* createElementNS(const AtomicString& namespace_uri,
+                           const AtomicString& qualified_name,
+                           ExceptionState&);
+  Element* createElementNS(
+      const AtomicString& namespace_uri,
+      const AtomicString& qualified_name,
+      const V8UnionElementCreationOptionsOrString* string_or_options,
+      ExceptionState& exception_state);
+
   CustomElementRegistry* customElementRegistry() const override;
 
   // Creates an element without custom element processing.
diff --git a/third_party/blink/renderer/core/dom/events/event.cc b/third_party/blink/renderer/core/dom/events/event.cc
index 061f421..68c8bab0 100644
--- a/third_party/blink/renderer/core/dom/events/event.cc
+++ b/third_party/blink/renderer/core/dom/events/event.cc
@@ -26,6 +26,9 @@
 #include "third_party/blink/renderer/core/dom/events/event_path.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/dom/events/window_event_context.h"
+#include "third_party/blink/renderer/core/dom/scroll_button_pseudo_element.h"
+#include "third_party/blink/renderer/core/dom/scroll_marker_group_pseudo_element.h"
+#include "third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.h"
 #include "third_party/blink/renderer/core/dom/static_node_list.h"
 #include "third_party/blink/renderer/core/event_interface_names.h"
 #include "third_party/blink/renderer/core/events/focus_event.h"
@@ -45,6 +48,33 @@
 
 namespace blink {
 
+namespace {
+
+// Retargets any pseudo element target to some element target.
+EventTarget* RetargetPseudoElement(EventTarget* target) {
+  if (!target) {
+    return nullptr;
+  }
+  Node* node = target->ToNode();
+  if (!node || !node->IsPseudoElement()) {
+    return target;
+  }
+  // For ::scroll-marker, the target should be the ultimate originating element
+  // of its ::scroll-marker-group.
+  if (auto* scroll_marker = DynamicTo<ScrollMarkerPseudoElement>(node)) {
+    CHECK(scroll_marker->ScrollMarkerGroup());
+    return &scroll_marker->ScrollMarkerGroup()->UltimateOriginatingElement();
+  }
+  // For ::scroll-button(), the target should be the ultimate originating
+  // element of its ::scroll-marker-group.
+  if (auto* scroll_button = DynamicTo<ScrollButtonPseudoElement>(node)) {
+    return &scroll_button->UltimateOriginatingElement();
+  }
+  NOTREACHED() << "target can't be a pseudo element! found " << node;
+}
+
+}  // namespace
+
 Event::Event() : Event(g_empty_atom, Bubbles::kNo, Cancelable::kNo) {
   was_initialized_ = false;
 }
@@ -252,6 +282,10 @@
     prevent_default_called_on_uncancelable_event_ = true;
 }
 
+EventTarget* Event::target() const {
+  return RetargetPseudoElement(target_.Get());
+}
+
 void Event::SetTarget(EventTarget* target) {
   if (target_ == target)
     return;
@@ -275,9 +309,12 @@
 
 Element* Event::Retarget(const Element* element) const {
   CHECK(RuntimeEnabledFeatures::ImprovedSourceRetargetingEnabled());
-  EventTarget* retarget_against = currentTarget() ? currentTarget() : target();
-  if (element && retarget_against && retarget_against->ToNode()) {
-    return &retarget_against->ToNode()->GetTreeScope().Retarget(*element);
+  EventTarget* raw_current_target = RawCurrentTarget();
+  if (!raw_current_target) {
+    raw_current_target = RawTarget();
+  }
+  if (element && raw_current_target && raw_current_target->ToNode()) {
+    return &raw_current_target->ToNode()->GetTreeScope().Retarget(*element);
   }
   return nullptr;
 }
@@ -353,12 +390,18 @@
 }
 
 EventTarget* Event::currentTarget() const {
-  if (!current_target_)
+  return RetargetPseudoElement(RawCurrentTarget());
+}
+
+EventTarget* Event::RawCurrentTarget() const {
+  if (!current_target_) {
     return nullptr;
+  }
   if (auto* curr_svg_element =
           DynamicTo<SVGElement>(current_target_->ToNode())) {
-    if (SVGElement* svg_element = curr_svg_element->CorrespondingElement())
+    if (SVGElement* svg_element = curr_svg_element->CorrespondingElement()) {
       return svg_element;
+    }
   }
   return current_target_.Get();
 }
diff --git a/third_party/blink/renderer/core/dom/events/event.h b/third_party/blink/renderer/core/dom/events/event.h
index d3d71c7..d14e46c 100644
--- a/third_party/blink/renderer/core/dom/events/event.h
+++ b/third_party/blink/renderer/core/dom/events/event.h
@@ -143,10 +143,18 @@
   const AtomicString& type() const { return type_; }
   void SetType(const AtomicString& type) { type_ = type; }
 
-  EventTarget* target() const { return target_.Get(); }
+  // Retargeted target for IDL call: the return object can never be a pseudo
+  // element.
+  EventTarget* target() const;
+  // Raw target for internal usage, can be a pseudo element.
+  EventTarget* RawTarget() const { return target_.Get(); }
   void SetTarget(EventTarget*);
 
+  // Retargeted target for IDL call: the return object can never be a pseudo
+  // element.
   EventTarget* currentTarget() const;
+  // Raw target for internal usage, can be a pseudo element.
+  EventTarget* RawCurrentTarget() const;
   void SetCurrentTarget(EventTarget* current_target) {
     current_target_ = current_target;
   }
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index bfa53302..6a211d6 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -200,7 +200,7 @@
   }
 
   if (frame && window) {
-    eventTiming = EventTiming::TryCreate(window, *event_, event_->target());
+    eventTiming = EventTiming::TryCreate(window, *event_, event_->RawTarget());
   }
 
   if (event_->type() == event_type_names::kChange && event_->isTrusted() &&
@@ -258,7 +258,7 @@
 #if DCHECK_IS_ON()
   DCHECK(!EventDispatchForbiddenScope::IsEventDispatchForbidden());
 #endif
-  DCHECK(event_->target());
+  DCHECK(event_->RawTarget());
   DEVTOOLS_TIMELINE_TRACE_EVENT("EventDispatch",
                                 inspector_event_dispatch_event::Data, *event_,
                                 document.GetAgent().isolate());
@@ -373,7 +373,7 @@
     // Fire an accessibility event indicating a node was clicked on.  This is
     // safe if event_->target()->ToNode() returns null.
     if (AXObjectCache* cache = node_->GetDocument().ExistingAXObjectCache())
-      cache->HandleClicked(event_->target()->ToNode());
+      cache->HandleClicked(event_->RawTarget()->ToNode());
 
     // Pass the data from the PreDispatchEventHandler to the
     // PostDispatchEventHandler.
@@ -448,8 +448,9 @@
   // 16. If target's root is a shadow root, then set event's target attribute
   // and event's relatedTarget to null.
   event_->SetTarget(event_->GetEventPath().GetWindowEventContext().Target());
-  if (!event_->target())
+  if (!event_->RawTarget()) {
     event_->SetRelatedTargetIfExists(nullptr);
+  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/events/event_queue.cc b/third_party/blink/renderer/core/dom/events/event_queue.cc
index 63a2f8dd..7168b19 100644
--- a/third_party/blink/renderer/core/dom/events/event_queue.cc
+++ b/third_party/blink/renderer/core/dom/events/event_queue.cc
@@ -54,7 +54,7 @@
   if (is_closed_)
     return false;
 
-  DCHECK(event.target());
+  DCHECK(event.RawTarget());
   DCHECK(GetExecutionContext());
 
   event.async_task_context()->Schedule(GetExecutionContext(), event.type());
@@ -98,7 +98,7 @@
 
   probe::AsyncTask async_task(GetExecutionContext(),
                               event->async_task_context());
-  EventTarget* target = event->target();
+  EventTarget* target = event->RawTarget();
   if (LocalDOMWindow* window = target->ToLocalDOMWindow())
     window->DispatchEvent(*event, nullptr);
   else
diff --git a/third_party/blink/renderer/core/dom/events/scoped_event_queue.cc b/third_party/blink/renderer/core/dom/events/scoped_event_queue.cc
index 1f32123..0311082 100644
--- a/third_party/blink/renderer/core/dom/events/scoped_event_queue.cc
+++ b/third_party/blink/renderer/core/dom/events/scoped_event_queue.cc
@@ -73,8 +73,8 @@
 }
 
 void ScopedEventQueue::DispatchEvent(Event& event) const {
-  DCHECK(event.target());
-  Node* node = event.target()->ToNode();
+  DCHECK(event.RawTarget());
+  Node* node = event.RawTarget()->ToNode();
   EventDispatcher::DispatchEvent(*node, event);
 }
 
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index ee3c2c58..13081aa 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -3259,8 +3259,9 @@
 }
 
 void Node::DefaultEventHandler(Event& event) {
-  if (event.target() != this)
+  if (event.RawTarget() != this) {
     return;
+  }
   const AtomicString& event_type = event.type();
   if (event_type == event_type_names::kKeydown ||
       event_type == event_type_names::kKeypress ||
diff --git a/third_party/blink/renderer/core/dom/scripted_animation_controller.cc b/third_party/blink/renderer/core/dom/scripted_animation_controller.cc
index 485cf2f..6d5436a0 100644
--- a/third_party/blink/renderer/core/dom/scripted_animation_controller.cc
+++ b/third_party/blink/renderer/core/dom/scripted_animation_controller.cc
@@ -45,14 +45,14 @@
 bool ScriptedAnimationController::InsertToPerFrameEventsMap(
     const Event* event) {
   HashSet<const StringImpl*>& set =
-      per_frame_events_.insert(event->target(), HashSet<const StringImpl*>())
+      per_frame_events_.insert(event->RawTarget(), HashSet<const StringImpl*>())
           .stored_value->value;
   return set.insert(event->type().Impl()).is_new_entry;
 }
 
 void ScriptedAnimationController::EraseFromPerFrameEventsMap(
     const Event* event) {
-  EventTarget* target = event->target();
+  EventTarget* target = event->RawTarget();
   PerFrameEventsMap::iterator it = per_frame_events_.find(target);
   if (it != per_frame_events_.end()) {
     HashSet<const StringImpl*>& set = it->value;
@@ -146,7 +146,7 @@
 
   for (const auto& event : events) {
     did_dispatch = true;
-    EventTarget* event_target = event->target();
+    EventTarget* event_target = event->RawTarget();
     // FIXME: we should figure out how to make dispatchEvent properly virtual to
     // avoid special casting window.
     // FIXME: We should not fire events for nodes that are no longer in the
@@ -211,8 +211,8 @@
 }
 
 void ScriptedAnimationController::EnqueueEvent(Event* event) {
-  event->async_task_context()->Schedule(event->target()->GetExecutionContext(),
-                                        event->type());
+  event->async_task_context()->Schedule(
+      event->RawTarget()->GetExecutionContext(), event->type());
   event_queue_.push_back(event);
   ScheduleAnimationIfNeeded();
 }
diff --git a/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc b/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc
index e5edefb..31f077b 100644
--- a/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/scroll_button_pseudo_element.cc
@@ -127,7 +127,7 @@
       is_key_down && (To<KeyboardEvent>(event).keyCode() == VKEY_RETURN ||
                       To<KeyboardEvent>(event).keyCode() == VKEY_SPACE);
   bool should_intercept =
-      event.target() == this && (is_click || is_enter_or_space);
+      event.RawTarget() == this && (is_click || is_enter_or_space);
   if (should_intercept) {
     HandleButtonActivation();
     event.SetDefaultHandled();
diff --git a/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc b/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc
index e614264..439f687 100644
--- a/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.cc
@@ -67,7 +67,7 @@
       is_key_down && (To<KeyboardEvent>(event).keyCode() == VKEY_RIGHT ||
                       To<KeyboardEvent>(event).keyCode() == VKEY_DOWN);
   bool should_intercept =
-      event.target() == this &&
+      event.RawTarget() == this &&
       (is_click || is_enter_or_space || is_left_or_up_arrow_key ||
        is_right_or_down_arrow_key);
   if (should_intercept) {
diff --git a/third_party/blink/renderer/core/dom/shadow_root.idl b/third_party/blink/renderer/core/dom/shadow_root.idl
index 79492da..b1d827d 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.idl
+++ b/third_party/blink/renderer/core/dom/shadow_root.idl
@@ -54,32 +54,6 @@
     // See https://crbug.com/346835896
     [RuntimeEnabled=ShadowRootReferenceTarget] attribute DOMString referenceTarget;
 
-    // Scoped element creation APIs
-    // https://wicg.github.io/webcomponents/proposals/Scoped-Custom-Element-Registries#scoped-element-creation-apis
-    [
-      NewObject, PerWorldBindings, RaisesException, CEReactions,
-      RuntimeEnabled=ScopedCustomElementRegistry,
-      ImplementedAs=CreateElementForBinding
-    ]
-    Element createElement(DOMString localName);
-    [
-      NewObject, PerWorldBindings, RaisesException, CEReactions,
-      RuntimeEnabled=ScopedCustomElementRegistry,
-      ImplementedAs=CreateElementForBinding
-    ]
-    Element createElement(DOMString localName, (DOMString or ElementCreationOptions) options);
-    [
-      NewObject, RaisesException, CEReactions,
-      RuntimeEnabled=ScopedCustomElementRegistry
-    ]
-    Element createElementNS(DOMString? namespaceURI, DOMString qualifiedName);
-    [
-      NewObject, RaisesException, CEReactions,
-      RuntimeEnabled=ScopedCustomElementRegistry
-    ]
-    Element createElementNS(DOMString? namespaceURI, DOMString qualifiedName,
-                            (DOMString or ElementCreationOptions) options);
-
     [RaisesException,MeasureAs=SetHTMLUnsafe,CEReactions] void setHTMLUnsafe(HTMLString string);
     [RuntimeEnabled=SanitizerAPI,RaisesException,MeasureAs=SetHTMLUnsafe,CEReactions] void setHTMLUnsafe(HTMLString html, SetHTMLUnsafeOptions options);
     [RuntimeEnabled=SanitizerAPI,RaisesException,MeasureAs=SetHTMLSafe,CEReactions] void setHTML(DOMString html, optional SetHTMLOptions options = {});
diff --git a/third_party/blink/renderer/core/dom/tree_scope.h b/third_party/blink/renderer/core/dom/tree_scope.h
index 1a15c9b7..8f4ced65 100644
--- a/third_party/blink/renderer/core/dom/tree_scope.h
+++ b/third_party/blink/renderer/core/dom/tree_scope.h
@@ -54,9 +54,6 @@
 class SVGTreeScopeResources;
 class ScopedStyleResolver;
 class StyleSheetList;
-class CreateElementFlags;
-class QualifiedName;
-class V8UnionElementCreationOptionsOrString;
 
 // The root node of a document tree (in which case this is a Document) or of a
 // shadow tree (in which case this is a ShadowRoot). Various things, like
@@ -182,26 +179,6 @@
   void SetAdoptedStyleSheetsForTesting(HeapVector<Member<CSSStyleSheet>>&);
   void ClearAdoptedStyleSheets();
 
-  Element* CreateElementForBinding(const AtomicString& local_name,
-                                   ExceptionState& = ASSERT_NO_EXCEPTION);
-  Element* CreateElementForBinding(
-      const AtomicString& local_name,
-      const V8UnionElementCreationOptionsOrString* string_or_options,
-      ExceptionState& exception_state);
-  Element* createElementNS(const AtomicString& namespace_uri,
-                           const AtomicString& qualified_name,
-                           ExceptionState&);
-  Element* createElementNS(
-      const AtomicString& namespace_uri,
-      const AtomicString& qualified_name,
-      const V8UnionElementCreationOptionsOrString* string_or_options,
-      ExceptionState& exception_state);
-
-  // "create an element" defined in DOM standard. This supports both of
-  // autonomous custom elements and customized built-in elements.
-  Element* CreateElement(const QualifiedName&,
-                         const CreateElementFlags,
-                         const AtomicString& is);
 
   virtual CustomElementRegistry* customElementRegistry() const = 0;
 
diff --git a/third_party/blink/renderer/core/editing/commands/insert_commands.cc b/third_party/blink/renderer/core/editing/commands/insert_commands.cc
index 7fbea34..c68e20fd 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_commands.cc
@@ -54,7 +54,7 @@
 LocalFrame& InsertCommands::TargetFrame(LocalFrame& frame, Event* event) {
   if (!event)
     return frame;
-  const Node* node = event->target()->ToNode();
+  const Node* node = event->RawTarget()->ToNode();
   if (!node)
     return frame;
   LocalFrame* local_frame = node->GetDocument().GetFrame();
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc
index c50482b..0bf3584 100644
--- a/third_party/blink/renderer/core/editing/editor.cc
+++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -118,11 +118,12 @@
     return selection;
   // If the target is a text control, and the current selection is outside of
   // its shadow tree, then use the saved selection for that text control.
-  if (!IsTextControl(*event->target()->ToNode()))
+  if (!IsTextControl(*event->RawTarget()->ToNode())) {
     return selection;
+  }
   auto* text_control_of_selection_start =
       EnclosingTextControl(selection.Anchor());
-  auto* text_control_of_target = ToTextControl(event->target()->ToNode());
+  auto* text_control_of_target = ToTextControl(event->RawTarget()->ToNode());
   if (!selection.IsNone() &&
       text_control_of_target == text_control_of_selection_start)
     return selection;
@@ -571,7 +572,7 @@
                               WebFeature feature_on_text_area,
                               WebFeature feature_on_content_editable,
                               WebFeature feature_on_non_node) {
-  EventTarget* event_target = event.target();
+  EventTarget* event_target = event.RawTarget();
   Node* node = event_target->ToNode();
   if (!node) {
     UseCounter::Count(execution_context, feature_on_non_node);
diff --git a/third_party/blink/renderer/core/editing/editor_key_bindings.cc b/third_party/blink/renderer/core/editing/editor_key_bindings.cc
index 29706cc..a6d54f758 100644
--- a/third_party/blink/renderer/core/editing/editor_key_bindings.cc
+++ b/third_party/blink/renderer/core/editing/editor_key_bindings.cc
@@ -84,7 +84,7 @@
   // text to the focused element.
   if (auto* edit_context =
           GetFrame().GetInputMethodController().GetActiveEditContext()) {
-    if (DispatchBeforeInputInsertText(evt->target()->ToNode(),
+    if (DispatchBeforeInputInsertText(evt->RawTarget()->ToNode(),
                                       key_event->text.data()) !=
         DispatchEventResult::kNotCanceled) {
       return true;
@@ -110,7 +110,7 @@
     return false;
 
   // Return true to prevent default action. e.g. Space key scroll.
-  if (DispatchBeforeInputInsertText(evt->target()->ToNode(),
+  if (DispatchBeforeInputInsertText(evt->RawTarget()->ToNode(),
                                     key_event->text.data()) !=
       DispatchEventResult::kNotCanceled) {
     return true;
diff --git a/third_party/blink/renderer/core/events/mouse_event.cc b/third_party/blink/renderer/core/events/mouse_event.cc
index 346fb2f..819e799 100644
--- a/third_party/blink/renderer/core/events/mouse_event.cc
+++ b/third_party/blink/renderer/core/events/mouse_event.cc
@@ -454,7 +454,7 @@
 }
 
 void MouseEvent::ComputeRelativePosition() {
-  Node* target_node = target() ? target()->ToNode() : nullptr;
+  Node* target_node = RawTarget() ? RawTarget()->ToNode() : nullptr;
   if (!target_node)
     return;
 
@@ -525,7 +525,7 @@
 }
 
 void MouseEvent::RecordLayerXYMetrics() {
-  Node* node = target() ? target()->ToNode() : nullptr;
+  Node* node = RawTarget() ? RawTarget()->ToNode() : nullptr;
   if (!node)
     return;
   // Using the target for these metrics is a heuristic for measuring the impact
diff --git a/third_party/blink/renderer/core/frame/cached_permission_status.cc b/third_party/blink/renderer/core/frame/cached_permission_status.cc
index a713098..90cd917 100644
--- a/third_party/blink/renderer/core/frame/cached_permission_status.cc
+++ b/third_party/blink/renderer/core/frame/cached_permission_status.cc
@@ -36,7 +36,6 @@
       permission_service_(local_dom_window),
       permission_observer_receivers_(this, local_dom_window) {
   CHECK(local_dom_window);
-  CHECK(RuntimeEnabledFeatures::PermissionElementEnabled(local_dom_window));
 }
 
 void CachedPermissionStatus::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/frame/cached_permission_status.h b/third_party/blink/renderer/core/frame/cached_permission_status.h
index aa74f4f..e22cb10 100644
--- a/third_party/blink/renderer/core/frame/cached_permission_status.h
+++ b/third_party/blink/renderer/core/frame/cached_permission_status.h
@@ -71,6 +71,7 @@
 
  private:
   friend class HTMLPermissionElement;
+  friend class Screen;
   friend class DocumentLoader;
   friend class CachedPermissionStatusTest;
 
diff --git a/third_party/blink/renderer/core/frame/screen.cc b/third_party/blink/renderer/core/frame/screen.cc
index 51c1a235..4325717 100644
--- a/third_party/blink/renderer/core/frame/screen.cc
+++ b/third_party/blink/renderer/core/frame/screen.cc
@@ -41,7 +41,24 @@
 namespace blink {
 
 Screen::Screen(LocalDOMWindow* window, int64_t display_id)
-    : ExecutionContextClient(window), display_id_(display_id) {}
+    : ExecutionContextClient(window), display_id_(display_id) {
+  // If we're potentially reducing information about the screen size, register
+  // ourselves as a client of CachedPermissionStatus to listen for changes to
+  // the WINDOW_MANAGEMENT permission. We're going to rely on this cache because
+  // we'd otherwise need to block each synchronous property getter on a call to
+  // retrieve the current permission status, which is quite expensive for this
+  // commonly-used object.
+  if (RuntimeEnabledFeatures::ReduceScreenSizeEnabled() && DomWindow() &&
+      DomWindow()->IsFeatureEnabled(
+          network::mojom::PermissionsPolicyFeature::kWindowManagement)) {
+    auto descriptor = mojom::blink::PermissionDescriptor::New();
+    descriptor->name = mojom::blink::PermissionName::WINDOW_MANAGEMENT;
+    Vector<mojom::blink::PermissionDescriptorPtr> descriptors;
+    descriptors.push_back(std::move(descriptor));
+    CachedPermissionStatus::From(DomWindow())
+        ->RegisterClient(this, std::move(descriptors));
+  }
+}
 
 // static
 bool Screen::AreWebExposedScreenPropertiesEqual(
@@ -195,7 +212,8 @@
 bool Screen::ShouldReduceScreenSize() const {
   // TODO(408932088): Take the current state of the window management permission
   // (`mojom::blink::PermissionName::WINDOW_MANAGEMENT`) into account here.
-  return RuntimeEnabledFeatures::ReduceScreenSizeEnabled();
+  return RuntimeEnabledFeatures::ReduceScreenSizeEnabled() &&
+         !window_management_permission_granted_;
 }
 
 bool Screen::isExtended() const {
@@ -235,4 +253,24 @@
   return kEmptyScreenInfo;
 }
 
+void Screen::OnPermissionStatusChange(mojom::blink::PermissionName name,
+                                      mojom::blink::PermissionStatus status) {
+  CHECK(name == mojom::blink::PermissionName::WINDOW_MANAGEMENT);
+  window_management_permission_granted_ =
+      status == mojom::blink::PermissionStatus::GRANTED;
+}
+
+void Screen::OnPermissionStatusInitialized(
+    CachedPermissionStatus::PermissionStatusMap map) {
+  // Window management permission is granted if the map we're given has entries,
+  // and they're all GRANTED:
+  window_management_permission_granted_ =
+      map.size() > 0U && std::ranges::all_of(map, [](const auto& status) {
+        return status.value == mojom::blink::PermissionStatus::GRANTED;
+      });
+
+  // If the permission is granted, it should be the only item in the map:
+  CHECK(!window_management_permission_granted_ || map.size() == 1U);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/screen.h b/third_party/blink/renderer/core/frame/screen.h
index 8efac99..431dfbd 100644
--- a/third_party/blink/renderer/core/frame/screen.h
+++ b/third_party/blink/renderer/core/frame/screen.h
@@ -32,6 +32,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
+#include "third_party/blink/renderer/core/frame/cached_permission_status.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/supplementable.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -47,6 +48,7 @@
 
 class CORE_EXPORT Screen : public EventTarget,
                            public ExecutionContextClient,
+                           public CachedPermissionStatus::Client,
                            public Supplementable<Screen> {
   DEFINE_WRAPPERTYPEINFO();
 
@@ -93,8 +95,18 @@
   gfx::Rect GetRect(bool available) const;
   const display::ScreenInfo& GetScreenInfo() const;
 
+  // CachedPermissionStatus::Client overrides:
+  void OnPermissionStatusChange(mojom::blink::PermissionName,
+                                mojom::blink::PermissionStatus) override;
+
+  void OnPermissionStatusInitialized(
+      CachedPermissionStatus::PermissionStatusMap) override;
+
   // The internal id of the underlying display, to support multi-screen devices.
   int64_t display_id_;
+
+ private:
+  bool window_management_permission_granted_ = false;
 };
 
 }  // namespace blink
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 0aca515..95a0cc0 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
@@ -430,7 +430,6 @@
 void CanvasRenderingContextHost::DiscardResources() {
   resource_provider_for_canvas2d_ = nullptr;
   resource_provider_for_image_bitmap_ = nullptr;
-  resource_provider_for_webgl_ = nullptr;
   resource_provider_for_webgpu_ = nullptr;
   UpdateMemoryUsage();
 }
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 327064dc..8fe6f3c 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
@@ -67,6 +67,9 @@
   virtual DispatchEventResult HostDispatchEvent(Event*) = 0;
   virtual const KURL& GetExecutionContextUrl() const = 0;
 
+  virtual void UpdateMemoryUsage() = 0;
+  virtual size_t GetMemoryUsage() const = 0;
+
   // If WebGL1 is disabled by enterprise policy or command line switch.
   virtual bool IsWebGL1Enabled() const = 0;
   // If WebGL2 is disabled by enterprise policy or command line switch.
@@ -101,9 +104,9 @@
 
   // Partial CanvasResourceHost implementation
   void InitializeForRecording(cc::PaintCanvas*) const final;
-  CanvasResourceProvider* GetOrCreateCanvasResourceProviderForCanvas2D()
-      override;
-  void PageVisibilityChanged() override;
+  virtual CanvasResourceProvider*
+  GetOrCreateCanvasResourceProviderForCanvas2D();
+  virtual void PageVisibilityChanged();
 
   CanvasResourceProvider* GetOrCreateCanvasResourceProviderForWebGPU();
 
@@ -140,18 +143,6 @@
 
   bool IsContextLost() const override;
 
-  // `resource_provider_` must be null.
-  void SetResourceProviderForWebGL(
-      std::unique_ptr<CanvasResourceProvider> resource_provider) {
-    CHECK(IsWebGL());
-    CHECK(!resource_provider_for_webgl_);
-    resource_provider_for_webgl_ = std::move(resource_provider);
-    UpdateMemoryUsage();
-  }
-  CanvasResourceProvider* GetResourceProviderForWebGL() const {
-    CHECK(IsWebGL());
-    return resource_provider_for_webgl_.get();
-  }
   CanvasResourceProvider* GetResourceProviderForWebGPU() const {
     CHECK(IsWebGPU());
     return resource_provider_for_webgpu_.get();
@@ -214,7 +205,6 @@
 
   std::unique_ptr<CanvasResourceProvider> resource_provider_for_canvas2d_;
   std::unique_ptr<CanvasResourceProvider> resource_provider_for_image_bitmap_;
-  std::unique_ptr<CanvasResourceProvider> resource_provider_for_webgl_;
   std::unique_ptr<CanvasResourceProvider> resource_provider_for_webgpu_;
   bool did_record_canvas_size_to_uma_ = false;
   HostType host_type_ = HostType::kNone;
diff --git a/third_party/blink/renderer/core/html/custom/custom_element.cc b/third_party/blink/renderer/core/html/custom/custom_element.cc
index b0efce2..9c79c5a 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_element.cc
@@ -126,25 +126,24 @@
 }
 
 static CustomElementDefinition* DefinitionFor(
-    const TreeScope& tree_scope,
+    const Document& document,
     const CustomElementDescriptor desc) {
-  if (CustomElementRegistry* registry = CustomElement::Registry(tree_scope)) {
+  if (CustomElementRegistry* registry = CustomElement::Registry(document)) {
     return registry->DefinitionFor(desc);
   }
   return nullptr;
 }
 
 // https://dom.spec.whatwg.org/#concept-create-element
-HTMLElement* CustomElement::CreateCustomElement(TreeScope& tree_scope,
+HTMLElement* CustomElement::CreateCustomElement(Document& document,
                                                 const QualifiedName& tag_name,
                                                 CreateElementFlags flags) {
   DCHECK(ShouldCreateCustomElement(tag_name)) << tag_name;
-  Document& document = tree_scope.GetDocument();
   // 4. Let definition be the result of looking up a custom element
   // definition given document, namespace, localName, and is.
   if (auto* definition = DefinitionFor(
-          tree_scope, CustomElementDescriptor(tag_name.LocalName(),
-                                              tag_name.LocalName()))) {
+          document, CustomElementDescriptor(tag_name.LocalName(),
+                                            tag_name.LocalName()))) {
     DCHECK(definition->Descriptor().IsAutonomous());
     // 6. Otherwise, if definition is non-null, then:
     return definition->CreateElement(document, tag_name, flags);
diff --git a/third_party/blink/renderer/core/html/custom/custom_element.h b/third_party/blink/renderer/core/html/custom/custom_element.h
index df8f382..5ecc47f 100644
--- a/third_party/blink/renderer/core/html/custom/custom_element.h
+++ b/third_party/blink/renderer/core/html/custom/custom_element.h
@@ -109,7 +109,7 @@
 
   // Look up a definition, and create an autonomous custom element if
   // it's found.
-  static HTMLElement* CreateCustomElement(TreeScope&,
+  static HTMLElement* CreateCustomElement(Document&,
                                           const QualifiedName&,
                                           const CreateElementFlags);
 
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 4620a44..a4834369 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -423,7 +423,7 @@
 
     if (popover.popover) {
       bool event_target_was_nested_popover = false;
-      if (auto* target_node = event.target()->ToNode()) {
+      if (auto* target_node = event.RawTarget()->ToNode()) {
         bool button_is_ancestor_of_popover =
             IsShadowIncludingAncestorOf(*popover.popover);
         event_target_was_nested_popover =
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 7a447113..0b4a9ae 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -193,7 +193,7 @@
 }
 
 void HTMLFormElement::HandleLocalEvents(Event& event) {
-  Node* target_node = event.target()->ToNode();
+  Node* target_node = event.RawTarget()->ToNode();
   if (event.eventPhase() != Event::PhaseType::kCapturingPhase && target_node &&
       target_node != this &&
       (event.type() == event_type_names::kSubmit ||
diff --git a/third_party/blink/renderer/core/html/forms/html_label_element.cc b/third_party/blink/renderer/core/html/forms/html_label_element.cc
index 4b08f5c3..5c38594 100644
--- a/third_party/blink/renderer/core/html/forms/html_label_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_label_element.cc
@@ -174,7 +174,7 @@
     // event, then there's no need for us to do anything.
     if (!element)
       return;
-    Node* target_node = evt.target() ? evt.target()->ToNode() : nullptr;
+    Node* target_node = evt.RawTarget() ? evt.RawTarget()->ToNode() : nullptr;
     if (target_node) {
       if (element->IsShadowIncludingInclusiveAncestorOf(*target_node))
         return;
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.cc b/third_party/blink/renderer/core/html/forms/range_input_type.cc
index 5018322..87a46a52 100644
--- a/third_party/blink/renderer/core/html/forms/range_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -179,7 +179,7 @@
   if (GetElement().IsDisabledFormControl())
     return;
 
-  Node* target_node = event.target()->ToNode();
+  Node* target_node = event.RawTarget()->ToNode();
   if (event.button() !=
           static_cast<int16_t>(WebPointerProperties::Button::kLeft) ||
       !target_node)
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index d81cf6fb..3efc5fe 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -81,7 +81,7 @@
 namespace {
 
 HTMLOptionElement* EventTargetOption(const Event& event) {
-  auto* element = DynamicTo<Element>(event.target()->ToNode());
+  auto* element = DynamicTo<Element>(event.RawTarget()->ToNode());
   if (!element) {
     return nullptr;
   }
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index 046c3c4..6dffe01 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -209,8 +209,8 @@
   if (!mouse_event)
     return;
 
-  DCHECK(event->target());
-  Node* target = event->target()->ToNode();
+  DCHECK(event->RawTarget());
+  Node* target = event->RawTarget()->ToNode();
   DCHECK(target);
   auto* image_element = DynamicTo<HTMLImageElement>(target);
   if (!image_element || !image_element->IsServerMap())
diff --git a/third_party/blink/renderer/core/html/html_summary_element.cc b/third_party/blink/renderer/core/html/html_summary_element.cc
index 93211e7..685f1e5 100644
--- a/third_party/blink/renderer/core/html/html_summary_element.cc
+++ b/third_party/blink/renderer/core/html/html_summary_element.cc
@@ -63,7 +63,7 @@
 void HTMLSummaryElement::DefaultEventHandler(Event& event) {
   if (IsMainSummary()) {
     if (event.type() == event_type_names::kDOMActivate &&
-        !IsClickableControl(event.target()->ToNode())) {
+        !IsClickableControl(event.RawTarget()->ToNode())) {
       if (HTMLDetailsElement* details = DetailsElement())
         details->ToggleOpen();
       event.SetDefaultHandled();
diff --git a/third_party/blink/renderer/core/html/media/media_document.cc b/third_party/blink/renderer/core/html/media/media_document.cc
index 6bd2466..c01d14e0 100644
--- a/third_party/blink/renderer/core/html/media/media_document.cc
+++ b/third_party/blink/renderer/core/html/media/media_document.cc
@@ -137,7 +137,7 @@
 }
 
 void MediaDocument::DefaultEventHandler(Event& event) {
-  Node* target_node = event.target()->ToNode();
+  Node* target_node = event.RawTarget()->ToNode();
   if (!target_node)
     return;
 
diff --git a/third_party/blink/renderer/core/html/track/cue_timeline.h b/third_party/blink/renderer/core/html/track/cue_timeline.h
index 6f96c2042..e242eeca 100644
--- a/third_party/blink/renderer/core/html/track/cue_timeline.h
+++ b/third_party/blink/renderer/core/html/track/cue_timeline.h
@@ -19,13 +19,13 @@
 class HTMLMediaElement;
 class TextTrackCueList;
 
-// TODO(Oilpan): This needs to be PODIntervalTree<double, Member<TextTrackCue>>.
-// However, it is not easy to move PODIntervalTree to the heap (for a
+// TODO(Oilpan): This needs to be PodIntervalTree<double, Member<TextTrackCue>>.
+// However, it is not easy to move PodIntervalTree to the heap (for a
 // C++-template reason) so we leave it as a raw pointer at the moment. This is
 // safe because CueTimeline and TextTrackCue are guaranteed to die at the same
 // time when the owner HTMLMediaElement dies. Thus the raw TextTrackCue* cannot
 // become stale pointers.
-typedef WTF::PODIntervalTree<double, TextTrackCue*> CueIntervalTree;
+using CueIntervalTree = PodIntervalTree<double, TextTrackCue*>;
 typedef CueIntervalTree::IntervalType CueInterval;
 typedef Vector<CueInterval> CueList;
 
@@ -111,18 +111,14 @@
   bool update_requested_while_ignoring_;
 };
 
-}  // namespace blink
-
-namespace WTF {
 #ifndef NDEBUG
 // Template specializations required by PodIntervalTree in debug mode.
 template <>
-struct ValueToString<blink::TextTrackCue*> {
-  static String ToString(blink::TextTrackCue* const& cue) {
-    return cue->ToString();
-  }
+struct ValueToString<TextTrackCue*> {
+  static String ToString(TextTrackCue* const& cue) { return cue->ToString(); }
 };
 #endif
-}
+
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_TRACK_CUE_TIMELINE_H_
diff --git a/third_party/blink/renderer/core/input/event_handler.cc b/third_party/blink/renderer/core/input/event_handler.cc
index 951cd79..48510501 100644
--- a/third_party/blink/renderer/core/input/event_handler.cc
+++ b/third_party/blink/renderer/core/input/event_handler.cc
@@ -2447,7 +2447,7 @@
 
   EventTarget* target;
   if (underlying_event)
-    target = underlying_event->target();
+    target = underlying_event->RawTarget();
   else
     target = EventTargetNodeForDocument(frame_->GetDocument());
   if (!target)
diff --git a/third_party/blink/renderer/core/layout/layout_flow_thread.h b/third_party/blink/renderer/core/layout/layout_flow_thread.h
index 0a0c5dc..9bd2de4 100644
--- a/third_party/blink/renderer/core/layout/layout_flow_thread.h
+++ b/third_party/blink/renderer/core/layout/layout_flow_thread.h
@@ -181,8 +181,8 @@
 
   typedef WTF::PODInterval<LayoutUnit, LayoutMultiColumnSet*>
       MultiColumnSetInterval;
-  typedef WTF::PODIntervalTree<LayoutUnit, LayoutMultiColumnSet*>
-      MultiColumnSetIntervalTree;
+  using MultiColumnSetIntervalTree =
+      PodIntervalTree<LayoutUnit, LayoutMultiColumnSet*>;
 
   class MultiColumnSetSearchAdapter {
     STACK_ALLOCATED();
@@ -214,24 +214,22 @@
   }
 };
 
-}  // namespace blink
-
-namespace WTF {
-// These structures are used by PODIntervalTree for debugging.
+// These structures are used by PodIntervalTree for debugging.
 #ifndef NDEBUG
 template <>
-struct ValueToString<blink::LayoutMultiColumnSet*> {
-  static String ToString(const blink::LayoutMultiColumnSet* value) {
+struct ValueToString<LayoutMultiColumnSet*> {
+  static String ToString(const LayoutMultiColumnSet* value) {
     return String::Format("%p", value);
   }
 };
 template <>
-struct ValueToString<blink::LayoutUnit> {
-  static String ToString(const blink::LayoutUnit value) {
+struct ValueToString<LayoutUnit> {
+  static String ToString(const LayoutUnit value) {
     return String::Number(value.ToFloat());
   }
 };
 #endif
-}  // namespace WTF
+
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_FLOW_THREAD_H_
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 4d749ab..a81090b 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -202,7 +202,8 @@
 
 void ContextMenuController::HandleContextMenuEvent(MouseEvent* mouse_event) {
   DCHECK(mouse_event->type() == event_type_names::kContextmenu);
-  LocalFrame* frame = mouse_event->target()->ToNode()->GetDocument().GetFrame();
+  LocalFrame* frame =
+      mouse_event->RawTarget()->ToNode()->GetDocument().GetFrame();
   PhysicalOffset location =
       PhysicalOffset::FromPointFRound(mouse_event->AbsoluteLocation());
 
@@ -482,7 +483,7 @@
           ->GetEditor());
 
   if (mouse_event && source_type == kMenuSourceKeyboard) {
-    Node* target_node = mouse_event->target()->ToNode();
+    Node* target_node = mouse_event->RawTarget()->ToNode();
     if (target_node && IsA<Element>(target_node)) {
       // Get the url from an explicitly set target, e.g. the focused element
       // when the context menu is evoked from the keyboard. Note: the innerNode
diff --git a/third_party/blink/renderer/core/page/focusgroup_controller.cc b/third_party/blink/renderer/core/page/focusgroup_controller.cc
index 935b7ef8..4892537 100644
--- a/third_party/blink/renderer/core/page/focusgroup_controller.cc
+++ b/third_party/blink/renderer/core/page/focusgroup_controller.cc
@@ -39,7 +39,7 @@
     return false;
 
   Element* focused = frame->GetDocument()->FocusedElement();
-  if (!focused || focused != event->target()) {
+  if (!focused || focused != event->RawTarget()) {
     // The FocusgroupController shouldn't handle this arrow key event when the
     // focus already moved to a different element than where it came from. The
     // webpage likely had a key-handler that moved the focus.
diff --git a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
index 368c462..d07cec6 100644
--- a/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
+++ b/third_party/blink/renderer/core/page/spatial_navigation_controller.cc
@@ -179,7 +179,7 @@
 
   // If the focus has already moved by a previous handler, return false.
   const Element* focused = GetFocusedElement();
-  if (focused && focused != event->target()) {
+  if (focused && focused != event->RawTarget()) {
     // SpatNav does not need to handle this arrow key because
     // the webpage had a key-handler that already moved focus.
     return false;
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
index d687e2fb..6099594 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.cc
@@ -164,7 +164,8 @@
     return SoftNavigationHeuristics::EventScope::Type::kNavigate;
   }
   if (event.IsKeyboardEvent()) {
-    Node* target_node = event.target() ? event.target()->ToNode() : nullptr;
+    Node* target_node =
+        event.RawTarget() ? event.RawTarget()->ToNode() : nullptr;
     if (target_node && target_node->IsHTMLElement() &&
         DynamicTo<HTMLElement>(target_node)->IsHTMLBodyElement()) {
       if (event.type() == event_type_names::kKeydown) {
@@ -424,12 +425,29 @@
     return;
   }
 
-  // We have met all criteria!
+  // We have met all Soft-Nav criteria!
+
+  // At this point, this navigation should be "committed" to the performance
+  // timeline. Thus, we increment the navigation id here, in the animation frame
+  // Paint where the criteria are first met. However, the navigation will not be
+  // ready for reporting until it also has an FCP measurement.
+  // We must *not* wait on this presentation time callback, because all other
+  // new performance entries created need to use this new navigation id, in
+  // order to match with the eventual soft-nav entry.
+  //
+  // TODO(crbug.com/424448145): Ideally, we should carefully ensure that this
+  // happens exactly where we want our timeOrigin, and also ensure that all
+  // performance entries are created at the time of the measurement they are
+  // reporting, rather than some time later, which risks assigning the wrong
+  // navigationId-- but this might be impossible.  Instead, we might need to
+  // re-write history when we get a new navigationId with a timeOrigin in the
+  // past.
   ++soft_navigation_count_;
   window_->GenerateNewNavigationId();
+
   context->SetNavigationId(window_->GetNavigationId());
 
-  needs_paint_timing_callback_ = true;
+  context_for_first_contentful_paint_ = context;
 }
 
 SoftNavigationContext*
@@ -464,14 +482,11 @@
 
 OptionalPaintTimingCallback
 SoftNavigationHeuristics::TakePaintTimingCallback() {
-  // If we need paint timing, we must have a context that needs FCP.
-  CHECK(!needs_paint_timing_callback_ ||
-        (context_for_current_url_ &&
-         !context_for_current_url_->HasFirstContentfulPaint()));
-  if (!needs_paint_timing_callback_) {
+  if (!context_for_first_contentful_paint_) {
     return {};
   }
-  needs_paint_timing_callback_ = false;
+  // If we need paint timing, we must have a context that needs FCP.
+  CHECK(!context_for_first_contentful_paint_->HasFirstContentfulPaint());
 
   // TODO(crbug.com/40871933): We are already only marking dom nodes when we
   // have a frame, and we are already limiting paints attribution to contexts
@@ -511,9 +526,11 @@
                             context->FirstContentfulPaint(), "context",
                             *context, "frame", frameIdForTracing);
       },
-      WrapWeakPersistent(this), WrapPersistent(context_for_current_url_.Get()),
+      WrapWeakPersistent(this),
+      WrapPersistent(context_for_first_contentful_paint_.Get()),
       frameIdForTracing);
 
+  context_for_first_contentful_paint_ = nullptr;
   return std::move(callback);
 }
 
@@ -586,6 +603,7 @@
 void SoftNavigationHeuristics::Trace(Visitor* visitor) const {
   visitor->Trace(active_interaction_context_);
   visitor->Trace(context_for_current_url_);
+  visitor->Trace(context_for_first_contentful_paint_);
   visitor->Trace(window_);
   visitor->Trace(paint_attribution_tracker_);
   // Register a custom weak callback, which runs after processing weakness for
diff --git a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
index 71c5b35..2068f95 100644
--- a/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
+++ b/third_party/blink/renderer/core/timing/soft_navigation_heuristics.h
@@ -202,13 +202,26 @@
   // continue measuring paints for a while.
   Member<SoftNavigationContext> context_for_current_url_;
 
+  // Save a strong reference to the most recent context that painted for the
+  // first time, and needs an FCP presentation callback.  This will be picked
+  // up by PaintTimingMixin, cleared, but held strongly until presententation
+  // feedback.  Soft-navigation entries are not reported to the performance
+  // timeline until after FCP is measured.
+  // TODO(crbug.com/424448145): Needs some changes:
+  // - measure first paint update, not the update after criteria met.
+  // - measure first paint of first contentful candidate, not fully loaded
+  // paint.
+  // - support multiple context in a single animation frame, rather than
+  // single value here.  Will become more important when all interactions
+  // measure paint.
+  Member<SoftNavigationContext> context_for_first_contentful_paint_;
+
   // Used to map DOM modifications to `SoftNavigationContext`s for paint
   // attribution. Only set when `IsPrePaintBasedAttributionEnabled()` is true.
   Member<SoftNavigationPaintAttributionTracker> paint_attribution_tracker_;
 
   uint32_t soft_navigation_count_ = 0;
   bool has_active_event_scope_ = false;
-  bool needs_paint_timing_callback_ = false;
 
   const features::SoftNavigationHeuristicsMode paint_attribution_mode_;
   // `task_attribution_tracker_` is cleared during `Shutdown()` (frame detach),
diff --git a/third_party/blink/renderer/core/timing/window_performance.cc b/third_party/blink/renderer/core/timing/window_performance.cc
index a82518e..472b162 100644
--- a/third_party/blink/renderer/core/timing/window_performance.cc
+++ b/third_party/blink/renderer/core/timing/window_performance.cc
@@ -693,11 +693,11 @@
 #endif  // BUILDFLAG(IS_MAC)
   }
 
-  if (event.target()) {
-    // `event->target()` is assigned as part of EventDispatch, and will be unset
-    // whenever we skip dispatch. (See: crbug.com/1367329).
-    // Note: target may be dom detached, and even GC-ed, before Observer fires.
-    entry->SetTarget(event.target()->ToNode());
+  if (EventTarget* raw_target = event.RawTarget()) {
+    // `event->RawTarget()` is assigned as part of EventDispatch, and will be
+    // unset whenever we skip dispatch. (See: crbug.com/1367329). Note: target
+    // may be dom detached, and even GC-ed, before Observer fires.
+    entry->SetTarget(raw_target->ToNode());
   }
 
   // Request presentation time first, because this might increment presentation
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
index b26284b..1be3507 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/script/fetch_client_settings_object_impl.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
+#include "third_party/blink/renderer/core/workers/worker_navigator.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
diff --git a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
index d88978d..2d711b7f 100644
--- a/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h
@@ -25,8 +25,8 @@
 #include "third_party/blink/renderer/core/loader/back_forward_cache_loader_helper_impl.h"
 #include "third_party/blink/renderer/core/script/modulator.h"
 #include "third_party/blink/renderer/core/workers/worker_clients.h"
-#include "third_party/blink/renderer/core/workers/worker_navigator.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
+#include "third_party/blink/renderer/platform/heap/cross_thread_persistent.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_load_scheduler.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
 #include "third_party/blink/renderer/platform/scheduler/public/worker_scheduler.h"
@@ -43,6 +43,7 @@
 class SubresourceFilter;
 class WebContentSettingsClient;
 class WebWorkerFetchContext;
+class WorkerNavigator;
 class WorkerOrWorkletScriptController;
 class WorkerReportingProxy;
 class WorkerThread;
diff --git a/third_party/blink/renderer/modules/locks/lock_manager.cc b/third_party/blink/renderer/modules/locks/lock_manager.cc
index 9833658..0e7adeb 100644
--- a/third_party/blink/renderer/modules/locks/lock_manager.cc
+++ b/third_party/blink/renderer/modules/locks/lock_manager.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_lock_manager_snapshot.h"
 #include "third_party/blink/renderer/core/dom/abort_signal.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/execution_context/navigator_base.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/web_feature.h"
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
index 9d54c49..d3d38c1 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_playback_speed_list_element.cc
@@ -128,7 +128,7 @@
     event.SetDefaultHandled();
   } else if (event.type() == event_type_names::kChange) {
     // Identify which input element was selected and update playback speed
-    Node* target = event.target()->ToNode();
+    Node* target = event.RawTarget()->ToNode();
     if (!target || !target->IsElementNode())
       return;
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
index 068f2ef..123d701 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
@@ -97,7 +97,8 @@
           break;
         case VKEY_RETURN:
         case VKEY_SPACE:
-          To<Element>(event->target()->ToNode())->DispatchSimulatedClick(event);
+          To<Element>(event->RawTarget()->ToNode())
+              ->DispatchSimulatedClick(event);
           popup_menu_->FocusPopupAnchorIfOverflowClosed();
           break;
         default:
@@ -153,10 +154,10 @@
 
 void MediaControlPopupMenuElement::DefaultEventHandler(Event& event) {
   if (event.type() == event_type_names::kPointermove &&
-      event.target() != this) {
-    To<Element>(event.target()->ToNode())
+      event.RawTarget() != this) {
+    To<Element>(event.RawTarget()->ToNode())
         ->Focus(FocusParams(FocusTrigger::kUserGesture));
-    last_focused_element_ = To<Element>(event.target()->ToNode());
+    last_focused_element_ = To<Element>(event.RawTarget()->ToNode());
   } else if (event.type() == event_type_names::kFocusout) {
     GetDocument()
         .GetTaskRunner(TaskType::kMediaElementEvent)
@@ -165,15 +166,15 @@
             WTF::BindOnce(&MediaControlPopupMenuElement::HideIfNotFocused,
                           WrapWeakPersistent(this)));
   } else if (event.type() == event_type_names::kClick &&
-             event.target() != this) {
-    // Since event.target() != this, we know that one of our children was
+             event.RawTarget() != this) {
+    // Since event.RawTarget() != this, we know that one of our children was
     // clicked.
     OnItemSelected();
 
     event.stopPropagation();
     event.SetDefaultHandled();
   } else if (event.type() == event_type_names::kFocus &&
-             event.target() == this) {
+             event.RawTarget() == this) {
     // When the popup menu gains focus from scrolling, switch focus
     // back to the last focused item in the menu.
     if (last_focused_element_) {
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
index 68e3d6e..d807420 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_text_track_list_element.cc
@@ -86,7 +86,7 @@
     event.SetDefaultHandled();
   } else if (event.type() == event_type_names::kChange) {
     // Identify which input element was selected and set track to showing
-    Node* target = event.target()->ToNode();
+    Node* target = event.RawTarget()->ToNode();
     if (!target || !target->IsElementNode())
       return;
 
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc
index dc8b5b3..cb41b25 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_track_selector_list_element.cc
@@ -125,7 +125,7 @@
   } else if (event.type() == event_type_names::kChange) {
     // Identify which input element was selected and select the corresponding
     // track.
-    Node* target = event.target()->ToNode();
+    Node* target = event.RawTarget()->ToNode();
     if (!target || !target->IsElementNode()) {
       return;
     }
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
index 8ff85ea2..33e3f08 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_shared_helper.cc
@@ -55,8 +55,9 @@
 void MediaControlsSharedHelpers::TransitionEventListener::Invoke(
     ExecutionContext* context,
     Event* event) {
-  if (event->target() != element_)
+  if (event->RawTarget() != element_) {
     return;
+  }
 
   if (event->type() == event_type_names::kTransitionend) {
     callback_.Run();
diff --git a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
index e81a3216..e988ded 100644
--- a/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
+++ b/third_party/blink/renderer/modules/peerconnection/media_stream_remote_video_source.cc
@@ -16,7 +16,9 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+#include "media/base/media_switches.h"
 #include "media/base/timestamp_constants.h"
+#include "media/base/video_color_space.h"
 #include "media/base/video_frame.h"
 #include "media/base/video_util.h"
 #include "third_party/blink/public/common/features.h"
@@ -204,7 +206,7 @@
   // WebRtcIgnoreUnspecifiedColorSpace. If the feature is enabled we won't try
   // to guess a color space if the webrtc::ColorSpace is unspecified. If the
   // feature is disabled (default), an unspecified color space will get
-  // converted into a gfx::ColorSpace set to BT709.
+  // converted into a gfx::ColorSpace set to BT601.
   if (incoming_frame.color_space() &&
       !(ignore_unspecified_color_space_ &&
         incoming_frame.color_space()->primaries() ==
@@ -213,8 +215,19 @@
             webrtc::ColorSpace::TransferID::kUnspecified &&
         incoming_frame.color_space()->matrix() ==
             webrtc::ColorSpace::MatrixID::kUnspecified)) {
-    video_frame->set_color_space(
-        WebRtcToGfxColorSpace(*incoming_frame.color_space()));
+    gfx::ColorSpace color_space =
+        WebRtcToGfxColorSpace(*incoming_frame.color_space());
+    if (!color_space.IsValid()) {
+      color_space = media::VideoColorSpace::FromGfxColorSpace(color_space)
+                        .GuessGfxColorSpace();
+    }
+    if (color_space.IsValid()) {
+      video_frame->set_color_space(color_space);
+    }
+  }
+  if (base::FeatureList::IsEnabled(media::kWebRTCColorAccuracy) &&
+      !incoming_frame.color_space()) {
+    video_frame->set_color_space(gfx::ColorSpace::CreateREC601());
   }
 
   // Run render smoothness algorithm only when we don't have to render
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_features.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_features.cc
index 5286e06f..05349b2 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_features.cc
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_features.cc
@@ -23,12 +23,6 @@
              "WebRtcEncryptedRtpHeaderExtensions",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// This feature enables using Post-Quantum Crypto(PQC) for DTLS to improve
-// WebRTC's security.
-BASE_FEATURE(kWebRtcPQCForDTLS,
-             "WebRtcPQCForDTLS",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 // This features enables the restriction that frames sent to an
 // RTCRtpScriptTransformer's writable must come from the transformer's readable
 // and must be written in the same order in which they are read. This feature
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_features.h b/third_party/blink/renderer/modules/peerconnection/peer_connection_features.h
index b49e1e7..027e69d 100644
--- a/third_party/blink/renderer/modules/peerconnection/peer_connection_features.h
+++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_features.h
@@ -9,7 +9,6 @@
 namespace blink {
 MODULES_EXPORT BASE_DECLARE_FEATURE(kWebRtcEncodedTransformDirectCallback);
 MODULES_EXPORT BASE_DECLARE_FEATURE(kWebRtcEncryptedRtpHeaderExtensions);
-MODULES_EXPORT BASE_DECLARE_FEATURE(kWebRtcPQCForDTLS);
 MODULES_EXPORT BASE_DECLARE_FEATURE(
     kWebRtcRtpScriptTransformerFrameRestrictions);
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
index a907df9..e5370ed 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection_handler.cc
@@ -885,7 +885,7 @@
   configuration_.crypto_options->srtp.enable_encrypted_rtp_header_extensions =
       base::FeatureList::IsEnabled(kWebRtcEncryptedRtpHeaderExtensions);
   configuration_.enable_implicit_rollback = true;
-  if (base::FeatureList::IsEnabled(kWebRtcPQCForDTLS)) {
+  if (base::FeatureList::IsEnabled(features::kWebRtcPqcForDtls)) {
     configuration_.crypto_options->ephemeral_key_exchange_cipher_groups
         .AddFirst(webrtc::CryptoOptions::EphemeralKeyExchangeCipherGroups::
                       kX25519_MLKEM768);
diff --git a/third_party/blink/renderer/modules/service_worker/fetch_event.cc b/third_party/blink/renderer/modules/service_worker/fetch_event.cc
index 7aa4cc3..7f578f97 100644
--- a/third_party/blink/renderer/modules/service_worker/fetch_event.cc
+++ b/third_party/blink/renderer/modules/service_worker/fetch_event.cc
@@ -10,6 +10,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/public/mojom/timing/performance_mark_or_measure.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
+#include "third_party/blink/public/platform/web_url.h"
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
 #include "third_party/blink/renderer/core/dom/abort_signal.h"
diff --git a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
index 779b6ba0..155e587 100644
--- a/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
+++ b/third_party/blink/renderer/modules/service_worker/service_worker_global_scope.cc
@@ -89,6 +89,7 @@
 #include "third_party/blink/renderer/core/workers/worker_backing_thread.h"
 #include "third_party/blink/renderer/core/workers/worker_classic_script_loader.h"
 #include "third_party/blink/renderer/core/workers/worker_clients.h"
+#include "third_party/blink/renderer/core/workers/worker_navigator.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
 #include "third_party/blink/renderer/modules/background_fetch/background_fetch_event.h"
 #include "third_party/blink/renderer/modules/background_fetch/background_fetch_registration.h"
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 3a4667c..50f49136 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -1695,6 +1695,11 @@
   return submitted_frame;
 }
 
+void WebGLRenderingContextBase::Dispose() {
+  resource_provider_.reset();
+  CanvasRenderingContext::Dispose();
+}
+
 bool WebGLRenderingContextBase::PushFrameWithCopy() {
   bool submitted_frame = false;
 
@@ -1868,7 +1873,7 @@
 }
 
 bool WebGLRenderingContextBase::IsAccelerated() const {
-  auto* resource_provider = Host()->GetResourceProviderForWebGL();
+  auto* resource_provider = resource_provider_.get();
   return resource_provider ? resource_provider->IsAccelerated()
                            : Host()->ShouldTryToUseGpuRaster();
 }
@@ -1884,6 +1889,7 @@
 
 void WebGLRenderingContextBase::SizeChanged() {
   did_fail_to_create_resource_provider_ = false;
+  resource_provider_.reset();
 }
 
 scoped_refptr<StaticBitmapImage>
@@ -1907,7 +1913,7 @@
   }
   PaintRenderingResultsToCanvas(source_buffer);
   if (has_dispatcher && was_dirty && GetOrCreateCanvasResourceProvider()) {
-    return Host()->GetResourceProviderForWebGL()->ProduceCanvasResource(reason);
+    return resource_provider_.get()->ProduceCanvasResource(reason);
   }
   return nullptr;
 }
@@ -1992,11 +1998,12 @@
 
 CanvasResourceProvider*
 WebGLRenderingContextBase::GetOrCreateCanvasResourceProvider() {
-  auto* provider = Host()->GetResourceProviderForWebGL();
+  auto* provider = resource_provider_.get();
   if (!provider && !did_fail_to_create_resource_provider_) {
     if (Host()->IsValidImageSize()) {
-      Host()->SetResourceProviderForWebGL(CreateCanvasResourceProvider());
-      provider = Host()->GetResourceProviderForWebGL();
+      resource_provider_ = CreateCanvasResourceProvider();
+      Host()->UpdateMemoryUsage();
+      provider = resource_provider_.get();
     }
     if (!provider) {
       did_fail_to_create_resource_provider_ = true;
@@ -2021,23 +2028,22 @@
   }
 
   if (isContextLost() || !GetDrawingBuffer()) {
-    return Host()->GetResourceProviderForWebGL();
+    return resource_provider_.get();
   }
 
   bool must_clear_now = ClearIfComposited(kClearCallerOther) != kSkipped;
 
-  if (Host()->GetResourceProviderForWebGL() &&
-      Host()->GetResourceProviderForWebGL()->Size() !=
-          GetDrawingBuffer()->Size()) {
+  if (resource_provider_.get() &&
+      resource_provider_.get()->Size() != GetDrawingBuffer()->Size()) {
+    resource_provider_.reset();
     Host()->DiscardResources();
   }
 
   // The host's ResourceProvider is purged to save memory when the tab
   // is backgrounded.
 
-  if (!must_paint_to_canvas_ && !must_clear_now &&
-      Host()->GetResourceProviderForWebGL()) {
-    return Host()->GetResourceProviderForWebGL();
+  if (!must_paint_to_canvas_ && !must_clear_now && resource_provider_.get()) {
+    return resource_provider_.get();
   }
 
   must_paint_to_canvas_ = false;
@@ -2076,7 +2082,7 @@
     return resource_provider;
 
   bool copy_succeeded = CopyRenderingResultsFromDrawingBuffer(
-      Host()->GetResourceProviderForWebGL(), source_buffer);
+      resource_provider_.get(), source_buffer);
   if (resource_provider_was_updated != nullptr) {
     *resource_provider_was_updated = copy_succeeded;
   }
@@ -9174,7 +9180,7 @@
     return buffer_count;
   }
 
-  auto* provider = Host()->GetResourceProviderForWebGL();
+  auto* provider = resource_provider_.get();
   if (provider) {
     buffer_count++;
     if (provider->IsAccelerated()) {
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
index 7c4b5cdf..6c5420c 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h
@@ -1996,6 +1996,8 @@
                                            GLenum precision_type,
                                            WebGLShaderPrecisionFormat* format);
 
+  void Dispose() override;
+
   // PushFrameWithCopy will make a potential copy if the resource is accelerated
   // or a drawImage if the resource is non accelerated.
   bool PushFrameWithCopy();
@@ -2003,6 +2005,7 @@
   // ExtenralCanvasResource.
   bool PushFrameNoCopy();
 
+  std::unique_ptr<CanvasResourceProvider> resource_provider_;
   static bool webgl_context_limits_initialized_;
   static unsigned max_active_webgl_contexts_;
   static unsigned max_active_webgl_contexts_on_worker_;
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.cc
index e7903b7..dd7814a 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_webgpu_base.cc
@@ -3494,25 +3494,9 @@
 int WebGLRenderingContextWebGPUBase::AllocatedBufferCountPerPixel() {
   // Front and back buffers.
   // TODO(413078308): Add support configuring MSAA and depth-stencil.
-  int buffer_count = 2;
-
-  if (!Host()) {
-    return buffer_count;
-  }
-
-  auto* provider = Host()->GetResourceProviderForWebGL();
-  if (provider) {
-    buffer_count++;
-    if (provider->IsAccelerated()) {
-      // The number of internal GPU buffers vary between one (stable
-      // non-displayed state) and three (triple-buffered animations).
-      // Adding 2 is a pessimistic but relevant estimate.
-      // Note: These buffers might be allocated in GPU memory.
-      buffer_count += 2;
-    }
-  }
-
-  return buffer_count;
+  // Note: If/once this class creates a CanvasResourceProvider it should track
+  // the memory of the provider here as well.
+  return 2;
 }
 
 bool WebGLRenderingContextWebGPUBase::isContextLost() const {
diff --git a/third_party/blink/renderer/platform/geometry/float_polygon.h b/third_party/blink/renderer/platform/geometry/float_polygon.h
index c64c4b7..0a7862a 100644
--- a/third_party/blink/renderer/platform/geometry/float_polygon.h
+++ b/third_party/blink/renderer/platform/geometry/float_polygon.h
@@ -65,7 +65,7 @@
 
  private:
   typedef WTF::PODInterval<float, FloatPolygonEdge*> EdgeInterval;
-  typedef WTF::PODIntervalTree<float, FloatPolygonEdge*> EdgeIntervalTree;
+  using EdgeIntervalTree = PodIntervalTree<float, FloatPolygonEdge*>;
 
   Vector<gfx::PointF> vertices_;
   gfx::RectF bounding_box_;
@@ -136,21 +136,18 @@
   raw_ptr<const FloatPolygon> polygon_;
 };
 
-}  // namespace blink
-
-namespace WTF {
-// These structures are used by PODIntervalTree for debugging.
+// These structures are used by PodIntervalTree for debugging.
 #ifndef NDEBUG
 template <>
-struct ValueToString<blink::FloatPolygonEdge*> {
+struct ValueToString<FloatPolygonEdge*> {
   STATIC_ONLY(ValueToString);
-  static String ToString(const blink::FloatPolygonEdge* edge) {
+  static String ToString(const FloatPolygonEdge* edge) {
     return String::Format("%p (%f,%f %f,%f)", edge, edge->Vertex1().x(),
                           edge->Vertex1().y(), edge->Vertex2().x(),
                           edge->Vertex2().y());
   }
 };
 #endif
-}  // namespace WTF
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_FLOAT_POLYGON_H_
diff --git a/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc b/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc
index 9aabd0a..e407596 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_hibernation_handler_test.cc
@@ -110,7 +110,7 @@
   return result;
 }
 
-void Draw(CanvasResourceHost& host) {
+void Draw(FakeCanvasResourceHost& host) {
   CanvasResourceProvider* provider =
       host.GetOrCreateCanvasResourceProviderForCanvas2D();
   provider->Canvas().drawLine(0, 0, 2, 2, cc::PaintFlags());
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
index af62713..f6d4c65 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h
@@ -46,11 +46,6 @@
   virtual bool IsContextLost() const = 0;
   virtual void SetNeedsCompositingUpdate() = 0;
   virtual void InitializeForRecording(cc::PaintCanvas* canvas) const = 0;
-  virtual void UpdateMemoryUsage() = 0;
-  virtual size_t GetMemoryUsage() const = 0;
-  virtual void PageVisibilityChanged() {}
-  virtual CanvasResourceProvider*
-  GetOrCreateCanvasResourceProviderForCanvas2D() = 0;
 
   // Initialize the indicated cc::Layer with the HTMLCanvasElement's CSS
   // properties. This is a no-op if `this` is not an HTMLCanvasElement.
diff --git a/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc b/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc
index 09ecafa8..2811786 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context_test.cc
@@ -15,8 +15,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
-#include "third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
 #include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h"
diff --git a/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h b/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h
index af91439..d804f9e 100644
--- a/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h
+++ b/third_party/blink/renderer/platform/graphics/test/fake_canvas_resource_host.h
@@ -27,7 +27,6 @@
   bool IsContextLost() const override { return false; }
   void SetNeedsCompositingUpdate() override {}
   void InitializeForRecording(cc::PaintCanvas*) const override {}
-  void UpdateMemoryUsage() override {}
   bool PrintedInCurrentTask() const override { return false; }
   bool IsPageVisible() const override { return page_visible_; }
   bool IsHibernating() const override { return is_hibernating_; }
@@ -45,44 +44,26 @@
     return old_provider;
   }
 
-  size_t GetMemoryUsage() const override { return 0; }
-  CanvasResourceProvider* GetOrCreateCanvasResourceProviderForCanvas2D()
-      override {
+  CanvasResourceProvider* GetOrCreateCanvasResourceProviderForCanvas2D() {
     if (GetResourceProviderForCanvas2D()) {
       return GetResourceProviderForCanvas2D();
     }
     constexpr auto kShouldInitialize =
         CanvasResourceProvider::ShouldInitialize::kCallClear;
-    std::unique_ptr<CanvasResourceProvider> provider;
     constexpr gpu::SharedImageUsageSet kSharedImageUsageFlags =
         gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | gpu::SHARED_IMAGE_USAGE_SCANOUT;
-    provider = CanvasResourceProvider::CreateSharedImageProvider(
+    resource_provider_ = CanvasResourceProvider::CreateSharedImageProvider(
         Size(), GetN32FormatForCanvas(), kPremul_SkAlphaType,
         gfx::ColorSpace::CreateSRGB(), kShouldInitialize,
         SharedGpuContext::ContextProviderWrapper(), RasterMode::kGPU,
         kSharedImageUsageFlags, this);
-    if (!provider) {
-      provider = CanvasResourceProvider::
-          CreateSharedImageProviderForSoftwareCompositor(
-              Size(), GetN32FormatForCanvas(), kPremul_SkAlphaType,
-              gfx::ColorSpace::CreateSRGB(), kShouldInitialize,
-              SharedGpuContext::SharedImageInterfaceProvider(), this);
-    }
-    if (!provider) {
-      provider = CanvasResourceProvider::CreateBitmapProvider(
-          Size(), GetN32FormatForCanvas(), kPremul_SkAlphaType,
-          gfx::ColorSpace::CreateSRGB(), kShouldInitialize, this);
-    }
 
-    resource_provider_ = std::move(provider);
-
-    return GetResourceProviderForCanvas2D();
+    return resource_provider_.get();
   }
 
   void SetPageVisible(bool visible) {
     if (page_visible_ != visible) {
       page_visible_ = visible;
-      PageVisibilityChanged();
     }
   }
 
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
index 3070f4fe..c72713e4 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder.cc
@@ -1225,11 +1225,10 @@
 // is not a black frame.
 #if BUILDFLAG(IS_WIN)
   {
-    // Check if the incoming frame is backed by owned or unowned memory type.
-    // This could happen when: 1. Zero-copy capture feature is turned on but
-    // device does not support MediaFoundation; 2. Zero-copy is enabled and
-    // video frame is backed up by an ArrayBuffer; 3. The video track gets
-    // disabled so black frames are sent.
+    // Check if the incoming frame is backed by unowned memory. This could
+    // happen when: 1. Zero-copy capture feature is turned on but device does
+    // not support MediaFoundation; 2. The video track gets disabled so black
+    // frames are sent.
     scoped_refptr<media::VideoFrame> frame;
     webrtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer =
         frame_chunk.video_frame_buffer;
@@ -1239,8 +1238,7 @@
     if (frame_buffer->type() == webrtc::VideoFrameBuffer::Type::kNative) {
       frame = static_cast<WebRtcVideoFrameAdapterInterface*>(frame_buffer.get())
                   ->getMediaVideoFrame();
-      if (frame->storage_type() == media::VideoFrame::STORAGE_UNOWNED_MEMORY ||
-          frame->storage_type() == media::VideoFrame::STORAGE_OWNED_MEMORY) {
+      if (frame->storage_type() == media::VideoFrame::STORAGE_UNOWNED_MEMORY) {
         if (use_native_input_) {
           use_native_input_ = false;
         }
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
index a8a1355..ccae910 100644
--- a/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
+++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_encoder_test.cc
@@ -128,9 +128,7 @@
  public:
   FakeNativeBufferI420(int width, int height, bool allow_to_i420)
       : blink::WebRtcVideoFrameAdapter(
-            media::VideoFrame::CreateBlackFrame(gfx::Size(480, 360)),
-            base::MakeRefCounted<WebRtcVideoFrameAdapter::SharedResources>(
-                nullptr)),
+            media::VideoFrame::CreateBlackFrame(gfx::Size(480, 360))),
         width_(width),
         height_(height),
         allow_to_i420_(allow_to_i420),
@@ -1320,12 +1318,8 @@
   ASSERT_EQ(WEBRTC_VIDEO_CODEC_OK,
             rtc_encoder_->InitEncode(&codec, kVideoEncoderSettings));
 
-#if !BUILDFLAG(IS_WIN)
   auto frame = media::VideoFrame::CreateBlackFrame(
       gfx::Size(kInputFrameWidth, kInputFrameHeight));
-#else
-  auto frame = media::VideoFrame::CreateEOSFrame();
-#endif
   frame->set_timestamp(base::Milliseconds(1));
   webrtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_adapter(
       new webrtc::RefCountedObject<WebRtcVideoFrameAdapter>(
diff --git a/third_party/blink/renderer/platform/wtf/pod_interval.h b/third_party/blink/renderer/platform/wtf/pod_interval.h
index 0ba0816..13f9869 100644
--- a/third_party/blink/renderer/platform/wtf/pod_interval.h
+++ b/third_party/blink/renderer/platform/wtf/pod_interval.h
@@ -33,6 +33,13 @@
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/gc_plugin.h"
 
+#ifndef NDEBUG
+namespace blink {
+template <class T>
+struct ValueToString;
+}
+#endif
+
 namespace WTF {
 
 // Class representing a closed interval which can hold an arbitrary
@@ -64,20 +71,14 @@
 // available:
 //
 //   template<> struct ValueToString<T> {
-//       static String toString(const T& t);
+//       static String ToString(const T& t);
 //   };
 //   template<> struct ValueToString<UserData> {
-//       static String toString(const UserData& t);
+//       static String ToString(const UserData& t);
 //   };
 //
 // Note that this class requires a copy constructor and assignment
 // operator in order to be stored in the red-black tree.
-
-#ifndef NDEBUG
-template <class T>
-struct ValueToString;
-#endif
-
 template <class T, class UserData = void*>
 class PODInterval {
   DISALLOW_NEW();
@@ -131,15 +132,15 @@
   String ToString() const {
     StringBuilder builder;
     builder.Append("[PODInterval (");
-    builder.Append(ValueToString<T>::ToString(Low()));
+    builder.Append(blink::ValueToString<T>::ToString(Low()));
     builder.Append(", ");
-    builder.Append(ValueToString<T>::ToString(High()));
+    builder.Append(blink::ValueToString<T>::ToString(High()));
     builder.Append("), data=");
-    builder.Append(ValueToString<UserData>::ToString(Data()));
+    builder.Append(blink::ValueToString<UserData>::ToString(Data()));
     builder.Append(", minLow=");
-    builder.Append(ValueToString<T>::ToString(MinLow()));
+    builder.Append(blink::ValueToString<T>::ToString(MinLow()));
     builder.Append(", maxHigh=");
-    builder.Append(ValueToString<T>::ToString(MaxHigh()));
+    builder.Append(blink::ValueToString<T>::ToString(MaxHigh()));
     builder.Append(']');
     return builder.ToString();
   }
@@ -153,7 +154,10 @@
   T max_high_;
 };
 
+}  // namespace WTF
+
 #ifndef NDEBUG
+namespace blink {
 template <>
 struct ValueToString<float> {
   STATIC_ONLY(ValueToString);
@@ -167,8 +171,7 @@
 struct ValueToString<int> {
   static String ToString(const int& value) { return String::Number(value); }
 };
+}  // namespace blink
 #endif
 
-}  // namespace WTF
-
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_POD_INTERVAL_H_
diff --git a/third_party/blink/renderer/platform/wtf/pod_interval_tree.h b/third_party/blink/renderer/platform/wtf/pod_interval_tree.h
index 2d58b5d..ce2da06 100644
--- a/third_party/blink/renderer/platform/wtf/pod_interval_tree.h
+++ b/third_party/blink/renderer/platform/wtf/pod_interval_tree.h
@@ -33,21 +33,16 @@
 #include "third_party/blink/renderer/platform/wtf/pod_red_black_tree.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
-namespace WTF {
-
-#ifndef NDEBUG
-template <class T>
-struct ValueToString;
-#endif
+namespace blink {
 
 template <class T, class UserData = void*>
-class PODIntervalSearchAdapter {
+class PodIntervalSearchAdapter {
   DISALLOW_NEW();
 
  public:
-  typedef PODInterval<T, UserData> IntervalType;
+  using IntervalType = WTF::PODInterval<T, UserData>;
 
-  PODIntervalSearchAdapter(Vector<IntervalType>& result,
+  PodIntervalSearchAdapter(Vector<IntervalType>& result,
                            const T& low_value,
                            const T& high_value)
       : result_(result), low_value_(low_value), high_value_(high_value) {}
@@ -69,27 +64,28 @@
 // supports efficient (O(lg n)) insertion, removal and querying of
 // intervals in the tree.
 template <class T, class UserData = void*>
-class PODIntervalTree final : public PODRedBlackTree<PODInterval<T, UserData>> {
+class PodIntervalTree final
+    : public WTF::PODRedBlackTree<WTF::PODInterval<T, UserData>> {
  public:
   // Typedef to reduce typing when declaring intervals to be stored in
   // this tree.
-  typedef PODInterval<T, UserData> IntervalType;
-  typedef PODIntervalSearchAdapter<T, UserData> IntervalSearchAdapterType;
+  using IntervalType = WTF::PODInterval<T, UserData>;
+  using IntervalSearchAdapterType = PodIntervalSearchAdapter<T, UserData>;
 
-  PODIntervalTree(UninitializedTreeEnum unitialized_tree)
-      : PODRedBlackTree<IntervalType>(unitialized_tree) {
+  explicit PodIntervalTree(WTF::UninitializedTreeEnum unitialized_tree)
+      : WTF::PODRedBlackTree<IntervalType>(unitialized_tree) {
     Init();
   }
 
-  PODIntervalTree() : PODRedBlackTree<IntervalType>() { Init(); }
+  PodIntervalTree() : WTF::PODRedBlackTree<IntervalType>() { Init(); }
 
-  explicit PODIntervalTree(scoped_refptr<PODArena> arena)
-      : PODRedBlackTree<IntervalType>(arena) {
+  explicit PodIntervalTree(scoped_refptr<WTF::PODArena> arena)
+      : WTF::PODRedBlackTree<IntervalType>(arena) {
     Init();
   }
 
-  PODIntervalTree(const PODIntervalTree&) = delete;
-  PODIntervalTree& operator=(const PODIntervalTree&) = delete;
+  PodIntervalTree(const PodIntervalTree&) = delete;
+  PodIntervalTree& operator=(const PodIntervalTree&) = delete;
 
   // Returns all intervals in the tree which overlap the given query
   // interval. The returned intervals are sorted by increasing low
@@ -126,8 +122,9 @@
   }
 
   bool CheckInvariants() const override {
-    if (!PODRedBlackTree<IntervalType>::CheckInvariants())
+    if (!WTF::PODRedBlackTree<IntervalType>::CheckInvariants()) {
       return false;
+    }
     if (!this->Root())
       return true;
     return CheckInvariantsFromNode(this->Root());
@@ -140,7 +137,7 @@
   }
 
  private:
-  typedef typename PODRedBlackTree<IntervalType>::Node IntervalNode;
+  using IntervalNode = typename WTF::PODRedBlackTree<IntervalType>::Node;
 
   // Initializes the tree.
   void Init() {
@@ -323,7 +320,7 @@
 
 #ifndef NDEBUG
   static void LogVerificationFailedAtNode(IntervalNode const* node) {
-    DLOG(ERROR) << "PODIntervalTree verification failed at node " << node
+    DLOG(ERROR) << "PodIntervalTree verification failed at node " << node
                 << ": data=" << node->Data().ToString();
   }
 #else
@@ -334,13 +331,13 @@
 #ifndef NDEBUG
 // Support for printing PODIntervals at the PODRedBlackTree level.
 template <class T, class UserData>
-struct ValueToString<PODInterval<T, UserData>> {
-  static String ToString(const PODInterval<T, UserData>& interval) {
+struct ValueToString<WTF::PODInterval<T, UserData>> {
+  static String ToString(const WTF::PODInterval<T, UserData>& interval) {
     return interval.ToString();
   }
 };
 #endif
 
-}  // namespace WTF
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_POD_INTERVAL_TREE_H_
diff --git a/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc b/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc
index 8a00fa0..12b2c1bd 100644
--- a/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc
+++ b/third_party/blink/renderer/platform/wtf/pod_interval_tree_test.cc
@@ -34,10 +34,11 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
-namespace WTF {
+namespace blink {
 
-using tree_test_helpers::InitRandom;
-using tree_test_helpers::NextRandom;
+using WTF::PODInterval;
+using WTF::tree_test_helpers::InitRandom;
+using WTF::tree_test_helpers::NextRandom;
 
 #ifndef NDEBUG
 template <>
@@ -48,14 +49,14 @@
 };
 #endif
 
-TEST(PODIntervalTreeTest, TestInsertion) {
-  PODIntervalTree<float> tree;
+TEST(PodIntevalTreeTest, TestInsertion) {
+  PodIntervalTree<float> tree;
   tree.Add(PODInterval<float>(2, 4));
   ASSERT_TRUE(tree.CheckInvariants());
 }
 
-TEST(PODIntervalTreeTest, TestInsertionAndQuery) {
-  PODIntervalTree<float> tree;
+TEST(PodIntevalTreeTest, TestInsertionAndQuery) {
+  PodIntervalTree<float> tree;
   tree.Add(PODInterval<float>(2, 4));
   ASSERT_TRUE(tree.CheckInvariants());
   Vector<PODInterval<float>> overlap =
@@ -80,8 +81,8 @@
   EXPECT_FALSE(next_point.has_value());
 }
 
-TEST(PODIntervalTreeTest, TestQueryAgainstZeroSizeInterval) {
-  PODIntervalTree<float> tree;
+TEST(PodIntevalTreeTest, TestQueryAgainstZeroSizeInterval) {
+  PodIntervalTree<float> tree;
   tree.Add(PODInterval<float>(1, 2.5));
   tree.Add(PODInterval<float>(3.5, 5));
   tree.Add(PODInterval<float>(2, 4));
@@ -102,11 +103,11 @@
 };
 #endif
 
-TEST(PODIntervalTreeTest, TestDuplicateElementInsertion) {
-  PODIntervalTree<float, int*> tree;
+TEST(PodIntevalTreeTest, TestDuplicateElementInsertion) {
+  PodIntervalTree<float, int*> tree;
   int tmp1 = 1;
   int tmp2 = 2;
-  typedef PODIntervalTree<float, int*>::IntervalType IntervalType;
+  using IntervalType = PodIntervalTree<float, int*>::IntervalType;
   IntervalType interval1(1, 3, &tmp1);
   IntervalType interval2(1, 3, &tmp2);
   tree.Add(interval1);
@@ -143,8 +144,8 @@
 };
 #endif
 
-TEST(PODIntervalTreeTest, TestInsertionOfComplexUserData) {
-  PODIntervalTree<float, UserData1> tree;
+TEST(PodIntevalTreeTest, TestInsertionOfComplexUserData) {
+  PodIntervalTree<float, UserData1> tree;
   UserData1 data1;
   data1.a = 5;
   data1.b = 6;
@@ -152,8 +153,8 @@
   ASSERT_TRUE(tree.CheckInvariants());
 }
 
-TEST(PODIntervalTreeTest, TestQueryingOfComplexUserData) {
-  PODIntervalTree<float, UserData1> tree;
+TEST(PodIntevalTreeTest, TestQueryingOfComplexUserData) {
+  PodIntervalTree<float, UserData1> tree;
   UserData1 data1;
   data1.a = 5;
   data1.b = 6;
@@ -204,8 +205,8 @@
 };
 #endif
 
-TEST(PODIntervalTreeTest, TestTreeDoesNotRequireMostOperators) {
-  PODIntervalTree<EndpointType1> tree;
+TEST(PodIntevalTreeTest, TestTreeDoesNotRequireMostOperators) {
+  PodIntervalTree<EndpointType1> tree;
   tree.Add(tree.CreateInterval(EndpointType1(1), EndpointType1(2)));
   ASSERT_TRUE(tree.CheckInvariants());
 }
@@ -220,7 +221,7 @@
   InitRandom(seed);
   int maximum_value = tree_size;
   // Build the tree
-  PODIntervalTree<int> tree;
+  PodIntervalTree<int> tree;
   Vector<PODInterval<int>> added_elements;
   Vector<PODInterval<int>> removed_elements;
   for (int i = 0; i < tree_size; i++) {
@@ -289,18 +290,18 @@
 
 }  // anonymous namespace
 
-TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest1) {
+TEST(PodIntevalTreeTest, RandomDeletionAndInsertionRegressionTest1) {
   TreeInsertionAndDeletionTest(13972, 100);
 }
 
-TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest2) {
+TEST(PodIntevalTreeTest, RandomDeletionAndInsertionRegressionTest2) {
   TreeInsertionAndDeletionTest(1283382113, 10);
 }
 
-TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest3) {
+TEST(PodIntevalTreeTest, RandomDeletionAndInsertionRegressionTest3) {
   // This is the sequence of insertions and deletions that triggered
   // the failure in RandomDeletionAndInsertionRegressionTest2.
-  PODIntervalTree<int> tree;
+  PodIntervalTree<int> tree;
   tree.Add(tree.CreateInterval(0, 5));
   ASSERT_TRUE(tree.CheckInvariants());
   tree.Add(tree.CreateInterval(4, 5));
@@ -335,10 +336,10 @@
   ASSERT_TRUE(tree.CheckInvariants());
 }
 
-TEST(PODIntervalTreeTest, RandomDeletionAndInsertionRegressionTest4) {
+TEST(PodIntevalTreeTest, RandomDeletionAndInsertionRegressionTest4) {
   // Even further reduced test case for
   // RandomDeletionAndInsertionRegressionTest3.
-  PODIntervalTree<int> tree;
+  PodIntervalTree<int> tree;
   tree.Add(tree.CreateInterval(0, 5));
   ASSERT_TRUE(tree.CheckInvariants());
   tree.Add(tree.CreateInterval(8, 9));
@@ -353,4 +354,4 @@
   ASSERT_TRUE(tree.CheckInvariants());
 }
 
-}  // namespace WTF
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h b/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h
index f1d753b..2c65a71 100644
--- a/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h
+++ b/third_party/blink/renderer/platform/wtf/pod_red_black_tree.h
@@ -80,13 +80,15 @@
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 #endif
 
-namespace WTF {
-
 #ifndef NDEBUG
+namespace blink {
 template <class T>
 struct ValueToString;
+}
 #endif
 
+namespace WTF {
+
 enum UninitializedTreeEnum { kUninitializedTree };
 
 template <class T>
@@ -776,7 +778,7 @@
     builder.Append('-');
     if (node) {
       builder.Append(' ');
-      builder.Append(ValueToString<T>::GetString(node->Data()));
+      builder.Append(blink::ValueToString<T>::GetString(node->Data()));
       builder.Append((node->GetColor() == kBlack) ? " (black)" : " (red)");
     }
     DLOG(ERROR) << builder.ToString();
diff --git a/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h b/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
index fd36446..127617b 100644
--- a/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
+++ b/third_party/blink/renderer/platform/wtf/text/ascii_fast_path.h
@@ -32,17 +32,16 @@
 #include "base/dcheck_is_on.h"
 #include "base/types/zip.h"
 #include "build/build_config.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_visitor.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_uchar.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
 
-namespace WTF {
+namespace blink {
 
 // Assuming that a pointer is the size of a "machine word", then
 // uintptr_t is an integer type that is also a machine word.
-typedef uintptr_t MachineWord;
+using MachineWord = uintptr_t;
 const uintptr_t kMachineWordAlignmentMask = sizeof(MachineWord) - 1;
 
 inline bool IsAlignedToMachineWord(const void* pointer) {
@@ -56,31 +55,31 @@
 }
 
 template <size_t size, typename CharacterType>
-struct NonASCIIMask;
+struct NonAsciiMask;
 template <>
-struct NonASCIIMask<4, UChar> {
+struct NonAsciiMask<4, UChar> {
   static inline uint32_t Value() { return 0xFF80FF80U; }
 };
 template <>
-struct NonASCIIMask<4, LChar> {
+struct NonAsciiMask<4, LChar> {
   static inline uint32_t Value() { return 0x80808080U; }
 };
 template <>
-struct NonASCIIMask<8, UChar> {
+struct NonAsciiMask<8, UChar> {
   static inline uint64_t Value() { return 0xFF80FF80FF80FF80ULL; }
 };
 template <>
-struct NonASCIIMask<8, LChar> {
+struct NonAsciiMask<8, LChar> {
   static inline uint64_t Value() { return 0x8080808080808080ULL; }
 };
 
 template <typename CharacterType>
-inline bool IsAllASCII(MachineWord word) {
-  return !(word & NonASCIIMask<sizeof(MachineWord), CharacterType>::Value());
+inline bool IsAllAscii(MachineWord word) {
+  return !(word & NonAsciiMask<sizeof(MachineWord), CharacterType>::Value());
 }
 
-struct ASCIIStringAttributes {
-  ASCIIStringAttributes(bool contains_only_ascii, bool is_lower_ascii)
+struct AsciiStringAttributes {
+  AsciiStringAttributes(bool contains_only_ascii, bool is_lower_ascii)
       : contains_only_ascii(contains_only_ascii),
         is_lower_ascii(is_lower_ascii) {}
   unsigned contains_only_ascii : 1;
@@ -90,7 +89,7 @@
 // Note: This function assumes the input is likely all ASCII, and
 // does not leave early if it is not the case.
 template <typename CharacterType>
-ALWAYS_INLINE ASCIIStringAttributes
+ALWAYS_INLINE AsciiStringAttributes
 CharacterAttributes(base::span<const CharacterType> chars) {
   DCHECK_GT(chars.size(), 0u);
 
@@ -103,11 +102,11 @@
     contains_upper_case |= IsASCIIUpper(ch);
   }
 
-  return ASCIIStringAttributes(IsASCII(all_char_bits), !contains_upper_case);
+  return AsciiStringAttributes(IsASCII(all_char_bits), !contains_upper_case);
 }
 
 template <typename CharacterType>
-ALWAYS_INLINE bool IsLowerASCII(base::span<const CharacterType> chars) {
+ALWAYS_INLINE bool IsLowerAscii(base::span<const CharacterType> chars) {
   bool contains_upper_case = false;
   for (CharacterType ch : chars) {
     contains_upper_case |= IsASCIIUpper(ch);
@@ -116,7 +115,7 @@
 }
 
 template <typename CharacterType>
-ALWAYS_INLINE bool IsUpperASCII(base::span<const CharacterType> chars) {
+ALWAYS_INLINE bool IsUpperAscii(base::span<const CharacterType> chars) {
   bool contains_lower_case = false;
   for (CharacterType ch : chars) {
     contains_lower_case |= IsASCIILower(ch);
@@ -128,7 +127,7 @@
  public:
   template <typename CharType>
   ALWAYS_INLINE static bool IsCorrectCase(base::span<const CharType> chars) {
-    return IsLowerASCII(chars);
+    return IsLowerAscii(chars);
   }
 
   template <typename CharType>
@@ -141,7 +140,7 @@
  public:
   template <typename CharType>
   ALWAYS_INLINE static bool IsCorrectCase(base::span<const CharType> chars) {
-    return IsUpperASCII(chars);
+    return IsUpperAscii(chars);
   }
 
   template <typename CharType>
@@ -151,7 +150,7 @@
 };
 
 template <typename StringType, typename Converter, typename Allocator>
-ALWAYS_INLINE typename Allocator::ResultStringType ConvertASCIICase(
+ALWAYS_INLINE typename Allocator::ResultStringType ConvertAsciiCase(
     const StringType& string,
     Converter&& converter,
     Allocator&& allocator) {
@@ -172,14 +171,6 @@
   });
 }
 
-}  // namespace WTF
-
-// TODO(crbug.com/422768753): Remove these `using` directives.
-namespace blink {
-using WTF::AlignToMachineWord;
-using WTF::IsAlignedToMachineWord;
-using WTF::IsAllASCII;
-using WTF::MachineWord;
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_ASCII_FAST_PATH_H_
diff --git a/third_party/blink/renderer/platform/wtf/text/case_map.cc b/third_party/blink/renderer/platform/wtf/text/case_map.cc
index 87e3187..e0930bb 100644
--- a/third_party/blink/renderer/platform/wtf/text/case_map.cc
+++ b/third_party/blink/renderer/platform/wtf/text/case_map.cc
@@ -16,7 +16,7 @@
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
-namespace WTF {
+namespace blink {
 
 namespace {
 
@@ -434,4 +434,4 @@
   return String();
 }
 
-}  // namespace WTF
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/case_map.h b/third_party/blink/renderer/platform/wtf/text/case_map.h
index 8c8df61f..85770f1 100644
--- a/third_party/blink/renderer/platform/wtf/text/case_map.h
+++ b/third_party/blink/renderer/platform/wtf/text/case_map.h
@@ -9,9 +9,8 @@
 #include "third_party/blink/renderer/platform/wtf/text/unicode.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_export.h"
 
-namespace WTF {
+namespace blink {
 
-class String;
 class TextOffsetMap;
 
 // This class performs the full Unicode case-mapping.
@@ -78,8 +77,6 @@
   const char* case_map_locale_;
 };
 
-}  // namespace WTF
-
-using WTF::CaseMap;
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_CASE_MAP_H_
diff --git a/third_party/blink/renderer/platform/wtf/text/case_map_test.cc b/third_party/blink/renderer/platform/wtf/text/case_map_test.cc
index eed4a6e..e814005 100644
--- a/third_party/blink/renderer/platform/wtf/text/case_map_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/case_map_test.cc
@@ -12,7 +12,7 @@
 
 using testing::ElementsAreArray;
 
-namespace WTF {
+namespace blink {
 
 namespace {
 
@@ -466,4 +466,4 @@
   EXPECT_EQ(result6, String::FromUTF8("ja"));
 }
 
-}  // namespace WTF
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.cc b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
index 1125b2ba..49276b1 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
@@ -168,9 +168,9 @@
 }
 
 unsigned StringImpl::ComputeASCIIFlags() const {
-  ASCIIStringAttributes ascii_attributes = VisitCharacters(
-      *this, [](auto chars) { return CharacterAttributes(chars); });
-  uint32_t new_flags = ASCIIStringAttributesToFlags(ascii_attributes);
+  blink::AsciiStringAttributes ascii_attributes = VisitCharacters(
+      *this, [](auto chars) { return blink::CharacterAttributes(chars); });
+  uint32_t new_flags = AsciiStringAttributesToFlags(ascii_attributes);
   const uint32_t previous_flags =
       hash_and_flags_.fetch_or(new_flags, std::memory_order_relaxed);
   static constexpr uint32_t mask =
@@ -340,12 +340,12 @@
 
 scoped_refptr<StringImpl> StringImpl::Create(
     base::span<const LChar> characters,
-    ASCIIStringAttributes ascii_attributes) {
+    blink::AsciiStringAttributes ascii_attributes) {
   scoped_refptr<StringImpl> ret = Create(characters);
   if (!characters.empty()) {
     // If length is 0 then `ret` is empty_ and should not have its
     // attributes calculated or changed.
-    uint32_t new_flags = ASCIIStringAttributesToFlags(ascii_attributes);
+    uint32_t new_flags = AsciiStringAttributesToFlags(ascii_attributes);
     ret->hash_and_flags_.fetch_or(new_flags, std::memory_order_relaxed);
   }
 
@@ -447,11 +447,13 @@
 };
 
 scoped_refptr<StringImpl> StringImpl::LowerASCII() {
-  return ConvertASCIICase(*this, LowerConverter(), StringImplAllocator());
+  return blink::ConvertAsciiCase(*this, blink::LowerConverter(),
+                                 StringImplAllocator());
 }
 
 scoped_refptr<StringImpl> StringImpl::UpperASCII() {
-  return ConvertASCIICase(*this, UpperConverter(), StringImplAllocator());
+  return blink::ConvertAsciiCase(*this, blink::UpperConverter(),
+                                 StringImplAllocator());
 }
 
 scoped_refptr<StringImpl> StringImpl::Fill(UChar character) {
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.h b/third_party/blink/renderer/platform/wtf/text/string_impl.h
index 231dc71a..b94aa6a 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.h
@@ -160,7 +160,7 @@
   static scoped_refptr<StringImpl> Create(base::span<const LChar>);
   static scoped_refptr<StringImpl> Create(
       base::span<const LChar>,
-      ASCIIStringAttributes ascii_attributes);
+      blink::AsciiStringAttributes ascii_attributes);
   static scoped_refptr<StringImpl> Create8BitIfPossible(
       base::span<const UChar>);
 
@@ -545,8 +545,8 @@
                : kAsciiPropertyCheckDone | kContainsOnlyAscii | kIsLowerAscii;
   }
 
-  static inline uint32_t ASCIIStringAttributesToFlags(
-      ASCIIStringAttributes ascii_attributes) {
+  static inline uint32_t AsciiStringAttributesToFlags(
+      blink::AsciiStringAttributes ascii_attributes) {
     uint32_t flags = kAsciiPropertyCheckDone;
     if (ascii_attributes.contains_only_ascii)
       flags |= kContainsOnlyAscii;
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc b/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc
index ac36b5cc..96b788f 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl_test.cc
@@ -64,7 +64,7 @@
       test_string_impl.get(),
       StringImpl::Create(base::span_from_cstring("lInk"))->LowerASCII().get()));
 
-  CaseMap case_map(g_empty_atom);
+  blink::CaseMap case_map(g_empty_atom);
   EXPECT_TRUE(Equal(
       case_map.ToLower(StringImpl::Create(base::span_from_cstring("LINK")))
           .Impl(),
@@ -137,7 +137,7 @@
       test_string_impl.get(),
       StringImpl::Create(base::span_from_cstring("lInk"))->UpperASCII().get()));
 
-  CaseMap case_map(g_empty_atom);
+  blink::CaseMap case_map(g_empty_atom);
   EXPECT_TRUE(Equal(
       case_map.ToUpper(StringImpl::Create(base::span_from_cstring("LINK")))
           .Impl(),
diff --git a/third_party/blink/renderer/platform/wtf/text/string_view.cc b/third_party/blink/renderer/platform/wtf/text/string_view.cc
index 47a54b2..01dd5c25 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_view.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_view.cc
@@ -165,7 +165,7 @@
     return impl->IsLowerASCII();
   }
   return VisitCharacters(*this,
-                         [](auto chars) { return WTF::IsLowerASCII(chars); });
+                         [](auto chars) { return blink::IsLowerAscii(chars); });
 }
 
 bool StringView::ContainsOnlyASCIIOrEmpty() const {
@@ -173,8 +173,8 @@
     return impl->ContainsOnlyASCIIOrEmpty();
   if (empty())
     return true;
-  ASCIIStringAttributes attrs = VisitCharacters(
-      *this, [](auto chars) { return CharacterAttributes(chars); });
+  blink::AsciiStringAttributes attrs = VisitCharacters(
+      *this, [](auto chars) { return blink::CharacterAttributes(chars); });
   return attrs.contains_only_ascii;
 }
 
@@ -298,8 +298,8 @@
 
 StringView StringView::LowerASCIIMaybeUsingBuffer(
     StackBackingStore& buffer) const {
-  return ConvertASCIICase(*this, LowerConverter(),
-                          StackStringViewAllocator(buffer));
+  return blink::ConvertAsciiCase(*this, blink::LowerConverter(),
+                                 StackStringViewAllocator(buffer));
 }
 
 UChar32 StringView::CodepointAt(unsigned i) const {
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h b/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h
index a10ae79..38d07fc 100644
--- a/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h
+++ b/third_party/blink/renderer/platform/wtf/text/text_codec_ascii_fast_path.h
@@ -84,11 +84,11 @@
 };
 
 inline void CopyAsciiMachineWord(LChar* destination, const uint8_t* source) {
-  UCharByteFiller<sizeof(WTF::MachineWord)>::Copy(destination, source);
+  UCharByteFiller<sizeof(MachineWord)>::Copy(destination, source);
 }
 
 inline void CopyAsciiMachineWord(UChar* destination, const uint8_t* source) {
-  UCharByteFiller<sizeof(WTF::MachineWord)>::Copy(destination, source);
+  UCharByteFiller<sizeof(MachineWord)>::Copy(destination, source);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc b/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc
index cabf8d1..82569e6 100644
--- a/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc
+++ b/third_party/blink/renderer/platform/wtf/text/text_codec_latin1.cc
@@ -132,8 +132,9 @@
         while (source < aligned_end) {
           MachineWord chunk = *reinterpret_cast_ptr<const MachineWord*>(source);
 
-          if (!IsAllASCII<LChar>(chunk))
+          if (!IsAllAscii<LChar>(chunk)) {
             goto useLookupTable;
+          }
 
           CopyAsciiMachineWord(destination, source);
           source += sizeof(MachineWord);
@@ -184,8 +185,9 @@
         while (source < aligned_end) {
           MachineWord chunk = *reinterpret_cast_ptr<const MachineWord*>(source);
 
-          if (!IsAllASCII<LChar>(chunk))
+          if (!IsAllAscii<LChar>(chunk)) {
             goto useLookupTable16;
+          }
 
           CopyAsciiMachineWord(destination16, source);
           source += sizeof(MachineWord);
diff --git a/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc b/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc
index 29aadbd..3f8d0222 100644
--- a/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc
+++ b/third_party/blink/renderer/platform/wtf/text/text_codec_utf8.cc
@@ -399,8 +399,9 @@
           while (source.data() < aligned_end) {
             MachineWord chunk =
                 *reinterpret_cast_ptr<const MachineWord*>(source.data());
-            if (!IsAllASCII<LChar>(chunk))
+            if (!IsAllAscii<LChar>(chunk)) {
               break;
+            }
             CopyAsciiMachineWord(
                 destination.take_first<sizeof(MachineWord)>().data(),
                 source.take_first<sizeof(MachineWord)>().data());
@@ -485,8 +486,9 @@
           while (source.data() < aligned_end) {
             MachineWord chunk =
                 *reinterpret_cast_ptr<const MachineWord*>(source.data());
-            if (!IsAllASCII<LChar>(chunk))
+            if (!IsAllAscii<LChar>(chunk)) {
               break;
+            }
 
             CopyAsciiMachineWord(
                 destination16.take_first<sizeof(MachineWord)>().data(),
diff --git a/third_party/blink/renderer/platform/wtf/text/text_offset_map.cc b/third_party/blink/renderer/platform/wtf/text/text_offset_map.cc
index a4c81319..7fd3e208 100644
--- a/third_party/blink/renderer/platform/wtf/text/text_offset_map.cc
+++ b/third_party/blink/renderer/platform/wtf/text/text_offset_map.cc
@@ -6,7 +6,7 @@
 
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
-namespace WTF {
+namespace blink {
 
 std::ostream& operator<<(std::ostream& stream,
                          const TextOffsetMap::Entry& entry) {
@@ -142,4 +142,4 @@
   return map;
 }
 
-}  // namespace WTF
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/text_offset_map.h b/third_party/blink/renderer/platform/wtf/text/text_offset_map.h
index 5b86ba6..205401c5 100644
--- a/third_party/blink/renderer/platform/wtf/text/text_offset_map.h
+++ b/third_party/blink/renderer/platform/wtf/text/text_offset_map.h
@@ -11,7 +11,7 @@
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 #include "third_party/blink/renderer/platform/wtf/wtf_export.h"
 
-namespace WTF {
+namespace blink {
 
 // Represents a mapping of text offset when |CaseMap| changes the length of the
 // input string. Similar to [icu::Edits], but tracks only when the length
@@ -75,8 +75,6 @@
     std::ostream& stream,
     const Vector<TextOffsetMap::Entry>& entries);
 
-}  // namespace WTF
-
-using WTF::TextOffsetMap;
+}  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WTF_TEXT_TEXT_OFFSET_MAP_H_
diff --git a/third_party/blink/renderer/platform/wtf/text/text_offset_map_test.cc b/third_party/blink/renderer/platform/wtf/text/text_offset_map_test.cc
index 6715313..ddcd2558 100644
--- a/third_party/blink/renderer/platform/wtf/text/text_offset_map_test.cc
+++ b/third_party/blink/renderer/platform/wtf/text/text_offset_map_test.cc
@@ -9,7 +9,7 @@
 #include "third_party/blink/renderer/platform/wtf/text/case_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
-namespace WTF {
+namespace blink {
 
 TEST(TextOffsetMapTest, MergeConstructor) {
   using Entry = TextOffsetMap::Entry;
@@ -116,4 +116,4 @@
   EXPECT_EQ(1000u, length_map[0]);
 }
 
-}  // namespace WTF
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/wtf_string.cc b/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
index 8f0135d2..7eb8b912 100644
--- a/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
+++ b/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
@@ -131,7 +131,7 @@
 String String::DeprecatedLower() const {
   if (!impl_)
     return String();
-  return CaseMap::FastToLowerInvariant(impl_.get());
+  return blink::CaseMap::FastToLowerInvariant(impl_.get());
 }
 
 String String::LowerASCII() const {
@@ -468,7 +468,7 @@
   if (!length)
     return g_empty_string;
 
-  ASCIIStringAttributes attributes = CharacterAttributes(bytes);
+  blink::AsciiStringAttributes attributes = blink::CharacterAttributes(bytes);
   if (attributes.contains_only_ascii)
     return StringImpl::Create(bytes, attributes);
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-button-event-target.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-button-event-target.html
new file mode 100644
index 0000000..f9be3f4f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-button-event-target.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: event.target and event.currentTarget for ::scroll-button is scroller</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#active-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+  body {
+    margin: 0;
+  }
+
+  #scroller {
+    width: 600px;
+    height: 300px;
+    overflow: auto;
+    white-space: nowrap;
+  }
+
+  #scroller div {
+    display: inline-block;
+    width: 600px;
+    height: 270px;
+  }
+
+  #scroller::scroll-button(inline-end) {
+    position: absolute;
+    top: 0;
+    left: 0;
+    content: "";
+    width: 100px;
+    height: 20px;
+  }
+</style>
+<div id="scroller">
+  <div></div>
+  <div></div>
+</div>
+<script>
+  var target;
+  var currentTarget;
+
+  scroller.addEventListener('click', (e) => {
+    target = e.target;
+    currentTarget = e.currentTarget;
+  });
+
+  promise_test(async t => {
+    actions_promise = new test_driver.Actions()
+    .pointerMove(15, 15)
+    .pointerDown()
+    .pointerUp()
+    .send();
+    await actions_promise;
+    assert_equals(currentTarget, scroller, "event.currentTarget for ::scroll-button click is scroller");
+    assert_equals(target, scroller, "event.target for ::scroll-button click is scroller");
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-marker-event-target.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-marker-event-target.html
new file mode 100644
index 0000000..ed862d0c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scroll-marker-event-target.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: event.target and event.currentTarget for ::scroll-marker is scroller</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#active-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+  body {
+    margin: 0;
+  }
+
+  #scroller {
+    width: 600px;
+    height: 300px;
+    overflow: auto;
+    scroll-marker-group: before;
+    white-space: nowrap;
+  }
+
+  #scroller div {
+    display: inline-block;
+    width: 600px;
+    height: 270px;
+  }
+
+  #scroller::scroll-marker-group {
+    display: flex;
+    height: 20px;
+    width: 40px;
+  }
+
+  #scroller div::scroll-marker {
+    content: "";
+    width: 100px;
+    height: 20px;
+    display: inline-block;
+  }
+</style>
+<div id="scroller">
+  <div></div>
+  <div></div>
+</div>
+<script>
+  var target;
+  var currentTarget;
+
+  scroller.addEventListener('click', (e) => {
+    target = e.target;
+    currentTarget = e.currentTarget;
+  });
+
+  promise_test(async t => {
+    actions_promise = new test_driver.Actions()
+    .pointerMove(15, 15)
+    .pointerDown()
+    .pointerUp()
+    .send();
+    await actions_promise;
+    assert_equals(currentTarget, scroller, "event.currentTarget for ::scroll-marker click is scroller");
+    assert_equals(target, scroller, "event.target for ::scroll-marker click is scroller");
+  });
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/wasm/OWNERS b/third_party/blink/web_tests/external/wpt/wasm/OWNERS
index 67907d6..088ab44 100644
--- a/third_party/blink/web_tests/external/wpt/wasm/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/wasm/OWNERS
@@ -1 +1,3 @@
+jkummerow@chromium.org
+clemensb@chromium.org
 ahaas@chromium.org
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-cross-origin-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-cross-origin-expected.txt
new file mode 100644
index 0000000..4115c04
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-cross-origin-expected.txt
@@ -0,0 +1,5 @@
+Verifies that cached cross-origin responses contain correct headers
+
+response code=200
+response code=304
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-cross-origin.js b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-cross-origin.js
new file mode 100644
index 0000000..a0c1aa97
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/extra-info-cross-origin.js
@@ -0,0 +1,22 @@
+(async function(/** @type {import('test_runner').TestRunner} */ testRunner) {
+  const {session, dp} = await testRunner.startURL(
+    '../resources/test-page.html',
+    `Verifies that cached cross-origin responses contain correct headers\n`);
+
+  await dp.Network.enable();
+
+  dp.Network.onResponseReceivedExtraInfo(event => {
+    testRunner.log('response code=' + event.params.statusCode);
+  });
+
+  async function fetch() {
+    await session.evaluateAsync(async (url) => {
+      await fetch(url);
+    }, testRunner.url('http://devtools.oopif.test:8000/inspector-protocol/network/resources/cached-revalidate.php'));
+  }
+
+  await fetch(); // initial response is a 200.
+  await fetch(); // subsequent response is a 304.
+
+  testRunner.completeTest();
+})
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/cached-revalidate.php b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/cached-revalidate.php
new file mode 100644
index 0000000..0a834aa
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/network/resources/cached-revalidate.php
@@ -0,0 +1,18 @@
+<?php
+    $HTTP_IF_NON_MATCH = $_SERVER["HTTP_IF_NONE_MATCH"] ?? null;
+    if ($HTTP_IF_NON_MATCH) {
+        header("HTTP/1.0 304 Not Modified");
+        header("Access-Control-Allow-Origin: *");
+        exit;
+    }
+
+    header("ETag: \"ABC\"");
+    header("Access-Control-Allow-Origin: *");
+    header("Cache-Control: max-age=0;must-revalidate");
+    header("Content-Type: text/html; charset=UTF-8");
+?>
+<html>
+<body>
+Today's lucky number: <?php echo(rand()) ?>
+</body>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/reduce_screen_size/screen.https.window.js b/third_party/blink/web_tests/wpt_internal/reduce_screen_size/screen.https.window.js
index ed4d7fc0..2902eb9 100644
--- a/third_party/blink/web_tests/wpt_internal/reduce_screen_size/screen.https.window.js
+++ b/third_party/blink/web_tests/wpt_internal/reduce_screen_size/screen.https.window.js
@@ -1,11 +1,28 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: script=/common/subset-tests-by-key.js
+// META: variant=?include=denied
+// META: variant=?include=granted
+
 // Testing the impact of the `ReduceScreenSize` flag.
 
-// Opens a new 300x400 window to `about:blank`, positioned at (100,200).
-function open_window(test) {
-  let popup = window.open("", "_blank", "popup, left=100, top=200, width=300, height=400");
+// Opens a new 300x400 window positioned at (100,200), and wait for its notification.
+async function open_window(test) {
+  let popup_promise = async _ => {
+    return new Promise(resolve => {
+      let w = window.open("/html/browsers/windows/resources/post-to-opener.html", "_blank", "popup, left=100, top=200, width=300, height=400");
+      window.addEventListener('message', e => {
+        if (e.source === w) {
+          assert_equals(w.innerWidth, 300, "Inner Width");
+          assert_equals(w.innerHeight, 400, "Inner Height");
+          assert_equals(typeof w.screen, "object", "Screen available.");
 
-  assert_equals(popup.innerWidth, 300, "Inner Width");
-  assert_equals(popup.innerHeight, 400, "Inner Height");
+          resolve(w);
+        }
+      });
+    });
+  };
+  const popup = await popup_promise();
 
   test.add_cleanup(() => {
     popup.close();
@@ -14,58 +31,99 @@
   return popup;
 }
 
-test(t => {
-  let popup = open_window(t);
-  assert_not_equals(popup.screen.width, popup.innerWidth, "Width");
-  assert_not_equals(popup.screen.height, popup.innerHeight, "Height");
-  assert_not_equals(popup.screen.availWidth, popup.innerWidth, "Avail Width");
-  assert_not_equals(popup.screen.availHeight, popup.innerHeight, "Avail Height");
+const FLAG_ENABLED = true;
+const FLAG_DISABLED = false;
 
-  // Media Queries
-  //
-  // If we're not reducing size information, then `device-width` will be
-  // larger than `innerWidth`, so asking for a minimum width that's just wider will pass.
-  assert_true(popup.matchMedia(`(min-device-width: ${popup.innerWidth + 1}px)`).matches, "Device Width");
-  assert_true(popup.matchMedia(`(min-device-height: ${popup.innerHeight + 1}px)`).matches, "Device Height");
+const PERMISSION_GRANTED = 'granted';
+const PERMISSION_DENIED = 'denied';
 
-  // `window.screen*` doesn't work in content_shell (and we force content_shell
-  // for `wpt_internal` tests).
-  //
-  // assert_equals(popup.screenX, 100, "screenX");
-  // assert_equals(popup.screenLeft, 100, "screenLeft");
-  // assert_equals(popup.screenY, 200, "screenY");
-  // assert_equals(popup.screenTop, 200, "screenY");
-  //
-  // TODO: There doesn't seem to be any way to pretend that `isExtended` should
-  // be true, which makes it hard to test in its unreduced state.
-  //
-  // TODO: There doesn't seem to be any way to pretend that the color depth is
-  // anything other than the default value.
-}, "Verify default behavior.");
+const EXPECT_REDUCTION = true;
+const EXPECT_NO_REDUCTION = false;
 
-test(t => {
-  internals.runtimeFlags.reduceScreenSizeEnabled = true;
+function generate_test(flagState, permissionState, expectReduction) {
+  subsetTestByKey(permissionState, promise_test, async t => {
+    // Set the runtime flag here so it will take effect when the popup loads:
+    internals.runtimeFlags.reduceScreenSizeEnabled = flagState;
 
-  let popup = open_window(t);
-  assert_equals(popup.screen.width, popup.innerWidth, "Width");
-  assert_equals(popup.screen.height, popup.innerHeight, "Height");
-  assert_equals(popup.screen.availWidth, popup.innerWidth, "Avail Width");
-  assert_equals(popup.screen.availHeight, popup.innerHeight, "Avail Height");
+    // Open the popup for testing:
+    let popup = await open_window(t);
 
-  assert_equals(popup.screenX, 0, "screenX");
-  assert_equals(popup.screenLeft, 0, "screenLeft");
-  assert_equals(popup.screenY, 0, "screenY");
-  assert_equals(popup.screenTop, 0, "screenY");
+    const permission = await navigator.permissions.query({ name: "window-management" });
+    assert_equals(permission.state, permissionState);
 
-  assert_false(popup.screen.isExtended, "isExtended");
+    if (expectReduction === EXPECT_REDUCTION) {
+      // When reducing the available information:
+      //
+      // 1. Screen heights and widths should match the window's height
+      //    and width.
+      assert_equals(popup.screen.width, popup.innerWidth, "Width");
+      assert_equals(popup.screen.height, popup.innerHeight, "Height");
+      assert_equals(popup.screen.availWidth, popup.innerWidth, "Avail Width");
+      assert_equals(popup.screen.availHeight, popup.innerHeight, "Avail Height");
+      assert_true(popup.matchMedia(`(device-width: ${popup.innerWidth}px)`).matches, "Device Width")
+      assert_true(popup.matchMedia(`(device-height: ${popup.innerHeight}px)`).matches, "Device Height");
 
-  assert_equals(popup.screen.colorDepth, 24, "Color Depth");
-  assert_equals(popup.screen.pixelDepth, 24, "Pixel Depth");
+      // 2. Windows should all appear positioned at the top-left corner.
+      assert_equals(popup.screenX, 0, "screenX");
+      assert_equals(popup.screenLeft, 0, "screenLeft");
+      assert_equals(popup.screenY, 0, "screenY");
+      assert_equals(popup.screenTop, 0, "screenY");
 
-  // Media Queries
-  //
-  // If we're reducing size information, then `device-width` will be
-  // `innerWidth`, so asking for a minimum width that's just wider will fail.
-  assert_false(popup.matchMedia(`(min-device-width: ${popup.innerWidth + 1}px)`).matches, "Device Width")
-  assert_false(popup.matchMedia(`(min-device-height: ${popup.innerHeight + 1}px)`).matches, "Device Height");
-}, "Verify reduced behavior.");
+      // 3. No information about secondary screens should be available.
+      assert_false(popup.screen.isExtended, "isExtended");
+
+      // 4. Color/pixel depth is 24.
+      assert_equals(popup.screen.colorDepth, 24, "Color Depth");
+      assert_equals(popup.screen.pixelDepth, 24, "Pixel Depth");
+    } else {
+      // Height/Width
+      assert_not_equals(popup.screen.width, popup.innerWidth, "Width");
+      assert_not_equals(popup.screen.height, popup.innerHeight, "Height");
+      assert_not_equals(popup.screen.availWidth, popup.innerWidth, "Avail Width");
+      assert_not_equals(popup.screen.availHeight, popup.innerHeight, "Avail Height");
+
+      // If we're not reducing size information, then `device-width` will be
+      // larger than `innerWidth`, so asking for a minimum width that's just wider will pass.
+      assert_true(popup.matchMedia(`(min-device-width: ${popup.innerWidth + 1}px)`).matches, "Device Width");
+      assert_true(popup.matchMedia(`(min-device-height: ${popup.innerHeight + 1}px)`).matches, "Device Height");
+
+      // TODO: `window.screen*` doesn't work in content_shell (and we force content_shell
+      // for `wpt_internal` tests).
+      //
+      // TODO: There doesn't seem to be any way to pretend that `isExtended` should
+      // be true, which makes it hard to test in its unreduced state.
+      //
+      // TODO: There doesn't seem to be any way to pretend that the color depth is
+      // anything other than the default value.
+    }
+  }, `Flag ${flagState ? "enabled" : "disabled"}, ` +
+     `Permission ${permissionState}: ` +
+     `${expectReduction ? "reduced" : "unreduced"}`);
+}
+
+// (Ab)using the variant mechanism to set the permission status for the
+// relevant tests. Since it's global state, setting it from within each
+// test would otherwise risk stomping on other test's expectations:
+//
+// Permission Granted
+//
+subsetTestByKey("granted", promise_test, async _ => {
+  return test_driver.set_permission({name: 'window-management'}, 'granted');
+  const permission = await navigator.permissions.query({ name: "window-management" });
+  assert_equals(permission.state, 'granted');
+}, "Precondition: Grant permission.");
+
+generate_test(FLAG_DISABLED, PERMISSION_GRANTED, EXPECT_NO_REDUCTION);
+generate_test(FLAG_ENABLED, PERMISSION_GRANTED, EXPECT_NO_REDUCTION);
+
+//
+// Permission Denied
+//
+subsetTestByKey("denied", promise_test, async _ => {
+  return test_driver.set_permission({name: 'window-management'}, 'denied');
+  const permission = await navigator.permissions.query({ name: "window-management" });
+  assert_equals(permission.state, 'denied');
+}, "Precondition: Deny permission.");
+
+generate_test(FLAG_DISABLED, PERMISSION_DENIED, EXPECT_NO_REDUCTION);
+generate_test(FLAG_ENABLED, PERMISSION_DENIED, EXPECT_REDUCTION);
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src
index d3919a2..2a4f69a 160000
--- a/third_party/compiler-rt/src
+++ b/third_party/compiler-rt/src
@@ -1 +1 @@
-Subproject commit d3919a20084ce47e31983ffa3724f6318f34390c
+Subproject commit 2a4f69a118bdc5d03c415de1b9b166b2f1d4084f
diff --git a/third_party/crossbench b/third_party/crossbench
index 6acc0ec..749d1a4 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 6acc0ec6a547f0d9cfc307fedd515459d239fa24
+Subproject commit 749d1a4f48bb0ae63aa8559ef96180a677b65307
diff --git a/third_party/dawn b/third_party/dawn
index d03fe7e..830dc86 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit d03fe7eda12dfce1578bf69da2814f89fd78c1e9
+Subproject commit 830dc86845102bb7c45f45ca14251b92add5dbb9
diff --git a/third_party/depot_tools b/third_party/depot_tools
index ae91718..8f87843 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit ae917185501cda874f12f3e1d870de8505b0807c
+Subproject commit 8f878438075a8d5d76f57bd10ab866d58d706319
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index ea58b67..fee355f 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit ea58b6707370f3b440b362fe178e7f6cd4a9170b
+Subproject commit fee355f9b7f54daccee81b9ef82034656243e834
diff --git a/third_party/pdfium b/third_party/pdfium
index b6457bc..1dbcd29 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit b6457bcccc5c86ac7159f957fa8dbea3d5e13f49
+Subproject commit 1dbcd29c45a96d9717e040662bba05395d790145
diff --git a/third_party/perfetto b/third_party/perfetto
index cca1748..faeba3e 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit cca1748cb16b9dc0cac21515813c3855a983917d
+Subproject commit faeba3e4b27b5adb924ece5548a61bef8013faed
diff --git a/third_party/skia b/third_party/skia
index 35d5edf..47766ee 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit 35d5edfa0d50984c22ff94f5438c31e0db12c6f8
+Subproject commit 47766ee60ccc6e9e0a024348192ba72391e5785e
diff --git a/tools/clang/spanify/Spanifier.cpp b/tools/clang/spanify/Spanifier.cpp
index e4637e1..d01de08f 100644
--- a/tools/clang/spanify/Spanifier.cpp
+++ b/tools/clang/spanify/Spanifier.cpp
@@ -1103,15 +1103,17 @@
 static void DecaySpanToBooleanOp(const MatchFinder::MatchResult& result) {
   const clang::SourceManager& source_manager = *result.SourceManager;
   const std::string& key = GetRHS(result);
-  const auto* operand =
-      result.Nodes.getNodeAs<clang::Expr>("boolean_op_operand");
 
   if (const auto* logical_not_op =
           result.Nodes.getNodeAs<clang::UnaryOperator>("logical_not_op")) {
+    const clang::SourceRange logical_not_range{
+        logical_not_op->getBeginLoc(),
+        logical_not_op->getBeginLoc().getLocWithOffset(1)};
     EmitReplacement(
-        key, GetReplacementDirective(logical_not_op->getSourceRange(), "",
-                                     source_manager));
+        key, GetReplacementDirective(logical_not_range, "", source_manager));
   } else {
+    const auto* operand =
+        result.Nodes.getNodeAs<clang::Expr>("boolean_op_operand");
     EmitReplacement(key, GetReplacementDirective(operand->getBeginLoc(), "!",
                                                  source_manager));
   }
@@ -1300,15 +1302,13 @@
 static void EmitSingleVariableSpan(const std::string& key,
                                    const MatchFinder::MatchResult& result) {
   const clang::SourceManager& source_manager = *result.SourceManager;
-  const clang::ASTContext& ast_context = *result.Context;
-  const auto& lang_opts = ast_context.getLangOpts();
+  const auto& lang_opts = result.Context->getLangOpts();
 
-  const auto* expr = result.Nodes.getNodeAs<clang::Expr>("address_expr");
-  const auto* operand_decl = result.Nodes.getNodeAs<clang::DeclaratorDecl>(
-      "address_expr_operand_decl");
+  const auto* expr =
+      result.Nodes.getNodeAs<clang::UnaryOperator>("address_expr");
   const auto* operand_expr =
       result.Nodes.getNodeAs<clang::Expr>("address_expr_operand");
-  if (!expr || !operand_decl || !operand_expr) {
+  if (!expr || !operand_expr) {
     llvm::errs()
         << "\n"
            "Error: EmitSingleVariableSpan() encountered an unexpected match.\n";
@@ -1316,16 +1316,24 @@
     assert(false && "Unexpected match in EmitSingleVariableSpan()");
   }
 
-  clang::SourceRange expr_range = {expr->getBeginLoc()};
-  std::string type = GetTypeAsString(operand_decl->getType(), ast_context);
-  std::string replacement_text = llvm::formatv("base::span<{0}, 1>(", type);
-  EmitReplacement(
-      key, GetReplacementDirective(expr_range, replacement_text, source_manager,
-                                   kEmitSingleVariableSpanPrecedence));
+  // This range is just one character, covering the '&' symbol.
+  clang::SourceLocation ampersand_loc = expr->getOperatorLoc();
+  clang::SourceRange ampersand_range = {
+      ampersand_loc, clang::Lexer::getLocForEndOfToken(
+                         ampersand_loc, 0u, source_manager, lang_opts)};
+
+  EmitReplacement(key, GetReplacementDirective(
+                           ampersand_range, "base::SpanFromSingleElement(",
+                           source_manager, kEmitSingleVariableSpanPrecedence));
   EmitReplacement(
       key, GetReplacementDirective(
                getExprRange(operand_expr, source_manager, lang_opts).getEnd(),
-               ", 1u)", source_manager, -kEmitSingleVariableSpanPrecedence));
+               ")", source_manager, -kEmitSingleVariableSpanPrecedence));
+
+  // Include the header for `base::SpanFromSingleElement()`.
+  EmitReplacement(
+      key, GetIncludeDirective(operand_expr->getSourceRange(), source_manager,
+                               kBaseAutoSpanificationHelperIncludePath));
 }
 
 // Rewrites unsafe third-party member function calls to helper macro calls.
@@ -2836,19 +2844,24 @@
             has(memberExpr().bind("data_member_expr")))
             .bind("member_data_call");
 
-    // Matchers |&var| where |var| is a local variable, a parameter or member
-    // field. Doesn't match when |var| is a function.
+    auto has_std_array_type = hasType(hasCanonicalType(hasDeclaration(
+        classTemplateSpecializationDecl(hasName("::std::array")))));
+
+    // Array excluded because it might be used as a buffer with >1 size.
+    auto single_var_span_exclusions =
+        unless(anyOf(exclusions, hasType(arrayType()), hasType(functionType()),
+                     has_std_array_type));
+
+    // Matches |&var| where |var| is a local variable, a parameter or member
+    // field. Doesn't match when |var| is a function or an array.
     auto buff_address_from_single_var =
         unaryOperator(
             hasOperatorName("&"),
             hasUnaryOperand(anyOf(
-                declRefExpr(to(anyOf(varDecl(unless(exclusions))
-                                         .bind("address_expr_operand_decl"),
-                                     parmVarDecl(unless(exclusions))
-                                         .bind("address_expr_operand_decl"))))
+                declRefExpr(to(anyOf(varDecl(single_var_span_exclusions),
+                                     parmVarDecl(single_var_span_exclusions))))
                     .bind("address_expr_operand"),
-                memberExpr(member(fieldDecl(unless(exclusions))
-                                      .bind("address_expr_operand_decl")))
+                memberExpr(member(fieldDecl(single_var_span_exclusions)))
                     .bind("address_expr_operand"))))
             .bind("address_expr");
 
@@ -2996,15 +3009,6 @@
             .bind("deref_expr"));
     Match(deref_expression, DecaySpanToPointer);
 
-    auto rhs_exprs_without_size_nodes_ignoring_non_spelled_nodes =
-        traverse(clang::TK_IgnoreUnlessSpelledInSource,
-                 expr(rhs_exprs_without_size_nodes));
-    auto raw_ptr_op_bool = cxxMemberCallExpr(
-        on(expr().bind("boolean_op_operand")),
-        callee(cxxMethodDecl(hasName("operator bool"),
-                             ofClass(hasName("raw_ptr")))),
-        has(memberExpr(has(expr(ignoringParenCasts(
-            rhs_exprs_without_size_nodes_ignoring_non_spelled_nodes))))));
     // Handles boolean operations that need to be adapted after a span rewrite.
     //   if(expr) => if(!expr.empty())
     //   if(!expr) => if(expr.empty())
@@ -3015,17 +3019,21 @@
     // `clang::TK_IgnoreUnlessSpelledInSource`, while very useful in simplifying
     // the matchers, wouldn't detect boolean operations on pointers hence the
     // need for a hybrid traversal mode in this matcher.
-    auto boolean_op = expr(
-        anyOf(
-            implicitCastExpr(
-                hasCastKind(clang::CastKind::CK_PointerToBoolean),
-                hasSourceExpression(
-                    expr(
-                        rhs_exprs_without_size_nodes_ignoring_non_spelled_nodes)
-                        .bind("boolean_op_operand"))),
-            raw_ptr_op_bool),
-        optionally(hasParent(
-            unaryOperator(hasOperatorName("!")).bind("logical_not_op"))));
+    auto boolean_op_operand =
+        traverse(clang::TK_IgnoreUnlessSpelledInSource,
+                 expr(rhs_exprs_without_size_nodes).bind("boolean_op_operand"));
+    auto raw_ptr_op_bool_call_expr =
+        cxxMemberCallExpr(on(boolean_op_operand),
+                          callee(cxxMethodDecl(hasName("operator bool"),
+                                               ofClass(hasName("raw_ptr")))));
+    auto boolean_op = traverse(
+        clang::TK_AsIs,
+        expr(anyOf(implicitCastExpr(
+                       hasCastKind(clang::CastKind::CK_PointerToBoolean),
+                       hasSourceExpression(boolean_op_operand)),
+                   implicitCastExpr(has(raw_ptr_op_bool_call_expr))),
+             optionally(hasParent(
+                 unaryOperator(hasOperatorName("!")).bind("logical_not_op")))));
     Match(boolean_op, DecaySpanToBooleanOp);
 
     // This is needed to remove the `.get()` call on raw_ptr from rewritten
diff --git a/tools/clang/spanify/tests/field-init-expected.cc b/tools/clang/spanify/tests/field-init-expected.cc
index 95f9533..60be7cb 100644
--- a/tools/clang/spanify/tests/field-init-expected.cc
+++ b/tools/clang/spanify/tests/field-init-expected.cc
@@ -4,6 +4,7 @@
 #include <tuple>
 #include <vector>
 
+#include "base/containers/auto_spanification_helper.h"
 #include "base/containers/span.h"
 
 // Expected rewrite:
@@ -28,9 +29,8 @@
   // base::span<int> buffer4 = getPtr();
   base::span<int> buffer4 = getPtr();
   // Expected rewrite:
-  // base::span<const int> buffer5 = base::span<const int, 1>(&default_value,
-  // 1u);
-  base::span<const int> buffer5 = base::span<const int, 1>(&default_value, 1u);
+  // base::span<const int> buffer5 = base::SpanFromSingleElement(default_value);
+  base::span<const int> buffer5 = base::SpanFromSingleElement(default_value);
   // Expected rewrite:
   // base::span<const int> buffer6 = default_data;
   base::span<const int> buffer6 = default_data;
diff --git a/tools/clang/spanify/tests/field-init-original.cc b/tools/clang/spanify/tests/field-init-original.cc
index 77a45d73..4ecff53 100644
--- a/tools/clang/spanify/tests/field-init-original.cc
+++ b/tools/clang/spanify/tests/field-init-original.cc
@@ -26,8 +26,7 @@
   // base::span<int> buffer4 = getPtr();
   int* buffer4 = getPtr();
   // Expected rewrite:
-  // base::span<const int> buffer5 = base::span<const int, 1>(&default_value,
-  // 1u);
+  // base::span<const int> buffer5 = base::SpanFromSingleElement(default_value);
   const int* buffer5 = &default_value;
   // Expected rewrite:
   // base::span<const int> buffer6 = default_data;
diff --git a/tools/clang/spanify/tests/operator-bool-expected.cc b/tools/clang/spanify/tests/operator-bool-expected.cc
index a2a4127..1c7ad8a 100644
--- a/tools/clang/spanify/tests/operator-bool-expected.cc
+++ b/tools/clang/spanify/tests/operator-bool-expected.cc
@@ -70,6 +70,47 @@
   // IMPLICITLY_CONVERT_TO_BOOL(!buf.empty());
   IMPLICITLY_CONVERT_TO_BOOL(!buf.empty());
   // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(buf.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(buf.empty());
+  // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(!buf.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(!buf.empty());
+
+  // Expected rewrite:
   // IMPLICITLY_CONVERT_TO_BOOL(!ptr.empty());
   IMPLICITLY_CONVERT_TO_BOOL(!ptr.empty());
+  // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(ptr.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(ptr.empty());
+  // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(!ptr.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(!ptr.empty());
+}
+
+// A function that will return a base::span.
+// Expected rewrite:
+// base::span<int> get_span() {
+base::span<int> get_span() {
+  static int buf[] = {1, 2, 3};
+  return buf;
+}
+
+void test_get_span() {
+  // Expected rewrite:
+  // base::span<int> buf = get_span();
+  base::span<int> buf = get_span();
+  buf[0] = 0;
+
+  // Expected rewrite:
+  // if (!get_span().empty()) {
+  if (!get_span().empty()) {
+  }
+  // Expected rewrite:
+  // if (get_span().empty()) {
+  if (get_span().empty()) {
+  }
+  // Expected rewrite:
+  // if (!get_span().empty()) {
+  if (!get_span().empty()) {
+  }
 }
diff --git a/tools/clang/spanify/tests/operator-bool-original.cc b/tools/clang/spanify/tests/operator-bool-original.cc
index 5f79a68..0ea1f42 100644
--- a/tools/clang/spanify/tests/operator-bool-original.cc
+++ b/tools/clang/spanify/tests/operator-bool-original.cc
@@ -68,6 +68,47 @@
   // IMPLICITLY_CONVERT_TO_BOOL(!buf.empty());
   IMPLICITLY_CONVERT_TO_BOOL(buf);
   // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(buf.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(!buf);
+  // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(!buf.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(!!buf);
+
+  // Expected rewrite:
   // IMPLICITLY_CONVERT_TO_BOOL(!ptr.empty());
   IMPLICITLY_CONVERT_TO_BOOL(ptr);
+  // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(ptr.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(!ptr);
+  // Expected rewrite:
+  // IMPLICITLY_CONVERT_TO_BOOL(!ptr.empty());
+  IMPLICITLY_CONVERT_TO_BOOL(!!ptr);
+}
+
+// A function that will return a base::span.
+// Expected rewrite:
+// base::span<int> get_span() {
+int* get_span() {
+  static int buf[] = {1, 2, 3};
+  return buf;
+}
+
+void test_get_span() {
+  // Expected rewrite:
+  // base::span<int> buf = get_span();
+  int* buf = get_span();
+  buf[0] = 0;
+
+  // Expected rewrite:
+  // if (!get_span().empty()) {
+  if (get_span()) {
+  }
+  // Expected rewrite:
+  // if (get_span().empty()) {
+  if (!get_span()) {
+  }
+  // Expected rewrite:
+  // if (!get_span().empty()) {
+  if (!!get_span()) {
+  }
 }
diff --git a/tools/clang/spanify/tests/single-variable-span-expected.cc b/tools/clang/spanify/tests/single-variable-span-expected.cc
index e795e29b..0caa4e7 100644
--- a/tools/clang/spanify/tests/single-variable-span-expected.cc
+++ b/tools/clang/spanify/tests/single-variable-span-expected.cc
@@ -2,21 +2,32 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <array>
+#include <cstdint>
 #include <vector>
 
 // Tests related to single_element_expr matcher.
 
+#include "base/containers/auto_spanification_helper.h"
 #include "base/containers/span.h"
 
 void processIntBuffer(base::span<int> buf) {
   std::ignore = buf[0];
 }
 
+void processUint8Buffer(base::span<uint8_t> buf) {
+  std::ignore = buf[0];
+}
+
 void testPointerPassing() {
   int singleInt;
   // Expected rewrite:
-  // processIntBuffer(base::span<int, 1>(&singleInt, 1u))
-  processIntBuffer(base::span<int, 1>(&singleInt, 1u));
+  // processIntBuffer(base::SpanFromSingleElement(singleInt));
+  processIntBuffer(base::SpanFromSingleElement(singleInt));
+  // processUint8Buffer(base::as_writable_byte_span(
+  //    base::SpanFromSingleElement(singleInt)));
+  processUint8Buffer(
+      base::as_writable_byte_span(base::SpanFromSingleElement(singleInt)));
 
   int intArray[10];
   // Not using &.
@@ -26,6 +37,14 @@
   // Expected rewrite:
   // processIntBuffer(intArray);
   processIntBuffer(intArray);
+  // Do not rewrite because code may be expecting an buffer with >1 size.
+  // No rewrite expected.
+  processUint8Buffer(reinterpret_cast<uint8_t*>(&intArray));
+
+  std::array<int, 5> stdArray;
+  // Do not rewrite because code may be expecting an buffer with >1 size.
+  // No rewrite expected.
+  processUint8Buffer(reinterpret_cast<uint8_t*>(&stdArray));
 
   std::vector<int> intVector;
   // We know how to get size from Vector so just leave it alone to
@@ -33,6 +52,14 @@
   // Expected rewrite:
   // processIntBuffer(intVector);
   processIntBuffer(intVector);
+
+  void* voidPtr;
+  // void** should get rewritten.
+  // Expected rewrite:
+  // processUint8Buffer(
+  //   base::as_writable_byte_span(base::SpanFromSingleElement(voidPtr)));
+  processUint8Buffer(
+      base::as_writable_byte_span(base::SpanFromSingleElement(voidPtr)));
 }
 
 // Function that takes a pointer to an integer pointer.
@@ -43,8 +70,8 @@
 void testPointerToPointerPassing() {
   int* singleIntPointer;
   // Expected rewrite:
-  // processIntPointerBuffer(base::span<int*, 1>(&singleIntPointer, 1u));
-  processIntPointerBuffer(base::span<int*, 1>(&singleIntPointer, 1u));
+  // processIntPointerBuffer(base::SpanFromSingleElement(singleIntPointer));
+  processIntPointerBuffer(base::SpanFromSingleElement(singleIntPointer));
 
   int* intArrayOfPointers[10];
   // Not using &.
@@ -69,12 +96,12 @@
 void testFieldPointerPassing() {
   MyStruct myStruct;
   // Expected rewrite:
-  // processIntBuffer(base::span<int, 1>(&myStruct.field, 1u));
-  processIntBuffer(base::span<int, 1>(&myStruct.field, 1u));
+  // processIntBuffer(base::SpanFromSingleElement(myStruct.field));
+  processIntBuffer(base::SpanFromSingleElement(myStruct.field));
 }
 
 void testParamPointerPassing(int param) {
   // Expected rewrite:
-  // processIntBuffer(base::span<int, 1>(&param, 1u));
-  processIntBuffer(base::span<int, 1>(&param, 1u));
+  // processIntBuffer(base::SpanFromSingleElement(param));
+  processIntBuffer(base::SpanFromSingleElement(param));
 }
diff --git a/tools/clang/spanify/tests/single-variable-span-original.cc b/tools/clang/spanify/tests/single-variable-span-original.cc
index b0dc29a4..ab5fa4ae 100644
--- a/tools/clang/spanify/tests/single-variable-span-original.cc
+++ b/tools/clang/spanify/tests/single-variable-span-original.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <array>
+#include <cstdint>
 #include <vector>
 
 // Tests related to single_element_expr matcher.
@@ -10,11 +12,18 @@
   std::ignore = buf[0];
 }
 
+void processUint8Buffer(uint8_t* buf) {
+  std::ignore = buf[0];
+}
+
 void testPointerPassing() {
   int singleInt;
   // Expected rewrite:
-  // processIntBuffer(base::span<int, 1>(&singleInt, 1u))
+  // processIntBuffer(base::SpanFromSingleElement(singleInt));
   processIntBuffer(&singleInt);
+  // processUint8Buffer(base::as_writable_byte_span(
+  //    base::SpanFromSingleElement(singleInt)));
+  processUint8Buffer(reinterpret_cast<uint8_t*>(&singleInt));
 
   int intArray[10];
   // Not using &.
@@ -24,6 +33,14 @@
   // Expected rewrite:
   // processIntBuffer(intArray);
   processIntBuffer(&intArray[0]);
+  // Do not rewrite because code may be expecting an buffer with >1 size.
+  // No rewrite expected.
+  processUint8Buffer(reinterpret_cast<uint8_t*>(&intArray));
+
+  std::array<int, 5> stdArray;
+  // Do not rewrite because code may be expecting an buffer with >1 size.
+  // No rewrite expected.
+  processUint8Buffer(reinterpret_cast<uint8_t*>(&stdArray));
 
   std::vector<int> intVector;
   // We know how to get size from Vector so just leave it alone to
@@ -31,6 +48,13 @@
   // Expected rewrite:
   // processIntBuffer(intVector);
   processIntBuffer(&intVector[0]);
+
+  void* voidPtr;
+  // void** should get rewritten.
+  // Expected rewrite:
+  // processUint8Buffer(
+  //   base::as_writable_byte_span(base::SpanFromSingleElement(voidPtr)));
+  processUint8Buffer(reinterpret_cast<uint8_t*>(&voidPtr));
 }
 
 // Function that takes a pointer to an integer pointer.
@@ -41,7 +65,7 @@
 void testPointerToPointerPassing() {
   int* singleIntPointer;
   // Expected rewrite:
-  // processIntPointerBuffer(base::span<int*, 1>(&singleIntPointer, 1u));
+  // processIntPointerBuffer(base::SpanFromSingleElement(singleIntPointer));
   processIntPointerBuffer(&singleIntPointer);
 
   int* intArrayOfPointers[10];
@@ -67,12 +91,12 @@
 void testFieldPointerPassing() {
   MyStruct myStruct;
   // Expected rewrite:
-  // processIntBuffer(base::span<int, 1>(&myStruct.field, 1u));
+  // processIntBuffer(base::SpanFromSingleElement(myStruct.field));
   processIntBuffer(&myStruct.field);
 }
 
 void testParamPointerPassing(int param) {
   // Expected rewrite:
-  // processIntBuffer(base::span<int, 1>(&param, 1u));
+  // processIntBuffer(base::SpanFromSingleElement(param));
   processIntBuffer(&param);
 }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 252f07c..cbdd1b31 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6575,12 +6575,14 @@
   <int value="2" label="User leaves the interstitial by closing the page"/>
 </enum>
 
+<!-- LINT.IfChange(IOSContentSizeCategory) -->
+
 <enum name="IOSContentSizeCategory">
   <int value="0" label="Unspecified"/>
   <int value="1" label="ExtraSmall"/>
   <int value="2" label="Small"/>
   <int value="3" label="Medium"/>
-  <int value="4" label="Large"/>
+  <int value="4" label="Large (default)"/>
   <int value="5" label="ExtraLarge"/>
   <int value="6" label="ExtraExtraLarge"/>
   <int value="7" label="ExtraExtraExtraLarge"/>
@@ -6591,6 +6593,8 @@
   <int value="12" label="AccessibilityExtraExtraExtraLarge"/>
 </enum>
 
+<!-- LINT.ThenChange(//ios/components/ui_util/dynamic_type_util.h:IOSContentSizeCategory) -->
+
 <!-- LINT.IfChange(IOSContextualPanelDismissedReason) -->
 
 <enum name="IOSContextualPanelDismissedReason">
@@ -10387,6 +10391,7 @@
   <int value="-1338237485" label="HardwareSecureDecryptionExperiment:disabled"/>
   <int value="-1337873095" label="CompareManagementInterface:disabled"/>
   <int value="-1337433513" label="FilesGoogleDriveSettingsPage:disabled"/>
+  <int value="-1337331698" label="WebRtcPqcForDtls:enabled"/>
   <int value="-1337185440" label="enable-webvr"/>
   <int value="-1336110789" label="CastMediaRouteProvider:disabled"/>
   <int value="-1335360580" label="ReduceTransferSizeUpdatedIPC:enabled"/>
@@ -16173,6 +16178,7 @@
   <int value="816907702" label="GlobalMediaControlsSeamlessTransfer:disabled"/>
   <int value="817080398" label="PreinstalledWebAppDuplicationFixer:disabled"/>
   <int value="817393533" label="GlobalMediaControlsModernUI:disabled"/>
+  <int value="818248688" label="WebRtcPqcForDtls:disabled"/>
   <int value="819620541"
       label="AccessibilityManifestV3AccessibilityCommon:enabled"/>
   <int value="820645399"
diff --git a/tools/metrics/histograms/metadata/browser/enums.xml b/tools/metrics/histograms/metadata/browser/enums.xml
index ac9280e6..fdc6d46e 100644
--- a/tools/metrics/histograms/metadata/browser/enums.xml
+++ b/tools/metrics/histograms/metadata/browser/enums.xml
@@ -483,6 +483,7 @@
   <int value="6" label="RefusedForPdfContent"/>
   <int value="7" label="RefusedForJitMismatch"/>
   <int value="8" label="RefusedForV8OptimizationMismatch"/>
+  <int value="9" label="RefusedNonNavigation"/>
 </enum>
 
 <!-- LINT.ThenChange(//content/browser/renderer_host/render_process_host_impl.h:SpareProcessMaybeTakeAction) -->
diff --git a/tools/metrics/histograms/metadata/browser/histograms.xml b/tools/metrics/histograms/metadata/browser/histograms.xml
index c1c7f2c..9b797cc 100644
--- a/tools/metrics/histograms/metadata/browser/histograms.xml
+++ b/tools/metrics/histograms/metadata/browser/histograms.xml
@@ -77,6 +77,7 @@
   <variant name="RefusedForJitMismatch"/>
   <variant name="RefusedForPdfContent"/>
   <variant name="RefusedForV8OptimizationMismatch"/>
+  <variant name="RefusedNonNavigation"/>
   <variant name="SpareTaken"/>
 </variants>
 
diff --git a/tools/metrics/histograms/metadata/image/histograms.xml b/tools/metrics/histograms/metadata/image/histograms.xml
index b4aaf3a..78c7ea2 100644
--- a/tools/metrics/histograms/metadata/image/histograms.xml
+++ b/tools/metrics/histograms/metadata/image/histograms.xml
@@ -181,7 +181,7 @@
 </histogram>
 
 <histogram name="ImageAnnotationService.AccessibilityV1.EngineKnown"
-    enum="BooleanKnown" expires_after="2025-07-31">
+    enum="BooleanKnown" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -197,7 +197,7 @@
 
 <histogram
     name="ImageAnnotationService.AccessibilityV1.ImageRequestIncludesDesc"
-    enum="BooleanIncluded" expires_after="2025-07-31">
+    enum="BooleanIncluded" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -217,7 +217,7 @@
 
 <histogram
     name="ImageAnnotationService.AccessibilityV1.ImageRequestIncludesIcon"
-    enum="BooleanIncluded" expires_after="2025-07-31">
+    enum="BooleanIncluded" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -235,7 +235,7 @@
 </histogram>
 
 <histogram name="ImageAnnotationService.AccessibilityV1.JsonParseSuccess"
-    enum="BooleanSuccess" expires_after="2025-07-31">
+    enum="BooleanSuccess" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -250,7 +250,7 @@
 </histogram>
 
 <histogram name="ImageAnnotationService.AccessibilityV1.PixelFetchSuccess"
-    enum="BooleanSuccess" expires_after="2025-07-31">
+    enum="BooleanSuccess" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -281,7 +281,7 @@
 </histogram>
 
 <histogram name="ImageAnnotationService.AccessibilityV1.ServerLatencyMs"
-    units="ms" expires_after="2025-07-31">
+    units="ms" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -296,7 +296,7 @@
 </histogram>
 
 <histogram name="ImageAnnotationService.AccessibilityV1.ServerNetError"
-    enum="NetErrorCodes" expires_after="2025-07-31">
+    enum="NetErrorCodes" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -328,7 +328,7 @@
 
 <histogram
     name="ImageAnnotationService.AccessibilityV1.ServerResponseSizeBytes"
-    units="bytes" expires_after="2025-07-31">
+    units="bytes" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -343,7 +343,7 @@
 </histogram>
 
 <histogram name="ImageAnnotationService.AccessibilityV1.SourcePixelCount"
-    units="pixels" expires_after="2025-07-31">
+    units="pixels" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -360,7 +360,7 @@
 
 <histogram
     name="ImageAnnotationService.{ImageAnnotationServiceAnnotationType}AccessibilityV1.Confidence"
-    units="%" expires_after="2025-07-31">
+    units="%" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -379,7 +379,7 @@
 
 <histogram
     name="ImageAnnotationService.{ImageAnnotationServiceAnnotationType}AccessibilityV1.Empty"
-    enum="BooleanEmpty" expires_after="2025-07-31">
+    enum="BooleanEmpty" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
@@ -399,7 +399,7 @@
 
 <histogram
     name="ImageAnnotationService.{ImageAnnotationServiceAnnotationType}AccessibilityV1.Status"
-    enum="GoogleRpcCode" expires_after="2025-07-31">
+    enum="GoogleRpcCode" expires_after="2026-01-31">
   <owner>dtseng@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <owner>mschillaci@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index fb3a36e..d2cc1e5 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -5514,6 +5514,16 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.System.PreferredSystemFontSize"
+    enum="IOSContentSizeCategory" expires_after="2026-06-01">
+  <owner>jlebel@chromium.org</owner>
+  <owner>bling-team@google.com</owner>
+  <summary>
+    Records the preferred system font size from iOS device. Recorded at cold
+    start.
+  </summary>
+</histogram>
+
 <histogram name="IOS.TabGrid.CloseTabs" units="Tabs" expires_after="2026-06-01">
   <owner>ewannpv@chromium.org</owner>
   <owner>gambard@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/net/enums.xml b/tools/metrics/histograms/metadata/net/enums.xml
index c3623c35..a2fd27b 100644
--- a/tools/metrics/histograms/metadata/net/enums.xml
+++ b/tools/metrics/histograms/metadata/net/enums.xml
@@ -2820,6 +2820,7 @@
   <int value="10" label="FailedToExecute"/>
   <int value="11" label="InvalidData"/>
   <int value="12" label="AlreadyExists"/>
+  <int value="13" label="NotFound"/>
 </enum>
 
 <!-- LINT.ThenChange(//net/disk_cache/sql/sql_persistent_store.h:SqlDiskCacheStoreError) -->
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 2310825..e987f72 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -134,6 +134,9 @@
 <variants name="SqlPersistentStoreMethodName">
   <variant name="CreateEntry"/>
   <variant name="DeleteAllEntries"/>
+  <variant name="DeleteDoomedEntry"/>
+  <variant name="DeleteLiveEntry"/>
+  <variant name="DoomEntry"/>
   <variant name="Initialize"/>
   <variant name="OpenEntry"/>
   <variant name="OpenOrCreateEntry"/>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 0c94f0c..2d87339 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -5786,6 +5786,22 @@
 </histogram>
 
 <histogram
+    name="NetworkService.Scheduler.IOThread.NumberOfPendingTasks.{QueueName}Queue"
+    units="count" expires_after="2025-12-25">
+  <owner>hayato@chromium.org</owner>
+  <owner>net-dev@chromium.org</owner>
+  <summary>
+    Records the number of pending tasks in {QueueName}Queue of
+    NetworkServiceScheduler. Recorded with 0.001 probability when a task
+    scheduled on the IO thread starts.
+  </summary>
+  <token key="QueueName">
+    <variant name="Default"/>
+    <variant name="HighPriority"/>
+  </token>
+</histogram>
+
+<histogram
     name="NetworkService.Scheduler.IOThread.QueuingTime.{QueueName}Queue"
     units="ms" expires_after="2025-12-25">
   <owner>hayato@chromium.org</owner>
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index 9cd45c5..766c041 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -513,6 +513,13 @@
                           arguments=arguments)
 
 
+def _crossbench_embedder(estimated_runtime=20, arguments=None):
+  return CrossbenchConfig('embedder.crossbench',
+                          'embedder',
+                          estimated_runtime=estimated_runtime,
+                          arguments=arguments)
+
+
 _CROSSBENCH_JETSTREAM_SPEEDOMETER = frozenset([
     _jetstream2_crossbench(),
     _speedometer3_crossbench(),
@@ -577,6 +584,18 @@
             '--stories=cnn',
         ]
     ),
+    _crossbench_embedder(
+        estimated_runtime=900,
+        arguments=[
+            '--wpr=crossbench_android_embedder_000.wprgo',
+            '--embedder=com.google.android.googlequicksearchbox',
+            '--splashscreen=skip',
+            '--cuj-config=../../third_party/crossbench/config/team/woa/embedder_cuj_config.hjson',
+            '--probe-config=../../clank/android_webview/tools/crossbench_config/'
+            'agsa_probe_config.hjson',
+            '--repetitions=50',
+        ]
+    ),
 ])
 
 _CHROME_HEALTH_BENCHMARK_CONFIGS_DESKTOP = PerfSuite(
diff --git a/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json
index 1250168..13fe069 100644
--- a/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4_webview-perf-pgo_map.json
@@ -8,7 +8,7 @@
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 46,
+                "end": 43,
                 "abridged": false
             },
             "speedometer2": {
@@ -22,14 +22,11 @@
     "1": {
         "benchmarks": {
             "blink_perf.bindings": {
-                "begin": 46,
+                "begin": 43,
                 "abridged": false
             },
             "blink_perf.css": {
-                "abridged": false
-            },
-            "blink_perf.dom": {
-                "end": 3,
+                "end": 74,
                 "abridged": false
             },
             "speedometer2": {
@@ -42,8 +39,11 @@
     },
     "2": {
         "benchmarks": {
+            "blink_perf.css": {
+                "begin": 74,
+                "abridged": false
+            },
             "blink_perf.dom": {
-                "begin": 3,
                 "abridged": false
             },
             "blink_perf.events": {
@@ -53,7 +53,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 25,
+                "end": 21,
                 "abridged": false
             },
             "speedometer2": {
@@ -67,7 +67,7 @@
     "3": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 25,
+                "begin": 21,
                 "end": 99,
                 "abridged": false
             },
@@ -92,7 +92,10 @@
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
-                "end": 37,
+                "abridged": false
+            },
+            "blink_perf.webaudio": {
+                "end": 1,
                 "abridged": false
             },
             "speedometer2": {
@@ -105,11 +108,8 @@
     },
     "5": {
         "benchmarks": {
-            "blink_perf.shadow_dom": {
-                "begin": 37,
-                "abridged": false
-            },
             "blink_perf.webaudio": {
+                "begin": 1,
                 "abridged": false
             },
             "blink_perf.webcodecs": {
@@ -127,16 +127,6 @@
             "dummy_benchmark.stable_benchmark_1": {
                 "abridged": false
             },
-            "media.mobile": {
-                "abridged": false
-            },
-            "octane": {
-                "abridged": false
-            },
-            "rasterize_and_record_micro.top_25": {
-                "end": 12,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             },
@@ -145,6 +135,17 @@
             }
         },
         "crossbench": {
+            "embedder": {
+                "display_name": "embedder.crossbench",
+                "arguments": [
+                    "--wpr=crossbench_android_embedder_000.wprgo",
+                    "--embedder=com.google.android.googlequicksearchbox",
+                    "--splashscreen=skip",
+                    "--cuj-config=../../third_party/crossbench/config/team/woa/embedder_cuj_config.hjson",
+                    "--probe-config=../../clank/android_webview/tools/crossbench_config/agsa_probe_config.hjson",
+                    "--repetitions=50"
+                ]
+            },
             "loading": {
                 "display_name": "loading.crossbench",
                 "arguments": [
@@ -158,12 +159,17 @@
     },
     "6": {
         "benchmarks": {
+            "media.mobile": {
+                "abridged": false
+            },
+            "octane": {
+                "abridged": false
+            },
             "rasterize_and_record_micro.top_25": {
-                "begin": 12,
                 "abridged": false
             },
             "rendering.mobile": {
-                "end": 46,
+                "end": 38,
                 "abridged": false
             },
             "speedometer2": {
@@ -177,8 +183,8 @@
     "7": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 46,
-                "end": 96,
+                "begin": 38,
+                "end": 88,
                 "abridged": false
             },
             "speedometer2": {
@@ -192,8 +198,8 @@
     "8": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 96,
-                "end": 155,
+                "begin": 88,
+                "end": 145,
                 "abridged": false
             },
             "speedometer2": {
@@ -207,8 +213,8 @@
     "9": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 155,
-                "end": 208,
+                "begin": 145,
+                "end": 200,
                 "abridged": false
             },
             "speedometer2": {
@@ -222,8 +228,8 @@
     "10": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 208,
-                "end": 267,
+                "begin": 200,
+                "end": 263,
                 "abridged": false
             },
             "speedometer2": {
@@ -237,8 +243,8 @@
     "11": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 267,
-                "end": 328,
+                "begin": 263,
+                "end": 324,
                 "abridged": false
             },
             "speedometer2": {
@@ -252,8 +258,8 @@
     "12": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 328,
-                "end": 377,
+                "begin": 324,
+                "end": 372,
                 "abridged": false
             },
             "speedometer2": {
@@ -267,7 +273,7 @@
     "13": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 377,
+                "begin": 372,
                 "abridged": false
             },
             "rendering.mobile.notracing": {
@@ -295,7 +301,7 @@
                 "abridged": false
             },
             "system_health.common_mobile": {
-                "end": 22,
+                "end": 19,
                 "abridged": false
             }
         }
@@ -303,11 +309,11 @@
     "14": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 22,
+                "begin": 19,
                 "abridged": false
             },
             "system_health.memory_mobile": {
-                "end": 3,
+                "end": 1,
                 "abridged": false
             },
             "speedometer2": {
@@ -321,8 +327,8 @@
     "15": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 3,
-                "end": 25,
+                "begin": 1,
+                "end": 24,
                 "abridged": false
             },
             "speedometer2": {
@@ -336,8 +342,8 @@
     "16": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 25,
-                "end": 43,
+                "begin": 24,
+                "end": 42,
                 "abridged": false
             },
             "speedometer2": {
@@ -351,8 +357,8 @@
     "17": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 43,
-                "end": 60,
+                "begin": 42,
+                "end": 59,
                 "abridged": false
             },
             "speedometer2": {
@@ -366,7 +372,7 @@
     "18": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 60,
+                "begin": 59,
                 "abridged": false
             },
             "system_health.webview_startup": {
@@ -400,30 +406,30 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1115,
-        "predicted_min_shard_time": 2510.0,
+        "num_stories": 1117,
+        "predicted_min_shard_time": 2617.0,
         "predicted_min_shard_index": 19,
-        "predicted_max_shard_time": 3490.0,
+        "predicted_max_shard_time": 3606.0,
         "predicted_max_shard_index": 18,
-        "shard #0": 3075.0,
-        "shard #1": 3045.0,
-        "shard #2": 3087.0,
-        "shard #3": 3065.0,
-        "shard #4": 3080.0,
-        "shard #5": 3081.0,
-        "shard #6": 3061.0,
-        "shard #7": 3074.0,
-        "shard #8": 3077.0,
-        "shard #9": 3078.0,
-        "shard #10": 3047.0,
-        "shard #11": 3107.0,
-        "shard #12": 3073.0,
-        "shard #13": 3041.0,
-        "shard #14": 3078.0,
-        "shard #15": 3190.0,
-        "shard #16": 3052.0,
-        "shard #17": 3010.0,
-        "shard #18": 3490.0,
-        "shard #19": 2510.0
+        "shard #0": 3092.0,
+        "shard #1": 3104.0,
+        "shard #2": 3111.0,
+        "shard #3": 3107.0,
+        "shard #4": 3071.0,
+        "shard #5": 3122.0,
+        "shard #6": 3120.0,
+        "shard #7": 3089.0,
+        "shard #8": 3102.0,
+        "shard #9": 3109.0,
+        "shard #10": 3099.0,
+        "shard #11": 3101.0,
+        "shard #12": 3130.0,
+        "shard #13": 3105.0,
+        "shard #14": 3122.0,
+        "shard #15": 3068.0,
+        "shard #16": 3200.0,
+        "shard #17": 3041.0,
+        "shard #18": 3606.0,
+        "shard #19": 2617.0
     }
 }
diff --git a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
index cb1a52a..cf58f02 100644
--- a/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel4_webview-perf_map.json
@@ -8,7 +8,7 @@
                 "abridged": false
             },
             "blink_perf.bindings": {
-                "end": 33,
+                "end": 22,
                 "abridged": false
             },
             "speedometer2": {
@@ -22,11 +22,11 @@
     "1": {
         "benchmarks": {
             "blink_perf.bindings": {
-                "begin": 33,
+                "begin": 22,
                 "abridged": false
             },
             "blink_perf.css": {
-                "end": 61,
+                "end": 56,
                 "abridged": false
             },
             "speedometer2": {
@@ -40,7 +40,7 @@
     "2": {
         "benchmarks": {
             "blink_perf.css": {
-                "begin": 61,
+                "begin": 56,
                 "abridged": false
             },
             "blink_perf.dom": {
@@ -49,10 +49,6 @@
             "blink_perf.events": {
                 "abridged": false
             },
-            "blink_perf.image_decoder": {
-                "end": 2,
-                "abridged": false
-            },
             "speedometer2": {
                 "abridged": false
             },
@@ -64,11 +60,10 @@
     "3": {
         "benchmarks": {
             "blink_perf.image_decoder": {
-                "begin": 2,
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 51,
+                "end": 50,
                 "abridged": false
             },
             "speedometer2": {
@@ -82,7 +77,7 @@
     "4": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 51,
+                "begin": 50,
                 "end": 110,
                 "abridged": false
             },
@@ -107,7 +102,7 @@
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
-                "end": 18,
+                "end": 23,
                 "abridged": false
             },
             "speedometer2": {
@@ -121,7 +116,7 @@
     "6": {
         "benchmarks": {
             "blink_perf.shadow_dom": {
-                "begin": 18,
+                "begin": 23,
                 "abridged": false
             },
             "blink_perf.webaudio": {
@@ -159,9 +154,6 @@
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
-                "abridged": false
-            },
-            "rendering.mobile": {
                 "end": 4,
                 "abridged": false
             },
@@ -173,6 +165,17 @@
             }
         },
         "crossbench": {
+            "embedder": {
+                "display_name": "embedder.crossbench",
+                "arguments": [
+                    "--wpr=crossbench_android_embedder_000.wprgo",
+                    "--embedder=com.google.android.googlequicksearchbox",
+                    "--splashscreen=skip",
+                    "--cuj-config=../../third_party/crossbench/config/team/woa/embedder_cuj_config.hjson",
+                    "--probe-config=../../clank/android_webview/tools/crossbench_config/agsa_probe_config.hjson",
+                    "--repetitions=50"
+                ]
+            },
             "loading": {
                 "display_name": "loading.crossbench",
                 "arguments": [
@@ -186,9 +189,12 @@
     },
     "8": {
         "benchmarks": {
-            "rendering.mobile": {
+            "rasterize_and_record_micro.top_25": {
                 "begin": 4,
-                "end": 50,
+                "abridged": false
+            },
+            "rendering.mobile": {
+                "end": 33,
                 "abridged": false
             },
             "speedometer2": {
@@ -202,8 +208,8 @@
     "9": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 50,
-                "end": 99,
+                "begin": 33,
+                "end": 84,
                 "abridged": false
             },
             "speedometer2": {
@@ -217,8 +223,8 @@
     "10": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 99,
-                "end": 144,
+                "begin": 84,
+                "end": 132,
                 "abridged": false
             },
             "speedometer2": {
@@ -232,8 +238,8 @@
     "11": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 144,
-                "end": 189,
+                "begin": 132,
+                "end": 180,
                 "abridged": false
             },
             "speedometer2": {
@@ -247,8 +253,8 @@
     "12": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 189,
-                "end": 247,
+                "begin": 180,
+                "end": 241,
                 "abridged": false
             },
             "speedometer2": {
@@ -262,8 +268,8 @@
     "13": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 247,
-                "end": 317,
+                "begin": 241,
+                "end": 314,
                 "abridged": false
             },
             "speedometer2": {
@@ -277,8 +283,8 @@
     "14": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 317,
-                "end": 365,
+                "begin": 314,
+                "end": 363,
                 "abridged": false
             },
             "speedometer2": {
@@ -292,7 +298,7 @@
     "15": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 365,
+                "begin": 363,
                 "abridged": false
             },
             "rendering.mobile.notracing": {
@@ -312,6 +318,13 @@
             },
             "speedometer3": {
                 "abridged": false
+            }
+        }
+    },
+    "16": {
+        "benchmarks": {
+            "speedometer3": {
+                "abridged": false
             },
             "speedometer3-future": {
                 "abridged": false
@@ -320,34 +333,22 @@
                 "abridged": false
             },
             "system_health.common_mobile": {
-                "end": 5,
-                "abridged": false
-            }
-        }
-    },
-    "16": {
-        "benchmarks": {
-            "system_health.common_mobile": {
-                "begin": 5,
-                "end": 62,
+                "end": 52,
                 "abridged": false
             },
             "speedometer2": {
                 "abridged": false
-            },
-            "speedometer3": {
-                "abridged": false
             }
         }
     },
     "17": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 62,
+                "begin": 52,
                 "abridged": false
             },
             "system_health.memory_mobile": {
-                "end": 13,
+                "end": 9,
                 "abridged": false
             },
             "speedometer2": {
@@ -361,8 +362,8 @@
     "18": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 13,
-                "end": 26,
+                "begin": 9,
+                "end": 24,
                 "abridged": false
             },
             "speedometer2": {
@@ -376,8 +377,8 @@
     "19": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 26,
-                "end": 45,
+                "begin": 24,
+                "end": 41,
                 "abridged": false
             },
             "speedometer2": {
@@ -391,8 +392,8 @@
     "20": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 45,
-                "end": 62,
+                "begin": 41,
+                "end": 59,
                 "abridged": false
             }
         }
@@ -400,16 +401,16 @@
     "21": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 62,
-                "abridged": false
-            },
-            "system_health.webview_startup": {
+                "begin": 59,
                 "abridged": false
             }
         }
     },
     "22": {
         "benchmarks": {
+            "system_health.webview_startup": {
+                "abridged": false
+            },
             "v8.browsing_mobile": {
                 "abridged": false
             },
@@ -422,33 +423,33 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1115,
-        "predicted_min_shard_time": 1814.0,
-        "predicted_min_shard_index": 22,
-        "predicted_max_shard_time": 2327.0,
-        "predicted_max_shard_index": 21,
-        "shard #0": 2113.0,
-        "shard #1": 2116.0,
-        "shard #2": 2124.0,
-        "shard #3": 2137.0,
-        "shard #4": 2138.0,
-        "shard #5": 2114.0,
-        "shard #6": 1998.0,
-        "shard #7": 2100.0,
-        "shard #8": 2136.0,
-        "shard #9": 2152.0,
-        "shard #10": 2125.0,
-        "shard #11": 2125.0,
-        "shard #12": 2130.0,
-        "shard #13": 2150.0,
-        "shard #14": 2131.0,
-        "shard #15": 2120.0,
-        "shard #16": 2139.0,
-        "shard #17": 2156.0,
-        "shard #18": 2199.0,
-        "shard #19": 2166.0,
-        "shard #20": 2139.0,
-        "shard #21": 2327.0,
-        "shard #22": 1814.0
+        "num_stories": 1117,
+        "predicted_min_shard_time": 1925.0,
+        "predicted_min_shard_index": 6,
+        "predicted_max_shard_time": 2577.0,
+        "predicted_max_shard_index": 22,
+        "shard #0": 2241.0,
+        "shard #1": 2219.0,
+        "shard #2": 2230.0,
+        "shard #3": 2223.0,
+        "shard #4": 2213.0,
+        "shard #5": 2230.0,
+        "shard #6": 1925.0,
+        "shard #7": 2239.0,
+        "shard #8": 2250.0,
+        "shard #9": 2257.0,
+        "shard #10": 2236.0,
+        "shard #11": 2234.0,
+        "shard #12": 2252.0,
+        "shard #13": 2238.0,
+        "shard #14": 2237.0,
+        "shard #15": 2256.0,
+        "shard #16": 2243.0,
+        "shard #17": 2210.0,
+        "shard #18": 2167.0,
+        "shard #19": 2215.0,
+        "shard #20": 2346.0,
+        "shard #21": 1935.0,
+        "shard #22": 2577.0
     }
 }
diff --git a/tools/perf/crossbench_result_converter.py b/tools/perf/crossbench_result_converter.py
index ddfb087..3203498 100755
--- a/tools/perf/crossbench_result_converter.py
+++ b/tools/perf/crossbench_result_converter.py
@@ -12,7 +12,7 @@
 import json
 import pathlib
 import sys
-from typing import Optional
+from typing import List, Optional
 
 tracing_dir = (pathlib.Path(__file__).absolute().parents[2] /
                'third_party/catapult/tracing')
@@ -22,7 +22,7 @@
 from tracing.value.diagnostics import reserved_infos
 
 
-def _get_crossbench_json_path(out_dir: pathlib.Path) -> pathlib.Path:
+def _get_crossbench_json_paths(out_dir: pathlib.Path) -> List[pathlib.Path]:
   """Given a crossbench output directory, find the result json file.
 
   Args:
@@ -30,7 +30,7 @@
         as --out-dir to crossbench.
 
   Returns:
-    Path to the result json file created by crossbench.
+    A list of paths to the result json files created by crossbench probes.
   """
 
   if not out_dir.exists():
@@ -55,7 +55,7 @@
   browser_info = list(browsers.values())[0]
   debug_info += f'browser_info={browser_info}\n'
 
-  probe_json_path = None
+  probe_json_paths = []
   try:
     for probe, probe_data in browser_info.get('probes', {}).items():
       if probe.startswith('cb.') or not probe_data:
@@ -65,19 +65,15 @@
         raise ValueError(f'Probe {probe} generated multiple json files, '
                          f'debug_info={debug_info}')
       if len(candidates) == 1:
-        if probe_json_path:
-          raise ValueError(
-              f'Multiple output json files found in {cb_results_json_path}, '
-              f'debug_info={debug_info}')
-        probe_json_path = pathlib.Path(candidates[0])
+        probe_json_paths.append(pathlib.Path(candidates[0]))
   except AttributeError as e:
     raise AttributeError(f'debug_info={debug_info}') from e
 
-  if not probe_json_path:
+  if not probe_json_paths:
     raise ValueError(f'No output json file found in {cb_results_json_path}, '
                      f'debug_info={debug_info}')
 
-  return probe_json_path
+  return probe_json_paths
 
 
 def convert(crossbench_out_dir: pathlib.Path,
@@ -94,9 +90,11 @@
     _loadline(crossbench_out_dir, out_filename, benchmark, results_label)
     return
 
-  crossbench_json_filename = _get_crossbench_json_path(crossbench_out_dir)
-  with crossbench_json_filename.open() as f:
-    crossbench_result = json.load(f)
+  crossbench_json_filenames = _get_crossbench_json_paths(crossbench_out_dir)
+  crossbench_result = {}
+  for filename in crossbench_json_filenames:
+    with filename.open() as f:
+      crossbench_result.update(json.load(f))
 
   results = histogram_set.HistogramSet()
   for key, value in crossbench_result.items():
diff --git a/tools/perf/crossbench_result_converter_unittest.py b/tools/perf/crossbench_result_converter_unittest.py
index d1a1bde..cd8ffe8 100644
--- a/tools/perf/crossbench_result_converter_unittest.py
+++ b/tools/perf/crossbench_result_converter_unittest.py
@@ -46,10 +46,10 @@
   def process_crossbench_result(self, subdir):
     return self.list_to_dict(self.call_converter(subdir))
 
-  def check_result(self, result, metric, value, unit):
+  def check_result(self, result, metric, value, unit, sample_size=1):
     sample_values = result[metric]['sampleValues']
     self.assertIsInstance(sample_values, list)
-    self.assertEqual(len(sample_values), 1)
+    self.assertEqual(len(sample_values), sample_size)
     self.assertAlmostEqual(sample_values[0], value)
     self.assertEqual(result[metric]['unit'], unit)
 
@@ -88,6 +88,13 @@
     self.check_result(result, 'TodoMVC-jQuery', 259.5599999189377,
                       'ms_smallerIsBetter')
 
+  def test_embedder(self):
+    result = self.process_crossbench_result('embedder')
+    self.check_result(result, 'Some.Histogram1_mean', 165.0,
+                      'ms_smallerIsBetter', sample_size=50)
+    self.check_result(result, 'MetricFoo', 207,
+                      'ms_smallerIsBetter', sample_size=50)
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/tools/perf/testdata/crossbench_output/embedder/cb.results.json b/tools/perf/testdata/crossbench_output/embedder/cb.results.json
new file mode 100644
index 0000000..ed33207
--- /dev/null
+++ b/tools/perf/testdata/crossbench_output/embedder/cb.results.json
@@ -0,0 +1,79 @@
+{
+  "cwd": "/example/path",
+  "browsers": {
+    "chrome_v139_android.arm64.remote_0": {
+      "cwd": "/example/path/chrome_v139_android.arm64.remote_0/stories",
+      "probes": {
+        "embedder": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/embedder.json"
+          ],
+          "csv": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/embedder.csv"
+          ]
+        },
+        "chrome_histograms": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/chrome_histograms.json"
+          ],
+          "csv": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/chrome_histograms.csv"
+          ]
+        },
+        "cb.thermal_monitor": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/cb.thermal_monitor.json"
+          ]
+        },
+        "cb.system.details": null,
+        "cb.log": null,
+        "cb.errors": null,
+        "cb.durations": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/cb.durations.json"
+          ]
+        }
+      },
+      "errors": []
+    }
+  },
+  "probes": {
+    "embedder": {
+      "json": [
+        "/example/path/embedder.json"
+      ],
+      "csv": [
+        "/example/path/embedder.csv"
+      ],
+      "xlsx": [
+        "/example/path/embedder.xlsx"
+      ]
+    },
+    "chrome_histograms": {
+      "json": [
+        "/example/path/chrome_histograms.json"
+      ],
+      "csv": [
+        "/example/path/chrome_histograms.csv"
+      ],
+      "xlsx": [
+        "/example/path/chrome_histograms.xlsx"
+      ]
+    },
+    "cb.thermal_monitor": {
+      "json": [
+        "/example/path/cb.thermal_monitor.json"
+      ]
+    },
+    "cb.system.details": null,
+    "cb.log": null,
+    "cb.errors": null,
+    "cb.durations": {
+      "json": [
+        "/example/path/cb.durations.json"
+      ]
+    }
+  },
+  "success": true,
+  "errors": []
+}
\ No newline at end of file
diff --git a/tools/perf/testdata/crossbench_output/embedder/chrome_histograms.json b/tools/perf/testdata/crossbench_output/embedder/chrome_histograms.json
new file mode 100644
index 0000000..01dc6a2
--- /dev/null
+++ b/tools/perf/testdata/crossbench_output/embedder/chrome_histograms.json
@@ -0,0 +1,142 @@
+{
+  "chrome_v139_android.arm64.remote_0": {
+    "info": {
+      "label": "android.arm64.remote_0",
+      "browser": "Chrome",
+      "version": "139.0.7251.0",
+      "major_version": 139,
+      "channel": "stable",
+      "os": "android 11 arm64",
+      "device": "Pixel 4",
+      "cpu": "cortex-a76 msmnile 8 cores",
+      "binary": "com.google.android.googlequicksearchbox",
+      "flags": "--no-default-browser-check --disable-component-update --disable-sync --no-first-run --disable-search-engine-choice-screen --disable-background-timer-throttling --disable-renderer-backgrounding --disable-field-trial-config --enable-features=DisablePrivacySandboxPrompts --disable-extensions",
+      "runs": 50,
+      "failed runs": 0
+    },
+    "data": {
+      "Some.Histogram1_mean": {
+        "values": [
+          165.0,
+          260.0,
+          233.0,
+          217.0,
+          203.0,
+          230.0,
+          238.0,
+          219.0,
+          256.0,
+          229.0,
+          253.0,
+          223.0,
+          267.0,
+          216.0,
+          247.0,
+          219.0,
+          219.0,
+          245.0,
+          228.0,
+          253.0,
+          246.0,
+          219.0,
+          224.0,
+          259.0,
+          249.0,
+          246.0,
+          234.0,
+          251.0,
+          217.0,
+          134.0,
+          253.0,
+          263.0,
+          222.0,
+          251.0,
+          245.0,
+          256.0,
+          289.0,
+          218.0,
+          222.0,
+          256.0,
+          245.0,
+          260.0,
+          261.0,
+          251.0,
+          262.0,
+          269.0,
+          248.0,
+          213.0,
+          256.0,
+          260.0
+        ],
+        "min": 134.0,
+        "average": 237.98,
+        "geomean": 236.3340412079503,
+        "max": 289.0,
+        "sum": 11899.0,
+        "stddev": 26.149093713565225,
+        "stddevPercent": 10.98793752145778
+      },
+      "Other.Histogram2_mean": {
+        "values": [
+          21.0,
+          13.0,
+          17.0,
+          13.0,
+          16.0,
+          19.0,
+          21.0,
+          13.0,
+          14.0,
+          14.0,
+          19.0,
+          16.0,
+          18.0,
+          15.0,
+          15.0,
+          15.0,
+          21.0,
+          13.0,
+          14.0,
+          20.0,
+          16.0,
+          14.0,
+          15.0,
+          15.0,
+          13.0,
+          14.0,
+          15.0,
+          12.0,
+          15.0,
+          15.0,
+          16.0,
+          14.0,
+          15.0,
+          19.0,
+          16.0,
+          17.0,
+          16.0,
+          18.0,
+          15.0,
+          13.0,
+          11.0,
+          15.0,
+          14.0,
+          13.0,
+          16.0,
+          16.0,
+          16.0,
+          13.0,
+          15.0,
+          13.0
+        ],
+        "min": 11.0,
+        "average": 15.44,
+        "geomean": 15.271810862762067,
+        "max": 21.0,
+        "sum": 772.0,
+        "stddev": 2.366086927172959,
+        "stddevPercent": 15.324397196716056
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/cb.results.json b/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/cb.results.json
new file mode 100644
index 0000000..4f7f288
--- /dev/null
+++ b/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/cb.results.json
@@ -0,0 +1,115 @@
+{
+  "cwd": "/example/path/chrome_v139_android.arm64.remote_0/stories",
+  "browser": {
+    "label": "android.arm64.remote_0",
+    "browser": "chrome",
+    "unique_name": "chrome_v139_android.arm64.remote_0",
+    "app_name": "chrome",
+    "version": "139.0.7251.0",
+    "channel": "stable",
+    "flags": [
+      "--no-default-browser-check",
+      "--disable-component-update",
+      "--disable-sync",
+      "--no-first-run",
+      "--disable-search-engine-choice-screen",
+      "--disable-background-timer-throttling",
+      "--disable-renderer-backgrounding",
+      "--disable-field-trial-config",
+      "--enable-features=DisablePrivacySandboxPrompts",
+      "--disable-extensions"
+    ],
+    "js_flags": [],
+    "path": "com.google.android.googlequicksearchbox",
+    "clear_cache_dir": true,
+    "major_version": 139
+  },
+  "stories": {
+    "agsa-simple": {
+      "cwd": "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple",
+      "duration": 30.0,
+      "probes": {
+        "embedder": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple/embedder.json"
+          ],
+          "csv": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple/embedder.csv"
+          ]
+        },
+        "chrome_histograms": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple/chrome_histograms.json"
+          ],
+          "csv": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple/chrome_histograms.csv"
+          ]
+        },
+        "cb.thermal_monitor": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple/cb.thermal_monitor.json"
+          ]
+        },
+        "cb.system.details": null,
+        "cb.log": null,
+        "cb.errors": null,
+        "cb.durations": {
+          "json": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple/cb.durations.json"
+          ],
+          "csv": [
+            "/example/path/chrome_v139_android.arm64.remote_0/stories/agsa-simple/cb.durations.csv"
+          ]
+        }
+      },
+      "errors": []
+    }
+  },
+  "group": {
+    "label": "android.arm64.remote_0",
+    "browser": "Chrome",
+    "version": "139.0.7251.0",
+    "major_version": 139,
+    "channel": "stable",
+    "os": "android 11 arm64",
+    "device": "Pixel 4",
+    "cpu": "cortex-a76 msmnile 8 cores",
+    "binary": "com.google.android.googlequicksearchbox",
+    "flags": "--no-default-browser-check --disable-component-update --disable-sync --no-first-run --disable-search-engine-choice-screen --disable-background-timer-throttling --disable-renderer-backgrounding --disable-field-trial-config --enable-features=DisablePrivacySandboxPrompts --disable-extensions",
+    "runs": 50,
+    "failed runs": 0
+  },
+  "probes": {
+    "embedder": {
+      "json": [
+        "/example/path/chrome_v139_android.arm64.remote_0/stories/embedder.json"
+      ],
+      "csv": [
+        "/example/path/chrome_v139_android.arm64.remote_0/stories/embedder.csv"
+      ]
+    },
+    "chrome_histograms": {
+      "json": [
+        "/example/path/chrome_v139_android.arm64.remote_0/stories/chrome_histograms.json"
+      ],
+      "csv": [
+        "/example/path/chrome_v139_android.arm64.remote_0/stories/chrome_histograms.csv"
+      ]
+    },
+    "cb.thermal_monitor": {
+      "json": [
+        "/example/path/chrome_v139_android.arm64.remote_0/stories/cb.thermal_monitor.json"
+      ]
+    },
+    "cb.system.details": null,
+    "cb.log": null,
+    "cb.errors": null,
+    "cb.durations": {
+      "json": [
+        "/example/path/chrome_v139_android.arm64.remote_0/stories/cb.durations.json"
+      ]
+    }
+  },
+  "success": true,
+  "errors": []
+}
\ No newline at end of file
diff --git a/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/chrome_histograms.json b/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/chrome_histograms.json
new file mode 100644
index 0000000..55cbfaf0
--- /dev/null
+++ b/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/chrome_histograms.json
@@ -0,0 +1,124 @@
+{
+  "Some.Histogram1_mean": {
+    "values": [
+      165.0,
+      260.0,
+      233.0,
+      217.0,
+      203.0,
+      230.0,
+      238.0,
+      219.0,
+      256.0,
+      229.0,
+      253.0,
+      223.0,
+      267.0,
+      216.0,
+      247.0,
+      219.0,
+      219.0,
+      245.0,
+      228.0,
+      253.0,
+      246.0,
+      219.0,
+      224.0,
+      259.0,
+      249.0,
+      246.0,
+      234.0,
+      251.0,
+      217.0,
+      134.0,
+      253.0,
+      263.0,
+      222.0,
+      251.0,
+      245.0,
+      256.0,
+      289.0,
+      218.0,
+      222.0,
+      256.0,
+      245.0,
+      260.0,
+      261.0,
+      251.0,
+      262.0,
+      269.0,
+      248.0,
+      213.0,
+      256.0,
+      260.0
+    ],
+    "min": 134.0,
+    "average": 237.98,
+    "geomean": 236.3340412079503,
+    "max": 289.0,
+    "sum": 11899.0,
+    "stddev": 26.149093713565225,
+    "stddevPercent": 10.98793752145778
+  },
+  "Other.Histogram2_mean": {
+    "values": [
+      21.0,
+      13.0,
+      17.0,
+      13.0,
+      16.0,
+      19.0,
+      21.0,
+      13.0,
+      14.0,
+      14.0,
+      19.0,
+      16.0,
+      18.0,
+      15.0,
+      15.0,
+      15.0,
+      21.0,
+      13.0,
+      14.0,
+      20.0,
+      16.0,
+      14.0,
+      15.0,
+      15.0,
+      13.0,
+      14.0,
+      15.0,
+      12.0,
+      15.0,
+      15.0,
+      16.0,
+      14.0,
+      15.0,
+      19.0,
+      16.0,
+      17.0,
+      16.0,
+      18.0,
+      15.0,
+      13.0,
+      11.0,
+      15.0,
+      14.0,
+      13.0,
+      16.0,
+      16.0,
+      16.0,
+      13.0,
+      15.0,
+      13.0
+    ],
+    "min": 11.0,
+    "average": 15.44,
+    "geomean": 15.271810862762067,
+    "max": 21.0,
+    "sum": 772.0,
+    "stddev": 2.366086927172959,
+    "stddevPercent": 15.324397196716056
+  }
+}
\ No newline at end of file
diff --git a/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/embedder.json b/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/embedder.json
new file mode 100644
index 0000000..7e6b9c4
--- /dev/null
+++ b/tools/perf/testdata/crossbench_output/embedder/chrome_v139_android.arm64.remote_0/stories/embedder.json
@@ -0,0 +1,124 @@
+{
+  "MetricFoo": {
+    "values": [
+      207,
+      305,
+      285,
+      267,
+      240,
+      266,
+      279,
+      269,
+      296,
+      267,
+      303,
+      261,
+      306,
+      251,
+      302,
+      259,
+      256,
+      281,
+      268,
+      291,
+      283,
+      257,
+      263,
+      296,
+      292,
+      283,
+      279,
+      288,
+      251,
+      174,
+      303,
+      307,
+      267,
+      290,
+      292,
+      297,
+      326,
+      256,
+      273,
+      292,
+      284,
+      304,
+      304,
+      309,
+      301,
+      305,
+      293,
+      271,
+      303,
+      299
+    ],
+    "min": 174,
+    "average": 280.02,
+    "geomean": 278.5924527064356,
+    "max": 326,
+    "sum": 14001,
+    "stddev": 26.76541325925635,
+    "stddevPercent": 9.558393421632866
+  },
+  "MetricBar": {
+    "values": [
+      21,
+      13,
+      17,
+      13,
+      16,
+      19,
+      21,
+      13,
+      14,
+      14,
+      19,
+      16,
+      18,
+      15,
+      15,
+      15,
+      21,
+      13,
+      14,
+      20,
+      16,
+      14,
+      15,
+      15,
+      13,
+      14,
+      15,
+      12,
+      15,
+      15,
+      16,
+      14,
+      15,
+      19,
+      16,
+      17,
+      16,
+      18,
+      15,
+      13,
+      11,
+      15,
+      14,
+      13,
+      16,
+      16,
+      16,
+      13,
+      15,
+      13
+    ],
+    "min": 11,
+    "average": 15.44,
+    "geomean": 15.271810862762067,
+    "max": 21,
+    "sum": 772,
+    "stddev": 2.366086927172959,
+    "stddevPercent": 15.324397196716056
+  }
+}
\ No newline at end of file
diff --git a/tools/perf/testdata/crossbench_output/embedder/embedder.json b/tools/perf/testdata/crossbench_output/embedder/embedder.json
new file mode 100644
index 0000000..74860e2
--- /dev/null
+++ b/tools/perf/testdata/crossbench_output/embedder/embedder.json
@@ -0,0 +1,142 @@
+{
+  "chrome_v139_android.arm64.remote_0": {
+    "info": {
+      "label": "android.arm64.remote_0",
+      "browser": "Chrome",
+      "version": "139.0.7251.0",
+      "major_version": 139,
+      "channel": "stable",
+      "os": "android 11 arm64",
+      "device": "Pixel 4",
+      "cpu": "cortex-a76 msmnile 8 cores",
+      "binary": "com.google.android.googlequicksearchbox",
+      "flags": "--no-default-browser-check --disable-component-update --disable-sync --no-first-run --disable-search-engine-choice-screen --disable-background-timer-throttling --disable-renderer-backgrounding --disable-field-trial-config --enable-features=DisablePrivacySandboxPrompts --disable-extensions",
+      "runs": 50,
+      "failed runs": 0
+    },
+    "data": {
+      "MetricFoo": {
+        "values": [
+          207,
+          305,
+          285,
+          267,
+          240,
+          266,
+          279,
+          269,
+          296,
+          267,
+          303,
+          261,
+          306,
+          251,
+          302,
+          259,
+          256,
+          281,
+          268,
+          291,
+          283,
+          257,
+          263,
+          296,
+          292,
+          283,
+          279,
+          288,
+          251,
+          174,
+          303,
+          307,
+          267,
+          290,
+          292,
+          297,
+          326,
+          256,
+          273,
+          292,
+          284,
+          304,
+          304,
+          309,
+          301,
+          305,
+          293,
+          271,
+          303,
+          299
+        ],
+        "min": 174,
+        "average": 280.02,
+        "geomean": 278.5924527064356,
+        "max": 326,
+        "sum": 14001,
+        "stddev": 26.76541325925635,
+        "stddevPercent": 9.558393421632866
+      },
+      "MetricBar": {
+        "values": [
+          21,
+          13,
+          17,
+          13,
+          16,
+          19,
+          21,
+          13,
+          14,
+          14,
+          19,
+          16,
+          18,
+          15,
+          15,
+          15,
+          21,
+          13,
+          14,
+          20,
+          16,
+          14,
+          15,
+          15,
+          13,
+          14,
+          15,
+          12,
+          15,
+          15,
+          16,
+          14,
+          15,
+          19,
+          16,
+          17,
+          16,
+          18,
+          15,
+          13,
+          11,
+          15,
+          14,
+          13,
+          16,
+          16,
+          16,
+          13,
+          15,
+          13
+        ],
+        "min": 11,
+        "average": 15.44,
+        "geomean": 15.271810862762067,
+        "max": 21,
+        "sum": 772,
+        "stddev": 2.366086927172959,
+        "stddevPercent": 15.324397196716056
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/v8 b/v8
index 9541b18..4d7c9f1 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 9541b18ce700cdef8cce07fef49f1ac9d74a1dd2
+Subproject commit 4d7c9f183f3c41ac42eb0d622a4d8e58a0982693